Class: Tina4::Request

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

Constructor Details

#initialize(env, path_params = {}) ⇒ Request

Returns a new instance of Request.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/tina4/request.rb', line 105

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

  # Client IP with X-Forwarded-For support
  @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_typeObject (readonly)

Returns the value of attribute content_type.



96
97
98
# File 'lib/tina4/request.rb', line 96

def content_type
  @content_type
end

#envObject (readonly)

Returns the value of attribute env.



96
97
98
# File 'lib/tina4/request.rb', line 96

def env
  @env
end

#ipObject (readonly)

Returns the value of attribute ip.



96
97
98
# File 'lib/tina4/request.rb', line 96

def ip
  @ip
end

#methodObject (readonly)

Returns the value of attribute method.



96
97
98
# File 'lib/tina4/request.rb', line 96

def method
  @method
end

#pathObject (readonly)

Returns the value of attribute path.



96
97
98
# File 'lib/tina4/request.rb', line 96

def path
  @path
end

#path_paramsObject (readonly)

Returns the value of attribute path_params.



96
97
98
# File 'lib/tina4/request.rb', line 96

def path_params
  @path_params
end

#query_stringObject (readonly)

Returns the value of attribute query_string.



96
97
98
# File 'lib/tina4/request.rb', line 96

def query_string
  @query_string
end

#userObject

Returns the value of attribute user.



98
99
100
# File 'lib/tina4/request.rb', line 98

def user
  @user
end

Instance Method Details

#[](key) ⇒ Object



194
195
196
# File 'lib/tina4/request.rb', line 194

def [](key)
  params[key.to_s] || params[key.to_sym] || @path_params[key.to_sym]
end

#bearer_tokenObject



213
214
215
216
# File 'lib/tina4/request.rb', line 213

def bearer_token
  auth = header("authorization") || ""
  auth.sub(/\ABearer\s+/i, "") if auth =~ /\ABearer\s+/i
end

#bodyObject

Raw body string



165
166
167
# File 'lib/tina4/request.rb', line 165

def body
  @body_raw ||= read_body
end

#body_parsedObject

Parsed body (JSON or form data)



170
171
172
# File 'lib/tina4/request.rb', line 170

def body_parsed
  @body_parsed ||= parse_body
end

#cookiesObject



156
157
158
# File 'lib/tina4/request.rb', line 156

def cookies
  @cookies ||= parse_cookies
end

#filesObject



179
180
181
# File 'lib/tina4/request.rb', line 179

def files
  @files ||= extract_files
end

#header(name) ⇒ Object



198
199
200
201
202
203
# File 'lib/tina4/request.rb', line 198

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

#headersObject

Lazy accessors



152
153
154
# File 'lib/tina4/request.rb', line 152

def headers
  @headers ||= extract_headers
end

#json_bodyObject



205
206
207
208
209
210
211
# File 'lib/tina4/request.rb', line 205

def json_body
  @json_body ||= begin
    JSON.parse(body)
  rescue JSON::ParserError, TypeError
    {}
  end
end

#param(key, default = nil) ⇒ Object

Look up a param by symbol or string key (indifferent access shortcut).



190
191
192
# File 'lib/tina4/request.rb', line 190

def param(key, default = nil)
  params[key.to_s] || params[key.to_sym] || default
end

#paramsObject

Merged params: query + body + path_params (path_params highest priority) Supports both string and symbol key access (indifferent access).



185
186
187
# File 'lib/tina4/request.rb', line 185

def params
  @params ||= build_params
end

#queryObject

Parsed query string as hash



175
176
177
# File 'lib/tina4/request.rb', line 175

def query
  @query_hash ||= parse_query_to_hash(@query_string)
end

#sessionObject



160
161
162
# File 'lib/tina4/request.rb', line 160

def session
  @session ||= Tina4::Session.new(@env)
end

#urlObject

Full absolute URL — scheme://host/path. Honours X-Forwarded-Proto / X-Forwarded-Host so apps behind a proxy still see the URL the client used. Matches Python/PHP/Node parity.



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/tina4/request.rb', line 138

def url
  scheme = env["HTTP_X_FORWARDED_PROTO"] || env["rack.url_scheme"] || "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