httpx 1.6.2 → 1.7.0

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_11_0.md +3 -3
  3. data/doc/release_notes/1_6_3.md +47 -0
  4. data/doc/release_notes/1_7_0.md +149 -0
  5. data/lib/httpx/adapters/datadog.rb +1 -1
  6. data/lib/httpx/adapters/faraday.rb +1 -1
  7. data/lib/httpx/adapters/sentry.rb +1 -1
  8. data/lib/httpx/altsvc.rb +3 -1
  9. data/lib/httpx/connection/http1.rb +14 -15
  10. data/lib/httpx/connection/http2.rb +16 -15
  11. data/lib/httpx/connection.rb +118 -110
  12. data/lib/httpx/domain_name.rb +1 -1
  13. data/lib/httpx/extensions.rb +0 -14
  14. data/lib/httpx/headers.rb +2 -2
  15. data/lib/httpx/io/ssl.rb +1 -1
  16. data/lib/httpx/loggable.rb +14 -2
  17. data/lib/httpx/options.rb +60 -17
  18. data/lib/httpx/plugins/auth/digest.rb +44 -4
  19. data/lib/httpx/plugins/auth.rb +87 -4
  20. data/lib/httpx/plugins/aws_sdk_authentication.rb +0 -1
  21. data/lib/httpx/plugins/callbacks.rb +15 -1
  22. data/lib/httpx/plugins/cookies/cookie.rb +1 -0
  23. data/lib/httpx/plugins/digest_auth.rb +4 -5
  24. data/lib/httpx/plugins/fiber_concurrency.rb +16 -1
  25. data/lib/httpx/plugins/grpc/grpc_encoding.rb +1 -1
  26. data/lib/httpx/plugins/grpc.rb +2 -2
  27. data/lib/httpx/plugins/internal_telemetry.rb +1 -1
  28. data/lib/httpx/plugins/ntlm_auth.rb +5 -3
  29. data/lib/httpx/plugins/oauth.rb +162 -56
  30. data/lib/httpx/plugins/proxy/http.rb +37 -9
  31. data/lib/httpx/plugins/rate_limiter.rb +2 -2
  32. data/lib/httpx/plugins/response_cache/file_store.rb +1 -0
  33. data/lib/httpx/plugins/response_cache.rb +16 -9
  34. data/lib/httpx/plugins/retries.rb +55 -16
  35. data/lib/httpx/plugins/ssrf_filter.rb +1 -1
  36. data/lib/httpx/plugins/stream.rb +59 -8
  37. data/lib/httpx/plugins/stream_bidi.rb +87 -22
  38. data/lib/httpx/pool.rb +65 -21
  39. data/lib/httpx/request.rb +13 -14
  40. data/lib/httpx/resolver/https.rb +100 -34
  41. data/lib/httpx/resolver/multi.rb +12 -27
  42. data/lib/httpx/resolver/native.rb +68 -38
  43. data/lib/httpx/resolver/resolver.rb +46 -29
  44. data/lib/httpx/resolver/system.rb +63 -39
  45. data/lib/httpx/resolver.rb +97 -29
  46. data/lib/httpx/response/body.rb +2 -0
  47. data/lib/httpx/response.rb +22 -6
  48. data/lib/httpx/selector.rb +44 -20
  49. data/lib/httpx/session.rb +23 -33
  50. data/lib/httpx/transcoder/body.rb +1 -1
  51. data/lib/httpx/transcoder/deflate.rb +13 -8
  52. data/lib/httpx/transcoder/json.rb +1 -1
  53. data/lib/httpx/transcoder/multipart/decoder.rb +4 -4
  54. data/lib/httpx/transcoder/multipart/encoder.rb +1 -1
  55. data/lib/httpx/transcoder/multipart.rb +16 -8
  56. data/lib/httpx/transcoder/utils/body_reader.rb +1 -2
  57. data/lib/httpx/transcoder/utils/deflater.rb +1 -2
  58. data/lib/httpx/transcoder.rb +4 -6
  59. data/lib/httpx/version.rb +1 -1
  60. data/sig/altsvc.rbs +3 -0
  61. data/sig/chainable.rbs +3 -3
  62. data/sig/connection.rbs +13 -6
  63. data/sig/loggable.rbs +5 -1
  64. data/sig/options.rbs +6 -2
  65. data/sig/plugins/auth/digest.rbs +6 -0
  66. data/sig/plugins/auth.rbs +28 -4
  67. data/sig/plugins/basic_auth.rbs +3 -3
  68. data/sig/plugins/callbacks.rbs +3 -0
  69. data/sig/plugins/digest_auth.rbs +2 -4
  70. data/sig/plugins/fiber_concurrency.rbs +6 -0
  71. data/sig/plugins/ntlm_auth.rbs +2 -2
  72. data/sig/plugins/oauth.rbs +46 -15
  73. data/sig/plugins/rate_limiter.rbs +1 -1
  74. data/sig/plugins/response_cache/file_store.rbs +2 -0
  75. data/sig/plugins/response_cache.rbs +4 -0
  76. data/sig/plugins/retries.rbs +8 -2
  77. data/sig/plugins/stream.rbs +13 -3
  78. data/sig/plugins/stream_bidi.rbs +5 -7
  79. data/sig/pool.rbs +1 -1
  80. data/sig/resolver/https.rbs +7 -0
  81. data/sig/resolver/multi.rbs +2 -9
  82. data/sig/resolver/native.rbs +1 -1
  83. data/sig/resolver/resolver.rbs +9 -8
  84. data/sig/resolver/system.rbs +4 -2
  85. data/sig/resolver.rbs +12 -3
  86. data/sig/response.rbs +3 -0
  87. data/sig/selector.rbs +2 -0
  88. data/sig/session.rbs +8 -8
  89. data/sig/transcoder/multipart.rbs +4 -2
  90. data/sig/transcoder.rbs +5 -1
  91. metadata +5 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c039169fa319d4e42cff7cb34d8a9cc7fc571e62565f673c32436645780222c
4
- data.tar.gz: 6affb65b4fa71aa813cffc498034e0c6a6609bcdb0fca9d0ae9df738d4786d1d
3
+ metadata.gz: 2af63a63fe08211db58764570618b2e7c234d3473a28fc1ad6f5a31c3d0fb13d
4
+ data.tar.gz: 6b2671b85ac69e4817b8b764dfdabf638dacf94cfdcab6c44627ad64a57bcb50
5
5
  SHA512:
6
- metadata.gz: 21be43709d51f7c50d1624923bc1657c9f096b84686612ff70f660af320d71d94d28a52ccef05f27558d74f22e1fd307944ca8ddf8b6c74c0b322e83eaad9b19
7
- data.tar.gz: c551d2b32a71c5bbf099283b3577abe80d5005f702af5edefb79f445296c37c8fb9afb3d52b18db254a0c4ddd115d5f45953549f9b3c99c90ff8b86aedd6cb91
6
+ metadata.gz: 50a2d2d3c0cb27f3bf84cc34553b06bd9fadf46ad789f9cfa08091cc458ac07751cb5593e01d242d88e444f10ac2b3014ea3e4e56f7c616ebc860517d08ef90d
7
+ data.tar.gz: 6b15f21262e85639c6d32b9851926d7e717b6f9bcbc226b9af3872d67461799fd24847a2d563dfbad1967d5349d371c18a1a6e1a28c721c5f33387f2fc1446a7
@@ -21,7 +21,7 @@ stub_http_request(:get, "https://www.google.com").and_return(status: 200, body:
21
21
 
22
22
  ```
23
23
 
24
- Read more about it in the [webmock integration documentation](https://os85.gitlab.io/httpx/wiki/Webmock-Adapter).
24
+ Read more about it in the [webmock integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Webmock-Adapter).
25
25
 
26
26
  ### Datadog Adapter
27
27
 
@@ -40,7 +40,7 @@ A trace will be emitted for every request, so this should be an interesting visu
40
40
 
41
41
  Customization options and traces are similar to what [the net-http adapter provides](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#nethttp).
42
42
 
43
- Read more about it in the [datadog integration documentation](https://os85.gitlab.io/httpx/wiki/Datadog-Adapter).
43
+ Read more about it in the [datadog integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Datadog-Adapter).
44
44
 
45
45
  ## Improvements
46
46
 
@@ -52,7 +52,7 @@ Read more about it in the [datadog integration documentation](https://os85.gitla
52
52
  HTTPX.plugin(:multipart).post(uri, form: {file: File.new("path/to/file")})
53
53
  ```
54
54
 
55
- Read more about it in the [multipart plugin documentation](https://os85.gitlab.io/httpx/wiki/Multipart-Uploads), including also about why this was made.
55
+ Read more about it in the [multipart plugin documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Multipart-Uploads), including also about why this was made.
56
56
 
57
57
  ### Expect Plugin
58
58
 
@@ -0,0 +1,47 @@
1
+ # 1.6.3
2
+
3
+ ## Features
4
+
5
+ * allow redacting only headers, or only the body, when using `debug_redact: :headers` or `debug_redact: :body` respectively.
6
+
7
+ ## Improvements
8
+
9
+ * `system` resolver now works in a non-blocking manner, initiating the dns query in a separate thread and waiting on the pipe after that (it was blocking the main thread during resolution before).
10
+ * reduce allocation to a single shared option object when headers are passed as a session-level option, like `HTTPX.with(headers: headers).get(...)`
11
+ * favour using `String#replace` in buffer operations (instead of "clean-then-append").
12
+ * using `Array#unshift` instead of `Array#concat` in order to ensure that request ordering is respected in the face of an in-between error which requires reconnect-and-resend.
13
+ * replaced more internal callback indirection with plain method calls.
14
+
15
+
16
+ ## Bugfixes
17
+
18
+ * https: prevent modification of the ssl context object when performing a reconnection.
19
+ * compression: do not return early if the decompression buffer yields an empty string (more frequent under jruby 10).
20
+ * response cache: take query params into account when caching or retrieving cached responses.
21
+ * response cache: do not decompress cached responses on body consumption (the response bodies are cached in plaintext).
22
+ * native resolver: pick next timeout associated with the hostname being resolved (and not the hostnames in the queue).
23
+ * pool: assume that, even when signalled that a connection is available, context may be switched to a session which also checks the same connection out, before it's able to pick it up; in such a case, start from the beginning, until the pool timeout expires.
24
+ * session: forego bookkeeping when a connection is coalesced (instead, allow it to be dropped).
25
+ * digest_auth: make sure that an array is sent back if the probe response fails.
26
+ * alt-svc: when alt-svc handshake happens with more in-flight requests, defer termination to when these requests are made.
27
+ * http2: fix use of unexisting var `ex` when processing the connection closed callback.
28
+ * connection: fix potential session dereferencing, which allowed connections to be used across sessions, therefore bypassing needed synchronization and leading to the `undefined method 'after' for nil:NilClass` error.
29
+ * selector: close only selected connections (instead of all selectable connections) when an error occurs during IO readiness wait calls.
30
+ * resolvers: correctly propagate abrupt termination errors to the connection objects waiting for the answer.
31
+ * resolvers: when errors happenm force-close unresolved connections (and ensure they're both pinned to the corresponding session before the error happens, and are unpinned after error is propagated).
32
+ * resolvers: ensure resolvers transition to "closed" state, on all cases, when any error happens.
33
+ * resolvers: ensure that the next hostname is resolved when a timeout happens on the current one.
34
+ * native resolver: fixed duplication of the hostname to resolve in the list of candidates.
35
+ * https resolver: use a `system` resolver to resolve the DoH server hostname (instead of rerouting it to itself).
36
+ * https resolver: skip loop error reporting when error happens outside of it.
37
+ * https resolver: close connection on resolve errors, which prevents it from being around in the pool after termination; also deactivate it after successful use.
38
+ * multi resolver: do not check resolvers back into the pool if it's a multi resolver and the peer is still resolving (and do the check outside of the critical area).
39
+ * sentry adapter: removed usage of deprecated method which has been removed in sentry-ruby 6.0.0.
40
+ * selector: when coalescing connections, pin the current session before merging connections, to prevent it from registering in a selector being used in a different thread, and inadvertedly allowing it to be used across threads.
41
+ * session: fix: always pin connection before early-or-lazy resolution (fixes connection pool accounting under connection coalescing).
42
+
43
+ ## Chores
44
+
45
+ * logging emits a timestamp as well (to monitor timeouts).
46
+ * `:stream_bidi` plugin: extends HTTP2 module by using plugin extensions.
47
+ * connection: remove session/selector references when closing a connection (prevents leaking them beyond the usage scope).
@@ -0,0 +1,149 @@
1
+ # 1.7.0
2
+
3
+ ## Features
4
+
5
+ ### All AUTH plugin improvements!!
6
+
7
+ #### `:auth`
8
+
9
+ The `:auth` plugin can now be used with a dynamic callable object (methods, procs...) to generate the token.
10
+
11
+ ```ruby
12
+ # static token, pre 1.7.0
13
+ HTTPX.plugin(:auth).authorization("API-TOKEN")
14
+ # dynamically generate token!
15
+ HTTPX.plugin(:auth).authorization { generate_new_ephemeral_token }
16
+ ```
17
+
18
+ The `.authorization` method is now syntactic sugar for a new option, `:auth_header_value`, which can be used directly, alongside a `:auth_header_type`:
19
+
20
+ ```ruby
21
+ HTTPX.plugin(:auth).authorization("API-TOKEN")
22
+ HTTPX.plugin(:auth).authorization { generate_new_ephemeral_token }
23
+ HTTPX.plugin(:auth).authorization("Bearer API-TOKEN")
24
+ # same as
25
+ HTTPX.plugin(:auth, auth_header_value: "API-TOKEN")
26
+ HTTPX.plugin(:auth, auth_header_value: -> { generate_new_ephemeral_token })
27
+ HTTPX.plugin(:auth, auth_header_type: "Bearer", auth_header_value: "API-TOKEN")
28
+ ```
29
+
30
+ A new option `:generate_auth_value_on_retry` (which can be passed a callable receiving a response object) is now available; when used alongside the `:retries` plugin, it'll use the callable passed to the `.authorization` method to generate a new token before retrying the request:
31
+
32
+ ```ruby
33
+ authed = HTTPX.plugin(:retries).plugin(:auth, generate_auth_value_on_retry: ->(res) {
34
+ res.status == 401
35
+ }).authorization { generate_new_ephemeral_token }
36
+ authed.get("https://example.com")
37
+ ```
38
+
39
+ Read more about it in the [auth plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/Auth).
40
+
41
+ #### `:oauth`
42
+
43
+ The `:oauth` plugin implementation was revamped to make use of the `:auth` plugin new functionality, in order to make managing an oauth session more seamless.
44
+
45
+ Take the following example:
46
+
47
+ ```ruby
48
+ session = HTTPX.plugin(:oauth).with_oauth_options(
49
+ issuer: server.origin,
50
+ client_id: "CLIENT_ID",
51
+ client_secret: "SECRET",
52
+ )
53
+ session.get("https://example.com") #=> will load server metadata, request an access token, and perform the request with the access token.
54
+ # 2 hours later...
55
+ session.get("https://example.com")
56
+ # it'll reuse the same acces token, and if the request fails with 401, it'll request a new
57
+ # access token using the refresh token grant (when supported by the token issuer), and
58
+ # reperform the original request with the new access token.
59
+ ```
60
+
61
+ A new option, `:oauth_options`, is now available. The same parameters previously supported by the `:oauth_session` options are supported.
62
+
63
+ The following components are therefore deprecated and scheduled for removal in a future major version:
64
+
65
+ * `:oauth_session` option
66
+ * `.oauth_auth` session method
67
+ * `.with_access_token` session method
68
+
69
+ #### `:bearer_auth`, `:digest_auth`; `:ntlm_auth`
70
+
71
+ The `:auth` plugin is now the foundation of each of these plugins, which haven't suffered major API changes.
72
+
73
+ Read more about it in the [auth plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/OAuth).
74
+
75
+ ### `:retries` plugin: `:retry_after` backoff algorithms
76
+
77
+ The `:retries` plugins supports two new possible values for the `:retry_after` option: `:exponential_backoff` and `:polynomial_backoff`. They'll implement the respective calculation per each retry of a given request.
78
+
79
+ ```ruby
80
+ # will wait 1, 2, 4, 8, 16 seconds... depending of how many retries it can wait for
81
+ session = HTTPX.plugin(:retries, retry_after: :exponential_backoff)
82
+ ```
83
+
84
+ Read more about it in the [retries plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/Retries).
85
+
86
+ ### Ractor compatibility
87
+
88
+ `httpx` can be used within a ractor:
89
+
90
+ ```ruby
91
+ # ruby 4.0 syntax
92
+ response = Ractor.new(uri) do |uri|
93
+ HTTPX.get(uri)
94
+ end.value
95
+ ```
96
+
97
+ Bear in mind that, if you're connection via HTTPS, you'll need make sure you're using version 4.0 or higher of the `openssl` gem.
98
+
99
+ The test suite isn't exhaustive for ractors yet, but most plugins should also be ractor-compatible. If they don't work, that's a bug, and you're recommended to report it.
100
+
101
+ ## Improvements
102
+
103
+ * When encoding the `:json` param to send it as `application/json` payload, (example: `HTTPX.post("https://example.com", json: { foo: "bar })`), and the method uses the `json` standard library, it'll use `JSON.generate` (instead of `JSON.dump`) to encode the JSON payload. The reason is that, unlike `JSON.dump`, it doesn't rely on access to a global mutable hash, and is therefore ractor-safe.
104
+ * `:stream` plugin: the stream response class (the object that is returned in request calls is a stream response) can be extended now. You can add a `StreamResponseMethods` method to your plugin. Read more about it in the documentation.
105
+ * The resolver name cache (used by the native and https resolvers) was remade into a LRU cache, and will therefore not keep on growing when `httpx` is used to connect to a huge number of hostnames in a process.
106
+ * the native and https DNS resolvers will ignore answers with SERVFAIL code while there are retries left (some resolvers use such error code for rate limiting).
107
+ * `:timeout` option values are now validated, and an error is raised when passing an unrecognized timeout option (which is a good layer of protection for typos).
108
+ * pool: try passing the scheduler to a thread waiting on a connection, to avoid the current case where a connection may be checked-in-then-immediately-out-after when doing multiple requests in a loop, never giving a chance to others and potentially making the pool time out.
109
+ * headers deep-freeze and dup.
110
+
111
+ ## Bugfixes
112
+
113
+ * recover and close connection when an `IOError` is raised while waiting for IO readiness (could cause busy loops during HTTP/2 termination handshake).
114
+ * `:stream_bidi` plugin: improve thread-safety of buffer operations when the session is used from multiple threads.
115
+ * `:stream_bidi` plugin: added missing methods to signal in order to comply with the Selectable API (it was reported as raising `NoMethodError` under certain conditions).
116
+ * `:stream_bidi` plugin: can support non-bidirectional stream requests using the same session.
117
+ * `:stream` plugin: is now compatible with fiber scheduler engines (via the `:fiber_concurrency` plugin).
118
+ * `:stream` plugin: make sure that stream long-running requests do not share the same connection as regular threads.
119
+ * `:digest_auth` plugin: can now support qop values wrapped inside parentheses in the `www-authenticate` header (i.e. `qop="('auth',)"`).
120
+ * https resolver: handle 3XX redirect responses in HTTP DNS queries.
121
+ * https resolver: do not close HTTP connections whhich are shared across AAAA and A resolution paths when its in use by one of them.
122
+ * fix access to private method from `http-2` which was made public in more recent versions, but not in older still-supported versions.
123
+ * fixed resolver log message using a "connection" label.
124
+ * `HTTPX::Response.copy_to` will explicitly close the response at the end; given that the body file can be moved as a result, there is no guarantee that the response is still usable, so might as well just close it altogether.
125
+ * selector: avoid skipping persistent connections in the selector to deactivate due to iterate-and-modify.
126
+
127
+ ## Breaking Changes
128
+
129
+ ### `:digest_auth` error
130
+
131
+ The main error class for the `:digest_auth` plugin has been moved to a different location. If you were rescuing the `HTTPX::Plugins::DigestAuth::DigestError` error, you should now point to the `HTTPX::Authentication::Digest::Error`.
132
+
133
+ ### `:stream` plugin: `build_request` should receive `stream: true` for stream requests
134
+
135
+ In case you're building request objects before passing them to the session, you're now forced to create them with the `:stream` option on:
136
+
137
+ ```ruby
138
+ session = HTTPX.plugin(:stream)
139
+
140
+ # before
141
+ req = session.build_request("GET", "https://example.com/stream")
142
+ session.request(req, stream: true)
143
+
144
+ # after
145
+ req = session.build_request("GET", "https://example.com/stream", stream: true)
146
+ session.request(req)
147
+ ```
148
+
149
+ Previous code may still work in a few cases, but it is not guaranteed to work on all cases.
@@ -80,7 +80,7 @@ module Datadog::Tracing
80
80
  else
81
81
  span.set_tag(TAG_STATUS_CODE, response.status.to_s)
82
82
 
83
- span.set_error(::HTTPX::HTTPError.new(response)) if response.status >= 400 && response.status <= 599
83
+ span.set_error(::HTTPX::HTTPError.new(response)) if response.status.between?(400, 599)
84
84
 
85
85
  span.set_tags(
86
86
  Datadog.configuration.tracing.header_tags.response_tags(response.headers.to_h)
@@ -9,7 +9,7 @@ module Faraday
9
9
  class HTTPX < Faraday::Adapter
10
10
  def initialize(app = nil, opts = {}, &block)
11
11
  @connection = @bind = nil
12
- super(app, opts, &block)
12
+ super
13
13
  end
14
14
 
15
15
  module RequestMixin
@@ -32,7 +32,7 @@ module HTTPX::Plugins
32
32
 
33
33
  return unless config.propagate_traces && config.trace_propagation_targets.any? { |target| url.match?(target) }
34
34
 
35
- trace = ::Sentry.get_current_client.generate_sentry_trace(sentry_span)
35
+ trace = sentry_span.to_sentry_trace
36
36
  request.headers[::Sentry::SENTRY_TRACE_HEADER_NAME] = trace if trace
37
37
  end
38
38
 
data/lib/httpx/altsvc.rb CHANGED
@@ -8,6 +8,8 @@ module HTTPX
8
8
  module ConnectionMixin
9
9
  using URIExtensions
10
10
 
11
+ H2_ALTSVC_SCHEMES = %w[https h2].freeze
12
+
11
13
  def send(request)
12
14
  request.headers["alt-used"] = @origin.authority if @parser && !@write_buffer.full? && match_altsvcs?(request.uri)
13
15
 
@@ -46,7 +48,7 @@ module HTTPX
46
48
  uri.origin == other_uri.origin || begin
47
49
  case uri.scheme
48
50
  when "h2"
49
- (other_uri.scheme == "https" || other_uri.scheme == "h2") &&
51
+ H2_ALTSVC_SCHEMES.include?(other_uri.scheme) &&
50
52
  uri.host == other_uri.host &&
51
53
  uri.port == other_uri.port
52
54
  else
@@ -10,6 +10,11 @@ module HTTPX
10
10
  MAX_REQUESTS = 200
11
11
  CRLF = "\r\n"
12
12
 
13
+ UPCASED = {
14
+ "www-authenticate" => "WWW-Authenticate",
15
+ "http2-settings" => "HTTP2-Settings",
16
+ "content-md5" => "Content-MD5",
17
+ }.freeze
13
18
  attr_reader :pending, :requests
14
19
 
15
20
  attr_accessor :max_concurrent_requests
@@ -44,12 +49,12 @@ module HTTPX
44
49
  @max_requests = @options.max_requests || MAX_REQUESTS
45
50
  @parser.reset!
46
51
  @handshake_completed = false
47
- @pending.concat(@requests) unless @requests.empty?
52
+ @pending.unshift(*@requests)
48
53
  end
49
54
 
50
55
  def close
51
56
  reset
52
- emit(:close, true)
57
+ emit(:close)
53
58
  end
54
59
 
55
60
  def exhausted?
@@ -114,7 +119,7 @@ module HTTPX
114
119
  @parser.http_version.join("."),
115
120
  headers)
116
121
  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(v)}" }.join("\n") }
122
+ log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v)}" }.join("\n") }
118
123
 
119
124
  @request.response = response
120
125
  on_complete if response.finished?
@@ -126,7 +131,7 @@ module HTTPX
126
131
  response = @request.response
127
132
  log(level: 2) { "trailer headers received" }
128
133
 
129
- log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{log_redact(v.join(", "))}" }.join("\n") }
134
+ log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{log_redact_headers(v.join(", "))}" }.join("\n") }
130
135
  response.merge_headers(h)
131
136
  end
132
137
 
@@ -136,7 +141,7 @@ module HTTPX
136
141
  return unless request
137
142
 
138
143
  log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
139
- log(level: 2, color: :green) { "-> #{log_redact(chunk.inspect)}" }
144
+ log(level: 2, color: :green) { "-> #{log_redact_body(chunk.inspect)}" }
140
145
  response = request.response
141
146
 
142
147
  response << chunk
@@ -182,7 +187,7 @@ module HTTPX
182
187
  end
183
188
 
184
189
  if exhausted?
185
- @pending.concat(@requests)
190
+ @pending.unshift(*@requests)
186
191
  @requests.clear
187
192
 
188
193
  emit(:exhausted)
@@ -236,7 +241,7 @@ module HTTPX
236
241
  when /keep-alive/i
237
242
  if @handshake_completed
238
243
  if @max_requests.zero?
239
- @pending.concat(@requests)
244
+ @pending.unshift(*@requests)
240
245
  @requests.clear
241
246
  emit(:exhausted)
242
247
  end
@@ -360,7 +365,7 @@ module HTTPX
360
365
 
361
366
  while (chunk = request.drain_body)
362
367
  log(color: :green) { "<- DATA: #{chunk.bytesize} bytes..." }
363
- log(level: 2, color: :green) { "<- #{log_redact(chunk.inspect)}" }
368
+ log(level: 2, color: :green) { "<- #{log_redact_body(chunk.inspect)}" }
364
369
  @buffer << chunk
365
370
  throw(:buffer_full, request) if @buffer.full?
366
371
  end
@@ -381,17 +386,11 @@ module HTTPX
381
386
  def join_headers2(headers)
382
387
  headers.each do |field, value|
383
388
  field = capitalized(field)
384
- log(color: :yellow) { "<- HEADER: #{[field, log_redact(value)].join(": ")}" }
389
+ log(color: :yellow) { "<- HEADER: #{[field, log_redact_headers(value)].join(": ")}" }
385
390
  @buffer << "#{field}: #{value}#{CRLF}"
386
391
  end
387
392
  end
388
393
 
389
- UPCASED = {
390
- "www-authenticate" => "WWW-Authenticate",
391
- "http2-settings" => "HTTP2-Settings",
392
- "content-md5" => "Content-MD5",
393
- }.freeze
394
-
395
394
  def capitalized(field)
396
395
  UPCASED[field] || field.split("-").map(&:capitalize).join("-")
397
396
  end
@@ -3,6 +3,8 @@
3
3
  require "securerandom"
4
4
  require "http/2"
5
5
 
6
+ HTTP2::Connection.__send__(:public, :send_buffer) if HTTP2::VERSION < "1.1.1"
7
+
6
8
  module HTTPX
7
9
  class Connection::HTTP2
8
10
  include Callbacks
@@ -89,7 +91,7 @@ module HTTPX
89
91
  @connection.goaway
90
92
  emit(:timeout, @options.timeout[:close_handshake_timeout])
91
93
  end
92
- emit(:close, true)
94
+ emit(:close)
93
95
  end
94
96
 
95
97
  def empty?
@@ -234,12 +236,12 @@ module HTTPX
234
236
  extra_headers = set_protocol_headers(request)
235
237
 
236
238
  if request.headers.key?("host")
237
- log { "forbidden \"host\" header found (#{log_redact(request.headers["host"])}), will use it as authority..." }
239
+ log { "forbidden \"host\" header found (#{log_redact_headers(request.headers["host"])}), will use it as authority..." }
238
240
  extra_headers[":authority"] = request.headers["host"]
239
241
  end
240
242
 
241
243
  log(level: 1, color: :yellow) do
242
- "\n#{request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{log_redact(v)}" }.join("\n")}"
244
+ "\n#{request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")}"
243
245
  end
244
246
  stream.headers(request.headers.each(extra_headers), end_stream: request.body.empty?)
245
247
  end
@@ -251,7 +253,7 @@ module HTTPX
251
253
  end
252
254
 
253
255
  log(level: 1, color: :yellow) do
254
- request.trailers.each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{log_redact(v)}" }.join("\n")
256
+ request.trailers.each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")
255
257
  end
256
258
  stream.headers(request.trailers.each, end_stream: true)
257
259
  end
@@ -279,7 +281,7 @@ module HTTPX
279
281
 
280
282
  def send_chunk(request, stream, chunk, next_chunk)
281
283
  log(level: 1, color: :green) { "#{stream.id}: -> DATA: #{chunk.bytesize} bytes..." }
282
- log(level: 2, color: :green) { "#{stream.id}: -> #{log_redact(chunk.inspect)}" }
284
+ log(level: 2, color: :green) { "#{stream.id}: -> #{log_redact_body(chunk.inspect)}" }
283
285
  stream.data(chunk, end_stream: end_stream?(request, next_chunk))
284
286
  end
285
287
 
@@ -300,7 +302,7 @@ module HTTPX
300
302
  end
301
303
 
302
304
  log(color: :yellow) do
303
- h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{log_redact(v)}" }.join("\n")
305
+ h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")
304
306
  end
305
307
  _, status = h.shift
306
308
  headers = request.options.headers_class.new(h)
@@ -313,14 +315,14 @@ module HTTPX
313
315
 
314
316
  def on_stream_trailers(stream, response, h)
315
317
  log(color: :yellow) do
316
- h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{log_redact(v)}" }.join("\n")
318
+ h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{log_redact_headers(v)}" }.join("\n")
317
319
  end
318
320
  response.merge_headers(h)
319
321
  end
320
322
 
321
323
  def on_stream_data(stream, request, data)
322
324
  log(level: 1, color: :green) { "#{stream.id}: <- DATA: #{data.bytesize} bytes..." }
323
- log(level: 2, color: :green) { "#{stream.id}: <- #{log_redact(data.inspect)}" }
325
+ log(level: 2, color: :green) { "#{stream.id}: <- #{log_redact_body(data.inspect)}" }
324
326
  request.response << data
325
327
  end
326
328
 
@@ -387,18 +389,17 @@ module HTTPX
387
389
  end
388
390
  else
389
391
  ex = GoawayError.new(error)
392
+ ex.set_backtrace(caller)
393
+
390
394
  @pending.unshift(*@streams.keys)
391
395
  teardown
392
- end
393
396
 
394
- if ex
395
- ex.set_backtrace(caller)
396
397
  handle_error(ex)
397
398
  end
398
399
  end
399
400
  return unless is_connection_closed && @streams.empty?
400
401
 
401
- emit(:close, is_connection_closed)
402
+ emit(:close) if is_connection_closed
402
403
  end
403
404
 
404
405
  def on_frame_sent(frame)
@@ -409,7 +410,7 @@ module HTTPX
409
410
  when :data
410
411
  frame.merge(payload: frame[:payload].bytesize)
411
412
  when :headers, :ping
412
- frame.merge(payload: log_redact(frame[:payload]))
413
+ frame.merge(payload: log_redact_headers(frame[:payload]))
413
414
  else
414
415
  frame
415
416
  end
@@ -425,7 +426,7 @@ module HTTPX
425
426
  when :data
426
427
  frame.merge(payload: frame[:payload].bytesize)
427
428
  when :headers, :ping
428
- frame.merge(payload: log_redact(frame[:payload]))
429
+ frame.merge(payload: log_redact_headers(frame[:payload]))
429
430
  else
430
431
  frame
431
432
  end
@@ -435,7 +436,7 @@ module HTTPX
435
436
 
436
437
  def on_altsvc(origin, frame)
437
438
  log(level: 2) { "#{frame[:stream]}: altsvc frame was received" }
438
- log(level: 2) { "#{frame[:stream]}: #{log_redact(frame.inspect)}" }
439
+ log(level: 2) { "#{frame[:stream]}: #{log_redact_headers(frame.inspect)}" }
439
440
  alt_origin = URI.parse("#{frame[:proto]}://#{frame[:host]}:#{frame[:port]}")
440
441
  params = { "ma" => frame[:max_age] }
441
442
  emit(:altsvc, origin, alt_origin, origin, params)