Class: Tipi::HTTP1Connection
- Inherits:
-
Connection
- Object
- Connection
- Tipi::HTTP1Connection
- Defined in:
- lib/tipi/controller/web_stock.rb
Constant Summary collapse
- CRLF =
response API
"\r\n"
- CRLF_ZERO_CRLF_CRLF =
"\r\n0\r\n\r\n"
- INTERNAL_HEADER_REGEXP =
/^:/.freeze
Instance Attribute Summary collapse
-
#io ⇒ Object
readonly
Returns the value of attribute io.
Instance Method Summary collapse
- #close_io ⇒ Object
- #collect_header_lines(lines, key, value) ⇒ Object
- #empty_status_line(status) ⇒ Object
-
#finish(request) ⇒ void
Finishes the response to the current request.
-
#format_headers(headers, body, chunked) ⇒ String
Formats response headers into an array.
- #format_status_line(body, status, chunked) ⇒ Object
- #handle_read_request ⇒ Object
- #handle_request ⇒ Object
- #handle_write(data = nil) ⇒ Object
- #http1_1?(request) ⇒ Boolean
-
#initialize(io, evloop, &app) ⇒ HTTP1Connection
constructor
A new instance of HTTP1Connection.
- #io_ready ⇒ Object
- #normalize_headers(headers) ⇒ Object
- #on_body(chunk) ⇒ Object
- #on_headers_complete(headers) ⇒ Object
- #on_message_complete ⇒ Object
-
#respond(request, body, headers) ⇒ Object
Sends response including headers and body.
- #scheme_from_connection ⇒ Object
-
#send_chunk(request, chunk, done: false) ⇒ void
Sends a response body chunk.
-
#send_headers(request, headers, empty_response: false, chunked: true) ⇒ void
Sends response headers.
- #setup_read_request ⇒ Object
- #watch_io(rw) ⇒ Object
- #with_body_status_line(status, body, chunked) ⇒ Object
Constructor Details
#initialize(io, evloop, &app) ⇒ HTTP1Connection
Returns a new instance of HTTP1Connection.
32 33 34 35 36 37 38 |
# File 'lib/tipi/controller/web_stock.rb', line 32 def initialize(io, evloop, &app) @io = io @evloop = evloop @parser = Http::Parser.new(self) @app = app setup_read_request end |
Instance Attribute Details
#io ⇒ Object (readonly)
Returns the value of attribute io.
30 31 32 |
# File 'lib/tipi/controller/web_stock.rb', line 30 def io @io end |
Instance Method Details
#close_io ⇒ Object
117 118 119 |
# File 'lib/tipi/controller/web_stock.rb', line 117 def close_io @evloop.emit([:close_io, self, @io]) end |
#collect_header_lines(lines, key, value) ⇒ Object
235 236 237 238 239 240 241 |
# File 'lib/tipi/controller/web_stock.rb', line 235 def collect_header_lines(lines, key, value) if value.is_a?(Array) value.inject(lines) { |_, item| lines << "#{key}: #{item}\r\n" } else lines << "#{key}: #{value}\r\n" end end |
#empty_status_line(status) ⇒ Object
219 220 221 222 223 224 225 |
# File 'lib/tipi/controller/web_stock.rb', line 219 def empty_status_line(status) if status == 204 +"HTTP/1.1 #{status}\r\n" else +"HTTP/1.1 #{status}\r\nContent-Length: 0\r\n" end end |
#finish(request) ⇒ 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.
185 186 187 188 |
# File 'lib/tipi/controller/web_stock.rb', line 185 def finish(request) request.tx_incr(5) handle_write("0\r\n\r\n") end |
#format_headers(headers, body, chunked) ⇒ String
Formats response headers into an array. If empty_response is true(thy), the response status code will default to 204, otherwise to 200.
198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/tipi/controller/web_stock.rb', line 198 def format_headers(headers, body, chunked) status = headers[':status'] status ||= (body ? Qeweney::Status::OK : Qeweney::Status::NO_CONTENT) lines = format_status_line(body, status, chunked) headers.each do |k, v| next if k =~ INTERNAL_HEADER_REGEXP collect_header_lines(lines, k, v) end lines << CRLF lines end |
#format_status_line(body, status, chunked) ⇒ Object
211 212 213 214 215 216 217 |
# File 'lib/tipi/controller/web_stock.rb', line 211 def format_status_line(body, status, chunked) if !body empty_status_line(status) else with_body_status_line(status, body, chunked) end end |
#handle_read_request ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/tipi/controller/web_stock.rb', line 89 def handle_read_request result = @io.read_nonblock(16384, exception: false) case result when :wait_readable watch_io(false) when :wait_writable watch_io(true) when nil close_io else @parser << result if @request_complete handle_request # @response = handle_request(@request_headers, @request_body) # handle_write_response else watch_io(false) end end rescue HTTP::Parser::Error, SystemCallError, IOError close_io end |
#handle_request ⇒ Object
121 122 123 124 125 126 |
# File 'lib/tipi/controller/web_stock.rb', line 121 def handle_request @app.call(@request) # req = Qeweney::Request.new(headers, self) # response_body = "Hello, world!" # "HTTP/1.1 200 OK\nContent-Length: #{response_body.bytesize}\n\n#{response_body}" end |
#handle_write(data = nil) ⇒ Object
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/tipi/controller/web_stock.rb', line 243 def handle_write(data = nil) if data if @response_buffer @response_buffer << data else @response_buffer = +data end end result = @io.write_nonblock(@response_buffer, exception: false) case result when :wait_readable watch_io(false) when :wait_writable watch_io(true) when nil close_io else setup_read_request watch_io(false) end end |
#http1_1?(request) ⇒ Boolean
161 162 163 |
# File 'lib/tipi/controller/web_stock.rb', line 161 def http1_1?(request) request.headers[':protocol'] == 'http/1.1' end |
#io_ready ⇒ Object
81 82 83 84 85 86 87 |
# File 'lib/tipi/controller/web_stock.rb', line 81 def io_ready if !@request_complete handle_read_request else handle_write_response end end |
#normalize_headers(headers) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/tipi/controller/web_stock.rb', line 56 def normalize_headers(headers) headers.each_with_object({}) do |(k, v), h| k = k.downcase hk = h[k] if hk hk = h[k] = [hk] unless hk.is_a?(Array) v.is_a?(Array) ? hk.concat(v) : hk << v else h[k] = v end end end |
#on_body(chunk) ⇒ Object
73 74 75 |
# File 'lib/tipi/controller/web_stock.rb', line 73 def on_body(chunk) @request.buffer_body_chunk(chunk) end |
#on_headers_complete(headers) ⇒ Object
46 47 48 49 50 51 52 53 54 |
# File 'lib/tipi/controller/web_stock.rb', line 46 def on_headers_complete(headers) headers = normalize_headers(headers) headers[':path'] = @parser.request_url headers[':method'] = @parser.http_method.downcase scheme = (proto = headers['x-forwarded-proto']) ? proto.downcase : scheme_from_connection headers[':scheme'] = scheme @request = Qeweney::Request.new(headers, self) end |
#on_message_complete ⇒ Object
77 78 79 |
# File 'lib/tipi/controller/web_stock.rb', line 77 def @request_complete = true end |
#respond(request, 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.
138 139 140 141 142 143 144 145 146 |
# File 'lib/tipi/controller/web_stock.rb', line 138 def respond(request, body, headers) formatted_headers = format_headers(headers, body, false) request.tx_incr(formatted_headers.bytesize + (body ? body.bytesize : 0)) if body handle_write(formatted_headers + body) else handle_write(formatted_headers) end end |
#scheme_from_connection ⇒ Object
69 70 71 |
# File 'lib/tipi/controller/web_stock.rb', line 69 def scheme_from_connection @io.is_a?(OpenSSL::SSL::SSLSocket) ? 'https' : 'http' end |
#send_chunk(request, 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.
172 173 174 175 176 177 178 179 180 |
# File 'lib/tipi/controller/web_stock.rb', line 172 def send_chunk(request, chunk, done: false) data = +'' data << "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n" if chunk data << "0\r\n\r\n" if done return if data.empty? request.tx_incr(data.bytesize) handle_write(data) end |
#send_headers(request, headers, empty_response: false, chunked: true) ⇒ 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.
155 156 157 158 159 |
# File 'lib/tipi/controller/web_stock.rb', line 155 def send_headers(request, headers, empty_response: false, chunked: true) formatted_headers = format_headers(headers, !empty_response, http1_1?(request) && chunked) request.tx_incr(formatted_headers.bytesize) handle_write(formatted_headers) end |
#setup_read_request ⇒ Object
40 41 42 43 44 |
# File 'lib/tipi/controller/web_stock.rb', line 40 def setup_read_request @request_complete = nil @request = nil @response_buffer = nil end |
#watch_io(rw) ⇒ Object
112 113 114 115 |
# File 'lib/tipi/controller/web_stock.rb', line 112 def watch_io(rw) @evloop.watch_io(self, @io, rw, true) # @evloop.emit([:watch_io, self, @io, rw, true]) end |
#with_body_status_line(status, body, chunked) ⇒ Object
227 228 229 230 231 232 233 |
# File 'lib/tipi/controller/web_stock.rb', line 227 def with_body_status_line(status, body, chunked) if chunked +"HTTP/1.1 #{status}\r\nTransfer-Encoding: chunked\r\n" else +"HTTP/1.1 #{status}\r\nContent-Length: #{body.is_a?(String) ? body.bytesize : body.to_i}\r\n" end end |