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", }.freeze |
Attributes
| max_concurrent_requests | [RW] | |
| pending | [R] | |
| requests | [R] |
Public Class methods
new(buffer, options)
[show source]
# File lib/httpx/connection/http1.rb 22 def initialize(buffer, options) 23 @options = options 24 @max_concurrent_requests = @options.max_concurrent_requests || MAX_REQUESTS 25 @max_requests = @options.max_requests 26 @parser = Parser::HTTP1.new(self) 27 @buffer = buffer 28 @version = [1, 1] 29 @pending = [] 30 @requests = [] 31 @handshake_completed = false 32 end
Public Instance methods
<<(data)
[show source]
# File lib/httpx/connection/http1.rb 80 def <<(data) 81 @parser << data 82 end
close()
[show source]
# File lib/httpx/connection/http1.rb 60 def close 61 reset 62 emit(:close) 63 end
consume()
[show source]
# File lib/httpx/connection/http1.rb 96 def consume 97 requests_limit = [@max_requests, @requests.size].min 98 concurrent_requests_limit = [@max_concurrent_requests, requests_limit].min 99 @requests.each_with_index do |request, idx| 100 break if idx >= concurrent_requests_limit 101 next unless request.can_buffer? 102 103 handle(request) 104 end 105 end
dispatch()
[show source]
# File lib/httpx/connection/http1.rb 168 def dispatch 169 request = @request 170 171 if request.expects? 172 @parser.reset! 173 return handle(request) 174 end 175 176 @request = nil 177 @requests.shift 178 response = request.response 179 emit(:response, request, response) 180 181 if @parser.upgrade? 182 response << @parser.upgrade_data 183 @parser.reset! 184 throw(:called) 185 end 186 187 @parser.reset! 188 @max_requests -= 1 189 if response.is_a?(ErrorResponse) 190 disable 191 else 192 manage_connection(request, response) 193 end 194 195 if exhausted? 196 @pending.unshift(*@requests) 197 @requests.clear 198 199 emit(:exhausted) 200 else 201 send(@pending.shift) unless @pending.empty? 202 end 203 end
empty?()
[show source]
# File lib/httpx/connection/http1.rb 69 def empty? 70 # this means that for every request there's an available 71 # partial response, so there are no in-flight requests waiting. 72 @requests.empty? || ( 73 # checking all responses can be time-consuming. Alas, as in HTTP/1, responses 74 # do not come out of order, we can get away with checking first and last. 75 !@requests.first.response.nil? && 76 (@requests.size == 1 || !@requests.last.response.nil?) 77 ) 78 end
exhausted?()
[show source]
# File lib/httpx/connection/http1.rb 65 def exhausted? 66 !@max_requests.positive? 67 end
handle_error(ex, request = nil)
[show source]
# File lib/httpx/connection/http1.rb 205 def handle_error(ex, request = nil) 206 if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request && 207 (response = @request.response) && response.is_a?(Response) && 208 !response.headers.key?("content-length") && 209 !response.headers.key?("transfer-encoding") 210 # if the response does not contain a content-length header, the server closing the 211 # connnection is the indicator of response consumed. 212 # https://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.4.4 213 catch(:called) { on_complete } 214 return 215 end 216 217 if @pipelining 218 catch(:called) { disable } 219 else 220 @requests.each do |req| 221 next if request && request == req 222 223 emit(:error, req, ex) 224 end 225 @pending.each do |req| 226 next if request && request == req 227 228 emit(:error, req, ex) 229 end 230 end 231 end
interests()
[show source]
# File lib/httpx/connection/http1.rb 38 def interests 39 request = @request || @requests.first 40 41 return unless request 42 43 return :w if request.interests == :w || !@buffer.empty? 44 45 :r 46 end
on_complete()
[show source]
# File lib/httpx/connection/http1.rb 159 def on_complete 160 request = @request 161 162 return unless request 163 164 log(level: 2) { "parsing complete" } 165 dispatch 166 end
on_data(chunk)
[show source]
# File lib/httpx/connection/http1.rb 143 def on_data(chunk) 144 request = @request 145 146 return unless request 147 148 log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." } 149 log(level: 2, color: :green) { "-> #{log_redact_body(chunk.inspect)}" } 150 response = request.response 151 152 response << chunk 153 rescue StandardError => e 154 error_response = ErrorResponse.new(request, e) 155 request.response = error_response 156 dispatch 157 end
on_headers(h)
[show source]
# File lib/httpx/connection/http1.rb 115 def on_headers(h) 116 @request = @requests.first 117 118 return if @request.response 119 120 log(level: 2) { "headers received" } 121 headers = @request.options.headers_class.new(h) 122 response = @request.options.response_class.new(@request, 123 @parser.status_code, 124 @parser.http_version.join("."), 125 headers) 126 log(color: :yellow) { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" } 127 log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v)}" }.join("\n") } 128 129 @request.response = response 130 on_complete if response.finished? 131 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 111 def on_start 112 log(level: 2) { "parsing begins" } 113 end
on_trailers(h)
[show source]
# File lib/httpx/connection/http1.rb 133 def on_trailers(h) 134 return unless @request 135 136 response = @request.response 137 log(level: 2) { "trailer headers received" } 138 139 log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v.join(", "))}" }.join("\n") } 140 response.merge_headers(h) 141 end
ping()
[show source]
# File lib/httpx/connection/http1.rb 233 def ping 234 reset 235 emit(:reset) 236 emit(:exhausted) 237 end
reset()
[show source]
# File lib/httpx/connection/http1.rb 48 def reset 49 @max_requests = @options.max_requests || MAX_REQUESTS 50 @parser.reset! 51 @handshake_completed = false 52 reset_requests 53 end
reset_requests()
[show source]
# File lib/httpx/connection/http1.rb 55 def reset_requests 56 @pending.unshift(*@requests) 57 @requests.clear 58 end
send(request)
[show source]
# File lib/httpx/connection/http1.rb 84 def send(request) 85 unless @max_requests.positive? 86 @pending << request 87 return 88 end 89 90 return if @requests.include?(request) 91 92 @requests << request 93 @pipelining = true if @requests.size > 1 94 end
timeout()
[show source]
# File lib/httpx/connection/http1.rb 34 def timeout 35 @options.timeout[:operation_timeout] 36 end
waiting_for_ping?()
[show source]
# File lib/httpx/connection/http1.rb 239 def waiting_for_ping? 240 false 241 end