Class: Mongrel2::Request
- Inherits:
-
Object
- Object
- Mongrel2::Request
- Extended by:
- Loggability
- Defined in:
- lib/mongrel2/request.rb
Overview
The Mongrel2 Request base class. Derivatives of this class represent a request from a Mongrel2 server.
Direct Known Subclasses
Class Attribute Summary collapse
-
.request_types ⇒ Object
readonly
Returns the value of attribute request_types.
Instance Attribute Summary collapse
-
#body ⇒ Object
The request body data, if there is any, as an IO(ish) object.
-
#conn_id ⇒ Object
readonly
The listener ID on the server.
-
#headers ⇒ Object
(also: #header)
readonly
The Mongrel2::Table object that contains the request headers.
-
#path ⇒ Object
readonly
The path component of the requested URL in HTTP, or the equivalent for other request types.
-
#raw ⇒ Object
readonly
The raw request content, if the request was parsed from mongrel2.
-
#sender_id ⇒ Object
readonly
The UUID of the requesting mongrel server.
Class Method Summary collapse
-
.parse(raw_request) ⇒ Object
Parse the given
raw_request
from a Mongrel2 server and return an appropriate request object. -
.register_request_type(subclass, *req_methods) ⇒ Object
Register the specified
subclass
as the class to instantiate when theMETHOD
header is one of the specifiedreq_methods
. -
.response_class ⇒ Object
Return the Mongrel2::Response class that corresponds with the receiver.
-
.subclass_for_method(methname) ⇒ Object
Return the Mongrel2::Request class registered for the request method
methname
.
Instance Method Summary collapse
-
#extended_reply? ⇒ Boolean
Indicate that a request is never an extended reply.
-
#initialize(sender_id, conn_id, path, headers, body = '', raw = nil) ⇒ Request
constructor
Create a new Request object with the given
sender_id
,conn_id
,path
,headers
, andbody
. -
#inspect ⇒ Object
Returns a string containing a human-readable representation of the Request, suitable for debugging.
-
#is_disconnect? ⇒ Boolean
Return
true
if the request is a special ‘disconnect’ notification from Mongrel2. -
#remote_ip ⇒ Object
Fetch the original requestor IP address.
-
#response ⇒ Object
Create a Mongrel2::Response that will respond to the same server/connection as the receiver.
-
#server_chroot ⇒ Object
Return the chroot directory of the mongrel2 daemon that received this request as a Pathname.
-
#socket_id ⇒ Object
Returns a string containing the request’s sender and connection IDs separated by a colon.
-
#upload_done? ⇒ Boolean
Returns
true
if this request is an ‘asynchronous upload done’ notification. -
#upload_headers_match? ⇒ Boolean
Returns
true
if this request is an ‘asynchronous upload done’ notification and the two headers match (trivial guard against forgery). -
#upload_started? ⇒ Boolean
Returns
true
if this request is an ‘asynchronous upload started’ notification. -
#uploaded_file ⇒ Object
The Pathname, relative to Mongrel2’s chroot path, of the uploaded entity body.
-
#valid_upload? ⇒ Boolean
Returns true if this request is an asynchronous upload, and the filename of the finished request matches the one from the starting notification.
Constructor Details
#initialize(sender_id, conn_id, path, headers, body = '', raw = nil) ⇒ Request
Create a new Request object with the given sender_id
, conn_id
, path
, headers
, and body
. The optional raw
is for the raw request content, which can be useful later for debugging.
114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/mongrel2/request.rb', line 114 def initialize( sender_id, conn_id, path, headers, body='', raw=nil ) @sender_id = sender_id @conn_id = Integer( conn_id ) @path = path @headers = Mongrel2::Table.new( headers ) @raw = raw @body = self.make_entity_body( body ) @response = nil end |
Class Attribute Details
.request_types ⇒ Object (readonly)
Returns the value of attribute request_types.
24 25 26 |
# File 'lib/mongrel2/request.rb', line 24 def request_types @request_types end |
Instance Attribute Details
#body ⇒ Object
The request body data, if there is any, as an IO(ish) object
146 147 148 |
# File 'lib/mongrel2/request.rb', line 146 def body @body end |
#conn_id ⇒ Object (readonly)
The listener ID on the server
135 136 137 |
# File 'lib/mongrel2/request.rb', line 135 def conn_id @conn_id end |
#headers ⇒ Object (readonly) Also known as: header
The Mongrel2::Table object that contains the request headers
142 143 144 |
# File 'lib/mongrel2/request.rb', line 142 def headers @headers end |
#path ⇒ Object (readonly)
The path component of the requested URL in HTTP, or the equivalent for other request types
139 140 141 |
# File 'lib/mongrel2/request.rb', line 139 def path @path end |
#raw ⇒ Object (readonly)
The raw request content, if the request was parsed from mongrel2
149 150 151 |
# File 'lib/mongrel2/request.rb', line 149 def raw @raw end |
#sender_id ⇒ Object (readonly)
The UUID of the requesting mongrel server
132 133 134 |
# File 'lib/mongrel2/request.rb', line 132 def sender_id @sender_id end |
Class Method Details
.parse(raw_request) ⇒ Object
Parse the given raw_request
from a Mongrel2 server and return an appropriate request object.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/mongrel2/request.rb', line 29 def self::parse( raw_request ) sender, conn_id, path, rest = raw_request.split( ' ', 4 ) self.log.debug "Parsing request for %p from %s:%s (rest: %p...)" % [ path, sender, conn_id, rest[0,20] ] # Extract the headers and the body, ignore the rest headers, rest = TNetstring.parse( rest ) body, _ = TNetstring.parse( rest ) # Headers will be a JSON String when not using the TNetString protocol if headers.is_a?( String ) self.log.debug " parsing old-style headers" headers = Yajl::Parser.parse( headers ) end # This isn't supposed to happen, but guard against it anyway headers['METHOD'] =~ /^(\w+)$/ or raise Mongrel2::UnhandledMethodError, headers['METHOD'] req_method = $1.untaint.to_sym self.log.debug "Request method is: %p" % [ req_method ] concrete_class = self.subclass_for_method( req_method ) return concrete_class.new( sender, conn_id, path, headers, body, raw_request ) end |
.register_request_type(subclass, *req_methods) ⇒ Object
Register the specified subclass
as the class to instantiate when the METHOD
header is one of the specified req_methods
. This method exists for frameworks which wish to provide their own Request types.
For example, if your framework has a JSONRequest class that inherits from Mongrel2::JSONRequest, and you want it to be returned from Mongrel2::Request.parse for METHOD=JSON requests:
class MyFramework::JSONRequest < Mongrel2::JSONRequest
register_request_type self, 'JSON'
# Override #initialize to do any stuff specific to your
# request type, but you'll likely want to super() to
# Mongrel2::JSONRequest.
def initialize( * )
super
# Do some other stuff
end
end # class MyFramework::JSONRequest
If you wish one of your subclasses to be used instead of Mongrel2::Request for the default request class, register it with a METHOD of :__default.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/mongrel2/request.rb', line 78 def self::register_request_type( subclass, *req_methods ) self.log.debug "Registering %p for %p requests" % [ subclass, req_methods ] req_methods.each do |methname| if methname == :__default # Clear cached lookups self.log.info "Registering %p as the default request type." % [ subclass ] Mongrel2::Request.request_types.delete_if {|_, klass| klass == Mongrel2::Request } Mongrel2::Request.request_types.default_proc = lambda {|h,k| h[k] = subclass } else self.log.info "Registering %p for the %p method." % [ subclass, methname ] Mongrel2::Request.request_types[ methname.to_sym ] = subclass end end end |
.response_class ⇒ Object
Return the Mongrel2::Response class that corresponds with the receiver.
101 102 103 |
# File 'lib/mongrel2/request.rb', line 101 def self::response_class return Mongrel2::Response end |
.subclass_for_method(methname) ⇒ Object
Return the Mongrel2::Request class registered for the request method methname
.
95 96 97 |
# File 'lib/mongrel2/request.rb', line 95 def self::subclass_for_method( methname ) return Mongrel2::Request.request_types[ methname.to_sym ] end |
Instance Method Details
#extended_reply? ⇒ Boolean
Indicate that a request is never an extended reply.
252 253 254 |
# File 'lib/mongrel2/request.rb', line 252 def extended_reply? return false end |
#inspect ⇒ Object
Returns a string containing a human-readable representation of the Request, suitable for debugging.
271 272 273 274 275 276 277 278 |
# File 'lib/mongrel2/request.rb', line 271 def inspect return "#<%p:0x%016x %s (%s)>" % [ self.class, self.object_id * 2, self.inspect_details, self.socket_id ] end |
#is_disconnect? ⇒ Boolean
Return true
if the request is a special ‘disconnect’ notification from Mongrel2.
170 171 172 |
# File 'lib/mongrel2/request.rb', line 170 def is_disconnect? return false end |
#remote_ip ⇒ Object
Fetch the original requestor IP address.
176 177 178 179 |
# File 'lib/mongrel2/request.rb', line 176 def remote_ip ips = [ self.headers.x_forwarded_for ] return IPAddr.new( ips.flatten.first ) end |
#response ⇒ Object
Create a Mongrel2::Response that will respond to the same server/connection as the receiver. If you wish your specialized Request class to have a corresponding response type, you can override the Mongrel2::Request.response_class method to achieve that.
164 165 166 |
# File 'lib/mongrel2/request.rb', line 164 def response return @response ||= self.class.response_class.from_request( self ) end |
#server_chroot ⇒ Object
Return the chroot directory of the mongrel2 daemon that received this request as a Pathname.
210 211 212 213 214 215 216 217 218 219 |
# File 'lib/mongrel2/request.rb', line 210 def server_chroot route = Mongrel2::Config::Route.for_request( self ) or raise Mongrel2::UploadError, "couldn't find the route config for %s" % [ self ] server = route.host.server path = server.chroot path = '/' if path.empty? return Pathname( path ) end |
#socket_id ⇒ Object
Returns a string containing the request’s sender and connection IDs separated by a colon.
263 264 265 |
# File 'lib/mongrel2/request.rb', line 263 def socket_id return "%s:%d" % [ self.sender_id, self.conn_id ] end |
#upload_done? ⇒ Boolean
Returns true
if this request is an ‘asynchronous upload done’ notification.
230 231 232 233 |
# File 'lib/mongrel2/request.rb', line 230 def upload_done? return self.headers.member?( :x_mongrel2_upload_start ) && self.headers.member?( :x_mongrel2_upload_done ) end |
#upload_headers_match? ⇒ Boolean
Returns true
if this request is an ‘asynchronous upload done’ notification and the two headers match (trivial guard against forgery)
238 239 240 241 |
# File 'lib/mongrel2/request.rb', line 238 def upload_headers_match? return self.upload_done? && self.headers.x_mongrel2_upload_start == self.headers.x_mongrel2_upload_done end |
#upload_started? ⇒ Boolean
Returns true
if this request is an ‘asynchronous upload started’ notification.
223 224 225 226 |
# File 'lib/mongrel2/request.rb', line 223 def upload_started? return self.headers.member?( :x_mongrel2_upload_start ) && !self.headers.member?( :x_mongrel2_upload_done ) end |
#uploaded_file ⇒ Object
The Pathname, relative to Mongrel2’s chroot path, of the uploaded entity body.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/mongrel2/request.rb', line 188 def uploaded_file raise Mongrel2::UploadError, "invalid upload: upload headers don't match" unless self.upload_headers_match? relpath = Pathname( self.headers.x_mongrel2_upload_done ) chrooted = self.server_chroot + relpath if chrooted.exist? return chrooted elsif relpath.exist? return relpath else self.log.error "uploaded body %s not found: tried relative to cwd and server chroot (%s)" % [ relpath, chrooted ] raise Mongrel2::UploadError, "couldn't find the path to uploaded body %p." % [ chrooted.to_s ] end end |
#valid_upload? ⇒ Boolean
Returns true if this request is an asynchronous upload, and the filename of the finished request matches the one from the starting notification.
246 247 248 |
# File 'lib/mongrel2/request.rb', line 246 def valid_upload? return self.upload_done? && self.upload_headers_match? end |