Methods
Public Class
Public Instance
Constants
| CRLF | = | "\r\n" | ||
| MAX_REQUESTS | = | 200 | ||
| UPCASED | = | { "www-authenticate" => "WWW-Authenticate", "http2-settings" => "HTTP2-Settings", "content-md5" => "Content-MD5", "last-event-id" => "Last-Event-ID", }.freeze |
Attributes
| max_concurrent_requests | [RW] | |
| pending | [R] | |
| requests | [R] |
Public Class methods
new(buffer, options)
[show source]
# File lib/httpx/connection/http1.rb 23 def initialize(buffer, options) 24 @options = options 25 @max_concurrent_requests = @options.max_concurrent_requests || MAX_REQUESTS 26 @max_requests = @options.max_requests 27 @parser = Parser::HTTP1.new(self, options.max_response_headers, options.max_response_header_value_size) 28 @buffer = buffer 29 @version = [1, 1] 30 @pending = [] 31 @requests = [] 32 @request = nil 33 @handshake_completed = @pipelining = false 34 end
Public Instance methods
<<(data)
[show source]
# File lib/httpx/connection/http1.rb 91 def <<(data) 92 @parser << data 93 end
close()
[show source]
# File lib/httpx/connection/http1.rb 71 def close 72 reset 73 emit(:close) 74 end
consume()
[show source]
# File lib/httpx/connection/http1.rb 107 def consume 108 requests_limit = [@max_requests, @requests.size].min 109 concurrent_requests_limit = [@max_concurrent_requests, requests_limit].min 110 @requests.each_with_index do |request, idx| 111 break if idx >= concurrent_requests_limit 112 next unless request.can_buffer? 113 114 handle(request) 115 end 116 end
dispatch(request)
[show source]
# File lib/httpx/connection/http1.rb 182 def dispatch(request) 183 if request.expects? 184 @parser.reset! 185 return handle(request) 186 end 187 188 @request = nil 189 @requests.shift 190 response = request.response 191 emit(:response, request, response) 192 193 if @parser.upgrade? 194 response << @parser.upgrade_data 195 @parser.reset! 196 throw(:called) 197 end 198 199 @parser.reset! 200 @max_requests -= 1 201 if response.is_a?(ErrorResponse) 202 disable 203 else 204 manage_connection(request, response) 205 end 206 207 if exhausted? 208 @pending.unshift(*@requests) 209 @requests.clear 210 211 emit(:exhausted) 212 else 213 send(@pending.shift) unless @pending.empty? 214 end 215 end
empty?()
[show source]
# File lib/httpx/connection/http1.rb 80 def empty? 81 # this means that for every request there's an available 82 # partial response, so there are no in-flight requests waiting. 83 @requests.empty? || ( 84 # checking all responses can be time-consuming. Alas, as in HTTP/1, responses 85 # do not come out of order, we can get away with checking first and last. 86 !@requests.first.response.nil? && 87 (@requests.size == 1 || !@requests.last.response.nil?) 88 ) 89 end
exhausted?()
[show source]
# File lib/httpx/connection/http1.rb 76 def exhausted? 77 !@max_requests.positive? 78 end
handle_error(ex, request = nil)
[show source]
# File lib/httpx/connection/http1.rb 217 def handle_error(ex, request = nil) 218 if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request && 219 (response = @request.response) && response.is_a?(Response) && 220 !response.headers.key?("content-length") && 221 !response.headers.key?("transfer-encoding") 222 # if the response does not contain a content-length header, the server closing the 223 # connnection is the indicator of response consumed. 224 # https://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.4.4 225 catch(:called) { on_complete } 226 return 227 end 228 229 if @pipelining 230 catch(:called) { disable } 231 else 232 while (req = @requests.shift) 233 next if request && request == req 234 235 emit(:error, req, ex) 236 end 237 while (req = @pending.shift) 238 next if request && request == req 239 240 emit(:error, req, ex) 241 end 242 end 243 end
interests()
[show source]
# File lib/httpx/connection/http1.rb 40 def interests 41 request = @request || @requests.first 42 43 return unless request 44 45 return :w if request.interests == :w || !@buffer.empty? 46 47 :r 48 end
on_complete()
[show source]
# File lib/httpx/connection/http1.rb 173 def on_complete 174 request = @request 175 176 return unless request 177 178 request.log(level: 2) { "parsing complete" } 179 dispatch(request) 180 end
on_data(chunk)
[show source]
# File lib/httpx/connection/http1.rb 160 def on_data(chunk) 161 request = @request 162 163 return unless request 164 165 request.log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." } 166 request.log(level: 2, color: :green) { "-> #{log_redact_body(chunk.inspect)}" } 167 168 response = request.response 169 170 response << chunk 171 end
on_headers(h)
[show source]
# File lib/httpx/connection/http1.rb 126 def on_headers(h) 127 request = @request = @requests.first 128 129 return if request.response 130 131 request.log(level: 2) { "headers received" } 132 headers = request.options.headers_class.new(h) 133 response = request.options.response_class.new(request, 134 @parser.status_code, 135 @parser.http_version.join("."), 136 headers) 137 request.log(color: :yellow) { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" } 138 request.log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v)}" }.join("\n") } 139 140 if response.content_length && response.content_length > request.options.max_response_body_size 141 raise HTTPX::Error, "maximum response body size exceeded" 142 end 143 144 request.response = response 145 on_complete if response.finished? 146 end
on_start()
HTTP Parser callbacks
must be public methods, or else they won’t be reachable
[show source]
# File lib/httpx/connection/http1.rb 122 def on_start 123 log(level: 2) { "parsing begins" } 124 end
on_trailers(h)
[show source]
# File lib/httpx/connection/http1.rb 148 def on_trailers(h) 149 request = @request 150 151 return unless request 152 153 response = request.response 154 155 request.log(level: 2) { "trailer headers received" } 156 request.log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v.join(", "))}" }.join("\n") } 157 response.merge_headers(h) 158 end
ping()
[show source]
# File lib/httpx/connection/http1.rb 245 def ping 246 reset 247 emit(:reset) 248 emit(:exhausted) 249 end
reset()
[show source]
# File lib/httpx/connection/http1.rb 50 def reset 51 if @ping_timer 52 @ping_timer.cancel 53 @ping_timer = nil 54 end 55 @max_requests = @options.max_requests || MAX_REQUESTS 56 @parser.reset! 57 @handshake_completed = false 58 reset_requests 59 end
reset_requests()
[show source]
# File lib/httpx/connection/http1.rb 61 def reset_requests 62 @requests.reverse_each do |request| 63 next if request.response 64 65 request.transition(:idle) 66 @pending.unshift(request) 67 end 68 @requests.clear 69 end
send(request)
[show source]
# File lib/httpx/connection/http1.rb 95 def send(request) 96 unless @max_requests.positive? 97 @pending << request 98 return 99 end 100 101 return if @requests.include?(request) 102 103 @requests << request 104 @pipelining = @max_concurrent_requests > 1 && @requests.size > 1 105 end
timeout()
[show source]
# File lib/httpx/connection/http1.rb 36 def timeout 37 @options.timeout[:operation_timeout] 38 end
waiting_for_ping?()
[show source]
# File lib/httpx/connection/http1.rb 251 def waiting_for_ping? 252 false 253 end