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