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