Class: Hyperion::ResponseWriter
- Inherits:
-
Object
- Object
- Hyperion::ResponseWriter
- Defined in:
- lib/hyperion/response_writer.rb
Overview
Serializes a Rack [status, headers, body] tuple to an HTTP/1.1 wire stream. Phase 5 replaces this with an io_buffer-batched writer; Phase 7 adds a sibling Http2ResponseWriter. Public surface (#write) stays stable.
Constant Summary collapse
- REASONS =
{ 200 => 'OK', 201 => 'Created', 204 => 'No Content', 301 => 'Moved Permanently', 302 => 'Found', 304 => 'Not Modified', 400 => 'Bad Request', 401 => 'Unauthorized', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 413 => 'Payload Too Large', 414 => 'URI Too Long', 422 => 'Unprocessable Entity', 429 => 'Too Many Requests', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout' }.freeze
- CRLF_HEADER_VALUE =
/[\r\n]/
Instance Method Summary collapse
Instance Method Details
#write(io, status, headers, body, keep_alive: false) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/hyperion/response_writer.rb', line 38 def write(io, status, headers, body, keep_alive: false) # Phase 1 buffers the full body so Content-Length is exact. # Phase 2 introduces chunked transfer-encoding for streaming bodies; # Phase 5 batches via IO::Buffer to avoid this intermediate String. buffered = +'' body.each { |chunk| buffered << chunk } reason = REASONS[status] || 'Unknown' date_str = Time.now.httpdate head = build_head(status, reason, headers, buffered.bytesize, keep_alive, date_str) # Phase 8 perf fix: coalesce status line + all headers + body into a # SINGLE io.write call. Each syscall round-trip is ~1 usec on macOS # kqueue; before this change we issued (1 status) + (N headers) + (1 blank) # + (1 body) = 8+ syscalls per response. Now: 1 syscall. bytes_out = if buffered.empty? io.write(head) head.bytesize else # Concatenate into the head buffer (which is already a fresh +'' # from the C builder or the Ruby fallback) so we still emit a # single write. head << buffered io.write(head) head.bytesize end Hyperion.metrics.increment(:bytes_written, bytes_out) ensure body.close if body.respond_to?(:close) end |