Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [#2540](https://github.com/ruby-grape/grape/pull/2540): Introduce Params builder with symbolized short name - [@ericproulx](https://github.com/ericproulx).
* [#2550](https://github.com/ruby-grape/grape/pull/2550): Drop ActiveSupport 6.0 - [@ericproulx](https://github.com/ericproulx).
* [#2549](https://github.com/ruby-grape/grape/pull/2549): Delegate cookies management to `Grape::Request` - [@ericproulx](https://github.com/ericproulx).
* [#2554](https://github.com/ruby-grape/grape/pull/2554): Remove `Grape::Http::Headers` and `Grape::Util::Lazy::Object` - [@ericproulx](https://github.com/ericproulx).
* Your contribution here.

#### Fixes
Expand Down
14 changes: 12 additions & 2 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Upgrading Grape
===============

### Upgrading to >= 2.4.0

#### Grape::Http::Headers, Grape::Util::Lazy::Object

Both have been removed. See [2554](https://github.com/ruby-grape/grape/pull/2554).
Here are the notable changes:

- Constants like `HTTP_ACCEPT` have been replaced by their literal value.
- `SUPPORTED_METHODS` has been moved to `Grape` module.
- `HTTP_HEADERS` has been moved to `Grape::Request` and renamed `KNOWN_HEADERS`. The last has been refreshed with new headers, and it's not lazy anymore.
- `SUPPORTED_METHODS_WITHOUT_OPTIONS` and `find_supported_method` have been removed.

#### Params Builder

- Passing a class to `build_with` or `Grape.config.param_builder` has been deprecated in favor of a symbolized short_name. See `SHORTNAME_LOOKUP` in [params_builder](lib/grape/params_builder.rb).
Expand All @@ -25,8 +37,6 @@ For the non provided case, 1.0 was automatically assigned and in a case of multi
Excluding the [header versioning strategy](https://github.com/ruby-grape/grape?tab=readme-ov-file#header), whenever a media type with the [vendor tree](https://datatracker.ietf.org/doc/html/rfc6838#section-3.2) leading facet `vnd.` like `application/vnd.api+json` was provided, Grape would also consider its closest generic when negotiating. In that case, `application/json` was added to the negotiation. Now, it will just consider the provided media types without considering any closest generics, and you'll need to [register](https://github.com/ruby-grape/grape?tab=readme-ov-file#api-formats) it.
You can find the official vendor tree registrations on [IANA](https://www.iana.org/assignments/media-types/media-types.xhtml)

### Upgrading to >= 2.4.0

#### Custom Validators

If you now receive an error of `'Grape::Validations.require_validator': unknown validator: your_custom_validation (Grape::Exceptions::UnknownValidator)` after upgrading to 2.4.0 then you will need to ensure that you require the `your_custom_validation` file before your Grape API code is loaded.
Expand Down
10 changes: 10 additions & 0 deletions lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@
module Grape
include ActiveSupport::Configurable

HTTP_SUPPORTED_METHODS = [
Rack::GET,
Rack::POST,
Rack::PUT,
Rack::PATCH,
Rack::DELETE,
Rack::HEAD,
Rack::OPTIONS
].freeze

def self.deprecator
@deprecator ||= ActiveSupport::Deprecation.new('2.0', 'Grape')
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/api/instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def call(env)
status, headers, response = @router.call(env)
unless cascade?
headers = Grape::Util::Header.new.merge(headers)
headers.delete(Grape::Http::Headers::X_CASCADE)
headers.delete('X-Cascade')
end

[status, headers, response]
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/dsl/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Headers
# 4. Delete a specifc header key-value pair
def header(key = nil, val = nil)
if key
val ? header[key.to_s] = val : header.delete(key.to_s)
val ? header[key] = val : header.delete(key)
else
@header ||= Grape::Util::Header.new
end
Expand Down
6 changes: 3 additions & 3 deletions lib/grape/dsl/inside_route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def redirect(url, permanent: false, body: nil)
status 302
body_message ||= "This resource has been moved temporarily to #{url}."
end
header Grape::Http::Headers::LOCATION, url
header 'Location', url
content_type 'text/plain'
body body_message
end
Expand Down Expand Up @@ -330,7 +330,7 @@ def stream(value = nil)
return if value.nil? && @stream.nil?

header Rack::CONTENT_LENGTH, nil
header Grape::Http::Headers::TRANSFER_ENCODING, nil
header 'Transfer-Encoding', nil
header Rack::CACHE_CONTROL, 'no-cache' # Skips ETag generation (reading the response up front)
if value.is_a?(String)
file_body = Grape::ServeStream::FileBody.new(value)
Expand Down Expand Up @@ -439,7 +439,7 @@ def entity_representation_for(entity_class, object, options)
end

def http_version
env.fetch(Grape::Http::Headers::HTTP_VERSION) { env[Rack::SERVER_PROTOCOL] }
env.fetch('HTTP_VERSION') { env[Rack::SERVER_PROTOCOL] }
end

def api_format(format)
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/dsl/routing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def route(methods, paths = ['/'], route_options = {}, &block)
reset_validations!
end

Grape::Http::Headers::SUPPORTED_METHODS.each do |supported_method|
Grape::HTTP_SUPPORTED_METHODS.each do |supported_method|
define_method supported_method.downcase do |*args, &block|
options = args.extract_options!
paths = args.first || ['/']
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def run
header['Allow'] = env[Grape::Env::GRAPE_ALLOWED_METHODS].join(', ')
raise Grape::Exceptions::MethodNotAllowed.new(header) unless options?

header Grape::Http::Headers::ALLOW, header['Allow']
header 'Allow', header['Allow']
response_object = ''
status 204
else
Expand Down
56 changes: 0 additions & 56 deletions lib/grape/http/headers.rb

This file was deleted.

4 changes: 2 additions & 2 deletions lib/grape/middleware/formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def read_body_input
(request.post? || request.put? || request.patch? || request.delete?) &&
(!request.form_data? || !request.media_type) &&
!request.parseable_data? &&
(request.content_length.to_i.positive? || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
(request.content_length.to_i.positive? || request.env['HTTP_TRANSFER_ENCODING'] == CHUNKED)

return unless (input = env[Rack::RACK_INPUT])

Expand Down Expand Up @@ -142,7 +142,7 @@ def format_from_params
end

def format_from_header
accept_header = env[Grape::Http::Headers::HTTP_ACCEPT].try(:scrub)
accept_header = env['HTTP_ACCEPT'].try(:scrub)
return if accept_header.blank?

media_type = Rack::Utils.best_q_match(accept_header, mime_types.keys)
Expand Down
4 changes: 1 addition & 3 deletions lib/grape/middleware/versioner/accept_version_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ module Versioner
# route.
class AcceptVersionHeader < Base
def before
potential_version = env[Grape::Http::Headers::HTTP_ACCEPT_VERSION]
potential_version = potential_version.scrub unless potential_version.nil?

potential_version = env['HTTP_ACCEPT_VERSION'].try(:scrub)
not_acceptable!('Accept-Version header must be set.') if strict? && potential_version.blank?

return if potential_version.blank?
Expand Down
4 changes: 2 additions & 2 deletions lib/grape/middleware/versioner/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ def vendor
end

def error_headers
cascade? ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
cascade? ? { 'X-Cascade' => 'pass' } : {}
end

def potential_version_match?(potential_version)
versions.blank? || versions.any? { |v| v.to_s == potential_version }
end

def version_not_found!
throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' }
throw :error, status: 404, message: '404 API Version Not Found', headers: { 'X-Cascade' => 'pass' }
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/middleware/versioner/header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def match_best_quality_media_type!
end

def accept_header
env[Grape::Http::Headers::HTTP_ACCEPT]
env['HTTP_ACCEPT']
end

def strict_header_checks!
Expand Down
142 changes: 135 additions & 7 deletions lib/grape/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,139 @@
module Grape
class Request < Rack::Request
DEFAULT_PARAMS_BUILDER = :hash_with_indifferent_access
HTTP_PREFIX = 'HTTP_'
# Based on rack 3 KNOWN_HEADERS
# https://github.com/rack/rack/blob/4f15e7b814922af79605be4b02c5b7c3044ba206/lib/rack/headers.rb#L10

KNOWN_HEADERS = %w[
Accept
Accept-CH
Accept-Encoding
Accept-Language
Accept-Patch
Accept-Ranges
Accept-Version
Access-Control-Allow-Credentials
Access-Control-Allow-Headers
Access-Control-Allow-Methods
Access-Control-Allow-Origin
Access-Control-Expose-Headers
Access-Control-Max-Age
Age
Allow
Alt-Svc
Authorization
Cache-Control
Client-Ip
Connection
Content-Disposition
Content-Encoding
Content-Language
Content-Length
Content-Location
Content-MD5
Content-Range
Content-Security-Policy
Content-Security-Policy-Report-Only
Content-Type
Cookie
Date
Delta-Base
Dnt
ETag
Expect-CT
Expires
Feature-Policy
Forwarded
Host
If-Modified-Since
If-None-Match
IM
Last-Modified
Link
Location
NEL
P3P
Permissions-Policy
Pragma
Preference-Applied
Proxy-Authenticate
Public-Key-Pins
Range
Referer
Referrer-Policy
Refresh
Report-To
Retry-After
Sec-Fetch-Dest
Sec-Fetch-Mode
Sec-Fetch-Site
Sec-Fetch-User
Server
Set-Cookie
Status
Strict-Transport-Security
Timing-Allow-Origin
Tk
Trailer
Transfer-Encoding
Upgrade
Upgrade-Insecure-Requests
User-Agent
Vary
Version
Via
Warning
WWW-Authenticate
X-Accel-Buffering
X-Accel-Charset
X-Accel-Expires
X-Accel-Limit-Rate
X-Accel-Mapping
X-Accel-Redirect
X-Access-Token
X-Auth-Request-Access-Token
X-Auth-Request-Email
X-Auth-Request-Groups
X-Auth-Request-Preferred-Username
X-Auth-Request-Redirect
X-Auth-Request-Token
X-Auth-Request-User
X-Cascade
X-Client-Ip
X-Content-Duration
X-Content-Security-Policy
X-Content-Type-Options
X-Correlation-Id
X-Download-Options
X-Forwarded-Access-Token
X-Forwarded-Email
X-Forwarded-For
X-Forwarded-Groups
X-Forwarded-Host
X-Forwarded-Port
X-Forwarded-Preferred-Username
X-Forwarded-Proto
X-Forwarded-Scheme
X-Forwarded-Ssl
X-Forwarded-Uri
X-Forwarded-User
X-Frame-Options
X-HTTP-Method-Override
X-Permitted-Cross-Domain-Policies
X-Powered-By
X-Real-IP
X-Redirect-By
X-Request-Id
X-Requested-With
X-Runtime
X-Sendfile
X-Sendfile-Type
X-UA-Compatible
X-WebKit-CS
X-XSS-Protection
].each_with_object({}) do |header, response|
response["HTTP_#{header.upcase.tr('-', '_')}"] = header
end.freeze

alias rack_params params
alias rack_cookies cookies
Expand Down Expand Up @@ -43,15 +175,11 @@ def make_params

def build_headers
each_header.with_object(Grape::Util::Header.new) do |(k, v), headers|
next unless k.start_with? HTTP_PREFIX
next unless k.start_with? 'HTTP_'

transformed_header = Grape::Http::Headers::HTTP_HEADERS[k] || transform_header(k)
transformed_header = KNOWN_HEADERS.fetch(k) { -k[5..].tr('_', '-').downcase }
headers[transformed_header] = v
end
end

def transform_header(header)
-header[5..].tr('_', '-').downcase
end
end
end
Loading