Class: ServerSide::HTTP::Request
- Includes:
- Static
- Defined in:
- lib/serverside/request.rb
Overview
The Request class encapsulates HTTP requests. The request class contains methods for parsing the request and rendering a response. HTTP requests are created by the connection. Descendants of HTTPRequest can be created When a connection is created, it creates new requests in a loop until the connection is closed.
Direct Known Subclasses
Constant Summary collapse
- LINE_BREAK =
"\r\n".freeze
- REQUEST_REGEXP =
Here’s a nice one - parses the first line of a request. The expected format is as follows: <method> </path>[?<query>] HTTP/<version>
/([A-Za-z0-9]+)\s(\/[^\/\?]*(?:\/[^\/\?]+)*)\/?(?:\?(.*))?\sHTTP\/(.+)\r/.freeze
- HEADER_REGEXP =
Regexp for parsing headers.
/([^:]+):\s?(.*)\r\n/.freeze
- CONTENT_LENGTH =
'Content-Length'.freeze
- VERSION_1_1 =
'1.1'.freeze
- CONNECTION =
'Connection'.freeze
- CLOSE =
'close'.freeze
- AMPERSAND =
'&'.freeze
- PARAMETER_REGEXP =
Regexp for parsing URI parameters.
/(.+)=(.*)/.freeze
- EQUAL_SIGN =
'='.freeze
- STATUS_CLOSE =
"HTTP/1.1 %d\r\nDate: %s\r\nConnection: close\r\nContent-Type: %s\r\n%s%sContent-Length: %d\r\n\r\n".freeze
- STATUS_STREAM =
"HTTP/1.1 %d\r\nDate: %s\r\nConnection: close\r\nContent-Type: %s\r\n%s%s\r\n".freeze
- STATUS_PERSIST =
"HTTP/1.1 %d\r\nDate: %s\r\nContent-Type: %s\r\n%s%sContent-Length: %d\r\n\r\n".freeze
- STATUS_REDIRECT =
"HTTP/1.1 %d\r\nDate: %s\r\nConnection: close\r\nLocation: %s\r\n\r\n".freeze
- HEADER =
"%s: %s\r\n".freeze
- EMPTY_STRING =
''.freeze
- EMPTY_HASH =
{}.freeze
- SLASH =
'/'.freeze
- LOCATION =
'Location'.freeze
- COOKIE =
'Cookie'
- SET_COOKIE =
"Set-Cookie: %s=%s; path=/; expires=%s\r\n".freeze
- COOKIE_SPLIT =
/[;,] */n.freeze
- COOKIE_REGEXP =
/\s*(.+)=(.*)\s*/.freeze
- COOKIE_EXPIRED_TIME =
Time.at(0).freeze
- CONTENT_TYPE =
"Content-Type".freeze
- CONTENT_TYPE_URL_ENCODED =
'application/x-www-form-urlencoded'.freeze
- MULTIPART_REGEXP =
/multipart\/form-data.*boundary=\"?([^\";,]+)/n.freeze
- CONTENT_DISPOSITION_REGEXP =
/^Content-Disposition: form-data;([^\r]*)/m.freeze
- FIELD_ATTRIBUTE_REGEXP =
/\s*(\w+)=\"([^\"]*)/.freeze
- CONTENT_TYPE_REGEXP =
/^Content-Type: ([^\r]*)/m.freeze
- CONTENT_DISPOSITION =
'Content-Disposition'.freeze
- CONTENT_DESCRIPTION =
'Content-Description'.freeze
Constants included from Static
Static::DIR_LISTING, Static::DIR_LISTING_START, Static::DIR_LISTING_STOP, Static::ETAG_FORMAT, Static::FILE_NOT_FOUND, Static::MAX_AGE, Static::MAX_CACHE_FILE_SIZE, Static::RHTML, Static::TEXT_HTML, Static::TEXT_PLAIN
Constants included from Caching
Caching::CACHE_CONTROL, Caching::ETAG, Caching::ETAG_QUOTE_FORMAT, Caching::EXPIRES, Caching::EXPIRY_ETAG_FORMAT, Caching::EXPIRY_ETAG_REGEXP, Caching::IF_MODIFIED_SINCE, Caching::IF_NONE_MATCH, Caching::IF_NONE_MATCH_REGEXP, Caching::LAST_MODIFIED, Caching::NOT_MODIFIED_CLOSE, Caching::NOT_MODIFIED_PERSIST, Caching::NO_CACHE, Caching::VARY, Caching::WILDCARD
Instance Attribute Summary collapse
-
#body ⇒ Object
readonly
Returns the value of attribute body.
-
#content_length ⇒ Object
readonly
Returns the value of attribute content_length.
-
#content_type ⇒ Object
readonly
Returns the value of attribute content_type.
-
#cookies ⇒ Object
readonly
Returns the value of attribute cookies.
-
#headers ⇒ Object
readonly
Returns the value of attribute headers.
-
#method ⇒ Object
readonly
Returns the value of attribute method.
-
#parameters ⇒ Object
readonly
Returns the value of attribute parameters.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#persistent ⇒ Object
readonly
Returns the value of attribute persistent.
-
#query ⇒ Object
readonly
Returns the value of attribute query.
-
#response_cookies ⇒ Object
readonly
Returns the value of attribute response_cookies.
-
#response_headers ⇒ Object
readonly
Returns the value of attribute response_headers.
-
#socket ⇒ Object
readonly
Returns the value of attribute socket.
-
#version ⇒ Object
readonly
Returns the value of attribute version.
Instance Method Summary collapse
-
#delete_cookie(name) ⇒ Object
Marks a cookie as deleted.
-
#initialize(socket) ⇒ Request
constructor
Initializes the request instance.
-
#parse ⇒ Object
Parses an HTTP request.
-
#parse_body ⇒ Object
parses the body, either by using.
-
#parse_cookies ⇒ Object
Parses cookie values passed in the request.
-
#parse_parameters(query) ⇒ Object
Parses query parameters by splitting the query string and unescaping parameter values.
-
#process ⇒ Object
Processes the request by parsing it and then responding.
-
#redirect(location, permanent = false) ⇒ Object
Send a redirect response.
- #send_file(content, content_type, disposition = :inline, filename = nil, description = nil) ⇒ Object
-
#send_response(status, content_type, body = nil, content_length = nil, headers = nil) ⇒ Object
Sends an HTTP response.
-
#set_cookie(name, value, expires) ⇒ Object
Sets a cookie to be included in the response.
-
#stream(body) ⇒ Object
Streams additional data to the client.
Methods included from Static
#serve_dir, #serve_file, #serve_static, #serve_template
Methods included from Caching
#disable_caching, #etag_validators, #expiry_etag, #send_not_modified_response, #valid_etag?, #valid_stamp?, #validate_cache
Constructor Details
#initialize(socket) ⇒ Request
Initializes the request instance. Any descendants of HTTP::Request which override the initialize method must receive socket as the single argument, and copy it to @socket.
55 56 57 58 |
# File 'lib/serverside/request.rb', line 55 def initialize(socket) @socket = socket @response_headers = {} end |
Instance Attribute Details
#body ⇒ Object (readonly)
Returns the value of attribute body.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def body @body end |
#content_length ⇒ Object (readonly)
Returns the value of attribute content_length.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def content_length @content_length end |
#content_type ⇒ Object (readonly)
Returns the value of attribute content_type.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def content_type @content_type end |
#cookies ⇒ Object (readonly)
Returns the value of attribute cookies.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def @cookies end |
#headers ⇒ Object (readonly)
Returns the value of attribute headers.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def headers @headers end |
#method ⇒ Object (readonly)
Returns the value of attribute method.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def method @method end |
#parameters ⇒ Object (readonly)
Returns the value of attribute parameters.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def parameters @parameters end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def path @path end |
#persistent ⇒ Object (readonly)
Returns the value of attribute persistent.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def persistent @persistent end |
#query ⇒ Object (readonly)
Returns the value of attribute query.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def query @query end |
#response_cookies ⇒ Object (readonly)
Returns the value of attribute response_cookies.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def @response_cookies end |
#response_headers ⇒ Object (readonly)
Returns the value of attribute response_headers.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def response_headers @response_headers end |
#socket ⇒ Object (readonly)
Returns the value of attribute socket.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def socket @socket end |
#version ⇒ Object (readonly)
Returns the value of attribute version.
48 49 50 |
# File 'lib/serverside/request.rb', line 48 def version @version end |
Instance Method Details
#delete_cookie(name) ⇒ Object
Marks a cookie as deleted. The cookie is given an expires stamp in the past.
205 206 207 |
# File 'lib/serverside/request.rb', line 205 def (name) (name, nil, COOKIE_EXPIRED_TIME) end |
#parse ⇒ Object
Parses an HTTP request. If the request is not valid, nil is returned. Otherwise, the HTTP headers are returned. Also determines whether the connection is persistent (by checking the HTTP version and the ‘Connection’ header).
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/serverside/request.rb', line 69 def parse return nil unless @socket.gets =~ REQUEST_REGEXP @method, @path, @query, @version = $1.downcase.to_sym, $2, $3, $4 @parameters = @query ? parse_parameters(@query) : {} @headers = {} while (line = @socket.gets) break if line.nil? || (line == LINE_BREAK) if line =~ HEADER_REGEXP @headers[$1.freeze] = $2.freeze end end @persistent = (@version == VERSION_1_1) && (@headers[CONNECTION] != CLOSE) @cookies = @headers[COOKIE] ? : EMPTY_HASH @response_cookies = nil if @content_length = @headers[CONTENT_LENGTH].to_i @content_type = @headers[CONTENT_TYPE] || CONTENT_TYPE_URL_ENCODED @body = @socket.read(@content_length) rescue nil parse_body end @headers end |
#parse_body ⇒ Object
parses the body, either by using
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/serverside/request.rb', line 121 def parse_body if @content_type == CONTENT_TYPE_URL_ENCODED @parameters.merge! parse_parameters(@body) elsif @content_type =~ MULTIPART_REGEXP boundary = "--#$1" r = /(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r\n/m @body.split(r).each do |pt| headers, payload = pt.split("\r\n\r\n", 2) atts = {} if headers =~ CONTENT_DISPOSITION_REGEXP $1.split(';').map do |part| if part =~ FIELD_ATTRIBUTE_REGEXP atts[$1.to_sym] = $2 end end end if headers =~ CONTENT_TYPE_REGEXP atts[:type] = $1 end if name = atts[:name] atts[:content] = payload @parameters[name.to_sym] = atts[:filename] ? atts : atts[:content] end end end end |
#parse_cookies ⇒ Object
Parses cookie values passed in the request
106 107 108 109 110 111 112 113 |
# File 'lib/serverside/request.rb', line 106 def @headers[COOKIE].split(COOKIE_SPLIT).inject({}) do |m, i| if i =~ COOKIE_REGEXP m[$1.to_sym] = $2.uri_unescape end m end end |
#parse_parameters(query) ⇒ Object
Parses query parameters by splitting the query string and unescaping parameter values.
96 97 98 99 100 101 102 103 |
# File 'lib/serverside/request.rb', line 96 def parse_parameters(query) query.split(AMPERSAND).inject({}) do |m, i| if i =~ PARAMETER_REGEXP m[$1.to_sym] = $2.uri_unescape end m end end |
#process ⇒ Object
Processes the request by parsing it and then responding.
61 62 63 |
# File 'lib/serverside/request.rb', line 61 def process parse && ((respond || true) && @persistent) end |
#redirect(location, permanent = false) ⇒ Object
Send a redirect response.
183 184 185 186 187 188 189 |
# File 'lib/serverside/request.rb', line 183 def redirect(location, permanent = false) @socket << (STATUS_REDIRECT % [permanent ? 301 : 302, Time.now.httpdate, location]) rescue ensure @persistent = false end |
#send_file(content, content_type, disposition = :inline, filename = nil, description = nil) ⇒ Object
173 174 175 176 177 178 179 180 |
# File 'lib/serverside/request.rb', line 173 def send_file(content, content_type, disposition = :inline, filename = nil, description = nil) disposition = filename ? "#{disposition}; filename=#{filename}" : disposition @response_headers[CONTENT_DISPOSITION] = disposition @response_headers[CONTENT_DESCRIPTION] = description if description send_response(200, content_type, content) end |
#send_response(status, content_type, body = nil, content_length = nil, headers = nil) ⇒ Object
Sends an HTTP response.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/serverside/request.rb', line 149 def send_response(status, content_type, body = nil, content_length = nil, headers = nil) @response_headers.merge!(headers) if headers h = @response_headers.inject('') {|m, kv| m << (HEADER % kv)} # calculate content_length if needed. if we dont have the # content_length, we consider the response as a streaming response, # and so the connection will not be persistent. content_length = body.length if content_length.nil? && body @persistent = false if content_length.nil? # Select the right format to use according to circumstances. @socket << ((@persistent ? STATUS_PERSIST : (body ? STATUS_CLOSE : STATUS_STREAM)) % [status, Time.now.httpdate, content_type, h, @response_cookies, content_length]) @socket << body if body rescue @persistent = false end |
#set_cookie(name, value, expires) ⇒ Object
Sets a cookie to be included in the response.
197 198 199 200 201 |
# File 'lib/serverside/request.rb', line 197 def (name, value, expires) @response_cookies ||= "" @response_cookies << (SET_COOKIE % [name, value.to_s.uri_escape, expires.rfc2822]) end |
#stream(body) ⇒ Object
Streams additional data to the client.
192 193 194 |
# File 'lib/serverside/request.rb', line 192 def stream(body) (@socket << body if body) rescue (@persistent = false) end |