Skip to content

Commit 0c827c1

Browse files
committed
Support Rack::Sendfile middleware
1 parent 223aa8d commit 0c827c1

File tree

7 files changed

+100
-1
lines changed

7 files changed

+100
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* [#1252](https://github.com/ruby-grape/grape/pull/1252): Allow default to be a subset or equal to allowed values without raising IncompatibleOptionValues - [@jeradphelps](https://github.com/jeradphelps).
1212
* [#1255](https://github.com/ruby-grape/grape/pull/1255): Allow param type definition in `route_param` - [@namusyaka](https://github.com/namusyaka).
1313
* [#1257](https://github.com/ruby-grape/grape/pull/1257): Allow Proc, Symbol or String in `rescue_from with: ...` - [@namusyaka](https://github.com/namusyaka).
14+
* [#1280](https://github.com/ruby-grape/grape/pull/1280): Support `Rack::Sendfile` middleware - [@lfidnl](https://github.com/lfidnl).
1415
* [#1285](https://github.com/ruby-grape/grape/pull/1285): Add a warning for errors appearing in `after` callbacks - [@gregormelhorn](https://github.com/gregormelhorn).
1516
* Your contribution here.
1617

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2328,6 +2328,30 @@ class API < Grape::API
23282328
end
23292329
```
23302330
2331+
If you want to take advantage of `Rack::Sendfile`, which intercepts responses whose body is
2332+
being served from a file and replaces it with a server specific X-Sendfile header, specify `to_path`
2333+
method in your file streamer class which returns path of served file:
2334+
2335+
```ruby
2336+
class FileStreamer
2337+
# ...
2338+
2339+
def to_path
2340+
@file_path
2341+
end
2342+
2343+
# ...
2344+
end
2345+
```
2346+
2347+
Note: don't forget turn on `Rack::Sendfile` middleware in your API:
2348+
2349+
```ruby
2350+
class API < Grape::API
2351+
use Rack::Sendfile
2352+
end
2353+
```
2354+
23312355
## Authentication
23322356

23332357
### Basic and Digest Auth

lib/grape.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ module Util
131131
autoload :InheritableSetting
132132
autoload :StrictHashConfiguration
133133
autoload :FileResponse
134+
autoload :SendfileResponse
134135
end
135136

136137
module DSL

lib/grape/middleware/formatter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def build_formatted_response(status, headers, bodies)
3535
headers = ensure_content_type(headers)
3636

3737
if bodies.is_a?(Grape::Util::FileResponse)
38-
Rack::Response.new([], status, headers) do |resp|
38+
Grape::Util::SendfileResponse.new([], status, headers) do |resp|
3939
resp.body = bodies.file
4040
end
4141
else

lib/grape/util/sendfile_response.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module Grape
2+
module Util
3+
# Response should respond to to_path method
4+
# for using Rack::SendFile middleware
5+
class SendfileResponse < Rack::Response
6+
def respond_to?(method_name, include_all = false)
7+
if method_name == :to_path
8+
@body.respond_to?(:to_path, include_all)
9+
else
10+
super
11+
end
12+
end
13+
14+
def to_path
15+
@body.to_path
16+
end
17+
end
18+
end
19+
end
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
require 'spec_helper'
2+
3+
describe Rack::Sendfile do
4+
subject do
5+
send_file = file_streamer
6+
app = Class.new(Grape::API) do
7+
use Rack::Sendfile
8+
format :json
9+
get do
10+
file send_file
11+
end
12+
end
13+
14+
options = {
15+
method: 'GET',
16+
'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect',
17+
'HTTP_X_ACCEL_MAPPING' => '/accel/mapping/=/replaced/'
18+
}
19+
env = Rack::MockRequest.env_for('/', options)
20+
app.call(env)
21+
end
22+
23+
context do
24+
let(:file_streamer) do
25+
double(:file_streamer, to_path: '/accel/mapping/some/path')
26+
end
27+
28+
it 'contains Sendfile headers' do
29+
headers = subject[1]
30+
expect(headers).to include('X-Accel-Redirect')
31+
end
32+
end
33+
34+
context do
35+
let(:file_streamer) do
36+
double(:file_streamer)
37+
end
38+
39+
it 'not contains Sendfile headers' do
40+
headers = subject[1]
41+
expect(headers).to_not include('X-Accel-Redirect')
42+
end
43+
end
44+
end

spec/grape/middleware/formatter_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,14 @@ def to_xml
282282
end
283283
end
284284
end
285+
286+
context 'send file' do
287+
let(:app) { ->(_env) { [200, {}, @body] } }
288+
289+
it 'returns Grape::Uril::SendFileReponse' do
290+
@body = Grape::Util::FileResponse.new('file')
291+
env = { 'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json' }
292+
expect(subject.call(env)).to be_a(Grape::Util::SendfileResponse)
293+
end
294+
end
285295
end

0 commit comments

Comments
 (0)