httpx 0.14.5 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16f2f2bc0c8dfff4c7a92bc573f25e717a703f06fa8f336bde41bd60ad86ad87
4
- data.tar.gz: 313f1fe9ec2c3d5a04f838c36d6ceee6982c60e3b4d6646644428f0c5c6d1d74
3
+ metadata.gz: db99d4bc2c59c9292882ced18f8b89a9137d13bca7cf0cb7a9e4a84609e0f385
4
+ data.tar.gz: a42b6a825a7c3d098a9dca4a0a705e37eb0c17012533e66a2bd85ad7cb8897e7
5
5
  SHA512:
6
- metadata.gz: 6780278b2b12254fe0f1a884a78d7e102e965d2d5f93b7431a2239e5b2d5e8c25e6e122715af89b03e786ba752b265d53d94d1f3a66cac7fe2a26299ff25491d
7
- data.tar.gz: e85a43e840647f60a721481e112c8b8672f3f478d4bacdae6337c52beca5e3a198e2e9f0206592ecb111dfd0b5be33addf2f2e930255750ed89f145631f8d66c
6
+ metadata.gz: be7a3184672e1c7193e4acf538b5c3f8b3aacd257334055b59fac4303cb02701f12d6e35cfd553b641ac12ca0b10b47ff011f396f7ee8a3068f3785be9ad07f8
7
+ data.tar.gz: 69cdeb266a69127135dd6d7c4cab6cc034ef7c5de11b4eee4bf135f3073c55dac4f25a7c1453fb6e73c4200470f9b374bde5aa1c91c004e264643813e9cb7e41
@@ -1,4 +1,4 @@
1
- # 0.13.1
1
+ # 0.13.2
2
2
 
3
3
  ## Improvements
4
4
 
@@ -0,0 +1,44 @@
1
+ # 0.15.0
2
+
3
+ ## Features
4
+
5
+ ### HTTP response pattern-matching (ruby 3 only)
6
+
7
+ You can now apply pattern matching in responses:
8
+
9
+ ```ruby
10
+ case response = HTTPX.get("https://google.com")
11
+ in { status: 200..399, headers: [*, ["x-special-token", token], *], body: }
12
+ puts "success: #{body.to_s}"
13
+ in { status: 400..499, body: }
14
+ puts "client error: #{body.to_s}"
15
+ in { status: 500.., body: }
16
+ puts "server error: #{body.to_s}"
17
+ in { error: error }
18
+ puts "error: #{error.message}"
19
+ else
20
+ raise "unexpected: #{response}"
21
+ end
22
+ ```
23
+
24
+ ### NTLM Authentication
25
+
26
+ A new plugin, `:ntml_authentication`, is now available. Like the name suggests, it allows authenticating requests via [NTLM](https://docs.microsoft.com/en-us/windows-server/security/kerberos/ntlm-overview).
27
+
28
+ ```ruby
29
+ ntlm_http = HTTPX.plugin(:ntlm_authentication)
30
+
31
+ ntlm.ntlm_authentication("user", "password").get("http://protected-area-requiring-ntlm.net")
32
+ # or for a specific domain
33
+ ntlm.ntlm_authentication("user", "password", "Domain\\User").get("http://protected-area-requiring-ntlm.net")
34
+ ```
35
+
36
+ ## Improvemennts
37
+
38
+ A new timeout option, `settings_timeout`, is supported for the HTTP/2 handshake; after the TCP and TLS handshakes are complete, and initiating the HTTP/2 handshake, the client terminates the connection with SETTINGS_TIMEOUT error code, if it doesn't receive the server settings for the amount of seconds set in `settings_timeout` (by default, 10 seconds).
39
+
40
+ ```ruby
41
+ # if you want to change
42
+ HTTPX.with(timeout: {settings_timeout: 5})....
43
+
44
+ ```
data/lib/httpx.rb CHANGED
@@ -6,6 +6,7 @@ require "httpx/extensions"
6
6
 
7
7
  require "httpx/errors"
8
8
  require "httpx/utils"
9
+ require "httpx/punycode"
9
10
  require "httpx/domain_name"
10
11
  require "httpx/altsvc"
11
12
  require "httpx/callbacks"
@@ -51,7 +51,7 @@ module HTTPX
51
51
  def initialize(type, uri, options)
52
52
  @type = type
53
53
  @origins = [uri.origin]
54
- @origin = Utils.uri(uri.origin)
54
+ @origin = Utils.to_uri(uri.origin)
55
55
  @options = Options.new(options)
56
56
  @window_size = @options.window_size
57
57
  @read_buffer = Buffer.new(BUFFER_SIZE)
@@ -254,6 +254,8 @@ module HTTPX
254
254
  end
255
255
 
256
256
  def consume
257
+ return unless @io
258
+
257
259
  catch(:called) do
258
260
  epiped = false
259
261
  loop do
@@ -311,7 +313,7 @@ module HTTPX
311
313
 
312
314
  # exit #consume altogether if all outstanding requests have been dealt with
313
315
  return if @pending.size.zero? && @inflight.zero?
314
- end unless (interests == :w || @state == :closing) && !epiped
316
+ end unless (interests.nil? || interests == :w || @state == :closing) && !epiped
315
317
 
316
318
  #
317
319
  # tight write loop.
@@ -424,6 +426,9 @@ module HTTPX
424
426
  emit(:close)
425
427
  end
426
428
  end
429
+ parser.on(:close_handshake) do
430
+ consume
431
+ end
427
432
  parser.on(:reset) do
428
433
  if parser.empty?
429
434
  reset
@@ -436,6 +441,9 @@ module HTTPX
436
441
  transition(:open)
437
442
  end
438
443
  end
444
+ parser.on(:current_timeout) do
445
+ @current_timeout = @timeout = parser.timeout
446
+ end
439
447
  parser.on(:timeout) do |tout|
440
448
  @timeout = tout
441
449
  end
@@ -466,10 +474,11 @@ module HTTPX
466
474
 
467
475
  send_pending
468
476
 
469
- @timeout = @current_timeout = @options.timeout[:operation_timeout]
477
+ @timeout = @current_timeout = parser.timeout
470
478
  emit(:open)
471
479
  when :closing
472
480
  return unless @state == :open
481
+
473
482
  when :closed
474
483
  return unless @state == :closing
475
484
  return unless @write_buffer.empty?
@@ -24,6 +24,10 @@ module HTTPX
24
24
  @handshake_completed = false
25
25
  end
26
26
 
27
+ def timeout
28
+ @options.timeout[:operation_timeout]
29
+ end
30
+
27
31
  def interests
28
32
  # this means we're processing incoming response already
29
33
  return :r if @request
@@ -34,6 +34,12 @@ module HTTPX
34
34
  init_connection
35
35
  end
36
36
 
37
+ def timeout
38
+ return @options.timeout[:operation_timeout] if @handshake_completed
39
+
40
+ @options.timeout[:settings_timeout]
41
+ end
42
+
37
43
  def interests
38
44
  # waiting for WINDOW_UPDATE frames
39
45
  return :r if @buffer.full?
@@ -117,6 +123,13 @@ module HTTPX
117
123
  end
118
124
 
119
125
  def handle_error(ex)
126
+ if ex.instance_of?(TimeoutError) && !@handshake_completed && @connection.state != :closed
127
+ @connection.goaway(:settings_timeout, "closing due to settings timeout")
128
+ emit(:close_handshake)
129
+ settings_ex = SettingsTimeoutError.new(ex.timeout, ex.message)
130
+ settings_ex.set_backtrace(ex.backtrace)
131
+ ex = settings_ex
132
+ end
120
133
  @streams.each_key do |request|
121
134
  emit(:error, request, ex)
122
135
  end
@@ -312,6 +325,7 @@ module HTTPX
312
325
 
313
326
  def on_settings(*)
314
327
  @handshake_completed = true
328
+ emit(:current_timeout)
315
329
 
316
330
  if @max_requests.zero?
317
331
  @max_requests = @connection.remote_settings[:settings_max_concurrent_streams]
@@ -144,295 +144,5 @@ module HTTPX
144
144
  1
145
145
  end
146
146
  end
147
-
148
- # :nocov:
149
- # rubocop:disable all
150
- # -*- coding: utf-8 -*-
151
- #--
152
- # punycode.rb - PunyCode encoder for the Domain Name library
153
- #
154
- # Copyright (C) 2011-2017 Akinori MUSHA, All rights reserved.
155
- #
156
- # Ported from puny.c, a part of VeriSign XCode (encode/decode) IDN
157
- # Library.
158
- #
159
- # Copyright (C) 2000-2002 Verisign Inc., All rights reserved.
160
- #
161
- # Redistribution and use in source and binary forms, with or
162
- # without modification, are permitted provided that the following
163
- # conditions are met:
164
- #
165
- # 1) Redistributions of source code must retain the above copyright
166
- # notice, this list of conditions and the following disclaimer.
167
- #
168
- # 2) Redistributions in binary form must reproduce the above copyright
169
- # notice, this list of conditions and the following disclaimer in
170
- # the documentation and/or other materials provided with the
171
- # distribution.
172
- #
173
- # 3) Neither the name of the VeriSign Inc. nor the names of its
174
- # contributors may be used to endorse or promote products derived
175
- # from this software without specific prior written permission.
176
- #
177
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
178
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
179
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
180
- # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
181
- # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
182
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
183
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
184
- # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
185
- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
186
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
187
- # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
188
- # POSSIBILITY OF SUCH DAMAGE.
189
- #
190
- # This software is licensed under the BSD open source license. For more
191
- # information visit www.opensource.org.
192
- #
193
- # Authors:
194
- # John Colosi (VeriSign)
195
- # Srikanth Veeramachaneni (VeriSign)
196
- # Nagesh Chigurupati (Verisign)
197
- # Praveen Srinivasan(Verisign)
198
- #++
199
- module Punycode
200
- BASE = 36
201
- TMIN = 1
202
- TMAX = 26
203
- SKEW = 38
204
- DAMP = 700
205
- INITIAL_BIAS = 72
206
- INITIAL_N = 0x80
207
- DELIMITER = "-"
208
-
209
- MAXINT = (1 << 32) - 1
210
-
211
- LOBASE = BASE - TMIN
212
- CUTOFF = LOBASE * TMAX / 2
213
-
214
- RE_NONBASIC = /[^\x00-\x7f]/.freeze
215
-
216
- # Returns the numeric value of a basic code point (for use in
217
- # representing integers) in the range 0 to base-1, or nil if cp
218
- # is does not represent a value.
219
- DECODE_DIGIT = {}.tap do |map|
220
- # ASCII A..Z map to 0..25
221
- # ASCII a..z map to 0..25
222
- (0..25).each { |i| map[65 + i] = map[97 + i] = i }
223
- # ASCII 0..9 map to 26..35
224
- (26..35).each { |i| map[22 + i] = i }
225
- end
226
-
227
- # Returns the basic code point whose value (when used for
228
- # representing integers) is d, which must be in the range 0 to
229
- # BASE-1. The lowercase form is used unless flag is true, in
230
- # which case the uppercase form is used. The behavior is
231
- # undefined if flag is nonzero and digit d has no uppercase
232
- # form.
233
- ENCODE_DIGIT = proc { |d, flag|
234
- (d + 22 + (d < 26 ? 75 : 0) - (flag ? (1 << 5) : 0)).chr
235
- # 0..25 map to ASCII a..z or A..Z
236
- # 26..35 map to ASCII 0..9
237
- }
238
-
239
- DOT = "."
240
- PREFIX = "xn--"
241
-
242
- # Most errors we raise are basically kind of ArgumentError.
243
- class ArgumentError < ::ArgumentError; end
244
- class BufferOverflowError < ArgumentError; end
245
-
246
- class << self
247
- # Encode a +string+ in Punycode
248
- def encode(string)
249
- input = string.unpack("U*")
250
- output = +""
251
-
252
- # Initialize the state
253
- n = INITIAL_N
254
- delta = 0
255
- bias = INITIAL_BIAS
256
-
257
- # Handle the basic code points
258
- input.each { |cp| output << cp.chr if cp < 0x80 }
259
-
260
- h = b = output.length
261
-
262
- # h is the number of code points that have been handled, b is the
263
- # number of basic code points, and out is the number of characters
264
- # that have been output.
265
-
266
- output << DELIMITER if b > 0
267
-
268
- # Main encoding loop
269
-
270
- while h < input.length
271
- # All non-basic code points < n have been handled already. Find
272
- # the next larger one
273
-
274
- m = MAXINT
275
- input.each do |cp|
276
- m = cp if (n...m) === cp
277
- end
278
-
279
- # Increase delta enough to advance the decoder's <n,i> state to
280
- # <m,0>, but guard against overflow
281
-
282
- delta += (m - n) * (h + 1)
283
- raise BufferOverflowError if delta > MAXINT
284
-
285
- n = m
286
-
287
- input.each do |cp|
288
- # AMC-ACE-Z can use this simplified version instead
289
- if cp < n
290
- delta += 1
291
- raise BufferOverflowError if delta > MAXINT
292
- elsif cp == n
293
- # Represent delta as a generalized variable-length integer
294
- q = delta
295
- k = BASE
296
- loop do
297
- t = k <= bias ? TMIN : k - bias >= TMAX ? TMAX : k - bias
298
- break if q < t
299
-
300
- q, r = (q - t).divmod(BASE - t)
301
- output << ENCODE_DIGIT[t + r, false]
302
- k += BASE
303
- end
304
-
305
- output << ENCODE_DIGIT[q, false]
306
-
307
- # Adapt the bias
308
- delta = h == b ? delta / DAMP : delta >> 1
309
- delta += delta / (h + 1)
310
- bias = 0
311
- while delta > CUTOFF
312
- delta /= LOBASE
313
- bias += BASE
314
- end
315
- bias += (LOBASE + 1) * delta / (delta + SKEW)
316
-
317
- delta = 0
318
- h += 1
319
- end
320
- end
321
-
322
- delta += 1
323
- n += 1
324
- end
325
-
326
- output
327
- end
328
-
329
- # Encode a hostname using IDN/Punycode algorithms
330
- def encode_hostname(hostname)
331
- hostname.match(RE_NONBASIC) || (return hostname)
332
-
333
- hostname.split(DOT).map do |name|
334
- if name.match(RE_NONBASIC)
335
- PREFIX + encode(name)
336
- else
337
- name
338
- end
339
- end.join(DOT)
340
- end
341
-
342
- # Decode a +string+ encoded in Punycode
343
- def decode(string)
344
- # Initialize the state
345
- n = INITIAL_N
346
- i = 0
347
- bias = INITIAL_BIAS
348
-
349
- if j = string.rindex(DELIMITER)
350
- b = string[0...j]
351
-
352
- b.match(RE_NONBASIC) &&
353
- raise(ArgumentError, "Illegal character is found in basic part: #{string.inspect}")
354
-
355
- # Handle the basic code points
356
-
357
- output = b.unpack("U*")
358
- u = string[(j + 1)..-1]
359
- else
360
- output = []
361
- u = string
362
- end
363
-
364
- # Main decoding loop: Start just after the last delimiter if any
365
- # basic code points were copied; start at the beginning
366
- # otherwise.
367
-
368
- input = u.unpack("C*")
369
- input_length = input.length
370
- h = 0
371
- out = output.length
372
-
373
- while h < input_length
374
- # Decode a generalized variable-length integer into delta,
375
- # which gets added to i. The overflow checking is easier
376
- # if we increase i as we go, then subtract off its starting
377
- # value at the end to obtain delta.
378
-
379
- oldi = i
380
- w = 1
381
- k = BASE
382
-
383
- loop do
384
- (digit = DECODE_DIGIT[input[h]]) ||
385
- raise(ArgumentError, "Illegal character is found in non-basic part: #{string.inspect}")
386
- h += 1
387
- i += digit * w
388
- raise BufferOverflowError if i > MAXINT
389
-
390
- t = k <= bias ? TMIN : k - bias >= TMAX ? TMAX : k - bias
391
- break if digit < t
392
-
393
- w *= BASE - t
394
- raise BufferOverflowError if w > MAXINT
395
-
396
- k += BASE
397
- (h < input_length) || raise(ArgumentError, "Malformed input given: #{string.inspect}")
398
- end
399
-
400
- # Adapt the bias
401
- delta = oldi == 0 ? i / DAMP : (i - oldi) >> 1
402
- delta += delta / (out + 1)
403
- bias = 0
404
- while delta > CUTOFF
405
- delta /= LOBASE
406
- bias += BASE
407
- end
408
- bias += (LOBASE + 1) * delta / (delta + SKEW)
409
-
410
- # i was supposed to wrap around from out+1 to 0, incrementing
411
- # n each time, so we'll fix that now:
412
-
413
- q, i = i.divmod(out + 1)
414
- n += q
415
- raise BufferOverflowError if n > MAXINT
416
-
417
- # Insert n at position i of the output:
418
-
419
- output[i, 0] = n
420
-
421
- out += 1
422
- i += 1
423
- end
424
- output.pack("U*")
425
- end
426
-
427
- # Decode a hostname using IDN/Punycode algorithms
428
- def decode_hostname(hostname)
429
- hostname.gsub(/(\A|#{Regexp.quote(DOT)})#{Regexp.quote(PREFIX)}([^#{Regexp.quote(DOT)}]*)/o) do
430
- Regexp.last_match(1) << decode(Regexp.last_match(2))
431
- end
432
- end
433
- end
434
- # rubocop:enable all
435
- # :nocov:
436
- end
437
147
  end
438
148
  end