httpx 0.10.0 → 0.11.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -3
- data/doc/release_notes/0_10_1.md +37 -0
- data/doc/release_notes/0_10_2.md +5 -0
- data/doc/release_notes/0_11_0.md +76 -0
- data/doc/release_notes/0_11_1.md +1 -0
- data/doc/release_notes/0_11_2.md +5 -0
- data/lib/httpx/adapters/datadog.rb +205 -0
- data/lib/httpx/adapters/faraday.rb +0 -2
- data/lib/httpx/adapters/webmock.rb +123 -0
- data/lib/httpx/chainable.rb +8 -7
- data/lib/httpx/connection.rb +4 -15
- data/lib/httpx/connection/http1.rb +14 -1
- data/lib/httpx/connection/http2.rb +15 -16
- data/lib/httpx/domain_name.rb +1 -3
- data/lib/httpx/errors.rb +3 -1
- data/lib/httpx/headers.rb +1 -0
- data/lib/httpx/io/ssl.rb +4 -8
- data/lib/httpx/io/udp.rb +4 -3
- data/lib/httpx/plugins/compression.rb +1 -1
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +1 -1
- data/lib/httpx/plugins/expect.rb +33 -8
- data/lib/httpx/plugins/multipart.rb +42 -23
- data/lib/httpx/plugins/multipart/encoder.rb +115 -0
- data/lib/httpx/plugins/multipart/mime_type_detector.rb +64 -0
- data/lib/httpx/plugins/multipart/part.rb +34 -0
- data/lib/httpx/plugins/proxy.rb +16 -2
- data/lib/httpx/plugins/proxy/socks4.rb +14 -16
- data/lib/httpx/plugins/proxy/socks5.rb +3 -2
- data/lib/httpx/plugins/push_promise.rb +2 -2
- data/lib/httpx/pool.rb +8 -14
- data/lib/httpx/request.rb +22 -12
- data/lib/httpx/resolver.rb +7 -6
- data/lib/httpx/resolver/https.rb +18 -23
- data/lib/httpx/resolver/native.rb +22 -19
- data/lib/httpx/resolver/resolver_mixin.rb +4 -2
- data/lib/httpx/resolver/system.rb +3 -3
- data/lib/httpx/selector.rb +9 -13
- data/lib/httpx/session.rb +24 -21
- data/lib/httpx/transcoder.rb +20 -0
- data/lib/httpx/transcoder/form.rb +9 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/connection.rbs +84 -1
- data/sig/connection/http1.rbs +66 -0
- data/sig/connection/http2.rbs +73 -0
- data/sig/headers.rbs +3 -0
- data/sig/httpx.rbs +1 -0
- data/sig/options.rbs +3 -3
- data/sig/plugins/basic_authentication.rbs +1 -1
- data/sig/plugins/compression.rbs +1 -1
- data/sig/plugins/compression/brotli.rbs +1 -1
- data/sig/plugins/compression/deflate.rbs +1 -1
- data/sig/plugins/compression/gzip.rbs +1 -1
- data/sig/plugins/h2c.rbs +1 -1
- data/sig/plugins/multipart.rbs +29 -4
- data/sig/plugins/persistent.rbs +1 -1
- data/sig/plugins/proxy.rbs +2 -2
- data/sig/plugins/proxy/ssh.rbs +1 -1
- data/sig/plugins/rate_limiter.rbs +1 -1
- data/sig/pool.rbs +36 -2
- data/sig/request.rbs +2 -2
- data/sig/resolver.rbs +26 -0
- data/sig/resolver/https.rbs +51 -0
- data/sig/resolver/native.rbs +60 -0
- data/sig/resolver/resolver_mixin.rbs +27 -0
- data/sig/resolver/system.rbs +17 -0
- data/sig/response.rbs +2 -2
- data/sig/selector.rbs +20 -0
- data/sig/session.rbs +3 -3
- data/sig/transcoder.rbs +4 -2
- data/sig/transcoder/body.rbs +2 -0
- data/sig/transcoder/form.rbs +8 -2
- data/sig/transcoder/json.rbs +3 -1
- metadata +47 -48
- data/lib/httpx/resolver/options.rb +0 -25
- data/sig/missing.rbs +0 -12
- data/sig/test.rbs +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3455bd74f9d6c0cf65936bae67bf74b4d1d5fde21d254446cb4a4615a65b9bb9
|
4
|
+
data.tar.gz: a8f4c6d743667725fcfb3f43c42ef0efbcdc9772f84ecd45f558d2d4c94c3345
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 835072956f05fcb3a42b3e128d64e5753409aa32e875426dda24d8ddbbffdd0fc6585a6d48abd48cd470962f0a57492c4b74afe529c4ab5c750d3039fdbe2b3d
|
7
|
+
data.tar.gz: 8c8be2d19b36b4ad37f00a9f8ea9378e2f60e6e65e4f9513e84b24139480f529650e74c2a3c36a200d0e318ce86c2b15be37a2522032225ba0a9055cbac4534b
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# HTTPX: A Ruby HTTP library for tomorrow... and beyond!
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/httpx.svg)](http://rubygems.org/gems/httpx)
|
4
|
-
[![pipeline status](https://gitlab.com/honeyryderchuck/httpx/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/httpx/
|
4
|
+
[![pipeline status](https://gitlab.com/honeyryderchuck/httpx/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/httpx/pipelines?page=1&scope=all&ref=master)
|
5
5
|
[![coverage report](https://gitlab.com/honeyryderchuck/httpx/badges/master/coverage.svg?job=coverage)](https://honeyryderchuck.gitlab.io/httpx/coverage/#_AllFiles)
|
6
6
|
|
7
7
|
HTTPX is an HTTP client library for the Ruby programming language.
|
@@ -85,7 +85,15 @@ However if the server supports HTTP/1.1, it will try to use HTTP pipelining, fal
|
|
85
85
|
|
86
86
|
### Clean API
|
87
87
|
|
88
|
-
`
|
88
|
+
`httpx` builds all functions around the `HTTPX` module, so that all calls can compose of each other. Here are a few examples:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
response = HTTPX.get("https://www.google.com")
|
92
|
+
response = HTTPX.post("https://www.nghttp2.org/httpbin/post", params: {name: "John", age: "22"})
|
93
|
+
response = HTTPX.plugin(:basic_authentication)
|
94
|
+
.basic_authentication("user", "pass")
|
95
|
+
.get("https://www.google.com")
|
96
|
+
```
|
89
97
|
|
90
98
|
### Lightweight
|
91
99
|
|
@@ -135,7 +143,7 @@ Doesn't work with ruby 2.4.0 for Windows (see [#36](https://gitlab.com/honeyryde
|
|
135
143
|
|
136
144
|
* Discuss your contribution in an issue
|
137
145
|
* Fork it
|
138
|
-
* Make your changes, add some
|
146
|
+
* Make your changes, add some tests
|
139
147
|
* Ensure all tests pass (`bundle exec rake test`)
|
140
148
|
* Open a Merge Request (that's Pull Request in Github-ish)
|
141
149
|
* Wait for feedback
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# 0.10.1
|
2
|
+
|
3
|
+
## Improvements
|
4
|
+
|
5
|
+
### URL-encoded nested params
|
6
|
+
|
7
|
+
url encoder now supports nested params, which is a standard of rack-based frameworks:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
HTTPX.post("https://httpbin.org/post", form: { a: { b: 1 }, c: [2, 3] })
|
11
|
+
# a[b]=1&c[]=2&c[]=3
|
12
|
+
```
|
13
|
+
|
14
|
+
This encoding scheme is now the standard for URL-encoded request bodies, query params, and `:multipart` plugin requests.
|
15
|
+
|
16
|
+
### Socks4 IPv6 addresses
|
17
|
+
|
18
|
+
HTTPX supports IPv6 Socks4 proxies now. This support is restricted to rubies where `IPAddr#hton` is implemented though, so you are encouraged to upgrade.
|
19
|
+
|
20
|
+
## More verbose HTTP Errors
|
21
|
+
|
22
|
+
`HTTPX::Response#raise_for_status` was raising exceptions for client/server HTTP errors codes (4xx/5xx). However, only the status code was part of the message.
|
23
|
+
|
24
|
+
From now on, both headers and the responnse payload will also appear, so expected more verbosity, but also more meaningful information.
|
25
|
+
|
26
|
+
## Bugfixes
|
27
|
+
|
28
|
+
* HTTP/2 and HTTP/1.1 exhausted connections now get properly migrated into a new connection;
|
29
|
+
* HTTP/2 421 responses will now correctly migrate the connection and pendign requests to HTTP/1.1 (a hanging loop was being caused);
|
30
|
+
* HTTP/2 connection failed with a GOAWAY settings timeout will now return error responses (instead of hanging indefinitely);
|
31
|
+
* Non-IP proxy name-resolving errors will now move on to the next available proxy in the list (instead of hanging indefinitely);
|
32
|
+
* Non-IP DNS resolve errors for `native` and `https` variants will now return the appropriate error response (instead of hanging indefinitely);
|
33
|
+
|
34
|
+
## Chore
|
35
|
+
|
36
|
+
* `HTTPX.plugins` is now officially deprecated (use `HTTPX.plugin` instead);
|
37
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# 0.11.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Webmock Adapter
|
6
|
+
|
7
|
+
`httpx` can now be integrated with `webmock`, a popular HTTP requests stubbing library.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# minitest
|
11
|
+
require "webmock/minitest"
|
12
|
+
require "httpx/adapters/webmock"
|
13
|
+
|
14
|
+
# in rspec
|
15
|
+
require "webmock/rspec"
|
16
|
+
require "httpx/adapters/webmock"
|
17
|
+
|
18
|
+
# and now you're free for mocking
|
19
|
+
WebMock.enable!
|
20
|
+
stub_http_request(:get, "https://www.google.com").and_return(status: 200, body: "here's google")
|
21
|
+
|
22
|
+
```
|
23
|
+
|
24
|
+
Read more about it in the [webmock integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Webmock-Adapter).
|
25
|
+
|
26
|
+
### Datadog Adapter
|
27
|
+
|
28
|
+
`httpx` ships with integration for [ddtrace, datadog's official tracing client](https://github.com/DataDog/dd-trace-rb). You just need to initialize it the following way:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require "ddtrace"
|
32
|
+
require "httpx/adapters/datadog"
|
33
|
+
|
34
|
+
Datadog.configure do |c|
|
35
|
+
c.use :httpx
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
A trace will be emitted for every request, so this should be an interesting visualization if concurrent requests are sent.
|
40
|
+
|
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
|
+
|
43
|
+
Read more about it in the [datadog integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Datadog-Adapter).
|
44
|
+
|
45
|
+
## Improvements
|
46
|
+
|
47
|
+
### Own multipart request encoder
|
48
|
+
|
49
|
+
`httpx` now ships with its own multipart formdata encoder, and does not rely on `http-form_data` anymore:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
HTTPX.plugin(:multipart).post(uri, form: {file: File.new("path/to/file")})
|
53
|
+
```
|
54
|
+
|
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
|
+
|
57
|
+
### Expect Plugin
|
58
|
+
|
59
|
+
The `:expect` plugin now works reliably when the server does not support the `expect: 100-continue` header, i.e. it'll upload the body after a certain timeout. Building onn that, two behaviours are now implemented:
|
60
|
+
|
61
|
+
* A cache of domains which did not respond to the `expect` header is now kept, so that subsequent requests can skip the timeout and immediately upload the payload.
|
62
|
+
* If the "100 Continue" response arrives **after** the timeout expired and the body has been uploaded, the domain is removed from the cache, and subsequent requests will send the `expect` header.
|
63
|
+
|
64
|
+
### SNI/Host options
|
65
|
+
|
66
|
+
Some extension of the API was applied in order to support custom TLS negotiation parameters. You can now pass `:hostname` under the `:ssl` options, and this will be used for the SNI part of the TLS negotiation. This is useful in scenarios where a proxy certificate doesn't apply for the host one wants to send the request to:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
response = session.get(proxy_ip, headers: { "host" => upstream_hostname }, ssl: { hostname: sni_hostname }
|
70
|
+
```
|
71
|
+
|
72
|
+
## Bugfixes
|
73
|
+
|
74
|
+
A default 5 second timeout is in-place when using the DNS `:system` resolver, as it was found out that. when using the `resolv` library, the DNS query will not be retried otherwise. You can change this setting py passing `resolver_options: { timeouts: ANOTHER_TIMEOUT}`. In the future, this may become another timeout option, however.
|
75
|
+
|
76
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
0_11_1.md
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ddtrace/contrib/integration"
|
4
|
+
require "ddtrace/contrib/rest_client/configuration/settings"
|
5
|
+
require "ddtrace/contrib/rest_client/patcher"
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module Contrib
|
9
|
+
module HTTPX
|
10
|
+
# HTTPX Datadog Plugin
|
11
|
+
#
|
12
|
+
# Enables tracing for httpx requests. A span will be created for each individual requests,
|
13
|
+
# and it'll trace since the moment it is fed to the connection, until the moment the response is
|
14
|
+
# fed back to the session.
|
15
|
+
#
|
16
|
+
module Plugin
|
17
|
+
class RequestTracer
|
18
|
+
SPAN_REQUEST = "httpx.request"
|
19
|
+
|
20
|
+
def initialize(request)
|
21
|
+
@request = request
|
22
|
+
end
|
23
|
+
|
24
|
+
def call
|
25
|
+
return if skip_tracing?
|
26
|
+
|
27
|
+
@request.on(:response, &method(:finish))
|
28
|
+
|
29
|
+
verb = @request.verb.to_s.upcase
|
30
|
+
uri = @request.uri
|
31
|
+
|
32
|
+
@span = datadog_pin.tracer.trace(SPAN_REQUEST)
|
33
|
+
service_name = datadog_config[:split_by_domain] ? uri.host : datadog_pin.service_name
|
34
|
+
|
35
|
+
begin
|
36
|
+
@span.service = service_name
|
37
|
+
@span.span_type = Datadog::Ext::HTTP::TYPE_OUTBOUND
|
38
|
+
@span.resource = verb
|
39
|
+
|
40
|
+
Datadog::HTTPPropagator.inject!(@span.context, @request.headers) if datadog_pin.tracer.enabled && !skip_distributed_tracing?
|
41
|
+
|
42
|
+
# Add additional request specific tags to the span.
|
43
|
+
|
44
|
+
@span.set_tag(Datadog::Ext::HTTP::URL, @request.path)
|
45
|
+
@span.set_tag(Datadog::Ext::HTTP::METHOD, verb)
|
46
|
+
|
47
|
+
@span.set_tag(Datadog::Ext::NET::TARGET_HOST, uri.host)
|
48
|
+
@span.set_tag(Datadog::Ext::NET::TARGET_PORT, uri.port.to_s)
|
49
|
+
|
50
|
+
# Tag as an external peer service
|
51
|
+
@span.set_tag(Datadog::Ext::Integration::TAG_PEER_SERVICE, @span.service)
|
52
|
+
|
53
|
+
# Set analytics sample rate
|
54
|
+
if Contrib::Analytics.enabled?(datadog_config[:analytics_enabled])
|
55
|
+
Contrib::Analytics.set_sample_rate(@span, datadog_config[:analytics_sample_rate])
|
56
|
+
end
|
57
|
+
rescue StandardError => e
|
58
|
+
Datadog.logger.error("error preparing span for http request: #{e}")
|
59
|
+
end
|
60
|
+
rescue StandardError => e
|
61
|
+
Datadog.logger.debug("Failed to start span: #{e}")
|
62
|
+
end
|
63
|
+
|
64
|
+
def finish(response)
|
65
|
+
return unless @span
|
66
|
+
|
67
|
+
if response.respond_to?(:error)
|
68
|
+
@span.set_error(response.error)
|
69
|
+
else
|
70
|
+
@span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.status.to_s)
|
71
|
+
|
72
|
+
@span.set_error(::HTTPX::HTTPError.new(response)) if response.status >= 400 && response.status <= 599
|
73
|
+
end
|
74
|
+
|
75
|
+
@span.finish
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def skip_tracing?
|
81
|
+
return true if @request.headers.key?(Datadog::Ext::Transport::HTTP::HEADER_META_TRACER_VERSION)
|
82
|
+
|
83
|
+
return false unless @datadog_pin
|
84
|
+
|
85
|
+
span = @datadog_pin.tracer.active_span
|
86
|
+
|
87
|
+
return true if span && (span.name == SPAN_REQUEST)
|
88
|
+
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
92
|
+
def skip_distributed_tracing?
|
93
|
+
return !datadog_pin.config[:distributed_tracing] if datadog_pin.config && datadog_pin.config.key?(:distributed_tracing)
|
94
|
+
|
95
|
+
!Datadog.configuration[:httpx][:distributed_tracing]
|
96
|
+
end
|
97
|
+
|
98
|
+
def datadog_pin
|
99
|
+
@datadog_pin ||= begin
|
100
|
+
service = datadog_config[:service_name]
|
101
|
+
tracer = datadog_config[:tracer]
|
102
|
+
|
103
|
+
Datadog::Pin.new(
|
104
|
+
service,
|
105
|
+
app: "httpx",
|
106
|
+
app_type: Datadog::Ext::AppTypes::WEB,
|
107
|
+
tracer: -> { tracer }
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def datadog_config
|
113
|
+
@datadog_config ||= Datadog.configuration[:httpx, @request.uri.host]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
module ConnectionMethods
|
118
|
+
def send(request)
|
119
|
+
RequestTracer.new(request).call
|
120
|
+
super
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
module Configuration
|
126
|
+
# Default settings for httpx
|
127
|
+
#
|
128
|
+
class Settings < Datadog::Contrib::Configuration::Settings
|
129
|
+
option :service_name, default: "httpx"
|
130
|
+
option :distributed_tracing, default: true
|
131
|
+
option :split_by_domain, default: false
|
132
|
+
|
133
|
+
option :enabled do |o|
|
134
|
+
o.default { env_to_bool("DD_TRACE_HTTPX_ENABLED", true) }
|
135
|
+
o.lazy
|
136
|
+
end
|
137
|
+
|
138
|
+
option :analytics_enabled do |o|
|
139
|
+
o.default { env_to_bool(%w[DD_TRACE_HTTPX_ANALYTICS_ENABLED DD_HTTPX_ANALYTICS_ENABLED], false) }
|
140
|
+
o.lazy
|
141
|
+
end
|
142
|
+
|
143
|
+
option :analytics_sample_rate do |o|
|
144
|
+
o.default { env_to_float(%w[DD_TRACE_HTTPX_ANALYTICS_SAMPLE_RATE DD_HTTPX_ANALYTICS_SAMPLE_RATE], 1.0) }
|
145
|
+
o.lazy
|
146
|
+
end
|
147
|
+
|
148
|
+
option :error_handler, default: Datadog::Tracer::DEFAULT_ON_ERROR
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Patcher enables patching of 'httpx' with datadog components.
|
153
|
+
#
|
154
|
+
module Patcher
|
155
|
+
include Datadog::Contrib::Patcher
|
156
|
+
|
157
|
+
module_function
|
158
|
+
|
159
|
+
def target_version
|
160
|
+
Integration.version
|
161
|
+
end
|
162
|
+
|
163
|
+
# loads a session instannce with the datadog plugin, and replaces the
|
164
|
+
# base HTTPX::Session with the patched session class.
|
165
|
+
def patch
|
166
|
+
datadog_session = ::HTTPX.plugin(Plugin)
|
167
|
+
|
168
|
+
::HTTPX.send(:remove_const, :Session)
|
169
|
+
::HTTPX.send(:const_set, :Session, datadog_session.class)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Datadog Integration for HTTPX.
|
174
|
+
#
|
175
|
+
class Integration
|
176
|
+
include Contrib::Integration
|
177
|
+
|
178
|
+
# MINIMUM_VERSION = Gem::Version.new('0.11.0')
|
179
|
+
MINIMUM_VERSION = Gem::Version.new("0.10.2")
|
180
|
+
|
181
|
+
register_as :httpx
|
182
|
+
|
183
|
+
def self.version
|
184
|
+
Gem.loaded_specs["httpx"] && Gem.loaded_specs["httpx"].version
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.loaded?
|
188
|
+
defined?(::HTTPX::Request)
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.compatible?
|
192
|
+
super && version >= MINIMUM_VERSION
|
193
|
+
end
|
194
|
+
|
195
|
+
def default_configuration
|
196
|
+
Configuration::Settings.new
|
197
|
+
end
|
198
|
+
|
199
|
+
def patcher
|
200
|
+
Patcher
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -65,7 +65,6 @@ module Faraday
|
|
65
65
|
plugin(:compression)
|
66
66
|
plugin(:persistent)
|
67
67
|
|
68
|
-
# :nocov:
|
69
68
|
module ReasonPlugin
|
70
69
|
if RUBY_VERSION < "2.5"
|
71
70
|
def self.load_dependencies(*)
|
@@ -88,7 +87,6 @@ module Faraday
|
|
88
87
|
end
|
89
88
|
end
|
90
89
|
end
|
91
|
-
# :nocov:
|
92
90
|
plugin(ReasonPlugin)
|
93
91
|
end
|
94
92
|
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WebMock
|
4
|
+
module HttpLibAdapters
|
5
|
+
if RUBY_VERSION < "2.5"
|
6
|
+
require "webrick/httpstatus"
|
7
|
+
HTTP_REASONS = WEBrick::HTTPStatus::StatusMessage
|
8
|
+
else
|
9
|
+
require "net/http/status"
|
10
|
+
HTTP_REASONS = Net::HTTP::STATUS_CODES
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# HTTPX plugin for webmock.
|
15
|
+
#
|
16
|
+
# Requests are "hijacked" at the session, before they're distributed to a connection.
|
17
|
+
#
|
18
|
+
module Plugin
|
19
|
+
module InstanceMethods
|
20
|
+
private
|
21
|
+
|
22
|
+
def send_requests(*requests, options)
|
23
|
+
request_signatures = requests.map do |request|
|
24
|
+
request_signature = _build_webmock_request_signature(request)
|
25
|
+
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
26
|
+
request_signature
|
27
|
+
end
|
28
|
+
|
29
|
+
responses = request_signatures.map do |request_signature|
|
30
|
+
WebMock::StubRegistry.instance.response_for_request(request_signature)
|
31
|
+
end
|
32
|
+
|
33
|
+
real_requests = {}
|
34
|
+
|
35
|
+
requests.each_with_index.each_with_object([request_signatures, responses]) do |(request, idx), (sig_reqs, mock_responses)|
|
36
|
+
if (webmock_response = mock_responses[idx])
|
37
|
+
mock_responses[idx] = _build_from_webmock_response(request, webmock_response)
|
38
|
+
WebMock::CallbackRegistry.invoke_callbacks({ lib: :httpx }, sig_reqs[idx], webmock_response)
|
39
|
+
log { "mocking #{request.uri} with #{mock_responses[idx].inspect}" }
|
40
|
+
elsif WebMock.net_connect_allowed?(sig_reqs[idx].uri)
|
41
|
+
log { "performing #{request.uri}" }
|
42
|
+
real_requests[request] = idx
|
43
|
+
else
|
44
|
+
raise WebMock::NetConnectNotAllowedError, sig_reqs[idx]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
unless real_requests.empty?
|
49
|
+
reqs = real_requests.keys
|
50
|
+
reqs.zip(super(*reqs, options)).each do |req, res|
|
51
|
+
idx = real_requests[req]
|
52
|
+
|
53
|
+
if WebMock::CallbackRegistry.any_callbacks?
|
54
|
+
webmock_response = _build_webmock_response(req, res)
|
55
|
+
WebMock::CallbackRegistry.invoke_callbacks(
|
56
|
+
{ lib: :httpx, real_request: true }, request_signatures[idx],
|
57
|
+
webmock_response
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
responses[idx] = res
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
responses
|
66
|
+
end
|
67
|
+
|
68
|
+
def _build_webmock_request_signature(request)
|
69
|
+
uri = WebMock::Util::URI.heuristic_parse(request.uri)
|
70
|
+
uri.path = uri.normalized_path.gsub("[^:]//", "/")
|
71
|
+
|
72
|
+
WebMock::RequestSignature.new(
|
73
|
+
request.verb,
|
74
|
+
uri.to_s,
|
75
|
+
body: request.body.each.to_a.join,
|
76
|
+
headers: request.headers.to_h
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def _build_webmock_response(_request, response)
|
81
|
+
webmock_response = WebMock::Response.new
|
82
|
+
webmock_response.status = [response.status, HTTP_REASONS[response.status]]
|
83
|
+
webmock_response.body = response.body.to_s
|
84
|
+
webmock_response.headers = response.headers.to_h
|
85
|
+
webmock_response
|
86
|
+
end
|
87
|
+
|
88
|
+
def _build_from_webmock_response(request, webmock_response)
|
89
|
+
return ErrorResponse.new(request, webmock_response.exception, request.options) if webmock_response.exception
|
90
|
+
|
91
|
+
response = request.options.response_class.new(request,
|
92
|
+
webmock_response.status[0],
|
93
|
+
"2.0",
|
94
|
+
webmock_response.headers)
|
95
|
+
response << webmock_response.body.dup
|
96
|
+
response
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class HttpxAdapter < HttpLibAdapter
|
102
|
+
adapter_for :httpx
|
103
|
+
|
104
|
+
class << self
|
105
|
+
def enable!
|
106
|
+
@original_session = ::HTTPX::Session
|
107
|
+
|
108
|
+
webmock_session = ::HTTPX.plugin(Plugin)
|
109
|
+
|
110
|
+
::HTTPX.send(:remove_const, :Session)
|
111
|
+
::HTTPX.send(:const_set, :Session, webmock_session.class)
|
112
|
+
end
|
113
|
+
|
114
|
+
def disable!
|
115
|
+
return unless @original_session
|
116
|
+
|
117
|
+
HTTPX.send(:remove_const, :Session)
|
118
|
+
HTTPX.send(:const_set, :Session, @original_session)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|