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