better-faraday 2.0.2 → 3.1.1
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +40 -0
- data/.ruby-version +1 -1
- data/Gemfile +6 -0
- data/Gemfile.lock +124 -0
- data/README.md +110 -3
- data/Rakefile +8 -0
- data/better-faraday.gemspec +18 -5
- data/lib/better-faraday.rb +1 -257
- data/lib/better_faraday.rb +440 -0
- data/spec/adapters_spec.rb +104 -0
- data/spec/array_masking_spec.rb +47 -0
- data/spec/assert_status_spec.rb +71 -0
- data/spec/binary_body_spec.rb +47 -0
- data/spec/custom_sensitive_signs_spec.rb +46 -0
- data/spec/http_error_spec.rb +56 -0
- data/spec/inspect_truncation_spec.rb +40 -0
- data/spec/invalid_json_spec.rb +37 -0
- data/spec/logger_spec.rb +52 -0
- data/spec/middleware_spec.rb +61 -0
- data/spec/multipart_spec.rb +39 -0
- data/spec/nested_sensitive_data_spec.rb +57 -0
- data/spec/sensitive_data_signs_spec.rb +109 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/timing_inspect_spec.rb +51 -0
- metadata +132 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 63e3069002b8c492c1974502babbe1ab1b0815a499fcb1fb6384860953957051
|
|
4
|
+
data.tar.gz: 79af46da23df8de021919a3e1fab6990b927524634cb705b3e88a06eeb115170
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef42fd021a83dc99654aef97736ac6364942774748865be68ca3b379fcb097213043274f0ab6ab16b0912918b5b9b68438ab69fc0326b7404b7d2d112c13780d
|
|
7
|
+
data.tar.gz: 1dba086f05f40addb2577ea44905cb7e7114abb6b2176b13884324df8dc4c3c8cee5a7d0d334cad6da6c1467cd6a58a91532b109071532df1edba4ed3c5cbce8
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
Exclude:
|
|
3
|
+
- 'spec/**/*'
|
|
4
|
+
|
|
5
|
+
Style/StringLiterals:
|
|
6
|
+
EnforcedStyle: double_quotes
|
|
7
|
+
|
|
8
|
+
Metrics/BlockLength:
|
|
9
|
+
Enabled: false
|
|
10
|
+
|
|
11
|
+
Metrics/MethodLength:
|
|
12
|
+
Enabled: false
|
|
13
|
+
|
|
14
|
+
Layout/IndentationWidth:
|
|
15
|
+
EnforcedStyleAlignWith: relative_to_receiver
|
|
16
|
+
|
|
17
|
+
Style/ParallelAssignment:
|
|
18
|
+
Enabled: false
|
|
19
|
+
|
|
20
|
+
Metrics/PerceivedComplexity:
|
|
21
|
+
Enabled: false
|
|
22
|
+
|
|
23
|
+
Metrics/CyclomaticComplexity:
|
|
24
|
+
Enabled: false
|
|
25
|
+
|
|
26
|
+
Style/Documentation:
|
|
27
|
+
Enabled: false
|
|
28
|
+
|
|
29
|
+
Metrics/ClassLength:
|
|
30
|
+
Enabled: false
|
|
31
|
+
|
|
32
|
+
Layout/EndAlignment:
|
|
33
|
+
EnforcedStyleAlignWith: start_of_line
|
|
34
|
+
|
|
35
|
+
Metrics/AbcSize:
|
|
36
|
+
Enabled: false
|
|
37
|
+
|
|
38
|
+
Style/MultilineBlockChain:
|
|
39
|
+
Enabled: false
|
|
40
|
+
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.7
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
better-faraday (3.1.1)
|
|
5
|
+
faraday (>= 1.0, < 3.0)
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: https://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
addressable (2.9.0)
|
|
11
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
12
|
+
bigdecimal (4.1.3)
|
|
13
|
+
byebug (13.0.0)
|
|
14
|
+
reline (>= 0.6.0)
|
|
15
|
+
coderay (1.1.3)
|
|
16
|
+
connection_pool (3.0.2)
|
|
17
|
+
crack (1.0.1)
|
|
18
|
+
bigdecimal
|
|
19
|
+
rexml
|
|
20
|
+
diff-lcs (1.6.2)
|
|
21
|
+
faraday (2.14.4)
|
|
22
|
+
faraday-net_http (>= 2.0, < 3.5)
|
|
23
|
+
json
|
|
24
|
+
logger
|
|
25
|
+
faraday-multipart (1.2.0)
|
|
26
|
+
multipart-post (~> 2.0)
|
|
27
|
+
faraday-net_http (3.4.4)
|
|
28
|
+
net-http (~> 0.5)
|
|
29
|
+
faraday-net_http_persistent (2.3.1)
|
|
30
|
+
faraday (~> 2.5)
|
|
31
|
+
net-http-persistent (>= 4.0.4, < 5)
|
|
32
|
+
hashdiff (1.2.1)
|
|
33
|
+
io-console (0.9.4)
|
|
34
|
+
json (3.0.2)
|
|
35
|
+
logger (1.7.0)
|
|
36
|
+
method_source (1.1.0)
|
|
37
|
+
multipart-post (2.4.1)
|
|
38
|
+
net-http (0.9.1)
|
|
39
|
+
uri (>= 0.11.1)
|
|
40
|
+
net-http-persistent (4.0.8)
|
|
41
|
+
connection_pool (>= 2.2.4, < 4)
|
|
42
|
+
pry (0.16.0)
|
|
43
|
+
coderay (~> 1.1)
|
|
44
|
+
method_source (~> 1.0)
|
|
45
|
+
reline (>= 0.6.0)
|
|
46
|
+
pry-byebug (3.12.0)
|
|
47
|
+
byebug (~> 13.0)
|
|
48
|
+
pry (>= 0.13, < 0.17)
|
|
49
|
+
public_suffix (7.0.5)
|
|
50
|
+
rake (13.4.2)
|
|
51
|
+
reline (0.7.0)
|
|
52
|
+
io-console (~> 0.5)
|
|
53
|
+
rexml (3.4.4)
|
|
54
|
+
rspec (3.13.2)
|
|
55
|
+
rspec-core (~> 3.13.0)
|
|
56
|
+
rspec-expectations (~> 3.13.0)
|
|
57
|
+
rspec-mocks (~> 3.13.0)
|
|
58
|
+
rspec-core (3.13.6)
|
|
59
|
+
rspec-support (~> 3.13.0)
|
|
60
|
+
rspec-expectations (3.13.5)
|
|
61
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
62
|
+
rspec-support (~> 3.13.0)
|
|
63
|
+
rspec-mocks (3.13.8)
|
|
64
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
65
|
+
rspec-support (~> 3.13.0)
|
|
66
|
+
rspec-support (3.13.7)
|
|
67
|
+
uri (1.1.1)
|
|
68
|
+
webmock (3.26.4)
|
|
69
|
+
addressable (>= 2.8.0)
|
|
70
|
+
crack (>= 0.3.2)
|
|
71
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
72
|
+
|
|
73
|
+
PLATFORMS
|
|
74
|
+
arm64-darwin-24
|
|
75
|
+
ruby
|
|
76
|
+
|
|
77
|
+
DEPENDENCIES
|
|
78
|
+
better-faraday!
|
|
79
|
+
bundler (~> 4.0)
|
|
80
|
+
faraday-multipart (~> 1.2)
|
|
81
|
+
faraday-net_http_persistent (~> 2.3)
|
|
82
|
+
pry-byebug (~> 3.12)
|
|
83
|
+
rake (~> 13.4)
|
|
84
|
+
rspec (~> 3.13)
|
|
85
|
+
webmock (~> 3.26)
|
|
86
|
+
|
|
87
|
+
CHECKSUMS
|
|
88
|
+
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
|
|
89
|
+
better-faraday (3.1.1)
|
|
90
|
+
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
|
|
91
|
+
bundler (4.0.22) sha256=d8d5ec84c8555e0af71db63ed7aee4d1a8fb839ec46d84212d61979242a5d75a
|
|
92
|
+
byebug (13.0.0) sha256=d2263efe751941ca520fa29744b71972d39cbc41839496706f5d9b22e92ae05d
|
|
93
|
+
coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b
|
|
94
|
+
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
|
95
|
+
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
|
|
96
|
+
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
97
|
+
faraday (2.14.4) sha256=9bb4408c44621b0dbaaaa39e5dbfc4dbe58cc6751b6eb94d16ae9e1f2f2fab6e
|
|
98
|
+
faraday-multipart (1.2.0) sha256=7d89a949693714176f612323ca13746a2ded204031a6ba528adee788694ef757
|
|
99
|
+
faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588
|
|
100
|
+
faraday-net_http_persistent (2.3.1) sha256=23ffba37d6a27807a10f033d01918ec958aa73fa6ff0fccfbcd5ce2d2e68fca3
|
|
101
|
+
hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
|
|
102
|
+
io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
|
|
103
|
+
json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
|
|
104
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
105
|
+
method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
|
|
106
|
+
multipart-post (2.4.1) sha256=9872d03a8e552020ca096adadbf5e3cb1cd1cdd6acd3c161136b8a5737cdb4a8
|
|
107
|
+
net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
|
|
108
|
+
net-http-persistent (4.0.8) sha256=ef3de8319d691537b329053fae3a33195f8b070bbbfae8bf1a58c796081960e6
|
|
109
|
+
pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e
|
|
110
|
+
pry-byebug (3.12.0) sha256=594e094ae8a8390a7ad4c7b36ae36e13304ed02664c67417d108dc5f7213d1b7
|
|
111
|
+
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
|
112
|
+
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
113
|
+
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
|
|
114
|
+
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
|
115
|
+
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
116
|
+
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
|
117
|
+
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
|
118
|
+
rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
|
|
119
|
+
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
|
|
120
|
+
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
|
|
121
|
+
webmock (3.26.4) sha256=8d8da206d217ebe6968cfb09c77f4533c23074e1432bad865f3994eacbaad50d
|
|
122
|
+
|
|
123
|
+
BUNDLED WITH
|
|
124
|
+
4.0.22
|
data/README.md
CHANGED
|
@@ -1,7 +1,114 @@
|
|
|
1
1
|
# better-faraday
|
|
2
2
|
|
|
3
|
-
A gem extending Faraday (popular Ruby HTTP client) with useful features without breaking
|
|
3
|
+
A lightweight gem extending Faraday (the popular Ruby HTTP client) with useful features, extensive logging, and smart HTTP exceptions—without breaking the standard API.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Requirements
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
* **Ruby** >= 2.7.0, < 5.0 (Fully compatible with Ruby 3.x and 4.x)
|
|
8
|
+
* **Faraday** >= 1.0, < 3.0
|
|
9
|
+
* **Zero dependencies**
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add this line to your application's Gemfile:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem "better-faraday", "~> 3.1"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage & Features
|
|
20
|
+
|
|
21
|
+
### 1. Advanced Exception Handling
|
|
22
|
+
|
|
23
|
+
Better Faraday automatically defines semantic HTTP exceptions and allows you to enforce strict status checks directly on the response object.
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
response = Faraday.get("https://api.example.com/data")
|
|
27
|
+
|
|
28
|
+
# Raises BetterFaraday::HTTP404 if the status is 404
|
|
29
|
+
# Raises BetterFaraday::HTTP5xx if the status is 500-599
|
|
30
|
+
response.assert_2xx!
|
|
31
|
+
|
|
32
|
+
# Or check specific ranges/codes:
|
|
33
|
+
response.assert_status!(200)
|
|
34
|
+
response.assert_status!(200..204)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If an exception is raised, its `.message` and `.inspect` will contain a beautifully formatted payload showing exactly what went wrong.
|
|
38
|
+
|
|
39
|
+
### 2. Comprehensive Response Inspection
|
|
40
|
+
|
|
41
|
+
Better Faraday enriches `response.inspect` to output detailed, formatted information about the entire lifecycle of the request. It automatically parses JSON bodies for readability, calculates request duration, and safely truncates huge payloads (over 2048 bytes) to prevent memory bloating in your logs.
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
begin
|
|
45
|
+
response.assert_2xx!
|
|
46
|
+
rescue BetterFaraday::HTTPError => e
|
|
47
|
+
puts e.inspect
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Output example:**
|
|
52
|
+
```text
|
|
53
|
+
BetterFaraday::HTTP422
|
|
54
|
+
|
|
55
|
+
-- HTTP 422 UNPROCESSABLE ENTITY --
|
|
56
|
+
|
|
57
|
+
-- Request URL --
|
|
58
|
+
https://api.example.com/users
|
|
59
|
+
|
|
60
|
+
-- Request Method --
|
|
61
|
+
POST
|
|
62
|
+
|
|
63
|
+
-- Request Headers --
|
|
64
|
+
{ "Content-Type": "application/json", "Authorization": "SECRET" }
|
|
65
|
+
|
|
66
|
+
-- Request Body (48 bytes) --
|
|
67
|
+
{ "email": "user@test.com", "password": "SECRET" }
|
|
68
|
+
|
|
69
|
+
-- Request Sent At --
|
|
70
|
+
2026-10-03 10:00:00.000 UTC
|
|
71
|
+
|
|
72
|
+
-- Response Headers --
|
|
73
|
+
{ "Content-Type": "application/json" }
|
|
74
|
+
|
|
75
|
+
-- Response Body (35 bytes) --
|
|
76
|
+
{ "error": "Email already exists" }
|
|
77
|
+
|
|
78
|
+
-- Response Received At --
|
|
79
|
+
2026-10-03 10:00:00.125 UTC
|
|
80
|
+
|
|
81
|
+
-- Response Received In --
|
|
82
|
+
125.0ms
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3. Smart Sensitive Data Masking
|
|
86
|
+
|
|
87
|
+
Better Faraday protects your logs by aggressively masking sensitive information in both headers and JSON bodies. Out of the box, it safely masks:
|
|
88
|
+
*
|
|
89
|
+
* Passwords and passphrases
|
|
90
|
+
* API keys and secrets
|
|
91
|
+
* Tokens (Access, Refresh, Bearer, CSRF, JWT)
|
|
92
|
+
* Authorization headers
|
|
93
|
+
* Credit Card numbers and CVV/CVC codes
|
|
94
|
+
* Crypto mnemonics and seed phrases
|
|
95
|
+
|
|
96
|
+
The masking algorithm is highly performant and seamlessly handles `snake_case`, `camelCase`, `kebab-case`, and spaced keys.
|
|
97
|
+
|
|
98
|
+
**Adding Custom Masking Rules:**
|
|
99
|
+
You can easily add your own regex patterns to the masking engine:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
BetterFaraday.sensitive_data_signs << /my_custom_secret_key/i.freeze
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Bulletproof Middleware & Adapter Compatibility
|
|
106
|
+
|
|
107
|
+
Better Faraday securely captures request data regardless of the adapter you use:
|
|
108
|
+
* **Standard Adapters:** Fully supports `Net::HTTP` and `Net::HTTP::Persistent`, capturing the exact wire headers mutated during transport.
|
|
109
|
+
* **Test Adapters:** Perfectly compatible with `Faraday::Adapter::Test` and `WebMock` using an eager-fallback mechanism.
|
|
110
|
+
* **Multipart & IO Streams:** Safely reads and logs `File` or `StringIO` upload payloads, ensuring the stream is automatically rewound back to position `0` so the HTTP adapter doesn't send an empty request.
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/better-faraday.gemspec
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
Gem::Specification.new do |s|
|
|
5
4
|
s.name = "better-faraday"
|
|
6
|
-
s.version = "
|
|
5
|
+
s.version = "3.1.1"
|
|
7
6
|
s.author = "Yaroslav Konoplov"
|
|
8
7
|
s.email = "eahome00@gmail.com"
|
|
9
|
-
s.summary = "Extends Faraday with useful features
|
|
10
|
-
s.description = "A gem extending Faraday
|
|
8
|
+
s.summary = "Extends Faraday with useful features"
|
|
9
|
+
s.description = "A gem extending Faraday HTTP client with useful features"
|
|
11
10
|
s.homepage = "https://github.com/yivo/better-faraday"
|
|
12
11
|
s.license = "MIT"
|
|
12
|
+
|
|
13
|
+
# Ruby requirements
|
|
14
|
+
s.required_ruby_version = [">= 2.7.0", "< 5.0"]
|
|
15
|
+
|
|
13
16
|
s.files = `git ls-files -z`.split("\x0")
|
|
14
17
|
s.test_files = `git ls-files -z -- {test,spec,features}/*`.split("\x0")
|
|
15
18
|
s.require_paths = ["lib"]
|
|
19
|
+
|
|
20
|
+
# Runtime dependencies
|
|
16
21
|
s.add_dependency "faraday", ">= 1.0", "< 3.0"
|
|
17
|
-
|
|
22
|
+
|
|
23
|
+
# Development and test dependencies
|
|
24
|
+
s.add_development_dependency "bundler", "~> 4.0"
|
|
25
|
+
s.add_development_dependency "faraday-multipart", "~> 1.2"
|
|
26
|
+
s.add_development_dependency "faraday-net_http_persistent", "~> 2.3"
|
|
27
|
+
s.add_development_dependency "pry-byebug", "~> 3.12"
|
|
28
|
+
s.add_development_dependency "rake", "~> 13.4"
|
|
29
|
+
s.add_development_dependency "rspec", "~> 3.13"
|
|
30
|
+
s.add_development_dependency "webmock", "~> 3.26"
|
|
18
31
|
end
|
data/lib/better-faraday.rb
CHANGED
|
@@ -1,259 +1,3 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
require "net/http"
|
|
6
|
-
require "faraday"
|
|
7
|
-
require "faraday/error"
|
|
8
|
-
require "faraday/options"
|
|
9
|
-
require "faraday/response"
|
|
10
|
-
require "active_support/core_ext/string/filters"
|
|
11
|
-
require "active_support/core_ext/string/inflections"
|
|
12
|
-
|
|
13
|
-
Module.new do
|
|
14
|
-
def call(environment)
|
|
15
|
-
environment.instance_variable_set(:@bf_request_sent_at, Time.now.utc)
|
|
16
|
-
super
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def save_response(environment, *)
|
|
20
|
-
environment.instance_variable_set \
|
|
21
|
-
:@bf_response_received_at,
|
|
22
|
-
Time.now.utc
|
|
23
|
-
|
|
24
|
-
body = environment.body
|
|
25
|
-
value = body.respond_to?(:read) ? body.read : body
|
|
26
|
-
value = value.to_s unless String === value
|
|
27
|
-
|
|
28
|
-
environment.instance_variable_set \
|
|
29
|
-
:@bf_request_body,
|
|
30
|
-
value
|
|
31
|
-
|
|
32
|
-
super
|
|
33
|
-
end
|
|
34
|
-
end.tap { |m| Faraday::Adapter.prepend(m) }
|
|
35
|
-
|
|
36
|
-
Module.new do
|
|
37
|
-
def end_transport(request, response)
|
|
38
|
-
headers = request.to_hash
|
|
39
|
-
headers.keys.each { |k| headers[k] = headers[k].join(", ") }
|
|
40
|
-
response.instance_variable_set :@bf_request_headers, headers
|
|
41
|
-
super
|
|
42
|
-
end
|
|
43
|
-
end.tap { |m| Net::HTTP.prepend(m) }
|
|
44
|
-
|
|
45
|
-
Module.new do
|
|
46
|
-
def perform_request(connection, environment)
|
|
47
|
-
super(connection, environment).tap do |response|
|
|
48
|
-
environment.instance_variable_set \
|
|
49
|
-
:@bf_request_headers,
|
|
50
|
-
response.instance_variable_get(:@bf_request_headers)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end.tap do |m|
|
|
54
|
-
Faraday::Adapter::NetHttp.prepend(m)
|
|
55
|
-
Faraday::Adapter::NetHttpPersistent.prepend(m) if defined?(Faraday::Adapter::NetHttpPersistent)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
module Faraday
|
|
59
|
-
class Env
|
|
60
|
-
attr_reader :bf_request_headers, :bf_request_body, :bf_request_sent_at, :bf_response_received_at
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
class Response
|
|
64
|
-
def assert_status!(code_or_range)
|
|
65
|
-
within_range = if Range === code_or_range
|
|
66
|
-
code_or_range.include?(status)
|
|
67
|
-
else
|
|
68
|
-
status == code_or_range
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
return self if within_range
|
|
72
|
-
|
|
73
|
-
klass = if status_4xx?
|
|
74
|
-
"BetterFaraday::HTTP#{status}".safe_constantize || BetterFaraday::HTTP4xx
|
|
75
|
-
elsif status_5xx?
|
|
76
|
-
"BetterFaraday::HTTP#{status}".safe_constantize || BetterFaraday::HTTP5xx
|
|
77
|
-
else
|
|
78
|
-
BetterFaraday::HTTPError
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
raise klass.new(self)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def assert_2xx!
|
|
85
|
-
assert_status!(200..299)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def assert_200!
|
|
89
|
-
assert_status! 200
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def status_2xx?
|
|
93
|
-
status >= 200 && status <= 299
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def status_3xx?
|
|
97
|
-
status >= 300 && status <= 399
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def status_4xx?
|
|
101
|
-
status >= 400 && status <= 499
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def status_5xx?
|
|
105
|
-
status >= 500 && status <= 599
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def inspect
|
|
109
|
-
@inspection_text ||= begin
|
|
110
|
-
request_headers = bf_protect_data(env.bf_request_headers.dup)
|
|
111
|
-
request_body = env.bf_request_body.yield_self { |body| String === body ? body : body.to_s }
|
|
112
|
-
request_body_bytes_count = env.bf_request_body.bytesize
|
|
113
|
-
response_body = env.body.yield_self { |body| String === body ? body : body.to_s }
|
|
114
|
-
response_body_bytes_count = response_body.bytesize
|
|
115
|
-
|
|
116
|
-
if env.bf_request_headers["content-type"].to_s.match?(/\bapplication\/json\b/i)
|
|
117
|
-
request_json = bf_json_parse(request_body).yield_self do |data|
|
|
118
|
-
bf_json_dump(bf_protect_data(data)) if data
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
if env.response_headers
|
|
123
|
-
response_headers = bf_protect_data(env.response_headers.to_hash)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
if env.response_headers && env.response_headers["content-type"].to_s.match?(/\bapplication\/json\b/i)
|
|
127
|
-
response_json = bf_json_parse(response_body).yield_self do |data|
|
|
128
|
-
bf_json_dump(bf_protect_data(data)) if data
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
lines = [
|
|
133
|
-
"-- #{status} #{reason_phrase} --".upcase,
|
|
134
|
-
"",
|
|
135
|
-
"-- Request URL --",
|
|
136
|
-
env.url.to_s,
|
|
137
|
-
"",
|
|
138
|
-
"-- Request Method --",
|
|
139
|
-
env.method.to_s.upcase,
|
|
140
|
-
"",
|
|
141
|
-
"-- Request Headers --",
|
|
142
|
-
bf_json_dump(request_headers).truncate(2048, omission: "... (truncated)"),
|
|
143
|
-
"",
|
|
144
|
-
|
|
145
|
-
%[-- Request Body (#{request_body_bytes_count} #{"byte".pluralize(request_body_bytes_count)}) --],
|
|
146
|
-
if request_json
|
|
147
|
-
request_json
|
|
148
|
-
else
|
|
149
|
-
# String#inspect returns \x{XXXX} for the encoding other than Unicode.
|
|
150
|
-
# [1..-2] removed leading and trailing " added by String#inspect.
|
|
151
|
-
# gsub(/\\"/, "\"") unescapes ".
|
|
152
|
-
request_body.inspect.gsub(/\\"/, "\"")[1..-2]
|
|
153
|
-
end.truncate(2048, omission: "... (truncated)"),
|
|
154
|
-
"",
|
|
155
|
-
|
|
156
|
-
"-- Request Sent At --",
|
|
157
|
-
env.bf_request_sent_at.strftime("%Y-%m-%d %H:%M:%S.%3N") + " UTC",
|
|
158
|
-
"",
|
|
159
|
-
|
|
160
|
-
"-- Response Headers --",
|
|
161
|
-
if response_headers
|
|
162
|
-
bf_json_dump(response_headers)
|
|
163
|
-
else
|
|
164
|
-
env.response_headers.to_s.inspect.gsub(/\\"/, "\"")[1..-2]
|
|
165
|
-
end.truncate(2048, omission: "... (truncated)"),
|
|
166
|
-
"",
|
|
167
|
-
|
|
168
|
-
%[-- Response Body (#{response_body_bytes_count} #{"byte".pluralize(response_body_bytes_count)}) --],
|
|
169
|
-
if response_json
|
|
170
|
-
response_json
|
|
171
|
-
else
|
|
172
|
-
response_body.inspect.gsub(/\\"/, "\"")[1..-2]
|
|
173
|
-
end.truncate(2048, omission: "... (truncated)"),
|
|
174
|
-
""
|
|
175
|
-
]
|
|
176
|
-
|
|
177
|
-
if env.bf_response_received_at
|
|
178
|
-
lines.concat [
|
|
179
|
-
"-- Response Received At --",
|
|
180
|
-
env.bf_response_received_at.strftime("%Y-%m-%d %H:%M:%S.%3N") + " UTC",
|
|
181
|
-
"",
|
|
182
|
-
"-- Response Received In --",
|
|
183
|
-
"#{((env.bf_response_received_at.to_f - env.bf_request_sent_at.to_f) * 1000.0).ceil(3)}ms",
|
|
184
|
-
""
|
|
185
|
-
]
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
lines.join("\n").freeze
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
@inspection_text.dup
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
private
|
|
195
|
-
|
|
196
|
-
def bf_json_parse(json)
|
|
197
|
-
return nil unless ::String === json
|
|
198
|
-
data = ::JSON.parse(json)
|
|
199
|
-
data if ::Hash === data || ::Array === data
|
|
200
|
-
rescue ::JSON::ParserError
|
|
201
|
-
nil
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
def bf_json_dump(data)
|
|
205
|
-
::JSON.generate(data, space: " ", object_nl: " ", array_nl: " ")
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def bf_protect_data(data)
|
|
209
|
-
return data.map(&method(:bf_protect_data)) if ::Array === data
|
|
210
|
-
return data unless ::Hash === data
|
|
211
|
-
|
|
212
|
-
signs = BetterFaraday.sensitive_data_signs
|
|
213
|
-
|
|
214
|
-
data.each_with_object({}) do |(key, value), memo|
|
|
215
|
-
memo[key] = if key.to_s.underscore.tr("_", " ").yield_self { |k| signs.any? { |r| k.match?(r) } }
|
|
216
|
-
"SECRET"
|
|
217
|
-
else
|
|
218
|
-
bf_protect_data(value)
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
module BetterFaraday
|
|
226
|
-
class HTTPError < Faraday::Error
|
|
227
|
-
def initialize(response)
|
|
228
|
-
super(response.inspect, response)
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
def inspect
|
|
232
|
-
%[#{self.class}\n\n#{response.inspect}]
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
class HTTP4xx < HTTPError; end
|
|
237
|
-
class HTTP400 < HTTP4xx; end
|
|
238
|
-
class HTTP401 < HTTP4xx; end
|
|
239
|
-
class HTTP403 < HTTP4xx; end
|
|
240
|
-
class HTTP404 < HTTP4xx; end
|
|
241
|
-
class HTTP408 < HTTP4xx; end
|
|
242
|
-
class HTTP422 < HTTP4xx; end
|
|
243
|
-
class HTTP429 < HTTP4xx; end
|
|
244
|
-
class HTTP5xx < HTTPError; end
|
|
245
|
-
class HTTP500 < HTTP5xx; end
|
|
246
|
-
class HTTP502 < HTTP5xx; end
|
|
247
|
-
class HTTP503 < HTTP5xx; end
|
|
248
|
-
|
|
249
|
-
class << self
|
|
250
|
-
attr_accessor :sensitive_data_signs
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
self.sensitive_data_signs = [
|
|
254
|
-
/\bpass(?:word|phrase)\b/i,
|
|
255
|
-
/\bauthorization\b/i,
|
|
256
|
-
/\bsecret\b/i,
|
|
257
|
-
/\b(:?access)?token\b/i
|
|
258
|
-
]
|
|
259
|
-
end
|
|
3
|
+
require_relative "better_faraday"
|