Class: Mongrel::HttpRequest

Inherits:
Object
  • Object
show all
Defined in:
lib/mongrel.rb

Overview

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.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params, initial_body, socket, notifier) ⇒ HttpRequest

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.

TODO: Implement tempfile removal when the request is done.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/mongrel.rb', line 189

def initialize(params, initial_body, socket, notifier)
  @params = params
  @socket = socket

  clen = params[Const::CONTENT_LENGTH].to_i - initial_body.length
  total = clen

  if clen > Const::MAX_BODY
    @body = Tempfile.new(Const::MONGREL_TMP_BASE)
    @body.binmode
  else
    @body = StringIO.new
  end

  begin
    @body.write(initial_body)
    notifier.request_begins(params) if notifier

    # write the odd sized chunk first
    clen -= @body.write(@socket.read(clen % Const::CHUNK_SIZE))
    notifier.request_progress(params, clen, total) if notifier

    # then stream out nothing but perfectly sized chunks
    while clen > 0 and !@socket.closed?
      data = @socket.read(Const::CHUNK_SIZE)
      # have to do it this way since @socket.eof? causes it to block
      raise "Socket closed or read failure" if not data or data.length != Const::CHUNK_SIZE
      clen -= @body.write(data)
      # ASSUME: we are writing to a disk and these writes always write the requested amount
      notifier.request_progress(params, clen, total) if notifier
    end

    # rewind to keep the world happy
    @body.rewind
  rescue Object
    # any errors means we should delete the file, including if the file is dumped
    @socket.close unless @socket.closed?
    @body.delete if @body.class == Tempfile
    @body = nil # signals that there was a problem
  end
end

Instance Attribute Details

#bodyObject (readonly)

Returns the value of attribute body.



182
183
184
# File 'lib/mongrel.rb', line 182

def body
  @body
end

#paramsObject (readonly)

Returns the value of attribute params.



182
183
184
# File 'lib/mongrel.rb', line 182

def params
  @params
end

Class Method Details

.escape(s) ⇒ Object

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).



234
235
236
237
238
# File 'lib/mongrel.rb', line 234

def self.escape(s)
  s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
    '%'+$1.unpack('H2'*$1.size).join('%').upcase
  }.tr(' ', '+') 
end

.query_parse(qs, d = '&;') ⇒ Object

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 ‘&;’.



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/mongrel.rb', line 252

def self.query_parse(qs, d = '&;')
  params = {}
  (qs||'').split(/[#{d}] */n).inject(params) { |h,p|
    k, v=unescape(p).split('=',2)
    if cur = params[k]
      if cur.class == Array
        params[k] << v
      else
        params[k] = [cur, v]
      end
    else
      params[k] = v
    end
  }

  return params
end

.unescape(s) ⇒ Object

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



242
243
244
245
246
# File 'lib/mongrel.rb', line 242

def self.unescape(s)
  s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
    [$1.delete('%')].pack('H*')
  } 
end