httpx 1.2.6 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/doc/release_notes/1_3_0.md +18 -0
  4. data/doc/release_notes/1_3_1.md +17 -0
  5. data/doc/release_notes/1_3_2.md +6 -0
  6. data/doc/release_notes/1_3_3.md +5 -0
  7. data/doc/release_notes/1_3_4.md +6 -0
  8. data/doc/release_notes/1_4_0.md +43 -0
  9. data/doc/release_notes/1_4_1.md +19 -0
  10. data/doc/release_notes/1_4_2.md +20 -0
  11. data/doc/release_notes/1_4_3.md +11 -0
  12. data/doc/release_notes/1_4_4.md +14 -0
  13. data/lib/httpx/adapters/datadog.rb +56 -80
  14. data/lib/httpx/adapters/faraday.rb +5 -2
  15. data/lib/httpx/adapters/webmock.rb +24 -8
  16. data/lib/httpx/callbacks.rb +2 -7
  17. data/lib/httpx/chainable.rb +3 -1
  18. data/lib/httpx/connection/http1.rb +11 -7
  19. data/lib/httpx/connection/http2.rb +57 -34
  20. data/lib/httpx/connection.rb +270 -71
  21. data/lib/httpx/errors.rb +15 -4
  22. data/lib/httpx/io/ssl.rb +6 -3
  23. data/lib/httpx/io/tcp.rb +1 -1
  24. data/lib/httpx/io/unix.rb +1 -1
  25. data/lib/httpx/loggable.rb +17 -10
  26. data/lib/httpx/options.rb +30 -23
  27. data/lib/httpx/plugins/aws_sdk_authentication.rb +3 -0
  28. data/lib/httpx/plugins/aws_sigv4.rb +36 -17
  29. data/lib/httpx/plugins/callbacks.rb +13 -2
  30. data/lib/httpx/plugins/circuit_breaker.rb +11 -5
  31. data/lib/httpx/plugins/content_digest.rb +202 -0
  32. data/lib/httpx/plugins/cookies.rb +9 -6
  33. data/lib/httpx/plugins/digest_auth.rb +3 -0
  34. data/lib/httpx/plugins/expect.rb +10 -4
  35. data/lib/httpx/plugins/follow_redirects.rb +68 -33
  36. data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -0
  37. data/lib/httpx/plugins/grpc.rb +2 -2
  38. data/lib/httpx/plugins/h2c.rb +23 -20
  39. data/lib/httpx/plugins/internal_telemetry.rb +48 -1
  40. data/lib/httpx/plugins/oauth.rb +1 -1
  41. data/lib/httpx/plugins/persistent.rb +16 -0
  42. data/lib/httpx/plugins/proxy/http.rb +19 -16
  43. data/lib/httpx/plugins/proxy/socks4.rb +1 -1
  44. data/lib/httpx/plugins/proxy/socks5.rb +1 -1
  45. data/lib/httpx/plugins/proxy.rb +96 -85
  46. data/lib/httpx/plugins/retries.rb +28 -10
  47. data/lib/httpx/plugins/ssrf_filter.rb +4 -1
  48. data/lib/httpx/plugins/stream.rb +42 -18
  49. data/lib/httpx/plugins/upgrade.rb +5 -10
  50. data/lib/httpx/plugins/webdav.rb +6 -0
  51. data/lib/httpx/plugins/xml.rb +76 -0
  52. data/lib/httpx/pool.rb +73 -244
  53. data/lib/httpx/request/body.rb +50 -55
  54. data/lib/httpx/request.rb +77 -14
  55. data/lib/httpx/resolver/https.rb +17 -20
  56. data/lib/httpx/resolver/multi.rb +34 -16
  57. data/lib/httpx/resolver/native.rb +140 -61
  58. data/lib/httpx/resolver/resolver.rb +64 -19
  59. data/lib/httpx/resolver/system.rb +32 -16
  60. data/lib/httpx/resolver.rb +21 -14
  61. data/lib/httpx/response/body.rb +12 -1
  62. data/lib/httpx/response.rb +16 -9
  63. data/lib/httpx/selector.rb +170 -91
  64. data/lib/httpx/session.rb +282 -139
  65. data/lib/httpx/timers.rb +17 -2
  66. data/lib/httpx/transcoder/body.rb +15 -29
  67. data/lib/httpx/transcoder/form.rb +2 -0
  68. data/lib/httpx/transcoder/gzip.rb +0 -3
  69. data/lib/httpx/transcoder/json.rb +16 -2
  70. data/lib/httpx/transcoder/multipart/encoder.rb +11 -2
  71. data/lib/httpx/transcoder/multipart/part.rb +1 -1
  72. data/lib/httpx/transcoder/utils/deflater.rb +7 -4
  73. data/lib/httpx/transcoder.rb +0 -1
  74. data/lib/httpx/version.rb +1 -1
  75. data/lib/httpx.rb +20 -21
  76. data/sig/callbacks.rbs +2 -3
  77. data/sig/chainable.rbs +6 -2
  78. data/sig/connection/http1.rbs +2 -2
  79. data/sig/connection/http2.rbs +22 -18
  80. data/sig/connection.rbs +40 -9
  81. data/sig/errors.rbs +9 -3
  82. data/sig/httpx.rbs +3 -3
  83. data/sig/io/tcp.rbs +1 -1
  84. data/sig/io/unix.rbs +1 -1
  85. data/sig/loggable.rbs +4 -2
  86. data/sig/options.rbs +8 -13
  87. data/sig/plugins/aws_sigv4.rbs +8 -2
  88. data/sig/plugins/content_digest.rbs +51 -0
  89. data/sig/plugins/cookies/cookie.rbs +9 -0
  90. data/sig/plugins/follow_redirects.rbs +1 -1
  91. data/sig/plugins/grpc/call.rbs +4 -0
  92. data/sig/plugins/persistent.rbs +4 -1
  93. data/sig/plugins/proxy/http.rbs +3 -0
  94. data/sig/plugins/proxy/socks5.rbs +11 -3
  95. data/sig/plugins/proxy.rbs +18 -9
  96. data/sig/plugins/push_promise.rbs +6 -3
  97. data/sig/plugins/rate_limiter.rbs +2 -0
  98. data/sig/plugins/retries.rbs +1 -1
  99. data/sig/plugins/ssrf_filter.rbs +26 -0
  100. data/sig/plugins/stream.rbs +3 -0
  101. data/sig/plugins/webdav.rbs +23 -0
  102. data/sig/plugins/xml.rbs +37 -0
  103. data/sig/pool.rbs +27 -33
  104. data/sig/request/body.rbs +4 -10
  105. data/sig/request.rbs +14 -1
  106. data/sig/resolver/multi.rbs +26 -1
  107. data/sig/resolver/native.rbs +6 -3
  108. data/sig/resolver/resolver.rbs +22 -3
  109. data/sig/resolver.rbs +5 -1
  110. data/sig/response/body.rbs +2 -2
  111. data/sig/response/buffer.rbs +2 -2
  112. data/sig/response.rbs +9 -4
  113. data/sig/selector.rbs +31 -4
  114. data/sig/session.rbs +54 -20
  115. data/sig/timers.rbs +15 -4
  116. data/sig/transcoder/body.rbs +2 -4
  117. data/sig/transcoder/chunker.rbs +1 -1
  118. data/sig/transcoder/deflate.rbs +1 -0
  119. data/sig/transcoder/form.rbs +8 -0
  120. data/sig/transcoder/gzip.rbs +4 -1
  121. data/sig/transcoder/json.rbs +1 -1
  122. data/sig/transcoder/multipart.rbs +6 -4
  123. data/sig/transcoder/utils/body_reader.rbs +3 -3
  124. data/sig/transcoder/utils/deflater.rbs +2 -3
  125. metadata +32 -14
  126. data/lib/httpx/session2.rb +0 -23
  127. data/lib/httpx/transcoder/utils/inflater.rb +0 -19
  128. data/lib/httpx/transcoder/xml.rb +0 -52
  129. data/sig/transcoder/utils/inflater.rbs +0 -12
  130. data/sig/transcoder/xml.rbs +0 -22
@@ -4,12 +4,17 @@ module HTTPX
4
4
  InsecureRedirectError = Class.new(Error)
5
5
  module Plugins
6
6
  #
7
- # This plugin adds support for following redirect (status 30X) responses.
7
+ # This plugin adds support for automatically following redirect (status 30X) responses.
8
8
  #
9
- # It has an upper bound of followed redirects (see *MAX_REDIRECTS*), after which it
10
- # will return the last redirect response. It will **not** raise an exception.
9
+ # It has a default upper bound of followed redirects (see *MAX_REDIRECTS* and the *max_redirects* option),
10
+ # after which it will return the last redirect response. It will **not** raise an exception.
11
11
  #
12
- # It also doesn't follow insecure redirects (https -> http) by default (see *follow_insecure_redirects*).
12
+ # It doesn't follow insecure redirects (https -> http) by default (see *follow_insecure_redirects*).
13
+ #
14
+ # It doesn't propagate authorization related headers to requests redirecting to different origins
15
+ # (see *allow_auth_to_other_origins*) to override.
16
+ #
17
+ # It allows customization of when to redirect via the *redirect_on* callback option).
13
18
  #
14
19
  # https://gitlab.com/os85/httpx/wikis/Follow-Redirects
15
20
  #
@@ -20,6 +25,14 @@ module HTTPX
20
25
 
21
26
  using URIExtensions
22
27
 
28
+ # adds support for the following options:
29
+ #
30
+ # :max_redirects :: max number of times a request will be redirected (defaults to <tt>3</tt>).
31
+ # :follow_insecure_redirects :: whether redirects to an "http://" URI, when coming from an "https//", are allowed
32
+ # (defaults to <tt>false</tt>).
33
+ # :allow_auth_to_other_origins :: whether auth-related headers, such as "Authorization", are propagated on redirection
34
+ # (defaults to <tt>false</tt>).
35
+ # :redirect_on :: optional callback which receives the redirect location and can halt the redirect chain if it returns <tt>false</tt>.
23
36
  module OptionsMethods
24
37
  def option_max_redirects(value)
25
38
  num = Integer(value)
@@ -44,15 +57,16 @@ module HTTPX
44
57
  end
45
58
 
46
59
  module InstanceMethods
60
+ # returns a session with the *max_redirects* option set to +n+
47
61
  def max_redirects(n)
48
62
  with(max_redirects: n.to_i)
49
63
  end
50
64
 
51
65
  private
52
66
 
53
- def fetch_response(request, connections, options)
67
+ def fetch_response(request, selector, options)
54
68
  redirect_request = request.redirect_request
55
- response = super(redirect_request, connections, options)
69
+ response = super(redirect_request, selector, options)
56
70
  return unless response
57
71
 
58
72
  max_redirects = redirect_request.max_redirects
@@ -71,40 +85,40 @@ module HTTPX
71
85
  # build redirect request
72
86
  request_body = redirect_request.body
73
87
  redirect_method = "GET"
88
+ redirect_params = {}
74
89
 
75
90
  if response.status == 305 && options.respond_to?(:proxy)
76
91
  request_body.rewind
77
92
  # The requested resource MUST be accessed through the proxy given by
78
93
  # the Location field. The Location field gives the URI of the proxy.
79
- retry_options = options.merge(headers: redirect_request.headers,
80
- proxy: { uri: redirect_uri },
81
- body: request_body,
82
- max_redirects: max_redirects - 1)
94
+ redirect_options = options.merge(headers: redirect_request.headers,
95
+ proxy: { uri: redirect_uri },
96
+ max_redirects: max_redirects - 1)
97
+
98
+ redirect_params[:body] = request_body
83
99
  redirect_uri = redirect_request.uri
84
- options = retry_options
100
+ options = redirect_options
85
101
  else
86
102
  redirect_headers = redirect_request_headers(redirect_request.uri, redirect_uri, request.headers, options)
87
-
88
- retry_opts = Hash[options].merge(max_redirects: max_redirects - 1)
103
+ redirect_opts = Hash[options]
104
+ redirect_params[:max_redirects] = max_redirects - 1
89
105
 
90
106
  unless request_body.empty?
91
107
  if response.status == 307
92
108
  # The method and the body of the original request are reused to perform the redirected request.
93
109
  redirect_method = redirect_request.verb
94
110
  request_body.rewind
95
- retry_opts[:body] = request_body
111
+ redirect_params[:body] = request_body
96
112
  else
97
113
  # redirects are **ALWAYS** GET, so remove body-related headers
98
114
  REQUEST_BODY_HEADERS.each do |h|
99
115
  redirect_headers.delete(h)
100
116
  end
101
- retry_opts.delete(:body)
117
+ redirect_params[:body] = nil
102
118
  end
103
119
  end
104
120
 
105
- retry_opts[:headers] = redirect_headers.to_h
106
-
107
- retry_options = options.class.new(retry_opts)
121
+ options = options.class.new(redirect_opts.merge(headers: redirect_headers.to_h))
108
122
  end
109
123
 
110
124
  redirect_uri = Utils.to_uri(redirect_uri)
@@ -114,34 +128,42 @@ module HTTPX
114
128
  redirect_uri.scheme == "http"
115
129
  error = InsecureRedirectError.new(redirect_uri.to_s)
116
130
  error.set_backtrace(caller)
117
- return ErrorResponse.new(request, error, options)
131
+ return ErrorResponse.new(request, error)
118
132
  end
119
133
 
120
- retry_request = build_request(redirect_method, redirect_uri, retry_options)
134
+ retry_request = build_request(redirect_method, redirect_uri, redirect_params, options)
121
135
 
122
136
  request.redirect_request = retry_request
123
137
 
124
- retry_after = response.headers["retry-after"]
138
+ redirect_after = response.headers["retry-after"]
125
139
 
126
- if retry_after
140
+ if redirect_after
127
141
  # Servers send the "Retry-After" header field to indicate how long the
128
142
  # user agent ought to wait before making a follow-up request.
129
143
  # When sent with any 3xx (Redirection) response, Retry-After indicates
130
144
  # the minimum time that the user agent is asked to wait before issuing
131
145
  # the redirected request.
132
146
  #
133
- retry_after = Utils.parse_retry_after(retry_after)
134
-
135
- log { "redirecting after #{retry_after} secs..." }
136
- pool.after(retry_after) do
137
- send_request(retry_request, connections, options)
147
+ redirect_after = Utils.parse_retry_after(redirect_after)
148
+
149
+ retry_start = Utils.now
150
+ log { "redirecting after #{redirect_after} secs..." }
151
+ selector.after(redirect_after) do
152
+ if request.response
153
+ # request has terminated abruptly meanwhile
154
+ retry_request.emit(:response, request.response)
155
+ else
156
+ log { "redirecting (elapsed time: #{Utils.elapsed_time(retry_start)})!!" }
157
+ send_request(retry_request, selector, options)
158
+ end
138
159
  end
139
160
  else
140
- send_request(retry_request, connections, options)
161
+ send_request(retry_request, selector, options)
141
162
  end
142
163
  nil
143
164
  end
144
165
 
166
+ # :nodoc:
145
167
  def redirect_request_headers(original_uri, redirect_uri, headers, options)
146
168
  headers = headers.dup
147
169
 
@@ -149,14 +171,14 @@ module HTTPX
149
171
 
150
172
  return headers unless headers.key?("authorization")
151
173
 
152
- unless original_uri.origin == redirect_uri.origin
153
- headers = headers.dup
154
- headers.delete("authorization")
155
- end
174
+ return headers if original_uri.origin == redirect_uri.origin
175
+
176
+ headers.delete("authorization")
156
177
 
157
178
  headers
158
179
  end
159
180
 
181
+ # :nodoc:
160
182
  def __get_location_from_response(response)
161
183
  # @type var location_uri: http_uri
162
184
  location_uri = URI(response.headers["location"])
@@ -166,12 +188,15 @@ module HTTPX
166
188
  end
167
189
 
168
190
  module RequestMethods
191
+ # returns the top-most original HTTPX::Request from the redirect chain
169
192
  attr_accessor :root_request
170
193
 
194
+ # returns the follow-up redirect request, or itself
171
195
  def redirect_request
172
196
  @redirect_request || self
173
197
  end
174
198
 
199
+ # sets the follow-up redirect request
175
200
  def redirect_request=(req)
176
201
  @redirect_request = req
177
202
  req.root_request = @root_request || self
@@ -179,7 +204,7 @@ module HTTPX
179
204
  end
180
205
 
181
206
  def response
182
- return super unless @redirect_request
207
+ return super unless @redirect_request && @response.nil?
183
208
 
184
209
  @redirect_request.response
185
210
  end
@@ -188,6 +213,16 @@ module HTTPX
188
213
  @options.max_redirects || MAX_REDIRECTS
189
214
  end
190
215
  end
216
+
217
+ module ConnectionMethods
218
+ private
219
+
220
+ def set_request_request_timeout(request)
221
+ return unless request.root_request.nil?
222
+
223
+ super
224
+ end
225
+ end
191
226
  end
192
227
  register_plugin :follow_redirects, FollowRedirects
193
228
  end
@@ -29,6 +29,8 @@ module HTTPX
29
29
 
30
30
  buf = outbuf if outbuf
31
31
 
32
+ buf = buf.b if buf.frozen?
33
+
32
34
  buf.prepend([compressed_flag, buf.bytesize].pack("CL>"))
33
35
  buf
34
36
  end
@@ -110,10 +110,10 @@ module HTTPX
110
110
  end
111
111
 
112
112
  module RequestBodyMethods
113
- def initialize(headers, _)
113
+ def initialize(*, **)
114
114
  super
115
115
 
116
- if (compression = headers["grpc-encoding"])
116
+ if (compression = @headers["grpc-encoding"])
117
117
  deflater_body = self.class.initialize_deflater_body(@body, compression)
118
118
  @body = Transcoder::GRPCEncoding.encode(deflater_body || @body, compressed: !deflater_body.nil?)
119
119
  else
@@ -25,26 +25,6 @@ module HTTPX
25
25
  end
26
26
  end
27
27
 
28
- module InstanceMethods
29
- def send_requests(*requests)
30
- upgrade_request, *remainder = requests
31
-
32
- return super unless VALID_H2C_VERBS.include?(upgrade_request.verb) && upgrade_request.scheme == "http"
33
-
34
- connection = pool.find_connection(upgrade_request.uri, upgrade_request.options)
35
-
36
- return super if connection && connection.upgrade_protocol == "h2c"
37
-
38
- # build upgrade request
39
- upgrade_request.headers.add("connection", "upgrade")
40
- upgrade_request.headers.add("connection", "http2-settings")
41
- upgrade_request.headers["upgrade"] = "h2c"
42
- upgrade_request.headers["http2-settings"] = HTTP2Next::Client.settings_header(upgrade_request.options.http2_settings)
43
-
44
- super(upgrade_request, *remainder)
45
- end
46
- end
47
-
48
28
  class H2CParser < Connection::HTTP2
49
29
  def upgrade(request, response)
50
30
  # skip checks, it is assumed that this is the first
@@ -65,6 +45,29 @@ module HTTPX
65
45
  module ConnectionMethods
66
46
  using URIExtensions
67
47
 
48
+ def initialize(*)
49
+ super
50
+ @h2c_handshake = false
51
+ end
52
+
53
+ def send(request)
54
+ return super if @h2c_handshake
55
+
56
+ return super unless VALID_H2C_VERBS.include?(request.verb) && request.scheme == "http"
57
+
58
+ return super if @upgrade_protocol == "h2c"
59
+
60
+ @h2c_handshake = true
61
+
62
+ # build upgrade request
63
+ request.headers.add("connection", "upgrade")
64
+ request.headers.add("connection", "http2-settings")
65
+ request.headers["upgrade"] = "h2c"
66
+ request.headers["http2-settings"] = ::HTTP2::Client.settings_header(request.options.http2_settings)
67
+
68
+ super
69
+ end
70
+
68
71
  def upgrade_to_h2c(request, response)
69
72
  prev_parser = @parser
70
73
 
@@ -13,6 +13,12 @@ module HTTPX
13
13
  # by the end user in $http_init_time, different diff metrics can be shown. The "point of time" is calculated
14
14
  # using the monotonic clock.
15
15
  module InternalTelemetry
16
+ DEBUG_LEVEL = 3
17
+
18
+ def self.extra_options(options)
19
+ options.merge(debug_level: 3)
20
+ end
21
+
16
22
  module TrackTimeMethods
17
23
  private
18
24
 
@@ -28,7 +34,19 @@ module HTTPX
28
34
  after_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
29
35
  # $http_init_time = after_time
30
36
  elapsed = after_time - prev_time
31
- warn(+"\e[31m" << "[ELAPSED TIME]: #{label}: #{elapsed} (ms)" << "\e[0m")
37
+ # klass = self.class
38
+
39
+ # until (class_name = klass.name)
40
+ # klass = klass.superclass
41
+ # end
42
+ log(
43
+ level: DEBUG_LEVEL,
44
+ color: :red,
45
+ debug_level: @options ? @options.debug_level : DEBUG_LEVEL,
46
+ debug: nil
47
+ ) do
48
+ "[ELAPSED TIME]: #{label}: #{elapsed} (ms)" << "\e[0m"
49
+ end
32
50
  end
33
51
  end
34
52
 
@@ -76,10 +94,19 @@ module HTTPX
76
94
  meter_elapsed_time("Session -> response") if response
77
95
  response
78
96
  end
97
+
98
+ def coalesce_connections(conn1, conn2, selector, *)
99
+ result = super
100
+
101
+ meter_elapsed_time("Connection##{conn2.object_id} coalescing to Connection##{conn1.object_id}") if result
102
+
103
+ result
104
+ end
79
105
  end
80
106
 
81
107
  module RequestMethods
82
108
  def self.included(klass)
109
+ klass.prepend Loggable
83
110
  klass.prepend TrackTimeMethods
84
111
  super
85
112
  end
@@ -103,6 +130,26 @@ module HTTPX
103
130
  meter_elapsed_time("Connection##{object_id}[#{@origin}]: #{state} -> #{nextstate}") if nextstate == @state
104
131
  end
105
132
  end
133
+
134
+ module PoolMethods
135
+ def self.included(klass)
136
+ klass.prepend Loggable
137
+ klass.prepend TrackTimeMethods
138
+ super
139
+ end
140
+
141
+ def checkout_connection(request_uri, options)
142
+ super.tap do |connection|
143
+ meter_elapsed_time("Pool##{object_id}: checked out connection for Connection##{connection.object_id}[#{connection.origin}]}")
144
+ end
145
+ end
146
+
147
+ def checkin_connection(connection)
148
+ super.tap do
149
+ meter_elapsed_time("Pool##{object_id}: checked in connection for Connection##{connection.object_id}[#{connection.origin}]}")
150
+ end
151
+ end
152
+ end
106
153
  end
107
154
  register_plugin :internal_telemetry, InternalTelemetry
108
155
  end
@@ -155,7 +155,7 @@ module HTTPX
155
155
  with(oauth_session: oauth_session.merge(access_token: access_token, refresh_token: refresh_token))
156
156
  end
157
157
 
158
- def build_request(*, _)
158
+ def build_request(*)
159
159
  request = super
160
160
 
161
161
  return request if request.headers.key?("authorization")
@@ -30,6 +30,22 @@ module HTTPX
30
30
  def self.extra_options(options)
31
31
  options.merge(persistent: true)
32
32
  end
33
+
34
+ module InstanceMethods
35
+ private
36
+
37
+ def get_current_selector
38
+ super(&nil) || begin
39
+ return unless block_given?
40
+
41
+ default = yield
42
+
43
+ set_current_selector(default)
44
+
45
+ default
46
+ end
47
+ end
48
+ end
33
49
  end
34
50
  register_plugin :persistent, Persistent
35
51
  end
@@ -23,24 +23,19 @@ module HTTPX
23
23
  with(proxy: opts.merge(scheme: "ntlm"))
24
24
  end
25
25
 
26
- def fetch_response(request, connections, options)
26
+ def fetch_response(request, selector, options)
27
27
  response = super
28
28
 
29
29
  if response &&
30
30
  response.is_a?(Response) &&
31
31
  response.status == 407 &&
32
32
  !request.headers.key?("proxy-authorization") &&
33
- response.headers.key?("proxy-authenticate")
34
-
35
- connection = find_connection(request, connections, options)
36
-
37
- if connection.options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
38
- request.transition(:idle)
39
- request.headers["proxy-authorization"] =
40
- connection.options.proxy.authenticate(request, response.headers["proxy-authenticate"])
41
- send_request(request, connections)
42
- return
43
- end
33
+ response.headers.key?("proxy-authenticate") && options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
34
+ request.transition(:idle)
35
+ request.headers["proxy-authorization"] =
36
+ options.proxy.authenticate(request, response.headers["proxy-authenticate"])
37
+ send_request(request, selector, options)
38
+ return
44
39
  end
45
40
 
46
41
  response
@@ -69,7 +64,14 @@ module HTTPX
69
64
  parser = @parser
70
65
  parser.extend(ProxyParser)
71
66
  parser.on(:response, &method(:__http_on_connect))
72
- parser.on(:close) { transition(:closing) }
67
+ parser.on(:close) do |force|
68
+ next unless @parser
69
+
70
+ if force
71
+ reset
72
+ emit(:terminate)
73
+ end
74
+ end
73
75
  parser.on(:reset) do
74
76
  if parser.empty?
75
77
  reset
@@ -90,8 +92,9 @@ module HTTPX
90
92
 
91
93
  case @state
92
94
  when :connecting
93
- @parser.close
95
+ parser = @parser
94
96
  @parser = nil
97
+ parser.close
95
98
  when :idle
96
99
  @parser.callbacks.clear
97
100
  set_parser_callbacks(@parser)
@@ -163,8 +166,8 @@ module HTTPX
163
166
  end
164
167
 
165
168
  class ConnectRequest < Request
166
- def initialize(uri, _options)
167
- super("CONNECT", uri, {})
169
+ def initialize(uri, options)
170
+ super("CONNECT", uri, options)
168
171
  @headers.delete("accept")
169
172
  end
170
173
 
@@ -89,7 +89,7 @@ module HTTPX
89
89
 
90
90
  def initialize(buffer, options)
91
91
  @buffer = buffer
92
- @options = Options.new(options)
92
+ @options = options
93
93
  end
94
94
 
95
95
  def close; end
@@ -141,7 +141,7 @@ module HTTPX
141
141
 
142
142
  def initialize(buffer, options)
143
143
  @buffer = buffer
144
- @options = Options.new(options)
144
+ @options = options
145
145
  end
146
146
 
147
147
  def close; end