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, &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 = '') ⇒ 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, &block) ⇒ Object
Adds paramaters to a Hash object, according to the Iodine’s server conventions.
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 301 302 303 304 |
# File 'lib/iodine/http/request.rb', line 268 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!(']'); 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.
263 264 265 |
# File 'lib/iodine/http/request.rb', line 263 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
319 320 321 322 323 324 325 |
# File 'lib/iodine/http/request.rb', line 319 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
307 308 309 310 311 312 313 |
# File 'lib/iodine/http/request.rb', line 307 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).
327 328 329 |
# File 'lib/iodine/http/request.rb', line 327 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
249 250 251 252 253 |
# File 'lib/iodine/http/request.rb', line 249 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 244 245 246 |
# 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.delete :headers_size 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.
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/iodine/http/request.rb', line 345 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/ extract_params request[:body].read.split(/[&;]/), request[:params] #, :form # :uri when /multipart\/form-data/ read_multipart request, request when /text\/xml/ # to-do support xml? # request[:xml] = make_utf8! request[:body].read nil when /application\/json/ 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 = '') ⇒ Object
parse a mime/multipart body or part.
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 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 |
# File 'lib/iodine/http/request.rb', line 366 def self.read_multipart request, headers = {}, boundary = [], name_prefix = '' body = request[:body] return unless headers['content-type'].to_s =~ /multipart/i part_headers = {} extract_header headers['content-type'].split(/[;,][\s]?/), 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]}]" 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/ 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 =~ /\:/ until line.nil? || line =~ /^[\r]?\n/ tmp = line.strip.split ':', 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]?/), 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" if part_headers['content-type'.freeze] if part_headers['content-type'.freeze] =~ /multipart/i 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', encoding: 'binary' 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" && 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, uri_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…
331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/iodine/http/request.rb', line 331 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
256 257 258 259 260 |
# File 'lib/iodine/http/request.rb', line 256 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).
315 316 317 |
# File 'lib/iodine/http/request.rb', line 315 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 |