Parent

Mongrel::HttpRequest

When a handler is found for a registered URI then this class is constructed and passed to your HttpHandler::process method. You should assume that one handler processes all requests. Included in the HttpRequest is a HttpRequest.params Hash that matches common CGI params, and a HttpRequest.body which is a string containing the request body (raw for now).

The HttpRequest.initialize method will convert any request that is larger than Const::MAX_BODY into a Tempfile and use that as the body. Otherwise it uses a StringIO object. To be safe, you should assume it works like a file.

The HttpHandler.request_notify system is implemented by having HttpRequest call HttpHandler.request_begins, HttpHandler.request_progress, HttpHandler.process during the IO processing. This adds a small amount of overhead but lets you implement finer controlled handlers and filters.

Attributes

body[R]
params[R]

Public Class Methods

escape(s) click to toggle source

Performs URI escaping so that you can construct proper query strings faster. Use this rather than the cgi.rb version since it’s faster. (Stolen from Camping).

     # File lib/mongrel/http_request.rb, line 119
119:     def self.escape(s)
120:       s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/) {
121:         '%'+$1.unpack('H2'*$1.size).join('%').upcase
122:       }.tr(' ', '+') 
123:     end
new(params, socket, dispatchers) click to toggle source

You don’t really call this. It’s made for you. Main thing it does is hook up the params, and store any remaining body data into the HttpRequest.body attribute.

    # File lib/mongrel/http_request.rb, line 25
25:     def initialize(params, socket, dispatchers)
26:       @params = params
27:       @socket = socket
28:       @dispatchers = dispatchers
29:       content_length = @params[Const::CONTENT_LENGTH].to_i
30:       remain = content_length - @params.http_body.length
31:       
32:       # tell all dispatchers the request has begun
33:       @dispatchers.each do |dispatcher|
34:         dispatcher.request_begins(@params) 
35:       end unless @dispatchers.nil? || @dispatchers.empty?
36: 
37:       # Some clients (like FF1.0) report 0 for body and then send a body.  This will probably truncate them but at least the request goes through usually.
38:       if remain <= 0
39:         # we've got everything, pack it up
40:         @body = StringIO.new
41:         @body.write @params.http_body
42:         update_request_progress(0, content_length)
43:       elsif remain > 0
44:         # must read more data to complete body
45:         if remain > Const::MAX_BODY
46:           # huge body, put it in a tempfile
47:           @body = Tempfile.new(Const::MONGREL_TMP_BASE)
48:           @body.binmode
49:         else
50:           # small body, just use that
51:           @body = StringIO.new 
52:         end
53: 
54:         @body.write @params.http_body
55:         read_body(remain, content_length)
56:       end
57: 
58:       @body.rewind if @body
59:     end
query_parse(qs, d = '&;') click to toggle source

Parses a query string by breaking it up at the ’&’ and ’;’ characters. You can also use this to parse cookies by changing the characters used in the second parameter (which defaults to ’&;’.

     # File lib/mongrel/http_request.rb, line 137
137:     def self.query_parse(qs, d = '&;')
138:       params = {}
139:       (qs||'').split(/[#{d}] */).inject(params) { |h,p|
140:         k, v=unescape(p).split('=',2)
141:         if cur = params[k]
142:           if cur.class == Array
143:             params[k] << v
144:           else
145:             params[k] = [cur, v]
146:           end
147:         else
148:           params[k] = v
149:         end
150:       }
151: 
152:       return params
153:     end
unescape(s) click to toggle source

Unescapes a URI escaped string. (Stolen from Camping).

     # File lib/mongrel/http_request.rb, line 127
127:     def self.unescape(s)
128:       s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/){
129:         [$1.delete('%')].pack('H*')
130:       } 
131:     end

Public Instance Methods

read_body(remain, total) click to toggle source

Does the heavy lifting of properly reading the larger body requests in small chunks. It expects @body to be an IO object, @socket to be valid, and will set @body = nil if the request fails. It also expects any initial part of the body that has been read to be in the @body already.

    # File lib/mongrel/http_request.rb, line 74
74:     def read_body(remain, total)
75:       begin
76:         # write the odd sized chunk first
77:         @params.http_body = read_socket(remain % Const::CHUNK_SIZE)
78: 
79:         remain -= @body.write(@params.http_body)
80: 
81:         update_request_progress(remain, total)
82: 
83:         # then stream out nothing but perfectly sized chunks
84:         until remain <= 0 or @socket.closed?
85:           # ASSUME: we are writing to a disk and these writes always write the requested amount
86:           @params.http_body = read_socket(Const::CHUNK_SIZE)
87:           remain -= @body.write(@params.http_body)
88: 
89:           update_request_progress(remain, total)
90:         end
91:       rescue Object => e
92:         STDERR.puts "#{Time.now}: Error reading HTTP body: #{e.inspect}"
93:         STDERR.puts e.backtrace.join("\n")
94:         # any errors means we should delete the file, including if the file is dumped
95:         @socket.close rescue nil
96:         @body.delete if @body.class == Tempfile
97:         @body = nil # signals that there was a problem
98:       end
99:     end
read_socket(len) click to toggle source
     # File lib/mongrel/http_request.rb, line 101
101:     def read_socket(len)
102:       if !@socket.closed?
103:         data = @socket.read(len)
104:         if !data
105:           raise "Socket read return nil"
106:         elsif data.length != len
107:           raise "Socket read returned insufficient data: #{data.length}"
108:         else
109:           data
110:         end
111:       else
112:         raise "Socket already closed when reading."
113:       end
114:     end

Private Instance Methods

update_request_progress(clen, total) click to toggle source

updates all dispatchers about our progress

    # File lib/mongrel/http_request.rb, line 62
62:     def update_request_progress(clen, total)
63:       return if @dispatchers.nil? || @dispatchers.empty?
64:       @dispatchers.each do |dispatcher|
65:         dispatcher.request_progress(@params, clen, total) 
66:       end 
67:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.