Class: Tina4::Request
- Inherits:
-
Object
- Object
- Tina4::Request
- Defined in:
- lib/tina4/request.rb
Defined Under Namespace
Classes: PayloadTooLarge
Constant Summary collapse
- TINA4_MAX_UPLOAD_SIZE =
Maximum upload size in bytes (default 10 MB). Override via TINA4_MAX_UPLOAD_SIZE env var.
Integer(ENV.fetch("TINA4_MAX_UPLOAD_SIZE", 10_485_760))
Instance Attribute Summary collapse
-
#content_type ⇒ Object
readonly
Returns the value of attribute content_type.
-
#env ⇒ Object
readonly
Returns the value of attribute env.
-
#ip ⇒ Object
readonly
Returns the value of attribute ip.
-
#method ⇒ Object
readonly
Returns the value of attribute method.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#path_params ⇒ Object
Returns the value of attribute path_params.
-
#query_string ⇒ Object
readonly
Returns the value of attribute query_string.
-
#remote_ip ⇒ Object
readonly
Returns the value of attribute remote_ip.
-
#user ⇒ Object
Returns the value of attribute user.
Class Method Summary collapse
-
.secure_scheme?(env) ⇒ Boolean
Is this request HTTPS from the CLIENT's point of view?.
Instance Method Summary collapse
- #[](key) ⇒ Object
- #bearer_token ⇒ Object
-
#body ⇒ Object
(also: #body_parsed)
Parsed body (JSON -> Hash, form-urlencoded -> Hash, multipart -> fields Hash, else the current fallback).
-
#body_raw ⇒ Object
Raw body string — the bytes exactly as the client sent them.
- #cookies ⇒ Object
- #files ⇒ Object
- #header(name) ⇒ Object
-
#headers ⇒ Object
Lazy accessors.
-
#initialize(env, path_params = {}) ⇒ Request
constructor
A new instance of Request.
- #json_body ⇒ Object
-
#param(key, default = nil) ⇒ Object
Look up a param by symbol or string key (indifferent access shortcut).
- #params ⇒ Object
-
#query ⇒ Object
Parsed query string as hash.
-
#session ⇒ Object
The session for THIS request.
- #url ⇒ Object
Constructor Details
#initialize(env, path_params = {}) ⇒ Request
Returns a new instance of Request.
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/tina4/request.rb', line 126 def initialize(env, path_params = {}) @env = env @method = env["REQUEST_METHOD"] @path = env["PATH_INFO"] || "/" @query_string = env["QUERY_STRING"] || "" @content_type = env["CONTENT_TYPE"] || "" @path_params = path_params # Check upload size limit content_length = (env["CONTENT_LENGTH"] || 0).to_i if content_length > TINA4_MAX_UPLOAD_SIZE raise PayloadTooLarge, "Request body (#{content_length} bytes) exceeds TINA4_MAX_UPLOAD_SIZE (#{TINA4_MAX_UPLOAD_SIZE} bytes)" end # Raw socket peer — NEVER honours X-Forwarded-For, so it can be trusted # for security decisions. Resolved BEFORE @ip: the peer decides whether # the forwarding headers may be believed at all. @remote_ip = (env["REMOTE_ADDR"] || "").to_s @ip = extract_client_ip # Lazy-initialized fields (nil = not yet computed) @headers = nil @cookies = nil @session = nil @body_raw = nil @params = nil @files = nil @json_body = nil @query_hash = nil @body_parsed = nil end |
Instance Attribute Details
#content_type ⇒ Object (readonly)
Returns the value of attribute content_type.
117 118 119 |
# File 'lib/tina4/request.rb', line 117 def content_type @content_type end |
#env ⇒ Object (readonly)
Returns the value of attribute env.
117 118 119 |
# File 'lib/tina4/request.rb', line 117 def env @env end |
#ip ⇒ Object (readonly)
Returns the value of attribute ip.
117 118 119 |
# File 'lib/tina4/request.rb', line 117 def ip @ip end |
#method ⇒ Object (readonly)
Returns the value of attribute method.
117 118 119 |
# File 'lib/tina4/request.rb', line 117 def method @method end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
117 118 119 |
# File 'lib/tina4/request.rb', line 117 def path @path end |
#path_params ⇒ Object
Returns the value of attribute path_params.
117 118 119 |
# File 'lib/tina4/request.rb', line 117 def path_params @path_params end |
#query_string ⇒ Object (readonly)
Returns the value of attribute query_string.
117 118 119 |
# File 'lib/tina4/request.rb', line 117 def query_string @query_string end |
#remote_ip ⇒ Object (readonly)
Returns the value of attribute remote_ip.
117 118 119 |
# File 'lib/tina4/request.rb', line 117 def remote_ip @remote_ip end |
#user ⇒ Object
Returns the value of attribute user.
119 120 121 |
# File 'lib/tina4/request.rb', line 119 def user @user end |
Class Method Details
.secure_scheme?(env) ⇒ Boolean
Is this request HTTPS from the CLIENT's point of view?
TLS is normally terminated at a proxy (nginx, HAProxy, ALB, Cloudflare, most container deploys) that then forwards plain HTTP to the app, so rack.url_scheme is "http" on exactly the deployments that ARE encrypted. x-forwarded-proto carries the scheme the client actually used, and a chain of proxies appends each hop ("https, http") — the FIRST is the client-facing one. Falls back to the native rack scheme, then the CGI HTTPS var.
This is the single source of truth for BOTH the URL scheme (#url) and the session-cookie Secure flag (Session#cookie_header). They used to decide it independently and could disagree — url() said https while the cookie concluded plain HTTP and dropped Secure (ruby#31, parity with PHP Request::isSecureScheme / tina4-php#175).
177 178 179 180 181 182 183 184 185 186 |
# File 'lib/tina4/request.rb', line 177 def self.secure_scheme?(env) forwarded = (env["HTTP_X_FORWARDED_PROTO"] || "").to_s unless forwarded.strip.empty? return forwarded.split(",").first.to_s.strip.casecmp("https").zero? end scheme = (env["rack.url_scheme"] || "").to_s return true if scheme.casecmp("https").zero? https = (env["HTTPS"] || "").to_s !https.empty? && https.casecmp("off") != 0 end |
Instance Method Details
#[](key) ⇒ Object
278 279 280 |
# File 'lib/tina4/request.rb', line 278 def [](key) params[key.to_s] || params[key.to_sym] || @path_params[key.to_sym] end |
#bearer_token ⇒ Object
297 298 299 300 |
# File 'lib/tina4/request.rb', line 297 def bearer_token auth = header("authorization") || "" auth.sub(/\ABearer\s+/i, "") if auth =~ /\ABearer\s+/i end |
#body ⇒ Object Also known as: body_parsed
Parsed body (JSON -> Hash, form-urlencoded -> Hash, multipart ->
fields Hash, else the current fallback). This matches Python's
request.body, PHP's, and Node's: body is the PARSED payload, not
the raw bytes. For the raw string use body_raw.
229 230 231 |
# File 'lib/tina4/request.rb', line 229 def body @body_parsed ||= parse_body end |
#body_raw ⇒ Object
Raw body string — the bytes exactly as the client sent them.
(This is what body used to return before the cross-framework
parity flip; SOAP/GraphQL and any consumer that needs the raw text
reads this.)
237 238 239 |
# File 'lib/tina4/request.rb', line 237 def body_raw @body_raw ||= read_body end |
#cookies ⇒ Object
209 210 211 |
# File 'lib/tina4/request.rb', line 209 def @cookies ||= end |
#files ⇒ Object
249 250 251 |
# File 'lib/tina4/request.rb', line 249 def files @files ||= extract_files end |
#header(name) ⇒ Object
282 283 284 285 286 287 |
# File 'lib/tina4/request.rb', line 282 def header(name) # Headers are stored in a CaseInsensitiveHash keyed by lowercase- # dashed names ("content-type", "x-api-key"). The hash normalises # the lookup case automatically, so pass the dashed form through. headers[name.to_s.tr("_", "-")] end |
#headers ⇒ Object
Lazy accessors
205 206 207 |
# File 'lib/tina4/request.rb', line 205 def headers @headers ||= extract_headers end |
#json_body ⇒ Object
289 290 291 292 293 294 295 |
# File 'lib/tina4/request.rb', line 289 def json_body @json_body ||= begin JSON.parse(body_raw) rescue JSON::ParserError, TypeError {} end end |
#param(key, default = nil) ⇒ Object
Look up a param by symbol or string key (indifferent access shortcut).
274 275 276 |
# File 'lib/tina4/request.rb', line 274 def param(key, default = nil) params[key.to_s] || params[key.to_sym] || default end |
#params ⇒ Object
269 270 271 |
# File 'lib/tina4/request.rb', line 269 def params @params ||= build_params end |
#query ⇒ Object
Parsed query string as hash
245 246 247 |
# File 'lib/tina4/request.rb', line 245 def query @query_hash ||= parse_query_to_hash(@query_string) end |
#session ⇒ Object
The session for THIS request.
degrade_on_backend_failure: this is the live request path, so a storage handler that cannot be built (an unreachable database, a refused backend name) is LOGGED and then degraded to an in-memory-only session rather than unwinding into RackApp's 500 handler and taking the request down with it (ADR-0021). TINA4_SESSION_STRICT still re-raises. Direct Session.new callers keep the loud raise - see the guard in Session#initialize.
221 222 223 |
# File 'lib/tina4/request.rb', line 221 def session @session ||= Tina4::Session.new(@env, degrade_on_backend_failure: true) end |
#url ⇒ Object
191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/tina4/request.rb', line 191 def url scheme = self.class.secure_scheme?(env) ? "https" : "http" host = env["HTTP_X_FORWARDED_HOST"] || env["HTTP_HOST"] || env["SERVER_NAME"] || "localhost" port = env["SERVER_PORT"] url_str = "#{scheme}://#{host}" # Only append :port when the host doesn't already include one # (HTTP_HOST often does) and it's not the default for the scheme. url_str += ":#{port}" if port && !host.include?(":") && port.to_s != "80" && port.to_s != "443" url_str += @path url_str += "?#{@query_string}" unless @query_string.empty? url_str end |