Class: Polyphony::HTTP::Server::HTTP1Adapter
- Inherits:
-
Object
- Object
- Polyphony::HTTP::Server::HTTP1Adapter
- Defined in:
- lib/polyphony/http/server/http1.rb
Overview
HTTP1 protocol implementation
Constant Summary collapse
- DEFAULT_HEADERS_OPTS =
{ empty_response: false, consume_request: true }.freeze
Instance Method Summary collapse
- #close ⇒ Object
-
#consume_request ⇒ Object
Waits for the current request to complete.
- #each(&block) ⇒ Object
- #finalize_client_loop ⇒ Object
-
#finish ⇒ void
Finishes the response to the current request.
-
#get_body_chunk ⇒ Object
Reads a body chunk for the current request.
-
#handle_incoming_data(data, &block) ⇒ Object
return [Boolean] true if client loop should stop.
-
#http2_upgraded_headers(headers) ⇒ Hash
Returns headers for HTTP2 upgrade.
-
#initialize(conn, opts) ⇒ HTTP1Adapter
constructor
Initializes a protocol adapter instance.
- #on_body(chunk) ⇒ Object
- #on_headers_complete(headers) ⇒ Object
- #on_message_complete ⇒ Object
- #protocol ⇒ Object
- #queue_request(request) ⇒ Object
-
#respond(body, headers) ⇒ Object
Sends response including headers and body.
-
#send_chunk(chunk, done: false) ⇒ void
Sends a response body chunk.
-
#send_headers(headers, opts = DEFAULT_HEADERS_OPTS) ⇒ void
Sends response headers.
-
#upgrade_connection(headers, &block) ⇒ boolean
Upgrades the connection to a different protocol, if the ‘Upgrade’ header is given.
- #upgrade_to_http2(headers, &block) ⇒ Object
- #upgrade_with_handler(handler, headers) ⇒ Object
Constructor Details
#initialize(conn, opts) ⇒ HTTP1Adapter
Initializes a protocol adapter instance
13 14 15 16 17 |
# File 'lib/polyphony/http/server/http1.rb', line 13 def initialize(conn, opts) @conn = conn @opts = opts @parser = ::HTTP::Parser.new(self) end |
Instance Method Details
#close ⇒ Object
219 220 221 |
# File 'lib/polyphony/http/server/http1.rb', line 219 def close @conn.close end |
#consume_request ⇒ Object
Waits for the current request to complete. Transfers control to the parse loop, and resumes once the parse_loop has fired the on_message_complete callback
72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/polyphony/http/server/http1.rb', line 72 def consume_request request = @requests_head loop do data = @conn.readpartial(8192) @parser << data return if request.complete? snooze rescue EOFError break end end |
#each(&block) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/polyphony/http/server/http1.rb', line 19 def each(&block) loop do data = @conn.readpartial(8192) return if handle_incoming_data(data, &block) rescue EOFError break end rescue SystemCallError, IOError # ignore ensure finalize_client_loop end |
#finalize_client_loop ⇒ Object
46 47 48 49 50 51 |
# File 'lib/polyphony/http/server/http1.rb', line 46 def finalize_client_loop # release references to various objects @requests_head = @requests_tail = nil @parser = nil @conn.close end |
#finish ⇒ void
This method returns an undefined value.
Finishes the response to the current request. If no headers were sent, default headers are sent using #send_headers.
215 216 217 |
# File 'lib/polyphony/http/server/http1.rb', line 215 def finish @conn << "0\r\n\r\n" end |
#get_body_chunk ⇒ Object
Reads a body chunk for the current request. Transfers control to the parse loop, and resumes once the parse_loop has fired the on_body callback
55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/polyphony/http/server/http1.rb', line 55 def get_body_chunk @waiting_for_body_chunk = true @next_chunk = nil while !@requests_tail.complete? && (data = @conn.readpartial(8192)) @parser << data return @next_chunk if @next_chunk snooze end nil ensure @waiting_for_body_chunk = nil end |
#handle_incoming_data(data, &block) ⇒ Object
return [Boolean] true if client loop should stop
33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/polyphony/http/server/http1.rb', line 33 def handle_incoming_data(data, &block) @parser << data snooze while (request = @requests_head) return true if upgrade_connection(request.headers, &block) @requests_head = request.__next__ block.call(request) return true unless request.keep_alive? end nil end |
#http2_upgraded_headers(headers) ⇒ Hash
Returns headers for HTTP2 upgrade
160 161 162 163 164 165 |
# File 'lib/polyphony/http/server/http1.rb', line 160 def http2_upgraded_headers(headers) headers.merge( ':scheme' => 'http', ':authority' => headers['Host'] ) end |
#on_body(chunk) ⇒ Object
105 106 107 108 109 110 111 112 |
# File 'lib/polyphony/http/server/http1.rb', line 105 def on_body(chunk) if @waiting_for_body_chunk @next_chunk = chunk @waiting_for_body_chunk = nil else @requests_tail.buffer_body_chunk(chunk) end end |
#on_headers_complete(headers) ⇒ Object
90 91 92 93 94 |
# File 'lib/polyphony/http/server/http1.rb', line 90 def on_headers_complete(headers) headers[':path'] = @parser.request_url headers[':method'] = @parser.http_method queue_request(Request.new(headers, self)) end |
#on_message_complete ⇒ Object
114 115 116 117 |
# File 'lib/polyphony/http/server/http1.rb', line 114 def @waiting_for_body_chunk = nil @requests_tail.complete!(@parser.keep_alive?) end |
#protocol ⇒ Object
85 86 87 88 |
# File 'lib/polyphony/http/server/http1.rb', line 85 def protocol version = @parser.http_version "HTTP #{version.join('.')}" end |
#queue_request(request) ⇒ Object
96 97 98 99 100 101 102 103 |
# File 'lib/polyphony/http/server/http1.rb', line 96 def queue_request(request) if @requests_head @requests_tail.__next__ = request @requests_tail = request else @requests_head = @requests_tail = request end end |
#respond(body, headers) ⇒ Object
Sends response including headers and body. Waits for the request to complete if not yet completed. The body is sent using chunked transfer encoding.
173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/polyphony/http/server/http1.rb', line 173 def respond(body, headers) consume_request if @parsing data = format_headers(headers, body) if body data << if @parser.http_minor == 0 body else "#{body.bytesize.to_s(16)}\r\n#{body}\r\n0\r\n\r\n" end end @conn << data end |
#send_chunk(chunk, done: false) ⇒ void
This method returns an undefined value.
Sends a response body chunk. If no headers were sent, default headers are sent using #send_headers. if the done option is true(thy), an empty chunk will be sent to signal response completion to the client.
206 207 208 209 210 |
# File 'lib/polyphony/http/server/http1.rb', line 206 def send_chunk(chunk, done: false) data = +"#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n" data << "0\r\n\r\n" if done @conn << data end |
#send_headers(headers, opts = DEFAULT_HEADERS_OPTS) ⇒ void
This method returns an undefined value.
Sends response headers. If empty_response is truthy, the response status code will default to 204, otherwise to 200.
196 197 198 |
# File 'lib/polyphony/http/server/http1.rb', line 196 def send_headers(headers, opts = DEFAULT_HEADERS_OPTS) @conn << format_headers(headers, !opts[:empty_response]) end |
#upgrade_connection(headers, &block) ⇒ boolean
Upgrades the connection to a different protocol, if the ‘Upgrade’ header is given. By default the only supported upgrade protocol is HTTP2. Additional protocols, notably WebSocket, can be specified by passing a hash to the :upgrade option when starting a server:
opts = {
upgrade: {
websocket: Polyphony::Websocket.handler(&method(:ws_handler))
}
}
Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) { |req| ... }
133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/polyphony/http/server/http1.rb', line 133 def upgrade_connection(headers, &block) upgrade_protocol = headers['Upgrade'] return nil unless upgrade_protocol upgrade_protocol = upgrade_protocol.downcase.to_sym upgrade_handler = @opts[:upgrade] && @opts[:upgrade][upgrade_protocol] return upgrade_with_handler(upgrade_handler, headers) if upgrade_handler return upgrade_to_http2(headers, &block) if upgrade_protocol == :h2c nil end |
#upgrade_to_http2(headers, &block) ⇒ Object
151 152 153 154 155 |
# File 'lib/polyphony/http/server/http1.rb', line 151 def upgrade_to_http2(headers, &block) @parser = @requests_head = @requests_tail = nil HTTP2Adapter.upgrade_each(@conn, @opts, http2_upgraded_headers(headers), &block) true end |
#upgrade_with_handler(handler, headers) ⇒ Object
145 146 147 148 149 |
# File 'lib/polyphony/http/server/http1.rb', line 145 def upgrade_with_handler(handler, headers) @parser = @requests_head = @requests_tail = nil handler.(@conn, headers) true end |