Class: Iodine::Http::Request
- Inherits:
-
Hash
- Object
- Hash
- Iodine::Http::Request
- Defined in:
- lib/iodine/http/request.rb
Overview
This class is the part of the Iodine server. The request object is a Hash and the Request provides simple shortcuts and access to the request’s Hash data.
An Http Request
Defined Under Namespace
Classes: Cookies
Constant Summary collapse
- HTTP_GET =
method recognition
'GET'.freeze
- HTTP_HEAD =
'HEAD'.freeze
- HTTP_POST =
'POST'.freeze
- HTTP_PUT =
'PUT'.freeze
- HTTP_DELETE =
'DELETE'.freeze
- HTTP_TRACE =
'TRACE'.freeze
- HTTP_OPTIONS =
'OPTIONS'.freeze
- HTTP_CONNECT =
'CONNECT'.freeze
- HTTP_PATCH =
'PATCH'.freeze
- HTTP_CTYPE =
'content-type'.freeze
- HTTP_JSON =
/application\/json/
- HTTP_XML =
/text\/xml/
Class Method Summary collapse
-
.add_param_to_hash(name, value, target) ⇒ Object
Adds paramaters to a Hash object, according to the Iodine’s server conventions.
-
.encode_url(str) ⇒ Object
encodes URL data.
-
.extract_header(data, target_hash) ⇒ Object
extracts parameters from header data.
-
.extract_params(data, target_hash) ⇒ Object
extracts parameters from the query.
-
.form_decode!(s) ⇒ Object
decode percent-encoded data (excluding the ‘+’ sign for encoding).
-
.make_utf8!(string, encoding = ::Encoding::UTF_8) ⇒ Object
re-encodes a string into UTF-8.
-
.parse(request) ⇒ Object
parses an HTTP request (quary, body data etc’).
-
.read_body(request) ⇒ Object
read the body’s data and parse any incoming data.
-
.read_multipart(request, headers, part, name_prefix = '') ⇒ Object
parse a mime/multipart body or part.
-
.rubyfy!(string) ⇒ Object
Changes String to a Ruby Object, if it’s a special string…
-
.try_utf8!(string, encoding = ::Encoding::UTF_8) ⇒ Object
re-encodes a string into UTF-8.
-
.uri_decode!(s) ⇒ Object
decode form / uri data (including the ‘+’ sign as a space (%20) replacement).
Instance Method Summary collapse
-
#base_url(switch_scheme = nil) ⇒ Object
the base url ([http/https]://host).
-
#connect? ⇒ Boolean
returns true of the method == CONNECT.
-
#cookies ⇒ Object
the cookies sent by the client.
-
#delete? ⇒ Boolean
returns true of the method == DELETE.
-
#get? ⇒ Boolean
returns true of the method == GET.
-
#head? ⇒ Boolean
returns true of the method == HEAD.
-
#headers ⇒ Object
the request’s headers.
-
#initialize(io = nil) ⇒ Request
constructor
A new instance of Request.
-
#io ⇒ Iodine::Http, ...
The Protocol used for the request.
-
#json? ⇒ Boolean
returns true if the request is of type JSON.
-
#options? ⇒ Boolean
returns true of the method == OPTIONS.
-
#original_path ⇒ Object
the original (frozen) path (resource requested).
-
#params ⇒ Object
the parameters sent by the client.
-
#patch? ⇒ Boolean
returns true of the method == PATCH.
-
#path ⇒ Object
the requested path (rewritable).
- #path=(new_path) ⇒ Object
-
#post? ⇒ Boolean
returns true of the method == POST.
-
#put? ⇒ Boolean
returns true of the method == PUT.
-
#query ⇒ Object
the query string.
-
#request_method ⇒ Object
the request’s method (GET, POST… etc’).
-
#request_method=(value) ⇒ Object
set request’s method (GET, POST… etc’).
-
#request_url(switch_scheme = nil) ⇒ Object
the request’s url, without any GET parameters ([http/https]://host/path).
-
#scheme ⇒ Object
the protocol’s scheme (http/https/ws/wss) managing this request.
-
#session ⇒ Hash like storage
Returns the session storage object IF a session was already initialized (use the response to initialize a session).
-
#ssl? ⇒ true, false
(also: #secure?)
Returns true if the requested was an SSL protocol (true also if the connection is clear-text behind an SSL Proxy, such as with some PaaS providers).
-
#trace? ⇒ Boolean
returns true of the method == TRACE.
-
#version ⇒ Object
The HTTP version for this request.
-
#websocket? ⇒ Boolean
(also: #upgrade?)
returns true if this is a websocket upgrade request.
-
#xml? ⇒ Boolean
returns true if the request is of type XML.
Constructor Details
Class Method Details
.add_param_to_hash(name, value, target) ⇒ Object
Adds paramaters to a Hash object, according to the Iodine’s server conventions.
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/iodine/http/request.rb', line 265 def self.add_param_to_hash name, value, target begin c = target val = rubyfy! value a = name.chomp('[]'.freeze).split('['.freeze) a[0...-1].inject(target) do |h, n| n.chomp!(']'.freeze); n.strip!; raise "malformed parameter name for #{name}" if n.empty? n = (n.to_i.to_s == n) ? n.to_i : n.to_sym c = (h[n] ||= {}) end n = a.last n.chomp!(']'); n.strip!; n = n.empty? ? nil : ( (n.to_i.to_s == n) ? n.to_i : n.to_sym ) if n if c[n] c[n].is_a?(Array) ? (c[n] << val) : (c[n] = [c[n], val]) else c[n] = val end else if c[n] c[n].is_a?(Array) ? (c[n] << val) : (c[n] = [c[n], val]) else c[n] = [val] end end val rescue => e Iodine.error e Iodine.error "(Silent): parameters parse error for #{name} ... maybe conflicts with a different set?" target[name] = val end end |
.encode_url(str) ⇒ Object
encodes URL data.
260 261 262 |
# File 'lib/iodine/http/request.rb', line 260 def self.encode_url str (str.to_s.gsub(/[^a-z0-9\*\.\_\-]/i) {|m| '%%%02x'.freeze % m.ord }).force_encoding(::Encoding::ASCII_8BIT) end |
.extract_header(data, target_hash) ⇒ Object
extracts parameters from header data
315 316 317 318 319 320 321 |
# File 'lib/iodine/http/request.rb', line 315 def self.extract_header data, target_hash data.each do |set| list = set.split('='.freeze, 2) list.each {|s| form_decode!(s) if s} add_param_to_hash list.shift, list.shift, target_hash end end |
.extract_params(data, target_hash) ⇒ Object
extracts parameters from the query
303 304 305 306 307 308 309 |
# File 'lib/iodine/http/request.rb', line 303 def self.extract_params data, target_hash data.each do |set| list = set.split('='.freeze, 2) list.each {|s| uri_decode!(s) if s} add_param_to_hash list.shift, list.shift, target_hash end end |
.form_decode!(s) ⇒ Object
decode percent-encoded data (excluding the ‘+’ sign for encoding).
323 324 325 |
# File 'lib/iodine/http/request.rb', line 323 def self.form_decode! s s.gsub!(/\%[0-9a-f]{2}/i) {|m| m[1..2].to_i(16).chr}; s.gsub!(/&#[0-9]{4};/i) {|m| [m[2..5].to_i].pack 'U'.freeze }; s end |
.make_utf8!(string, encoding = ::Encoding::UTF_8) ⇒ Object
re-encodes a string into UTF-8
246 247 248 249 250 |
# File 'lib/iodine/http/request.rb', line 246 def self.make_utf8!(string, encoding= ::Encoding::UTF_8) return false unless string string.force_encoding(::Encoding::ASCII_8BIT).encode!(encoding, ::Encoding::ASCII_8BIT, invalid: :replace, undef: :replace, replace: ''.freeze) unless string.force_encoding(encoding).valid_encoding? string end |
.parse(request) ⇒ Object
parses an HTTP request (quary, body data etc’)
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/iodine/http/request.rb', line 195 def self.parse request # if m = request[:query].match /(([a-z0-9A-Z]+):\/\/)?(([^\/\:]+))?(:([0-9]+))?([^\?\#]*)(\?([^\#]*))?/ # request[:requested_protocol] = m[1] || request['x-forwarded-proto'] || ( request[:io].ssl? ? 'https' : 'http') # request[:host_name] = m[4] || (request['host'] ? request['host'].match(/^[^:]*/).to_s : nil) # request[:port] = m[6] || (request['host'] ? request['host'].match(/:([0-9]*)/).to_a[1] : nil) # request[:original_path] = HTTP.decode(m[7], :uri) || '/' # request['host'] ||= "#{request[:host_name]}:#{request[:port]}" # # parse query for params - m[9] is the data part of the query # if m[9] # extract_params m[9].split(/[&;]/), request[:params] # end # end return request if request[:client_ip] request[:client_ip] = request['x-forwarded-for'.freeze].to_s.split(/,[\s]?/)[0] || (request[:io].io.to_io.remote_address.ip_address) rescue 'unknown IP'.freeze request[:version] ||= '1' request[:scheme] ||= request['x-forwarded-proto'.freeze] ? request['x-forwarded-proto'.freeze].downcase : ( request[:io].ssl? ? 'https'.freeze : 'http'.freeze) tmp = (request['host'.freeze] || request[:authority] || ''.freeze).split(':') request[:host_name] = tmp[0] request[:port] = tmp[1] || nil tmp = (request[:query] ||= request[:path] ).split('?', 2) request[:path] = tmp[0].chomp('/') request[:original_path] = tmp[0].freeze request[:quary_params] = tmp[1] extract_params tmp[1].split(/[&;]/.freeze), (request[:params] ||= {}) if tmp[1] if request['cookie'.freeze] if request['cookie'.freeze].is_a?(Array) tmp = [] request['cookie'.freeze].each {|s| s.split(/[;,][\s]?/.freeze).each { |c| tmp << c } } request['cookie'.freeze] = tmp extract_header tmp, request. else extract_header request['cookie'.freeze].split(/[;,][\s]?/.freeze), request. end elsif request['set-cookie'.freeze] request['set-cookie'.freeze] = [ request['set-cookie'.freeze] ] unless request['set-cookie'.freeze].is_a?(Array) tmp = [] request['set-cookie'.freeze].each {|s| tmp << s.split(/[;][\s]?/.freeze)[0] } request['set-cookie'.freeze] = tmp extract_header tmp, request. end read_body request if request[:body] request end |
.read_body(request) ⇒ Object
read the body’s data and parse any incoming data.
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/iodine/http/request.rb', line 341 def self.read_body request # save body for Rack, if applicable request[:rack_input] = StringIO.new(request[:body].dup.force_encoding(::Encoding::ASCII_8BIT)) if ::Iodine::Http.on_http == ::Iodine::Http::Rack # parse content case request['content-type'.freeze].to_s when /x-www-form-urlencoded/ extract_params request.delete(:body).split(/[&;]/), request[:params] #, :form # :uri when /multipart\/form-data/ read_multipart request, request, request.delete(:body) when /text\/xml/ # to-do support xml? make_utf8! request[:body] nil when /application\/json/ JSON.parse(make_utf8! request[:body]).each {|k, v| add_param_to_hash k, v, request[:params]} rescue true end end |
.read_multipart(request, headers, part, name_prefix = '') ⇒ Object
parse a mime/multipart body or part.
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/iodine/http/request.rb', line 360 def self.read_multipart request, headers, part, name_prefix = '' if headers['content-type'].to_s =~ /multipart/i tmp = {} extract_header headers['content-type'].split(/[;,][\s]?/), tmp boundry = tmp[:boundary] if tmp[:name] if name_prefix.empty? name_prefix << tmp[:name] else name_prefix << "[#{tmp[:name]}]" end end part.split(/([\r]?\n)?--#{boundry}(--)?[\r]?\n/).each do |p| unless p.strip.empty? || p=='--'.freeze # read headers h = {} m = p.slice! /\A[^\r\n]*[\r]?\n/ while m break if m =~ /\A[\r]?\n/ m = m.match(/^([^:]+):[\s]?([^\r\n]+)/) h[m[1].downcase] = m[2] if m m = p.slice! /\A[^\r\n]*[\r]?\n/ end # send headers and body to be read read_multipart request, h, p, name_prefix end end return end # require a part body to exist (data exists) for parsing return true if part.to_s.empty? # convert part to `charset` if charset is defined? if !headers['content-disposition'.freeze] Iodine.error "Wrong multipart format with headers: #{headers} and body: #{part}" return end cd = {} extract_header headers['content-disposition'.freeze].split(/[;,][\s]?/), cd if name_prefix.empty? name = cd[:name][1..-2] else name = "#{name_prefix}[cd[:name][1..-2]}]" end if headers['content-type'.freeze] add_param_to_hash "#{name}[data]", part, request[:params] add_param_to_hash "#{name}[type]", make_utf8!(headers['content-type'.freeze]), request[:params] cd.each {|k,v| add_param_to_hash "#{name}[#{k.to_s}]", make_utf8!(v[1..-2].to_s), request[:params] unless k == :name || !v} else add_param_to_hash name, uri_decode!(part), request[:params] end true end |
.rubyfy!(string) ⇒ Object
Changes String to a Ruby Object, if it’s a special string…
327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/iodine/http/request.rb', line 327 def self.rubyfy!(string) return string unless string.is_a?(String) try_utf8! string if string == 'true'.freeze string = true elsif string == 'false'.freeze string = false elsif string.to_i.to_s == string string = string.to_i end string end |
.try_utf8!(string, encoding = ::Encoding::UTF_8) ⇒ Object
re-encodes a string into UTF-8
253 254 255 256 257 |
# File 'lib/iodine/http/request.rb', line 253 def self.try_utf8!(string, encoding= ::Encoding::UTF_8) return false unless string string.force_encoding(::Encoding::ASCII_8BIT) unless string.force_encoding(encoding).valid_encoding? string end |
.uri_decode!(s) ⇒ Object
decode form / uri data (including the ‘+’ sign as a space (%20) replacement).
311 312 313 |
# File 'lib/iodine/http/request.rb', line 311 def self.uri_decode! s s.gsub!('+'.freeze, '%20'.freeze); s.gsub!(/\%[0-9a-f]{2}/i) {|m| m[1..2].to_i(16).chr}; s.gsub!(/&#[0-9]{4};/i) {|m| [m[2..5].to_i].pack 'U'.freeze }; s end |
Instance Method Details
#base_url(switch_scheme = nil) ⇒ Object
the base url ([http/https]://host)
100 101 102 |
# File 'lib/iodine/http/request.rb', line 100 def base_url switch_scheme = nil "#{switch_scheme || self[:scheme]}://#{self[:host_name]}#{self[:port]? ":#{self[:port]}" : ''}" end |
#connect? ⇒ Boolean
returns true of the method == CONNECT
170 171 172 |
# File 'lib/iodine/http/request.rb', line 170 def connect? self[:method] == HTTP_CONNECT end |
#cookies ⇒ Object
the cookies sent by the client.
72 73 74 |
# File 'lib/iodine/http/request.rb', line 72 def self[:cookies] end |
#delete? ⇒ Boolean
returns true of the method == DELETE
155 156 157 |
# File 'lib/iodine/http/request.rb', line 155 def delete? self[:method] == HTTP_DELETE end |
#get? ⇒ Boolean
returns true of the method == GET
134 135 136 |
# File 'lib/iodine/http/request.rb', line 134 def get? self[:method] == HTTP_GET end |
#head? ⇒ Boolean
returns true of the method == HEAD
140 141 142 |
# File 'lib/iodine/http/request.rb', line 140 def head? self[:method] == HTTP_HEAD end |
#headers ⇒ Object
the request’s headers
56 57 58 |
# File 'lib/iodine/http/request.rb', line 56 def headers self.select {|k,v| k.is_a? String } end |
#io ⇒ Iodine::Http, ...
Returns the Protocol used for the request.
121 122 123 |
# File 'lib/iodine/http/request.rb', line 121 def io self[:io] end |
#json? ⇒ Boolean
returns true if the request is of type JSON.
180 181 182 |
# File 'lib/iodine/http/request.rb', line 180 def json? self[HTTP_CTYPE] =~ HTTP_JSON end |
#options? ⇒ Boolean
returns true of the method == OPTIONS
165 166 167 |
# File 'lib/iodine/http/request.rb', line 165 def self[:method] == HTTP_OPTIONS end |
#original_path ⇒ Object
the original (frozen) path (resource requested).
82 83 84 |
# File 'lib/iodine/http/request.rb', line 82 def original_path self[:original_path] end |
#params ⇒ Object
the parameters sent by the client.
68 69 70 |
# File 'lib/iodine/http/request.rb', line 68 def params self[:params] end |
#patch? ⇒ Boolean
returns true of the method == PATCH
175 176 177 |
# File 'lib/iodine/http/request.rb', line 175 def patch? self[:method] == HTTP_PATCH end |
#path ⇒ Object
the requested path (rewritable).
87 88 89 |
# File 'lib/iodine/http/request.rb', line 87 def path self[:path] end |
#path=(new_path) ⇒ Object
90 91 92 |
# File 'lib/iodine/http/request.rb', line 90 def path=(new_path) self[:path] = new_path end |
#post? ⇒ Boolean
returns true of the method == POST
145 146 147 |
# File 'lib/iodine/http/request.rb', line 145 def post? self[:method] == HTTP_POST end |
#put? ⇒ Boolean
returns true of the method == PUT
150 151 152 |
# File 'lib/iodine/http/request.rb', line 150 def put? self[:method] == HTTP_PUT end |
#query ⇒ Object
the query string
77 78 79 |
# File 'lib/iodine/http/request.rb', line 77 def query self[:query] end |
#request_method ⇒ Object
the request’s method (GET, POST… etc’).
60 61 62 |
# File 'lib/iodine/http/request.rb', line 60 def request_method self[:method] end |
#request_method=(value) ⇒ Object
set request’s method (GET, POST… etc’).
64 65 66 |
# File 'lib/iodine/http/request.rb', line 64 def request_method= value self[:method] = value end |
#request_url(switch_scheme = nil) ⇒ Object
the request’s url, without any GET parameters ([http/https]://host/path)
105 106 107 |
# File 'lib/iodine/http/request.rb', line 105 def request_url switch_scheme = nil "#{base_url switch_scheme}#{self[:original_path]}" end |
#scheme ⇒ Object
the protocol’s scheme (http/https/ws/wss) managing this request
110 111 112 |
# File 'lib/iodine/http/request.rb', line 110 def scheme self[:scheme] end |
#session ⇒ Hash like storage
Returns the session storage object IF a session was already initialized (use the response to initialize a session).
126 127 128 |
# File 'lib/iodine/http/request.rb', line 126 def session self[:session] end |
#ssl? ⇒ true, false Also known as: secure?
Returns true if the requested was an SSL protocol (true also if the connection is clear-text behind an SSL Proxy, such as with some PaaS providers).
115 116 117 |
# File 'lib/iodine/http/request.rb', line 115 def ssl? self[:io].ssl? || self[:scheme] == 'https'.freeze || self[:scheme] == 'wss'.freeze end |
#trace? ⇒ Boolean
returns true of the method == TRACE
160 161 162 |
# File 'lib/iodine/http/request.rb', line 160 def trace? self[:method] == HTTP_TRACE end |
#version ⇒ Object
The HTTP version for this request
95 96 97 |
# File 'lib/iodine/http/request.rb', line 95 def version self[:version] end |
#websocket? ⇒ Boolean Also known as: upgrade?
returns true if this is a websocket upgrade request
189 190 191 |
# File 'lib/iodine/http/request.rb', line 189 def websocket? @is_websocket ||= (self['upgrade'.freeze] && self['upgrade'.freeze].to_s =~ /websocket/i.freeze && self['connection'.freeze].to_s =~ /upg/i.freeze && true) end |
#xml? ⇒ Boolean
returns true if the request is of type XML.
185 186 187 |
# File 'lib/iodine/http/request.rb', line 185 def xml? self[HTTP_CTYPE].match HTTP_XML end |