Object
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.
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
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
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
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
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
# 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
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.
Generated with the Darkfish Rdoc Generator 1.1.6.