webmock 3.0.1 → 3.18.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/CI.yml +38 -0
- data/CHANGELOG.md +496 -2
- data/Gemfile +1 -1
- data/README.md +169 -34
- data/Rakefile +12 -4
- data/lib/webmock/api.rb +12 -0
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +221 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +19 -5
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -2
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +2 -1
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +27 -3
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +9 -3
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +7 -3
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +28 -9
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
- data/lib/webmock/http_lib_adapters/net_http.rb +36 -89
- data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
- data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -4
- data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
- data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
- data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
- data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
- data/lib/webmock/rack_response.rb +1 -1
- data/lib/webmock/request_body_diff.rb +1 -1
- data/lib/webmock/request_execution_verifier.rb +2 -3
- data/lib/webmock/request_pattern.rb +129 -51
- data/lib/webmock/request_registry.rb +1 -1
- data/lib/webmock/request_signature.rb +3 -3
- data/lib/webmock/request_signature_snippet.rb +4 -4
- data/lib/webmock/request_stub.rb +15 -0
- data/lib/webmock/response.rb +19 -13
- data/lib/webmock/rspec.rb +10 -3
- data/lib/webmock/stub_registry.rb +26 -11
- data/lib/webmock/stub_request_snippet.rb +10 -6
- data/lib/webmock/test_unit.rb +1 -3
- data/lib/webmock/util/hash_counter.rb +3 -3
- data/lib/webmock/util/headers.rb +17 -2
- data/lib/webmock/util/json.rb +1 -2
- data/lib/webmock/util/query_mapper.rb +9 -7
- data/lib/webmock/util/uri.rb +10 -10
- data/lib/webmock/util/values_stringifier.rb +20 -0
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +20 -3
- data/lib/webmock.rb +53 -48
- data/minitest/webmock_spec.rb +3 -3
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
- data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/spec/acceptance/curb/curb_spec.rb +44 -0
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +57 -1
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +2 -2
- data/spec/acceptance/excon/excon_spec.rb +4 -2
- data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
- data/spec/acceptance/http_rb/http_rb_spec.rb +20 -0
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +5 -2
- data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
- data/spec/acceptance/manticore/manticore_spec.rb +51 -0
- data/spec/acceptance/net_http/net_http_shared.rb +47 -10
- data/spec/acceptance/net_http/net_http_spec.rb +102 -24
- data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
- data/spec/acceptance/patron/patron_spec.rb +26 -21
- data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
- data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
- data/spec/acceptance/shared/callbacks.rb +3 -2
- data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
- data/spec/acceptance/shared/request_expectations.rb +14 -0
- data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
- data/spec/acceptance/shared/stubbing_requests.rb +95 -0
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
- data/spec/support/webmock_server.rb +1 -0
- data/spec/unit/api_spec.rb +103 -3
- data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/spec/unit/request_execution_verifier_spec.rb +12 -12
- data/spec/unit/request_pattern_spec.rb +207 -49
- data/spec/unit/request_signature_snippet_spec.rb +2 -2
- data/spec/unit/request_signature_spec.rb +21 -1
- data/spec/unit/request_stub_spec.rb +35 -0
- data/spec/unit/response_spec.rb +51 -19
- data/spec/unit/stub_request_snippet_spec.rb +30 -10
- data/spec/unit/util/query_mapper_spec.rb +13 -0
- data/spec/unit/util/uri_spec.rb +74 -2
- data/spec/unit/webmock_spec.rb +108 -5
- data/test/shared_test.rb +15 -2
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +15 -7
- metadata +86 -37
- data/.travis.yml +0 -20
data/README.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
WebMock
|
2
2
|
=======
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/webmock.svg)](http://badge.fury.io/rb/webmock)
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/webmock.svg)](http://badge.fury.io/rb/webmock)
|
4
|
+
[![Build Status](https://github.com/bblimke/webmock/workflows/CI/badge.svg?branch=master)](https://github.com/bblimke/webmock/actions)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/bblimke/webmock/badges/gpa.svg)](https://codeclimate.com/github/bblimke/webmock)
|
6
|
+
[![Mentioned in Awesome Ruby](https://awesome.re/mentioned-badge.svg)](https://github.com/markets/awesome-ruby)
|
7
|
+
[![Inline docs](http://inch-ci.org/github/bblimke/webmock.svg?branch=master)](http://inch-ci.org/github/bblimke/webmock)
|
8
|
+
[![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=webmock&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=webmock&package-manager=bundler&version-scheme=semver)
|
4
9
|
|
5
10
|
Library for stubbing and setting expectations on HTTP requests in Ruby.
|
6
11
|
|
@@ -19,30 +24,42 @@ Features
|
|
19
24
|
Supported HTTP libraries
|
20
25
|
------------------------
|
21
26
|
|
22
|
-
*
|
23
|
-
*
|
24
|
-
*
|
25
|
-
*
|
26
|
-
*
|
27
|
-
*
|
28
|
-
*
|
29
|
-
*
|
30
|
-
*
|
27
|
+
* [Async::HTTP::Client](https://github.com/socketry/async-http)
|
28
|
+
* [Curb](https://github.com/taf2/curb) (currently only Curb::Easy)
|
29
|
+
* [EM-HTTP-Request](https://github.com/igrigorik/em-http-request)
|
30
|
+
* [Excon](https://github.com/excon/excon)
|
31
|
+
* [HTTPClient](https://github.com/nahi/httpclient)
|
32
|
+
* [HTTP Gem (also known as http.rb)](https://github.com/httprb/http)
|
33
|
+
* [httpx](https://honeyryderchuck.gitlab.io/httpx/wiki/Webmock-Adapter)
|
34
|
+
* [Manticore](https://github.com/cheald/manticore)
|
35
|
+
* [Net::HTTP](https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html) and other libraries based on Net::HTTP, e.g.:
|
36
|
+
* [HTTParty](https://github.com/jnunemaker/httparty)
|
37
|
+
* [REST Client](https://github.com/rest-client/rest-client)
|
38
|
+
* [Patron](https://github.com/toland/patron)
|
39
|
+
* [Typhoeus](https://github.com/typhoeus/typhoeus) (currently only Typhoeus::Hydra)
|
31
40
|
|
32
41
|
Supported Ruby Interpreters
|
33
42
|
---------------------------
|
34
43
|
|
35
|
-
* MRI 2.
|
36
|
-
* MRI 2.
|
37
|
-
* MRI 2.
|
38
|
-
* MRI 2.3
|
39
|
-
* MRI 2.4
|
44
|
+
* MRI 2.5
|
45
|
+
* MRI 2.6
|
46
|
+
* MRI 2.7
|
40
47
|
* JRuby
|
41
48
|
* Rubinius
|
42
49
|
|
43
50
|
## Installation
|
44
51
|
|
52
|
+
```bash
|
45
53
|
gem install webmock
|
54
|
+
```
|
55
|
+
or alternatively:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
# add to your Gemfile
|
59
|
+
group :test do
|
60
|
+
gem "webmock"
|
61
|
+
end
|
62
|
+
```
|
46
63
|
|
47
64
|
### or to install the latest development version from github master
|
48
65
|
|
@@ -54,36 +71,36 @@ Supported Ruby Interpreters
|
|
54
71
|
|
55
72
|
WebMock 2.x has changed somewhat since version 1.x. Changes are listed in [CHANGELOG.md](CHANGELOG.md)
|
56
73
|
|
57
|
-
###
|
74
|
+
### Cucumber
|
58
75
|
|
59
|
-
|
76
|
+
Create a file `features/support/webmock.rb` with the following contents:
|
60
77
|
|
61
78
|
```ruby
|
62
|
-
require 'webmock/
|
79
|
+
require 'webmock/cucumber'
|
63
80
|
```
|
64
81
|
|
65
|
-
###
|
82
|
+
### MiniTest
|
66
83
|
|
67
|
-
Add the following code to `
|
84
|
+
Add the following code to `test/test_helper`:
|
68
85
|
|
69
86
|
```ruby
|
70
|
-
require 'webmock/
|
87
|
+
require 'webmock/minitest'
|
71
88
|
```
|
72
89
|
|
73
|
-
###
|
90
|
+
### RSpec
|
74
91
|
|
75
|
-
Add the following code to `
|
92
|
+
Add the following code to `spec/spec_helper`:
|
76
93
|
|
77
94
|
```ruby
|
78
|
-
require 'webmock/
|
95
|
+
require 'webmock/rspec'
|
79
96
|
```
|
80
97
|
|
81
|
-
###
|
98
|
+
### Test::Unit
|
82
99
|
|
83
|
-
|
100
|
+
Add the following code to `test/test_helper.rb`
|
84
101
|
|
85
102
|
```ruby
|
86
|
-
require 'webmock/
|
103
|
+
require 'webmock/test_unit'
|
87
104
|
```
|
88
105
|
|
89
106
|
### Outside a test framework
|
@@ -97,13 +114,10 @@ include WebMock::API
|
|
97
114
|
WebMock.enable!
|
98
115
|
```
|
99
116
|
|
100
|
-
|
101
|
-
|
102
|
-
|
117
|
+
# Examples
|
103
118
|
|
104
119
|
## Stubbing
|
105
120
|
|
106
|
-
|
107
121
|
### Stubbed request based on uri only and with the default response
|
108
122
|
|
109
123
|
```ruby
|
@@ -236,6 +250,12 @@ stub_request(:any, /example/)
|
|
236
250
|
Net::HTTP.get('www.example.com', '/') # ===> Success
|
237
251
|
```
|
238
252
|
|
253
|
+
### Matching uris using lambda
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
stub_request(:any, ->(uri) { true })
|
257
|
+
```
|
258
|
+
|
239
259
|
### Matching uris using RFC 6570 - Basic Example
|
240
260
|
|
241
261
|
```ruby
|
@@ -273,6 +293,16 @@ stub_request(:get, "www.example.com").
|
|
273
293
|
RestClient.get("http://www.example.com/?a[]=b&a[]=c&x=1") # ===> Success
|
274
294
|
```
|
275
295
|
|
296
|
+
### Matching partial query params using hash_excluding
|
297
|
+
|
298
|
+
```ruby
|
299
|
+
stub_request(:get, "www.example.com").
|
300
|
+
with(query: hash_excluding({"a" => "b"}))
|
301
|
+
|
302
|
+
RestClient.get("http://www.example.com/?a=b") # ===> Failure
|
303
|
+
RestClient.get("http://www.example.com/?a=c") # ===> Success
|
304
|
+
```
|
305
|
+
|
276
306
|
### Stubbing with custom response
|
277
307
|
|
278
308
|
```ruby
|
@@ -283,6 +313,12 @@ stub_request(:any, "www.example.com").
|
|
283
313
|
Net::HTTP.get("www.example.com", '/') # ===> "abc"
|
284
314
|
```
|
285
315
|
|
316
|
+
Set appropriate Content-Type for HTTParty's `parsed_response`.
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
stub_request(:any, "www.example.com").to_return body: '{}', headers: {content_type: 'application/json'}
|
320
|
+
```
|
321
|
+
|
286
322
|
### Response with body specified as IO object
|
287
323
|
|
288
324
|
```ruby
|
@@ -514,9 +550,9 @@ RestClient.get('sample.org', '/bar') # ===> Failure
|
|
514
550
|
With an object that responds to `#call`, receiving a `URI` object and returning a boolean:
|
515
551
|
|
516
552
|
```ruby
|
517
|
-
|
553
|
+
denylist = ['google.com', 'facebook.com', 'apple.com']
|
518
554
|
allowed_sites = lambda{|uri|
|
519
|
-
|
555
|
+
denylist.none?{|site| uri.host.include?(site) }
|
520
556
|
}
|
521
557
|
WebMock.disable_net_connect!(allow: allowed_sites)
|
522
558
|
|
@@ -696,6 +732,28 @@ Net::HTTP.get('www.example.com', '/') # ===> Failure
|
|
696
732
|
assert_not_requested :get, "www.example.com" # ===> Success
|
697
733
|
```
|
698
734
|
|
735
|
+
## Clearing request counters
|
736
|
+
|
737
|
+
If you want to reset **only** the counters of the executed requests use `WebMock.reset_executed_requests!`
|
738
|
+
|
739
|
+
```ruby
|
740
|
+
stub = stub_request(:get, "www.example.com")
|
741
|
+
stub2 = stub_request(:get, "www.example2.com")
|
742
|
+
|
743
|
+
Net::HTTP.get('www.example.com', '/')
|
744
|
+
Net::HTTP.get('www.example.com', '/')
|
745
|
+
|
746
|
+
Net::HTTP.get('www.example2.com', '/')
|
747
|
+
|
748
|
+
expect(stub).to have_been_requested.times(2)
|
749
|
+
expect(stub2).to have_been_requested.times(1)
|
750
|
+
|
751
|
+
WebMock.reset_executed_requests!
|
752
|
+
|
753
|
+
expect(stub).not_to have_been_requested
|
754
|
+
expect(stub2).not_to have_been_requested
|
755
|
+
```
|
756
|
+
|
699
757
|
## Disabling and enabling WebMock or only some http client adapters
|
700
758
|
|
701
759
|
```ruby
|
@@ -841,6 +899,10 @@ end
|
|
841
899
|
|
842
900
|
Please submit them here [http://github.com/bblimke/webmock/issues](http://github.com/bblimke/webmock/issues)
|
843
901
|
|
902
|
+
## Issue triage [![Open Source Helpers](https://www.codetriage.com/bblimke/webmock/badges/users.svg)](https://www.codetriage.com/bblimke/webmock)
|
903
|
+
|
904
|
+
You can contribute by triaging issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to webmock on CodeTriage](https://www.codetriage.com/bblimke/webmock).
|
905
|
+
|
844
906
|
## Suggestions
|
845
907
|
|
846
908
|
If you have any suggestions on how to improve WebMock please send an email to the mailing list [groups.google.com/group/webmock-users](http://groups.google.com/group/webmock-users)
|
@@ -1035,13 +1097,86 @@ People who submitted patches and new features or suggested improvements. Many th
|
|
1035
1097
|
* George Ulmer
|
1036
1098
|
* Christof Koenig
|
1037
1099
|
* Chung-Yi Chi
|
1100
|
+
* Olexandr Hoshylyk
|
1101
|
+
* Janko Marohnić
|
1102
|
+
* Pat Allan
|
1103
|
+
* Rick Song
|
1104
|
+
* NARUSE, Yui
|
1105
|
+
* Piotr Boniecki
|
1106
|
+
* Olia Kremmyda
|
1107
|
+
* Michał Matyas
|
1108
|
+
* Matt Brictson
|
1109
|
+
* Kenny Ortmann
|
1110
|
+
* redbar0n
|
1111
|
+
* Lukas Pokorny
|
1112
|
+
* Arkadiy Tetelman
|
1113
|
+
* Kazato Sugimoto
|
1114
|
+
* Olle Jonsson
|
1115
|
+
* Pavel Rosický
|
1116
|
+
* Geremia Taglialatela
|
1117
|
+
* Koichi Sasada
|
1118
|
+
* Yusuke Endoh
|
1119
|
+
* Grey Baker
|
1120
|
+
* SoonKhen OwYong
|
1121
|
+
* Pavel Valena
|
1122
|
+
* Adam Sokolnicki
|
1123
|
+
* Jeff Felchner
|
1124
|
+
* Eike Send
|
1125
|
+
* Claudio Poli
|
1126
|
+
* Csaba Apagyi
|
1127
|
+
* Frederick Cheung
|
1128
|
+
* Fábio D. Batista
|
1129
|
+
* Andriy Yanko
|
1130
|
+
* y-yagi
|
1131
|
+
* Rafael França
|
1132
|
+
* George Claghorn
|
1133
|
+
* Alex Junger
|
1134
|
+
* Orien Madgwick
|
1135
|
+
* Andrei Sidorov
|
1136
|
+
* Marco Costa
|
1137
|
+
* Ryan Davis
|
1138
|
+
* Brandur
|
1139
|
+
* Samuel Williams
|
1140
|
+
* Patrik Ragnarsson
|
1141
|
+
* Alex Coomans
|
1142
|
+
* Vesa Laakso
|
1143
|
+
* John Hawthorn
|
1144
|
+
* guppy0356
|
1145
|
+
* Thilo Rusche
|
1146
|
+
* Andrew Stuntz
|
1147
|
+
* Lucas Uyezu
|
1148
|
+
* Bruno Sutic
|
1149
|
+
* Ryan Kerr
|
1150
|
+
* Adam Harwood
|
1151
|
+
* Ben Koshy
|
1152
|
+
* Jesse Bowes
|
1153
|
+
* Marek Kasztelnik
|
1154
|
+
* ce07c3
|
1155
|
+
* Jun Jiang
|
1156
|
+
* Oleksiy Kovyrin
|
1157
|
+
* Matt Larraz
|
1158
|
+
* Tony Schneider
|
1159
|
+
* Niklas Hösl
|
1160
|
+
* Johanna Hartmann
|
1161
|
+
* Alex Vondrak
|
1162
|
+
* Will Storey
|
1163
|
+
* Eduardo Hernandez
|
1164
|
+
* ojab
|
1165
|
+
* Giorgio Gambino
|
1166
|
+
* Timmitry
|
1167
|
+
* Michael Fairley
|
1168
|
+
* Ray Zane
|
1169
|
+
* Go Sueyoshi
|
1170
|
+
* Cedric Sohrauer
|
1171
|
+
* Akira Matsuda
|
1172
|
+
* Mark Spangler
|
1038
1173
|
|
1039
1174
|
For a full list of contributors you can visit the
|
1040
1175
|
[contributors](https://github.com/bblimke/webmock/contributors) page.
|
1041
1176
|
|
1042
1177
|
## Background
|
1043
1178
|
|
1044
|
-
Thank you Fakeweb! This library was inspired by [FakeWeb](
|
1179
|
+
Thank you Fakeweb! This library was inspired by [FakeWeb](https://github.com/chrisk/fakeweb).
|
1045
1180
|
I imported some solutions from that project to WebMock. I also copied some code i.e Net:HTTP adapter.
|
1046
1181
|
Fakeweb architecture unfortunately didn't allow me to extend it easily with the features I needed.
|
1047
1182
|
I also preferred some things to work differently i.e request stub precedence.
|
data/Rakefile
CHANGED
@@ -1,28 +1,36 @@
|
|
1
|
-
#!/usr/bin/env rake
|
2
|
-
|
3
1
|
require 'bundler'
|
4
2
|
Bundler::GemHelper.install_tasks
|
5
3
|
|
6
4
|
require "rspec/core/rake_task"
|
7
5
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
-
t.rspec_opts = [
|
6
|
+
t.rspec_opts = %w[
|
7
|
+
--force-color
|
8
|
+
--format progress
|
9
|
+
--require ./spec/spec_helper.rb
|
10
|
+
]
|
9
11
|
t.pattern = 'spec/**/*_spec.rb'
|
10
12
|
end
|
11
13
|
|
12
14
|
RSpec::Core::RakeTask.new(:spec_http_without_webmock) do |t|
|
13
|
-
t.rspec_opts = [
|
15
|
+
t.rspec_opts = %w[
|
16
|
+
--force-color
|
17
|
+
--format progress
|
18
|
+
--require ./spec/acceptance/net_http/real_net_http_spec.rb
|
19
|
+
]
|
14
20
|
t.pattern = 'spec/acceptance/net_http/real_net_http_spec.rb'
|
15
21
|
end
|
16
22
|
|
17
23
|
require 'rake/testtask'
|
18
24
|
Rake::TestTask.new(:test) do |test|
|
19
25
|
test.test_files = FileList["test/**/*.rb"].exclude("test/test_helper.rb")
|
26
|
+
test.options = "--use-color"
|
20
27
|
test.verbose = false
|
21
28
|
test.warning = false
|
22
29
|
end
|
23
30
|
|
24
31
|
Rake::TestTask.new(:minitest) do |test|
|
25
32
|
test.test_files = FileList["minitest/**/*.rb"].exclude("test/test_helper.rb")
|
33
|
+
test.options = "--pride"
|
26
34
|
test.verbose = false
|
27
35
|
test.warning = false
|
28
36
|
end
|
data/lib/webmock/api.rb
CHANGED
@@ -54,10 +54,22 @@ module WebMock
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
def hash_excluding(*args)
|
58
|
+
if defined?(super)
|
59
|
+
super
|
60
|
+
else
|
61
|
+
WebMock::Matchers::HashExcludingMatcher.new(anythingize_lonely_keys(*args))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
57
65
|
def remove_request_stub(stub)
|
58
66
|
WebMock::StubRegistry.instance.remove_request_stub(stub)
|
59
67
|
end
|
60
68
|
|
69
|
+
def reset_executed_requests!
|
70
|
+
WebMock::RequestRegistry.instance.reset!
|
71
|
+
end
|
72
|
+
|
61
73
|
private
|
62
74
|
|
63
75
|
def convert_uri_method_and_options_to_request_and_options(method, uri, options, &block)
|
@@ -0,0 +1,221 @@
|
|
1
|
+
begin
|
2
|
+
require 'async'
|
3
|
+
require 'async/http'
|
4
|
+
rescue LoadError
|
5
|
+
# async-http not found
|
6
|
+
end
|
7
|
+
|
8
|
+
if defined?(Async::HTTP)
|
9
|
+
module WebMock
|
10
|
+
module HttpLibAdapters
|
11
|
+
class AsyncHttpClientAdapter < HttpLibAdapter
|
12
|
+
adapter_for :async_http_client
|
13
|
+
|
14
|
+
OriginalAsyncHttpClient = Async::HTTP::Client unless const_defined?(:OriginalAsyncHttpClient)
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def enable!
|
18
|
+
Async::HTTP.send(:remove_const, :Client)
|
19
|
+
Async::HTTP.send(:const_set, :Client, Async::HTTP::WebMockClientWrapper)
|
20
|
+
end
|
21
|
+
|
22
|
+
def disable!
|
23
|
+
Async::HTTP.send(:remove_const, :Client)
|
24
|
+
Async::HTTP.send(:const_set, :Client, OriginalAsyncHttpClient)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Async
|
32
|
+
module HTTP
|
33
|
+
class WebMockClientWrapper < Client
|
34
|
+
def initialize(
|
35
|
+
endpoint,
|
36
|
+
protocol = endpoint.protocol,
|
37
|
+
scheme = endpoint.scheme,
|
38
|
+
authority = endpoint.authority,
|
39
|
+
**options
|
40
|
+
)
|
41
|
+
webmock_endpoint = WebMockEndpoint.new(scheme, authority, protocol)
|
42
|
+
|
43
|
+
@network_client = WebMockClient.new(endpoint, **options)
|
44
|
+
@webmock_client = WebMockClient.new(webmock_endpoint, **options)
|
45
|
+
|
46
|
+
@scheme = scheme
|
47
|
+
@authority = authority
|
48
|
+
end
|
49
|
+
|
50
|
+
def call(request)
|
51
|
+
request.scheme ||= self.scheme
|
52
|
+
request.authority ||= self.authority
|
53
|
+
|
54
|
+
request_signature = build_request_signature(request)
|
55
|
+
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
56
|
+
webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
|
57
|
+
net_connect_allowed = WebMock.net_connect_allowed?(request_signature.uri)
|
58
|
+
real_request = false
|
59
|
+
|
60
|
+
if webmock_response
|
61
|
+
webmock_response.raise_error_if_any
|
62
|
+
raise Async::TimeoutError, 'WebMock timeout error' if webmock_response.should_timeout
|
63
|
+
WebMockApplication.add_webmock_response(request, webmock_response)
|
64
|
+
response = @webmock_client.call(request)
|
65
|
+
elsif net_connect_allowed
|
66
|
+
response = @network_client.call(request)
|
67
|
+
real_request = true
|
68
|
+
else
|
69
|
+
raise WebMock::NetConnectNotAllowedError.new(request_signature) unless webmock_response
|
70
|
+
end
|
71
|
+
|
72
|
+
if WebMock::CallbackRegistry.any_callbacks?
|
73
|
+
webmock_response ||= build_webmock_response(response)
|
74
|
+
WebMock::CallbackRegistry.invoke_callbacks(
|
75
|
+
{
|
76
|
+
lib: :async_http_client,
|
77
|
+
real_request: real_request
|
78
|
+
},
|
79
|
+
request_signature,
|
80
|
+
webmock_response
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
response
|
85
|
+
end
|
86
|
+
|
87
|
+
def close
|
88
|
+
@network_client.close
|
89
|
+
@webmock_client.close
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def build_request_signature(request)
|
95
|
+
body = request.read
|
96
|
+
request.body = ::Protocol::HTTP::Body::Buffered.wrap(body)
|
97
|
+
WebMock::RequestSignature.new(
|
98
|
+
request.method.downcase.to_sym,
|
99
|
+
"#{request.scheme}://#{request.authority}#{request.path}",
|
100
|
+
headers: request.headers.to_h,
|
101
|
+
body: body
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_webmock_response(response)
|
106
|
+
body = response.read
|
107
|
+
response.body = ::Protocol::HTTP::Body::Buffered.wrap(body)
|
108
|
+
|
109
|
+
webmock_response = WebMock::Response.new
|
110
|
+
webmock_response.status = [
|
111
|
+
response.status,
|
112
|
+
::Protocol::HTTP1::Reason::DESCRIPTIONS[response.status]
|
113
|
+
]
|
114
|
+
webmock_response.headers = build_webmock_response_headers(response)
|
115
|
+
webmock_response.body = body
|
116
|
+
webmock_response
|
117
|
+
end
|
118
|
+
|
119
|
+
def build_webmock_response_headers(response)
|
120
|
+
response.headers.each.each_with_object({}) do |(k, v), o|
|
121
|
+
o[k] ||= []
|
122
|
+
o[k] << v
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class WebMockClient < Client
|
128
|
+
end
|
129
|
+
|
130
|
+
class WebMockEndpoint
|
131
|
+
def initialize(scheme, authority, protocol)
|
132
|
+
@scheme = scheme
|
133
|
+
@authority = authority
|
134
|
+
@protocol = protocol
|
135
|
+
end
|
136
|
+
|
137
|
+
attr :scheme, :authority, :protocol
|
138
|
+
|
139
|
+
def connect
|
140
|
+
server_socket, client_socket = create_connected_sockets
|
141
|
+
Async(transient: true) do
|
142
|
+
accept_socket(server_socket)
|
143
|
+
end
|
144
|
+
client_socket
|
145
|
+
end
|
146
|
+
|
147
|
+
def inspect
|
148
|
+
"\#<#{self.class}> #{scheme}://#{authority} protocol=#{protocol}"
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def create_connected_sockets
|
154
|
+
pair = begin
|
155
|
+
Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM)
|
156
|
+
rescue Errno::EAFNOSUPPORT
|
157
|
+
Async::IO::Socket.pair(Socket::AF_INET, Socket::SOCK_STREAM)
|
158
|
+
end
|
159
|
+
pair.tap do |sockets|
|
160
|
+
sockets.each do |socket|
|
161
|
+
socket.instance_variable_set :@alpn_protocol, nil
|
162
|
+
socket.instance_eval do
|
163
|
+
def alpn_protocol
|
164
|
+
nil # means HTTP11 will be used for HTTPS
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def accept_socket(socket)
|
172
|
+
server = Async::HTTP::Server.new(WebMockApplication, self)
|
173
|
+
server.accept(socket, socket.remote_address)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
module WebMockApplication
|
178
|
+
WEBMOCK_REQUEST_ID_HEADER = 'x-webmock-request-id'.freeze
|
179
|
+
|
180
|
+
class << self
|
181
|
+
def call(request)
|
182
|
+
request.read
|
183
|
+
webmock_response = get_webmock_response(request)
|
184
|
+
build_response(webmock_response)
|
185
|
+
end
|
186
|
+
|
187
|
+
def add_webmock_response(request, webmock_response)
|
188
|
+
webmock_request_id = request.object_id.to_s
|
189
|
+
request.headers.add(WEBMOCK_REQUEST_ID_HEADER, webmock_request_id)
|
190
|
+
webmock_responses[webmock_request_id] = webmock_response
|
191
|
+
end
|
192
|
+
|
193
|
+
def get_webmock_response(request)
|
194
|
+
webmock_request_id = request.headers[WEBMOCK_REQUEST_ID_HEADER][0]
|
195
|
+
webmock_responses.fetch(webmock_request_id)
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def webmock_responses
|
201
|
+
@webmock_responses ||= {}
|
202
|
+
end
|
203
|
+
|
204
|
+
def build_response(webmock_response)
|
205
|
+
headers = (webmock_response.headers || {}).each_with_object([]) do |(k, value), o|
|
206
|
+
Array(value).each do |v|
|
207
|
+
o.push [k, v]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
::Protocol::HTTP::Response[
|
212
|
+
webmock_response.status[0],
|
213
|
+
headers,
|
214
|
+
webmock_response.body
|
215
|
+
]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -5,7 +5,7 @@ rescue LoadError
|
|
5
5
|
end
|
6
6
|
|
7
7
|
if defined?(Curl)
|
8
|
-
WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16', '0.
|
8
|
+
WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16', '1.0.1', ['0.8.7']).check_version!
|
9
9
|
|
10
10
|
module WebMock
|
11
11
|
module HttpLibAdapters
|
@@ -54,7 +54,6 @@ if defined?(Curl)
|
|
54
54
|
|
55
55
|
module Curl
|
56
56
|
class WebMockCurlEasy < Curl::Easy
|
57
|
-
|
58
57
|
def curb_or_webmock
|
59
58
|
request_signature = build_request_signature
|
60
59
|
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
@@ -129,7 +128,7 @@ if defined?(Curl)
|
|
129
128
|
def headers_as_hash(headers)
|
130
129
|
if headers.is_a?(Array)
|
131
130
|
headers.inject({}) {|hash, header|
|
132
|
-
name, value = header.split(":").map(&:strip)
|
131
|
+
name, value = header.split(":", 2).map(&:strip)
|
133
132
|
hash[name] = value
|
134
133
|
hash
|
135
134
|
}
|
@@ -153,7 +152,7 @@ if defined?(Curl)
|
|
153
152
|
@body_str = webmock_response.body
|
154
153
|
@response_code = webmock_response.status[0]
|
155
154
|
|
156
|
-
@header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n"
|
155
|
+
@header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n".dup
|
157
156
|
|
158
157
|
@on_debug.call(@header_str, 1) if defined?( @on_debug )
|
159
158
|
|
@@ -183,7 +182,7 @@ if defined?(Curl)
|
|
183
182
|
self.url = location
|
184
183
|
|
185
184
|
curb_or_webmock do
|
186
|
-
send(
|
185
|
+
send( :http, {'method' => @webmock_method} )
|
187
186
|
end
|
188
187
|
|
189
188
|
self.url = first_url
|
@@ -271,6 +270,8 @@ if defined?(Curl)
|
|
271
270
|
def perform
|
272
271
|
@webmock_method ||= :get
|
273
272
|
curb_or_webmock { super }
|
273
|
+
ensure
|
274
|
+
reset_webmock_method
|
274
275
|
end
|
275
276
|
|
276
277
|
def put_data= data
|
@@ -332,6 +333,19 @@ if defined?(Curl)
|
|
332
333
|
end
|
333
334
|
METHOD
|
334
335
|
end
|
336
|
+
|
337
|
+
def reset_webmock_method
|
338
|
+
@webmock_method = :get
|
339
|
+
end
|
340
|
+
|
341
|
+
def reset
|
342
|
+
instance_variable_set(:@body_str, nil)
|
343
|
+
instance_variable_set(:@content_type, nil)
|
344
|
+
instance_variable_set(:@header_str, nil)
|
345
|
+
instance_variable_set(:@last_effective_url, nil)
|
346
|
+
instance_variable_set(:@response_code, nil)
|
347
|
+
super
|
348
|
+
end
|
335
349
|
end
|
336
350
|
end
|
337
351
|
end
|