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/.freeze
- HTTP_XML =
/text\/xml/.freeze
Class Method Summary collapse
-
.add_param_to_hash(name, value, target, &block) ⇒ 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 = {}, boundary = [], name_prefix = String.new) ⇒ 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.
-
#host_name ⇒ Object
the host’s name (without the port).
-
#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, &block) ⇒ Object
Adds paramaters to a Hash object, according to the Iodine’s server conventions.
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/iodine/http/request.rb', line 280 def self.add_param_to_hash name, value, target, &block 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!(']'.freeze); 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 c.default_proc = block if block 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.
275 276 277 |
# File 'lib/iodine/http/request.rb', line 275 def self.encode_url str (str.to_s.gsub(/[^a-z0-9\*\.\_\-]/i.freeze) {|m| '%%%02x'.freeze % m.ord }).force_encoding(::Encoding::ASCII_8BIT) end |
.extract_header(data, target_hash) ⇒ Object
extracts parameters from header data
331 332 333 334 335 336 337 |
# File 'lib/iodine/http/request.rb', line 331 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
319 320 321 322 323 324 325 |
# File 'lib/iodine/http/request.rb', line 319 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).
339 340 341 |
# File 'lib/iodine/http/request.rb', line 339 def self.form_decode! s s.gsub!(/\%[0-9a-f]{2}/i.freeze) {|m| m[1..2].to_i(16).chr}; s.gsub!(/&#[0-9]{4};/i.freeze) {|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
261 262 263 264 265 |
# File 'lib/iodine/http/request.rb', line 261 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’)
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 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/iodine/http/request.rb', line 200 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.delete :headers_size # 2014 RFC 7239 Forwarded: for=192.0.2.60; proto=http; by=203.0.113.43 if tmp = request['forwarded'.freeze] tmp = tmp.join(';'.freeze) if tmp.is_a?(Array) tmp.match(/proto=([^\s;]+)/i.freeze).tap {|m| request[:scheme] = m[1].downcase if m } unless request[:scheme] tmp.match(/for=[\[]?[\"]?([^\s;\",]+)/i.freeze).tap {|m| request[:client_ip] = m[1] if m } unless request[:client_ip] end request[:client_ip] ||= (request['x-forwarded-for'.freeze].to_s.match(/[^\s\[\"\,]+/.freeze) || request[:io].io.to_io.remote_address.ip_address).to_s rescue 'unknown'.freeze request[:version] ||= '1'.freeze 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(':'.freeze) request[:host_name] = tmp[0] request[:port] = tmp[1] tmp = (request[:query] ||= request[:path] ).split('?'.freeze, 2) request[:path] = tmp[0].chomp('/'.freeze) 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.
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/iodine/http/request.rb', line 357 def self.read_body request # save body for Rack, if applicable # request[:rack_input] = request[:body] if ::Iodine::Http.on_http == ::Iodine::Http::Rack # parse content request[:body].rewind case request['content-type'.freeze].to_s when /x-www-form-urlencoded/.freeze extract_params request[:body].read.split(/[&;]/.freeze), request[:params] #, :form # :uri when /multipart\/form-data/.freeze read_multipart request, request when /text\/xml/.freeze # to-do support xml? # request[:xml] = make_utf8! request[:body].read nil when /application\/json/.freeze JSON.parse(make_utf8! request[:body].read).each {|k, v| add_param_to_hash k, v, request[:params]} rescue true end request[:body].rewind if request[:body] end |
.read_multipart(request, headers = {}, boundary = [], name_prefix = String.new) ⇒ Object
parse a mime/multipart body or part.
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 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 |
# File 'lib/iodine/http/request.rb', line 378 def self.read_multipart request, headers = {}, boundary = [], name_prefix = String.new body = request[:body] return unless headers['content-type'.freeze].to_s =~ /multipart/i.freeze part_headers = {} extract_header headers['content-type'.freeze].split(/[;,][\s]?/.freeze), part_headers boundary << part_headers[:boundary] if part_headers[:name] if name_prefix.empty? name_prefix << part_headers[:name] else name_prefix << "[#{part_headers[:name]}]".freeze end end part_headers.delete :name part_headers.clear line = nil boundary_length = nil true until ( (line = body.gets) ) && line =~ /\A--(#{boundary.join '|'})(--)?[\r]?\n/ until body.eof? return if line =~ /--[\r]?\n/.freeze return boundary.pop if boundary.count > 1 && line.match(/--(#{boundary.join '|'})/)[1] != boundary.last boundary_length = line.bytesize line = body.gets until line.nil? || line =~ /\:/.freeze until line.nil? || line =~ /^[\r]?\n/.freeze tmp = line.strip.split ':'.freeze, 2 return Iodine.error "Http multipart parsing error (multipart header data malformed): #{line}" unless tmp && tmp.count == 2 tmp[0].strip!; tmp[0].downcase!; tmp[1].strip!; part_headers[tmp[0]] = tmp[1] line = body.gets end return if line.nil? if !part_headers['content-disposition'.freeze] Iodine.error "Wrong multipart format with headers: #{part_headers}" return end extract_header part_headers['content-disposition'.freeze].split(/[;,][\s]?/.freeze), part_headers if name_prefix.empty? name = part_headers[:name][1..-2] else name = "#{name_prefix}[part_headers[:name][1..-2]}]" end part_headers.delete :name start_part_pos = body.pos tmp = /\A--(#{boundary.join '|'})(--)?[\r]?\n/ line.clear until ( (line = body.gets) && line =~ tmp) end_part_pos = (body.pos - line.bytesize) - 2 new_part_pos = body.pos body.pos = end_part_pos end_part_pos += 1 unless body.getc =~ /[\r\n]/.freeze end_part_pos += 1 unless body.getc =~ /[\r\n\-]/.freeze if part_headers['content-type'.freeze] if part_headers['content-type'.freeze] =~ /multipart/i.freeze body.pos = start_part_pos read_multipart request, part_headers, boundary, name_prefix else part_headers.delete 'content-disposition'.freeze add_param_to_hash "#{name}[type]", make_utf8!(part_headers['content-type'.freeze]), request[:params] part_headers.each {|k,v| add_param_to_hash "#{name}[#{k.to_s}]", make_utf8!(v[0] == '"' ? v[1..-2].to_s : v), request[:params] if v} tmp = Tempfile.new 'upload'.freeze, encoding: 'binary'.freeze body.pos = start_part_pos ((end_part_pos - start_part_pos)/65_536).to_i.times {tmp << body.read(65_536)} tmp << body.read(end_part_pos - body.pos) add_param_to_hash "#{name}[size]", tmp.size, request[:params] add_param_to_hash "#{name}[file]", tmp, request[:params] do |hash, key| if key == :data || key == "data".freeze && hash.has_key?(:file) && hash[:file].is_a?(::Tempfile) hash[:file].rewind (hash[:data] = hash[:file].read) end end tmp.rewind end else body.pos = start_part_pos add_param_to_hash name, form_decode!( body.read(end_part_pos - start_part_pos) ), request[:params] end body.pos = new_part_pos end end |
.rubyfy!(string) ⇒ Object
Changes String to a Ruby Object, if it’s a special string…
343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/iodine/http/request.rb', line 343 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
268 269 270 271 272 |
# File 'lib/iodine/http/request.rb', line 268 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).
327 328 329 |
# File 'lib/iodine/http/request.rb', line 327 def self.uri_decode! s s.gsub!('+'.freeze, '%20'.freeze); s.gsub!(/\%[0-9a-f]{2}/i.freeze) {|m| m[1..2].to_i(16).chr}; s.gsub!(/&#[0-9]{4};/i.freeze) {|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]}" : ''.freeze}".freeze end |
#connect? ⇒ Boolean
returns true of the method == CONNECT
175 176 177 |
# File 'lib/iodine/http/request.rb', line 175 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
160 161 162 |
# File 'lib/iodine/http/request.rb', line 160 def delete? self[:method] == HTTP_DELETE end |
#get? ⇒ Boolean
returns true of the method == GET
139 140 141 |
# File 'lib/iodine/http/request.rb', line 139 def get? self[:method] == HTTP_GET end |
#head? ⇒ Boolean
returns true of the method == HEAD
145 146 147 |
# File 'lib/iodine/http/request.rb', line 145 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 |
#host_name ⇒ Object
the host’s name (without the port)
115 116 117 |
# File 'lib/iodine/http/request.rb', line 115 def host_name self[:host_name] end |
#io ⇒ Iodine::Http, ...
Returns the Protocol used for the request.
126 127 128 |
# File 'lib/iodine/http/request.rb', line 126 def io self[:io] end |
#json? ⇒ Boolean
returns true if the request is of type JSON.
185 186 187 |
# File 'lib/iodine/http/request.rb', line 185 def json? self[HTTP_CTYPE] =~ HTTP_JSON end |
#options? ⇒ Boolean
returns true of the method == OPTIONS
170 171 172 |
# File 'lib/iodine/http/request.rb', line 170 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
180 181 182 |
# File 'lib/iodine/http/request.rb', line 180 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
150 151 152 |
# File 'lib/iodine/http/request.rb', line 150 def post? self[:method] == HTTP_POST end |
#put? ⇒ Boolean
returns true of the method == PUT
155 156 157 |
# File 'lib/iodine/http/request.rb', line 155 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]}".freeze 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).
131 132 133 |
# File 'lib/iodine/http/request.rb', line 131 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).
120 121 122 |
# File 'lib/iodine/http/request.rb', line 120 def ssl? self[:io].ssl? || self[:scheme] == 'https'.freeze || self[:scheme] == 'wss'.freeze end |
#trace? ⇒ Boolean
returns true of the method == TRACE
165 166 167 |
# File 'lib/iodine/http/request.rb', line 165 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
194 195 196 |
# File 'lib/iodine/http/request.rb', line 194 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.
190 191 192 |
# File 'lib/iodine/http/request.rb', line 190 def xml? self[HTTP_CTYPE].match HTTP_XML end |