philiprehberger-http_client 0.7.0 → 0.8.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +21 -0
- data/lib/philiprehberger/http_client/client.rb +19 -0
- data/lib/philiprehberger/http_client/connection.rb +15 -1
- data/lib/philiprehberger/http_client/response.rb +3 -3
- data/lib/philiprehberger/http_client/version.rb +1 -1
- data/lib/philiprehberger/http_client.rb +9 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c2d06598cd67b2eacacb8b5782991c53204d9d2d0975161e8a342f02b6ea9ea
|
|
4
|
+
data.tar.gz: 65df219a10eff45bdd84e202dc74336881c979f0cedcfb33855118e519b0d0d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4b987d048b25358a516553179038dbcdda91f417205fd43f58aa6bc10dd524a7c6835c951ac1f4e2b8e0639f3daad2de648d49876e787f6f911ce2130ee822d
|
|
7
|
+
data.tar.gz: 48ea64a75160012db6e407df73067252dc570a370c5b7968fb061f2621f468efde0973b26ad484caed947859e6f37122d7c5f3316d0f220b62a120c8b8fd81ef
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.0] - 2026-04-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Client#close` method to explicitly drain the connection pool
|
|
14
|
+
- `Client.open(**opts, &block)` block form with automatic cleanup
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Response objects now properly initialize `metrics` and `redirects` attributes
|
|
18
|
+
- 307 and 308 redirects now preserve the original HTTP method instead of converting to GET
|
|
19
|
+
- Clarified that `dns_time`, `connect_time`, and `tls_time` metrics are unavailable from Net::HTTP
|
|
20
|
+
|
|
10
21
|
## [0.7.0] - 2026-04-04
|
|
11
22
|
|
|
12
23
|
### Added
|
data/README.md
CHANGED
|
@@ -299,6 +299,8 @@ metrics.first_byte_time # => 0.180
|
|
|
299
299
|
metrics.to_h # => { dns_time: 0.0, connect_time: 0.0, ... }
|
|
300
300
|
```
|
|
301
301
|
|
|
302
|
+
> **Note:** `dns_time`, `connect_time`, and `tls_time` are not available from Ruby's stdlib `Net::HTTP` and will always be `0.0`. Only `total_time` and `first_byte_time` are populated.
|
|
303
|
+
|
|
302
304
|
### Connection pooling
|
|
303
305
|
|
|
304
306
|
Reuse TCP connections to the same host for better performance:
|
|
@@ -317,6 +319,23 @@ client = Philiprehberger::HttpClient.new(
|
|
|
317
319
|
client.pool.drain
|
|
318
320
|
```
|
|
319
321
|
|
|
322
|
+
### Client lifecycle
|
|
323
|
+
|
|
324
|
+
Use `Client.open` for automatic cleanup, or call `close` manually to drain the connection pool:
|
|
325
|
+
|
|
326
|
+
```ruby
|
|
327
|
+
# Block form — pool is drained automatically
|
|
328
|
+
Philiprehberger::HttpClient.open(base_url: "https://api.example.com", pool: true) do |client|
|
|
329
|
+
client.get("/data")
|
|
330
|
+
client.post("/submit", json: { key: "value" })
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Manual form
|
|
334
|
+
client = Philiprehberger::HttpClient.new(base_url: "https://api.example.com", pool: true)
|
|
335
|
+
client.get("/data")
|
|
336
|
+
client.close # drains the connection pool
|
|
337
|
+
```
|
|
338
|
+
|
|
320
339
|
### Request ID tracking
|
|
321
340
|
|
|
322
341
|
Every request is assigned a unique `X-Request-ID` header automatically:
|
|
@@ -428,6 +447,8 @@ client.head("/resource")
|
|
|
428
447
|
| `bearer_token(token)` | Set Bearer token auth for all subsequent requests |
|
|
429
448
|
| `basic_auth(user, pass)` | Set Basic auth for all subsequent requests |
|
|
430
449
|
| `clear_cache!` | Flush the response cache |
|
|
450
|
+
| `close` | Drain the connection pool (no-op if pooling disabled) |
|
|
451
|
+
| `self.open(**opts, &block)` | Block form — creates client, yields it, ensures `close` is called |
|
|
431
452
|
| `pool` | Returns the `Pool` instance (nil if pooling disabled) |
|
|
432
453
|
| `cache` | Returns the `Cache` instance (nil if caching disabled) |
|
|
433
454
|
|
|
@@ -204,6 +204,25 @@ module Philiprehberger
|
|
|
204
204
|
@cache&.clear!
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
+
# Drain the connection pool. No-op if pooling is disabled.
|
|
208
|
+
#
|
|
209
|
+
# @return [void]
|
|
210
|
+
def close
|
|
211
|
+
@pool&.drain
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Create a client, yield it to the block, and ensure it is closed afterward.
|
|
215
|
+
#
|
|
216
|
+
# @param opts [Hash] Options forwarded to {#initialize}
|
|
217
|
+
# @yield [Client] the client instance
|
|
218
|
+
# @return [Object] the return value of the block
|
|
219
|
+
def self.open(**opts)
|
|
220
|
+
client = new(**opts)
|
|
221
|
+
yield client
|
|
222
|
+
ensure
|
|
223
|
+
client&.close
|
|
224
|
+
end
|
|
225
|
+
|
|
207
226
|
private
|
|
208
227
|
|
|
209
228
|
def assign_timeout_opts(opts)
|
|
@@ -185,6 +185,15 @@ module Philiprehberger
|
|
|
185
185
|
@follow_redirects && REDIRECT_CODES.include?(response.status)
|
|
186
186
|
end
|
|
187
187
|
|
|
188
|
+
METHOD_CLASS_MAP = {
|
|
189
|
+
'GET' => Net::HTTP::Get,
|
|
190
|
+
'POST' => Net::HTTP::Post,
|
|
191
|
+
'PUT' => Net::HTTP::Put,
|
|
192
|
+
'PATCH' => Net::HTTP::Patch,
|
|
193
|
+
'DELETE' => Net::HTTP::Delete,
|
|
194
|
+
'HEAD' => Net::HTTP::Head
|
|
195
|
+
}.freeze
|
|
196
|
+
|
|
188
197
|
def follow_redirect_chain(response, original_request, **timeout_opts)
|
|
189
198
|
redirect_count = 0
|
|
190
199
|
redirects = []
|
|
@@ -199,7 +208,12 @@ module Philiprehberger
|
|
|
199
208
|
redirect_uri = URI.parse(location)
|
|
200
209
|
redirect_uri = URI.join("#{original_request.uri.scheme}://#{original_request.uri.host}", location) unless redirect_uri.host
|
|
201
210
|
|
|
202
|
-
|
|
211
|
+
redirect_class = if [307, 308].include?(current_response.status)
|
|
212
|
+
METHOD_CLASS_MAP.fetch(original_request.method, Net::HTTP::Get)
|
|
213
|
+
else
|
|
214
|
+
Net::HTTP::Get
|
|
215
|
+
end
|
|
216
|
+
redirect_request = redirect_class.new(redirect_uri)
|
|
203
217
|
@default_headers.each { |key, value| redirect_request[key] = value }
|
|
204
218
|
redirect_request['accept-encoding'] ||= 'gzip, deflate'
|
|
205
219
|
apply_cookie_header(redirect_request)
|
|
@@ -18,6 +18,8 @@ module Philiprehberger
|
|
|
18
18
|
@headers = headers
|
|
19
19
|
@streaming = streaming
|
|
20
20
|
@request_id = request_id
|
|
21
|
+
@metrics = nil
|
|
22
|
+
@redirects = []
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
# Returns true if the status code is in the 2xx range.
|
|
@@ -50,9 +52,7 @@ module Philiprehberger
|
|
|
50
52
|
# Returns the redirect chain (empty if no redirects occurred).
|
|
51
53
|
#
|
|
52
54
|
# @return [Array<String>]
|
|
53
|
-
|
|
54
|
-
@redirects || []
|
|
55
|
-
end
|
|
55
|
+
attr_reader :redirects
|
|
56
56
|
|
|
57
57
|
# Returns true if the response was redirected.
|
|
58
58
|
#
|
|
@@ -22,5 +22,14 @@ module Philiprehberger
|
|
|
22
22
|
def self.new(**options)
|
|
23
23
|
Client.new(**options)
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
# Block form — creates a client, yields it, and ensures cleanup.
|
|
27
|
+
#
|
|
28
|
+
# @param options [Hash] Forwarded to {Client#initialize}
|
|
29
|
+
# @yield [Client]
|
|
30
|
+
# @return [Object] the return value of the block
|
|
31
|
+
def self.open(...)
|
|
32
|
+
Client.open(...)
|
|
33
|
+
end
|
|
25
34
|
end
|
|
26
35
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-http_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A zero-dependency HTTP client built on Ruby's net/http with automatic
|
|
14
14
|
retries, request/response interceptors, and a clean API for JSON services.
|