Module: Unicorn::HttpResponse
Relationships & Source Files | |
Extension / Inclusion / Inheritance Descendants | |
Included In:
| |
Defined in: | lib/unicorn/http_response.rb, ext/unicorn_http/httpdate.c |
Constant Summary
-
STATUS_CODES =
Internal use only
# File 'lib/unicorn/http_response.rb', line 14defined?(Rack::Utils::HTTP_STATUS_CODES) ? Rack::Utils::HTTP_STATUS_CODES : {}
-
STATUS_WITH_NO_ENTITY_BODY =
Internal use only
# File 'lib/unicorn/http_response.rb', line 16defined?( Rack::Utils::STATUS_WITH_NO_ENTITY_BODY) ? Rack::Utils::STATUS_WITH_NO_ENTITY_BODY : begin warn 'Rack::Utils::STATUS_WITH_NO_ENTITY_BODY missing' {} end
Instance Method Summary
-
#httpdate
Returns a string which represents the time as rfc1123-date of HTTP-date defined by RFC 2616:
- #append_header(buf, key, value) Internal use only
-
#err_response(code, response_start_sent)
Internal use only
internal API, code will always be common-enough-for-even-old-Rack.
-
#http_response_write(socket, status, headers, body, req = Unicorn::HttpRequest.new)
Internal use only
writes the rack_response to socket as an HTTP response.
Instance Method Details
#append_header(buf, key, value)
# File 'lib/unicorn/http_response.rb', line 29
def append_header(buf, key, value) case value when Array # Rack 3 value.each { |v| buf << "#{key}: #{v}\r\n" } when /\n/ # Rack 2 # avoiding blank, key-only cookies with /\n+/ value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" } else buf << "#{key}: #{value}\r\n" end end
#err_response(code, response_start_sent)
internal API, code will always be common-enough-for-even-old-Rack
# File 'lib/unicorn/http_response.rb', line 24
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 = Unicorn::HttpRequest.new)
writes the rack_response to socket as an HTTP response
# File 'lib/unicorn/http_response.rb', line 42
def http_response_write(socket, status, headers, body, req = Unicorn::HttpRequest.new) hijack = nil do_chunk = false if headers code = status.to_i msg = STATUS_CODES[code] start = req.response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze term = STATUS_WITH_NO_ENTITY_BODY.include?(code) || false buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \ "Date: #{httpdate}\r\n" \ "Connection: close\r\n" headers.each do |key, value| case key when %r{\A(?:Date|Connection)\z}i next when %r{\AContent-Length\z}i append_header(buf, key, value) term = true when %r{\ATransfer-Encoding\z}i append_header(buf, key, value) term = true if /\bchunked\b/i === value # value may be Array :x 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 if !hijack && !term && req.chunkable_response? do_chunk = true buf << "Transfer-Encoding: chunked\r\n".freeze end socket.write(buf << "\r\n".freeze) buf.clear # remove this line if C Ruby gets escape analysis end if hijack req.hijacked! hijack.call(socket) elsif do_chunk begin body.each do |b| socket.write("#{b.bytesize.to_s(16)}\r\n", b, "\r\n".freeze) end ensure socket.write("0\r\n\r\n".freeze) end else body.each { |chunk| socket.write(chunk) } end end
#httpdate
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.
# File 'ext/unicorn_http/httpdate.c', line 44
static VALUE httpdate(VALUE self) { static time_t last; struct timeval now; struct tm tm; /* * Favor gettimeofday(2) over time(2), as the latter can return the * wrong value in the first 1 .. 2.5 ms of every second(!) * * https://lore.kernel.org/git/20230320230507.3932018-1-gitster@pobox.com/ * https://inbox.sourceware.org/libc-alpha/20230306160321.2942372-1-adhemerval.zanella@linaro.org/T/ * https://sourceware.org/bugzilla/show_bug.cgi?id=30200 */ if (gettimeofday(&now, NULL)) rb_sys_fail("gettimeofday"); if (last == now.tv_sec) return buf; last = now.tv_sec; gmtime_r(&now.tv_sec, &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; }