Module: Pitchfork::HttpResponse
- Included in:
- HttpServer
- Defined in:
- lib/pitchfork/http_response.rb,
ext/pitchfork_http/httpdate.c
Overview
Writes a Rack response to your client using the HTTP/1.1 specification. You use it by simply doing:
status, headers, body = rack_app.call(env)
http_response_write(socket, status, headers, body)
Most header correctness (including Content-Length and Content-Type) is the job of Rack, with the exception of the “Date” and “Status” header.
Constant Summary collapse
- STATUS_CODES =
defined?(Rack::Utils::HTTP_STATUS_CODES) ? Rack::Utils::HTTP_STATUS_CODES : {}
- ILLEGAL_HEADER_VALUE =
/[\x00-\x08\x0A-\x1F]/
Instance Method Summary collapse
- #append_header(buf, key, value) ⇒ Object
-
#err_response(code, response_start_sent) ⇒ Object
internal API, code will always be common-enough-for-even-old-Rack.
-
#http_response_write(socket, status, headers, body, req = Pitchfork::HttpParser.new) ⇒ Object
writes the rack_response to socket as an HTTP response.
-
#httpdate ⇒ Object
Returns a string which represents the time as rfc1123-date of HTTP-date defined by RFC 2616:.
Instance Method Details
#append_header(buf, key, value) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/pitchfork/http_response.rb', line 26 def append_header(buf, key, value) case value when Array # Rack 3 value.each do |v| next if ILLEGAL_HEADER_VALUE.match?(v) buf << "#{key}: #{v}\r\n" end when /\n/ # Rack 2 # avoiding blank, key-only cookies with /\n+/ value.split(/\n+/).each do |v| next if ILLEGAL_HEADER_VALUE.match?(v) buf << "#{key}: #{v}\r\n" end else buf << "#{key}: #{value}\r\n" end end |
#err_response(code, response_start_sent) ⇒ Object
internal API, code will always be common-enough-for-even-old-Rack
21 22 23 24 |
# File 'lib/pitchfork/http_response.rb', line 21 def err_response(code, response_start_sent) "#{response_start_sent ? '' : 'HTTP/1.1 '}" \ "#{code} #{STATUS_CODES[code]}\r\n\r\n" end |
#http_response_write(socket, status, headers, body, req = Pitchfork::HttpParser.new) ⇒ Object
writes the rack_response to socket as an HTTP response
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/pitchfork/http_response.rb', line 45 def http_response_write(socket, status, headers, body, req = Pitchfork::HttpParser.new) hijack = nil if headers code = status.to_i msg = STATUS_CODES[code] start = req.response_start_sent ? '' : 'HTTP/1.1 ' buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \ "Date: #{httpdate}\r\n" \ "Connection: close\r\n".b headers.each do |key, value| case key when %r{\A(?:Date|Connection)\z}i next when "rack.hijack" # This should only be hit under Rack >= 1.5, as this was an illegal # key in Rack < 1.5 hijack = value else append_header(buf, key, value) end end socket.write(buf << "\r\n") end if hijack req.hijacked! hijack.call(socket) elsif body.respond_to?(:each) body.each { |chunk| socket.write(chunk) } else body.call(socket) end end |
#httpdate ⇒ Object
Returns a string which represents the time as rfc1123-date of HTTP-date defined by RFC 2616:
day-of-week, DD month-name CCYY hh:mm:ss GMT
Note that the result is always GMT.
This method is identical to Time#httpdate in the Ruby standard library, except it is implemented in C for performance. We always saw Time#httpdate at or near the top of the profiler output so we decided to rewrite this in C.
Caveats: it relies on a Ruby implementation with the global VM lock, a thread-safe version will be provided when a Unix-only, GVL-free Ruby implementation becomes viable.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'ext/pitchfork_http/httpdate.c', line 43 static VALUE httpdate(VALUE self) { static time_t last; time_t now = time(NULL); /* not a syscall on modern 64-bit systems */ struct tm tm; if (last == now) return buf; last = now; gmtime_r(&now, &tm); /* we can make this thread-safe later if our Ruby loses the GVL */ snprintf(buf_ptr, buf_capa, "%s, %02d %s %4d %02d:%02d:%02d GMT", week + (tm.tm_wday * 4), tm.tm_mday, months + (tm.tm_mon * 4), tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); return buf; } |