Class: Mongrel2::Request

Inherits:
Object
  • Object
show all
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.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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_typesObject (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

#bodyObject

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_idObject (readonly)

The listener ID on the server



135
136
137
# File 'lib/mongrel2/request.rb', line 135

def conn_id
  @conn_id
end

#headersObject (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

#pathObject (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

#rawObject (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_idObject (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_classObject

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.

Returns:

  • (Boolean)


252
253
254
# File 'lib/mongrel2/request.rb', line 252

def extended_reply?
	return false
end

#inspectObject

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.

Returns:

  • (Boolean)


170
171
172
# File 'lib/mongrel2/request.rb', line 170

def is_disconnect?
	return false
end

#remote_ipObject

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

#responseObject

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_chrootObject

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_idObject

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.

Returns:

  • (Boolean)


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)

Returns:

  • (Boolean)


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.

Returns:

  • (Boolean)


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_fileObject

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.

Returns:

  • (Boolean)


246
247
248
# File 'lib/mongrel2/request.rb', line 246

def valid_upload?
	return self.upload_done? && self.upload_headers_match?
end