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