Class: Async::HTTP::Protocol::HTTP11
- Inherits:
-
IO::Protocol::Line
- Object
- IO::Protocol::Line
- Async::HTTP::Protocol::HTTP11
- Defined in:
- lib/async/http/protocol/http11.rb
Overview
Implements basic HTTP/1.1 request/response.
Direct Known Subclasses
Defined Under Namespace
Classes: Request
Constant Summary collapse
- CRLF =
"\r\n".freeze
- CONNECTION =
'connection'.freeze
- HOST =
'host'.freeze
- CLOSE =
'close'.freeze
- VERSION =
"HTTP/1.1".freeze
Instance Attribute Summary collapse
-
#count ⇒ Object
readonly
Returns the value of attribute count.
Instance Method Summary collapse
- #call(request) ⇒ Object
-
#good? ⇒ Boolean
Can we use this connection to make requests?.
- #hijack ⇒ Object
-
#initialize(stream) ⇒ HTTP11
constructor
A new instance of HTTP11.
-
#multiplex ⇒ Object
Only one simultaneous connection at a time.
- #next_request ⇒ Object
- #persistent?(headers) ⇒ Boolean
- #read_request ⇒ Object
- #read_response(request) ⇒ Object
-
#receive_requests(task: Task.current) ⇒ Object
Server loop.
- #reusable? ⇒ Boolean
- #version ⇒ Object
- #write_request(authority, method, path, version, headers) ⇒ Object
- #write_response(version, status, headers, body) ⇒ Object
Constructor Details
Instance Attribute Details
#count ⇒ Object (readonly)
Returns the value of attribute count.
55 56 57 |
# File 'lib/async/http/protocol/http11.rb', line 55 def count @count end |
Instance Method Details
#call(request) ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/async/http/protocol/http11.rb', line 147 def call(request) request.version ||= self.version Async.logger.debug(self) {"#{request.method} #{request.path} #{request.headers.inspect}"} # We carefully interpret https://tools.ietf.org/html/rfc7230#section-6.3.1 to implement this correctly. begin write_request(request., request.method, request.path, request.version, request.headers) rescue # If we fail to fully write the request and body, we can retry this request. raise RequestFailed.new end # Once we start writing the body, we can't recover if the request fails. That's because the body might be generated dynamically, streaming, etc. write_body(request.body) return Response.new(*read_response(request)) rescue # This will ensure that #reusable? returns false. @stream.close raise end |
#good? ⇒ Boolean
Can we use this connection to make requests?
63 64 65 |
# File 'lib/async/http/protocol/http11.rb', line 63 def good? @stream.connected? end |
#hijack ⇒ Object
88 89 90 91 92 93 94 |
# File 'lib/async/http/protocol/http11.rb', line 88 def hijack @persistent = false @stream.flush return @stream.io end |
#multiplex ⇒ Object
Only one simultaneous connection at a time.
58 59 60 |
# File 'lib/async/http/protocol/http11.rb', line 58 def multiplex 1 end |
#next_request ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/async/http/protocol/http11.rb', line 114 def next_request # The default is true. return nil unless @persistent request = Request.new(self) unless persistent?(request.headers) @persistent = false end return request rescue # Bad Request write_response(self.version, 400, {}, nil) raise end |
#persistent?(headers) ⇒ Boolean
80 81 82 83 84 85 86 |
# File 'lib/async/http/protocol/http11.rb', line 80 def persistent?(headers) if connection = headers[CONNECTION] return !connection.include?(CLOSE) else return true end end |
#read_request ⇒ Object
194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/async/http/protocol/http11.rb', line 194 def read_request method, path, version = read_line.split(/\s+/, 3) headers = read_headers @persistent = persistent?(headers) body = read_request_body(headers) @count += 1 return headers.delete(HOST), method, path, version, headers, body end |
#read_response(request) ⇒ Object
179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/async/http/protocol/http11.rb', line 179 def read_response(request) version, status, reason = read_line.split(/\s+/, 3) Async.logger.debug(self) {"#{version} #{status} #{reason}"} headers = read_headers @persistent = persistent?(headers) body = read_response_body(request, status, headers) @count += 1 return version, Integer(status), reason, headers, body end |
#receive_requests(task: Task.current) ⇒ Object
Server loop.
133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/async/http/protocol/http11.rb', line 133 def receive_requests(task: Task.current) while request = next_request if response = yield(request, self) write_response(response.version || self.version, response.status, response.headers, response.body) request.finish # This ensures we yield at least once every iteration of the loop and allow other fibers to execute. task.yield else break end end end |
#reusable? ⇒ Boolean
67 68 69 |
# File 'lib/async/http/protocol/http11.rb', line 67 def reusable? @persistent && !@stream.closed? end |
#version ⇒ Object
76 77 78 |
# File 'lib/async/http/protocol/http11.rb', line 76 def version VERSION end |
#write_request(authority, method, path, version, headers) ⇒ Object
171 172 173 174 175 176 177 |
# File 'lib/async/http/protocol/http11.rb', line 171 def write_request(, method, path, version, headers) @stream.write("#{method} #{path} #{version}\r\n") @stream.write("host: #{}\r\n") write_headers(headers) @stream.flush end |
#write_response(version, status, headers, body) ⇒ Object
207 208 209 210 211 212 213 |
# File 'lib/async/http/protocol/http11.rb', line 207 def write_response(version, status, headers, body) @stream.write("#{version} #{status}\r\n") write_headers(headers) write_body(body) @stream.flush end |