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