Skip to content

Remove Grape::Http::Headers #2554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 10, 2025
Merged
Show file tree
Hide file tree
Changes from all 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.

### Grape::Middleware::Base

- Constant `TEXT_HTML` has been removed in favor of using literal string 'text/html'.
Expand Down Expand Up @@ -30,8 +42,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 @@ -119,7 +119,7 @@ def read_body_input?
(rack_request.post? || rack_request.put? || rack_request.patch? || rack_request.delete?) &&
!(rack_request.form_data? && rack_request.content_type) &&
!rack_request.parseable_data? &&
(rack_request.content_length.to_i.positive? || rack_request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == 'chunked')
(rack_request.content_length.to_i.positive? || rack_request.env['HTTP_TRANSFER_ENCODING'] == 'chunked')
end

def negotiate_content_type
Expand All @@ -141,7 +141,7 @@ def format_from_extension
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 @@ -49,15 +181,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