From 5ecb7c7442d3ae7e09a0b85d83e4a3e3ce4bee73 Mon Sep 17 00:00:00 2001 From: Stuart Chinery <163900+schinery@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:06:08 +0100 Subject: [PATCH] Other changes needed to support both Rack 2 and 3 (#1) --- UPGRADING.md | 10 ++++ lib/grape/dsl/inside_route.rb | 8 +-- lib/grape/endpoint.rb | 2 +- lib/grape/http/headers.rb | 18 +++++-- lib/grape/request.rb | 6 ++- spec/grape/api/custom_validations_spec.rb | 12 +++-- spec/grape/api_spec.rb | 52 +++++++++---------- spec/grape/dsl/inside_route_spec.rb | 44 ++++++++-------- spec/grape/endpoint_spec.rb | 6 +-- .../exceptions/invalid_accept_header_spec.rb | 4 +- .../versioner/accept_version_header_spec.rb | 6 +-- .../grape/middleware/versioner/header_spec.rb | 12 ++--- spec/grape/request_spec.rb | 4 +- spec/support/headers_helpers.rb | 36 ++++++++++--- 14 files changed, 137 insertions(+), 83 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index f7c143133f..fbf23c4a0d 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -7,12 +7,22 @@ Upgrading Grape As per [rack/rack#1592](https://github.com/rack/rack/issues/1592) Rack 3.0 is enforcing the HTTP/2 semantics, and thus treats all headers as lowercase. Starting with Grape 1.9.0, the following headers are now lowercase: +* `allow` +* `cache-control` +* `content-length` * `content-type` +* `location` +* `transfer-encoding` * `x-cascade` For Rack < 3 the following response headers are returned using HTTP/1 semantics, like so: +* `Allow` +* `Cache-Control` +* `Content-Length` * `Content-Type` +* `Location` +* `Transfer-Encoding` * `X-Cascade` See [#2355](https://github.com/ruby-grape/grape/pull/2355) for more information. diff --git a/lib/grape/dsl/inside_route.rb b/lib/grape/dsl/inside_route.rb index 7023d69dd9..e2bc9169ad 100644 --- a/lib/grape/dsl/inside_route.rb +++ b/lib/grape/dsl/inside_route.rb @@ -185,7 +185,7 @@ def redirect(url, permanent: false, body: nil, **_options) status 302 body_message ||= "This resource has been moved temporarily to #{url}." end - header 'Location', url + header Grape::Http::Headers::LOCATION, url content_type 'text/plain' body body_message end @@ -328,9 +328,9 @@ def sendfile(value = nil) def stream(value = nil) return if value.nil? && @stream.nil? - header 'Content-Length', nil - header 'Transfer-Encoding', nil - header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front) + header Grape::Http::Headers::CONTENT_LENGTH, nil + header Grape::Http::Headers::TRANSFER_ENCODING, nil + header Grape::Http::Headers::CACHE_CONTROL, 'no-cache' # Skips ETag generation (reading the response up front) if value.is_a?(String) file_body = Grape::ServeStream::FileBody.new(value) @stream = Grape::ServeStream::StreamResponse.new(file_body) diff --git a/lib/grape/endpoint.rb b/lib/grape/endpoint.rb index 8dfe915bce..663a3e8f1d 100644 --- a/lib/grape/endpoint.rb +++ b/lib/grape/endpoint.rb @@ -250,7 +250,7 @@ def run if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS]) raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allowed_methods)) unless options? - header 'Allow', allowed_methods + header Grape::Http::Headers::ALLOW, allowed_methods response_object = '' status 204 else diff --git a/lib/grape/http/headers.rb b/lib/grape/http/headers.rb index 314334b0ae..3014ccff0d 100644 --- a/lib/grape/http/headers.rb +++ b/lib/grape/http/headers.rb @@ -12,11 +12,21 @@ module Headers QUERY_STRING = 'QUERY_STRING' if Gem::Version.new(Rack.release) < Gem::Version.new('3') - CONTENT_TYPE = 'Content-Type' - X_CASCADE = 'X-Cascade' + ALLOW = 'Allow' + CACHE_CONTROL = 'Cache-Control' + CONTENT_LENGTH = 'Content-Length' + CONTENT_TYPE = 'Content-Type' + LOCATION = 'Location' + TRANSFER_ENCODING = 'Transfer-Encoding' + X_CASCADE = 'X-Cascade' else - CONTENT_TYPE = 'content-type' - X_CASCADE = 'x-cascade' + ALLOW = 'allow' + CACHE_CONTROL = 'cache-control' + CONTENT_LENGTH = 'content-length' + CONTENT_TYPE = 'content-type' + LOCATION = 'location' + TRANSFER_ENCODING = 'transfer-encoding' + X_CASCADE = 'x-cascade' end GET = 'GET' diff --git a/lib/grape/request.rb b/lib/grape/request.rb index 10fd28cc62..669f04f413 100644 --- a/lib/grape/request.rb +++ b/lib/grape/request.rb @@ -47,7 +47,11 @@ def build_headers end def transform_header(header) - -header[5..].split('_').each(&:capitalize!).join('-') + if Gem::Version.new(Rack.release) < Gem::Version.new('3') + -header[5..].split('_').map(&:capitalize).join('-') + else + -header[5..].tr('_', '-').downcase + end end end end diff --git a/spec/grape/api/custom_validations_spec.rb b/spec/grape/api/custom_validations_spec.rb index bfb821de86..4023a4dcac 100644 --- a/spec/grape/api/custom_validations_spec.rb +++ b/spec/grape/api/custom_validations_spec.rb @@ -162,9 +162,15 @@ def validate(request) return unless request.params.key? @attrs.first # check if admin flag is set to true return unless @option + # check if user is admin or not # as an example get a token from request and check if it's admin or not - raise Grape::Exceptions::Validation.new(params: @attrs, message: 'Can not set Admin only field.') unless request.headers['X-Access-Token'] == 'admin' + header = if Gem::Version.new(Rack.release) < Gem::Version.new('3') + 'X-Access-Token' + else + 'x-access-token' + end + raise Grape::Exceptions::Validation.new(params: @attrs, message: 'Can not set Admin only field.') unless request.headers[header] == 'admin' end end end @@ -197,14 +203,14 @@ def validate(request) end it 'does not fail when we send admin fields and we are admin' do - header 'X-Access-Token', 'admin' + header rack_versioned_headers[:x_access_token], 'admin' get '/', admin_field: 'tester', non_admin_field: 'toaster', admin_false_field: 'test' expect(last_response.status).to eq 200 expect(last_response.body).to eq 'bacon' end it 'fails when we send admin fields and we are not admin' do - header 'X-Access-Token', 'user' + header rack_versioned_headers[:x_access_token], 'user' get '/', admin_field: 'tester', non_admin_field: 'toaster', admin_false_field: 'test' expect(last_response.status).to eq 400 expect(last_response.body).to include 'Can not set Admin only field.' diff --git a/spec/grape/api_spec.rb b/spec/grape/api_spec.rb index 36546b86bd..3df5731d9b 100644 --- a/spec/grape/api_spec.rb +++ b/spec/grape/api_spec.rb @@ -689,7 +689,7 @@ class DummyFormatClass 'example' end put '/example' - expect(last_response.headers[content_type_header]).to eql 'text/plain' + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eql 'text/plain' end describe 'adds an OPTIONS route that' do @@ -1196,32 +1196,32 @@ class DummyFormatClass it 'sets content type for txt format' do get '/foo' - expect(last_response.headers[content_type_header]).to eq('text/plain') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('text/plain') end it 'does not set Cache-Control' do get '/foo' - expect(last_response.headers['Cache-Control']).to be_nil + expect(last_response.headers[rack_versioned_headers[:cache_control]]).to be_nil end it 'sets content type for xml' do get '/foo.xml' - expect(last_response.headers[content_type_header]).to eq('application/xml') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('application/xml') end it 'sets content type for json' do get '/foo.json' - expect(last_response.headers[content_type_header]).to eq('application/json') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('application/json') end it 'sets content type for serializable hash format' do get '/foo.serializable_hash' - expect(last_response.headers[content_type_header]).to eq('application/json') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('application/json') end it 'sets content type for binary format' do get '/foo.binary' - expect(last_response.headers[content_type_header]).to eq('application/octet-stream') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('application/octet-stream') end it 'returns raw data when content type binary' do @@ -1230,7 +1230,7 @@ class DummyFormatClass subject.format :binary subject.get('/binary_file') { File.binread(image_filename) } get '/binary_file' - expect(last_response.headers[content_type_header]).to eq('application/octet-stream') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('application/octet-stream') expect(last_response.body).to eq(file) end @@ -1242,8 +1242,8 @@ class DummyFormatClass subject.get('/file') { file test_file } get '/file' - expect(last_response.headers['Content-Length']).to eq('25') - expect(last_response.headers[content_type_header]).to eq('text/plain') + expect(last_response.headers[rack_versioned_headers[:content_length]]).to eq('25') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('text/plain') expect(last_response.body).to eq(file_content) end @@ -1257,10 +1257,10 @@ class DummyFormatClass subject.get('/stream') { stream test_stream } get '/stream', {}, 'HTTP_VERSION' => 'HTTP/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1' - expect(last_response.headers[content_type_header]).to eq('text/plain') - expect(last_response.headers['Content-Length']).to be_nil - expect(last_response.headers['Cache-Control']).to eq('no-cache') - expect(last_response.headers['Transfer-Encoding']).to eq('chunked') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('text/plain') + expect(last_response.headers[rack_versioned_headers[:content_length]]).to be_nil + expect(last_response.headers[rack_versioned_headers[:cache_control]]).to eq('no-cache') + expect(last_response.headers[rack_versioned_headers[:transfer_encoding]]).to eq('chunked') expect(last_response.body).to eq("c\r\nThis is some\r\nd\r\n file content\r\n0\r\n\r\n") end @@ -1268,7 +1268,7 @@ class DummyFormatClass it 'sets content type for error' do subject.get('/error') { error!('error in plain text', 500) } get '/error' - expect(last_response.headers[content_type_header]).to eql 'text/plain' + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eql 'text/plain' end it 'sets content type for json error' do @@ -1276,7 +1276,7 @@ class DummyFormatClass subject.get('/error') { error!('error in json', 500) } get '/error.json' expect(last_response.status).to be 500 - expect(last_response.headers[content_type_header]).to eql 'application/json' + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eql 'application/json' end it 'sets content type for xml error' do @@ -1284,7 +1284,7 @@ class DummyFormatClass subject.get('/error') { error!('error in xml', 500) } get '/error' expect(last_response.status).to be 500 - expect(last_response.headers[content_type_header]).to eql 'application/xml' + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eql 'application/xml' end it 'includes extension in format' do @@ -1314,12 +1314,12 @@ class DummyFormatClass it 'sets content type' do get '/custom.custom' - expect(last_response.headers[content_type_header]).to eql 'application/custom' + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eql 'application/custom' end it 'sets content type for error' do get '/error.custom' - expect(last_response.headers[content_type_header]).to eql 'application/custom' + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eql 'application/custom' end end @@ -1339,7 +1339,7 @@ class DummyFormatClass image_filename = 'grape.png' post url, file: Rack::Test::UploadedFile.new(image_filename, 'image/png', true) expect(last_response.status).to eq(201) - expect(last_response.headers[content_type_header]).to eq('image/png') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('image/png') expect(last_response.headers['Content-Disposition']).to eq("attachment; filename*=UTF-8''grape.png") File.open(image_filename, 'rb') do |io| expect(last_response.body).to eq io.read @@ -1351,7 +1351,7 @@ class DummyFormatClass filename = __FILE__ post '/attachment.rb', file: Rack::Test::UploadedFile.new(filename, 'application/x-ruby', true) expect(last_response.status).to eq(201) - expect(last_response.headers[content_type_header]).to eq('application/x-ruby') + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq('application/x-ruby') expect(last_response.headers['Content-Disposition']).to eq("attachment; filename*=UTF-8''api_spec.rb") File.open(filename, 'rb') do |io| expect(last_response.body).to eq io.read @@ -3311,7 +3311,7 @@ def static it 'is able to cascade' do subject.mount lambda { |env| headers = {} - headers[x_cascade_header] == 'pass' if env['PATH_INFO'].exclude?('boo') + headers[rack_versioned_headers[:x_cascade]] == 'pass' if env['PATH_INFO'].exclude?('boo') [200, headers, ['Farfegnugen']] } => '/' @@ -4081,14 +4081,14 @@ def before subject.version 'v1', using: :path, cascade: true get '/v1/hello' expect(last_response.status).to eq(404) - expect(last_response.headers[x_cascade_header]).to eq('pass') + expect(last_response.headers[rack_versioned_headers[:x_cascade]]).to eq('pass') end it 'does not cascade' do subject.version 'v2', using: :path, cascade: false get '/v2/hello' expect(last_response.status).to eq(404) - expect(last_response.headers.keys).not_to include x_cascade_header + expect(last_response.headers.keys).not_to include rack_versioned_headers[:x_cascade] end end @@ -4097,14 +4097,14 @@ def before subject.cascade true get '/hello' expect(last_response.status).to eq(404) - expect(last_response.headers[x_cascade_header]).to eq('pass') + expect(last_response.headers[rack_versioned_headers[:x_cascade]]).to eq('pass') end it 'does not cascade' do subject.cascade false get '/hello' expect(last_response.status).to eq(404) - expect(last_response.headers.keys).not_to include x_cascade_header + expect(last_response.headers.keys).not_to include rack_versioned_headers[:x_cascade] end end end diff --git a/spec/grape/dsl/inside_route_spec.rb b/spec/grape/dsl/inside_route_spec.rb index 9547af46b2..17856ce21e 100644 --- a/spec/grape/dsl/inside_route_spec.rb +++ b/spec/grape/dsl/inside_route_spec.rb @@ -73,7 +73,7 @@ def initialize end it 'sets location header' do - expect(subject.header['Location']).to eq '/' + expect(subject.header[rack_versioned_headers[:location]]).to eq '/' end end @@ -87,7 +87,7 @@ def initialize end it 'sets location header' do - expect(subject.header['Location']).to eq '/' + expect(subject.header[rack_versioned_headers[:location]]).to eq '/' end end end @@ -263,9 +263,9 @@ def initialize end before do - subject.header 'Cache-Control', 'cache' - subject.header 'Content-Length', 123 - subject.header 'Transfer-Encoding', 'base64' + subject.header rack_versioned_headers[:cache_control], 'cache' + subject.header rack_versioned_headers[:content_length], 123 + subject.header rack_versioned_headers[:transfer_encoding], 'base64' end it 'sends no deprecation warnings' do @@ -283,19 +283,19 @@ def initialize it 'does not change the Cache-Control header' do subject.sendfile file_path - expect(subject.header['Cache-Control']).to eq 'cache' + expect(subject.header[rack_versioned_headers[:cache_control]]).to eq 'cache' end it 'does not change the Content-Length header' do subject.sendfile file_path - expect(subject.header['Content-Length']).to eq 123 + expect(subject.header[rack_versioned_headers[:content_length]]).to eq 123 end it 'does not change the Transfer-Encoding header' do subject.sendfile file_path - expect(subject.header['Transfer-Encoding']).to eq 'base64' + expect(subject.header[rack_versioned_headers[:transfer_encoding]]).to eq 'base64' end end @@ -324,9 +324,9 @@ def initialize end before do - subject.header 'Cache-Control', 'cache' - subject.header 'Content-Length', 123 - subject.header 'Transfer-Encoding', 'base64' + subject.header rack_versioned_headers[:cache_control], 'cache' + subject.header rack_versioned_headers[:content_length], 123 + subject.header rack_versioned_headers[:transfer_encoding], 'base64' end it 'emits no deprecation warnings' do @@ -344,25 +344,25 @@ def initialize it 'sets Cache-Control header to no-cache' do subject.stream file_path - expect(subject.header['Cache-Control']).to eq 'no-cache' + expect(subject.header[rack_versioned_headers[:cache_control]]).to eq 'no-cache' end it 'does not change Cache-Control header' do subject.stream - expect(subject.header['Cache-Control']).to eq 'cache' + expect(subject.header[rack_versioned_headers[:cache_control]]).to eq 'cache' end it 'sets Content-Length header to nil' do subject.stream file_path - expect(subject.header['Content-Length']).to be_nil + expect(subject.header[rack_versioned_headers[:content_length]]).to be_nil end it 'sets Transfer-Encoding header to nil' do subject.stream file_path - expect(subject.header['Transfer-Encoding']).to be_nil + expect(subject.header[rack_versioned_headers[:transfer_encoding]]).to be_nil end end @@ -374,9 +374,9 @@ def initialize end before do - subject.header 'Cache-Control', 'cache' - subject.header 'Content-Length', 123 - subject.header 'Transfer-Encoding', 'base64' + subject.header rack_versioned_headers[:cache_control], 'cache' + subject.header rack_versioned_headers[:content_length], 123 + subject.header rack_versioned_headers[:transfer_encoding], 'base64' end it 'emits no deprecation warnings' do @@ -394,19 +394,19 @@ def initialize it 'sets Cache-Control header to no-cache' do subject.stream stream_object - expect(subject.header['Cache-Control']).to eq 'no-cache' + expect(subject.header[rack_versioned_headers[:cache_control]]).to eq 'no-cache' end it 'sets Content-Length header to nil' do subject.stream stream_object - expect(subject.header['Content-Length']).to be_nil + expect(subject.header[rack_versioned_headers[:content_length]]).to be_nil end it 'sets Transfer-Encoding header to nil' do subject.stream stream_object - expect(subject.header['Transfer-Encoding']).to be_nil + expect(subject.header[rack_versioned_headers[:transfer_encoding]]).to be_nil end end @@ -421,7 +421,7 @@ def initialize it 'returns default' do expect(subject.stream).to be_nil - expect(subject.header['Cache-Control']).to be_nil + expect(subject.header[rack_versioned_headers[:cache_control]]).to be_nil end end diff --git a/spec/grape/endpoint_spec.rb b/spec/grape/endpoint_spec.rb index fad8551706..200badb788 100644 --- a/spec/grape/endpoint_spec.rb +++ b/spec/grape/endpoint_spec.rb @@ -146,14 +146,14 @@ def app it 'includes additional request headers' do get '/headers', nil, 'HTTP_X_GRAPE_CLIENT' => '1' - expect(JSON.parse(last_response.body)['X-Grape-Client']).to eq('1') + expect(JSON.parse(last_response.body)[rack_versioned_headers[:x_grape_client]]).to eq('1') end it 'includes headers passed as symbols' do env = Rack::MockRequest.env_for('/headers') env[:HTTP_SYMBOL_HEADER] = 'Goliath passes symbols' body = read_chunks(subject.call(env)[2]).join - expect(JSON.parse(body)['Symbol-Header']).to eq('Goliath passes symbols') + expect(JSON.parse(body)[rack_versioned_headers[:symbol_header]]).to eq('Goliath passes symbols') end end @@ -497,7 +497,7 @@ def app end it 'responses with given content type in headers' do - expect(last_response.headers[content_type_header]).to eq 'application/json; charset=utf-8' + expect(last_response.headers[rack_versioned_headers[:content_type]]).to eq 'application/json; charset=utf-8' end end diff --git a/spec/grape/exceptions/invalid_accept_header_spec.rb b/spec/grape/exceptions/invalid_accept_header_spec.rb index 12330b42bc..e1af693257 100644 --- a/spec/grape/exceptions/invalid_accept_header_spec.rb +++ b/spec/grape/exceptions/invalid_accept_header_spec.rb @@ -19,7 +19,7 @@ shared_examples_for 'a not-cascaded request' do it 'does not include the X-Cascade=pass header' do - expect(last_response.headers[x_cascade_header]).to be_nil + expect(last_response.headers[rack_versioned_headers[:x_cascade]]).to be_nil end it 'does not accept the request' do @@ -29,7 +29,7 @@ shared_examples_for 'a rescued request' do it 'does not include the X-Cascade=pass header' do - expect(last_response.headers[x_cascade_header]).to be_nil + expect(last_response.headers[rack_versioned_headers[:x_cascade]]).to be_nil end it 'does show rescue handler processing' do diff --git a/spec/grape/middleware/versioner/accept_version_header_spec.rb b/spec/grape/middleware/versioner/accept_version_header_spec.rb index e40e8698db..4e98884ce1 100644 --- a/spec/grape/middleware/versioner/accept_version_header_spec.rb +++ b/spec/grape/middleware/versioner/accept_version_header_spec.rb @@ -36,7 +36,7 @@ end.to throw_symbol( :error, status: 406, - headers: { x_cascade_header => 'pass' }, + headers: { rack_versioned_headers[:x_cascade] => 'pass' }, message: 'The requested version is not supported.' ) end @@ -65,7 +65,7 @@ end.to throw_symbol( :error, status: 406, - headers: { x_cascade_header => 'pass' }, + headers: { rack_versioned_headers[:x_cascade] => 'pass' }, message: 'Accept-Version header must be set.' ) end @@ -76,7 +76,7 @@ end.to throw_symbol( :error, status: 406, - headers: { x_cascade_header => 'pass' }, + headers: { rack_versioned_headers[:x_cascade] => 'pass' }, message: 'Accept-Version header must be set.' ) end diff --git a/spec/grape/middleware/versioner/header_spec.rb b/spec/grape/middleware/versioner/header_spec.rb index ace5e6b82e..69afb9dcb4 100644 --- a/spec/grape/middleware/versioner/header_spec.rb +++ b/spec/grape/middleware/versioner/header_spec.rb @@ -88,7 +88,7 @@ expect { subject.call('HTTP_ACCEPT' => 'application/vnd.othervendor+json').last } .to raise_exception do |exception| expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader) - expect(exception.headers).to eql(x_cascade_header => 'pass') + expect(exception.headers).to eql(rack_versioned_headers[:x_cascade] => 'pass') expect(exception.status).to be 406 expect(exception.message).to include 'API vendor not found' end @@ -115,7 +115,7 @@ expect { subject.call('HTTP_ACCEPT' => 'application/vnd.othervendor-v1+json').last } .to raise_exception do |exception| expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader) - expect(exception.headers).to eql(x_cascade_header => 'pass') + expect(exception.headers).to eql(rack_versioned_headers[:x_cascade] => 'pass') expect(exception.status).to be 406 expect(exception.message).to include('API vendor not found') end @@ -143,7 +143,7 @@ it 'fails with 406 Not Acceptable if version is invalid' do expect { subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v2+json').last }.to raise_exception do |exception| expect(exception).to be_a(Grape::Exceptions::InvalidVersionHeader) - expect(exception.headers).to eql(x_cascade_header => 'pass') + expect(exception.headers).to eql(rack_versioned_headers[:x_cascade] => 'pass') expect(exception.status).to be 406 expect(exception.message).to include('API version not found') end @@ -176,7 +176,7 @@ it 'fails with 406 Not Acceptable if header is not set' do expect { subject.call({}).last }.to raise_exception do |exception| expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader) - expect(exception.headers).to eql(x_cascade_header => 'pass') + expect(exception.headers).to eql(rack_versioned_headers[:x_cascade] => 'pass') expect(exception.status).to be 406 expect(exception.message).to include('Accept header must be set.') end @@ -185,7 +185,7 @@ it 'fails with 406 Not Acceptable if header is empty' do expect { subject.call('HTTP_ACCEPT' => '').last }.to raise_exception do |exception| expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader) - expect(exception.headers).to eql(x_cascade_header => 'pass') + expect(exception.headers).to eql(rack_versioned_headers[:x_cascade] => 'pass') expect(exception.status).to be 406 expect(exception.message).to include('Accept header must be set.') end @@ -262,7 +262,7 @@ it 'fails with another version' do expect { subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v3+json') }.to raise_exception do |exception| expect(exception).to be_a(Grape::Exceptions::InvalidVersionHeader) - expect(exception.headers).to eql(x_cascade_header => 'pass') + expect(exception.headers).to eql(rack_versioned_headers[:x_cascade] => 'pass') expect(exception.status).to be 406 expect(exception.message).to include('API version not found') end diff --git a/spec/grape/request_spec.rb b/spec/grape/request_spec.rb index 6a3f1948f0..9199c346dc 100644 --- a/spec/grape/request_spec.rb +++ b/spec/grape/request_spec.rb @@ -91,7 +91,7 @@ module Grape end it 'cuts HTTP_ prefix and capitalizes header name words' do - expect(request.headers).to eq('X-Grape-Is-Cool' => 'yeah') + expect(request.headers).to eq(rack_versioned_headers[:x_grape_is_cool] => 'yeah') end end @@ -118,7 +118,7 @@ module Grape end it 'converts them to string' do - expect(request.headers).to eq('Grape-Likes-Symbolic' => 'it is true') + expect(request.headers).to eq(rack_versioned_headers[:grape_likes_symbolic] => 'it is true') end end end diff --git a/spec/support/headers_helpers.rb b/spec/support/headers_helpers.rb index 7386fde8c3..05a8e133ac 100644 --- a/spec/support/headers_helpers.rb +++ b/spec/support/headers_helpers.rb @@ -3,12 +3,36 @@ module Spec module Support module Helpers - def content_type_header - Grape::Http::Headers::CONTENT_TYPE - end - - def x_cascade_header - Grape::Http::Headers::X_CASCADE + def rack_versioned_headers + if Gem::Version.new(Rack.release) < Gem::Version.new('3') + { + cache_control: 'Cache-Control', + content_length: 'Content-Length', + content_type: 'Content-Type', + grape_likes_symbolic: 'Grape-Likes-Symbolic', + location: 'Location', + symbol_header: 'Symbol-Header', + transfer_encoding: 'Transfer-Encoding', + x_access_token: 'X-Access-Token', + x_cascade: 'X-Cascade', + x_grape_client: 'X-Grape-Client', + x_grape_is_cool: 'X-Grape-Is-Cool' + } + else + { + cache_control: 'cache-control', + content_length: 'content-length', + content_type: 'content-type', + grape_likes_symbolic: 'grape-likes-symbolic', + location: 'location', + symbol_header: 'symbol-header', + transfer_encoding: 'transfer-encoding', + x_access_token: 'x-access-token', + x_cascade: 'x-cascade', + x_grape_client: 'x-grape-client', + x_grape_is_cool: 'x-grape-is-cool' + } + end end end end