rack-proxy 1.0.2 → 2.0.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 +45 -1
- data/README.md +14 -3
- data/SECURITY.md +5 -4
- data/lib/rack/http_streaming_response.rb +10 -1
- data/lib/rack/proxy/version.rb +1 -1
- data/lib/rack/proxy.rb +124 -47
- 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: 0b7446fb448e8e91e1fbc8a052fc170901649bcb3df26fbabf7741ca5b331449
|
|
4
|
+
data.tar.gz: c87d52a7ebe5e144e9254e84ebae969f3b8f4fed74c79483ce89ae9075e2b547
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b0cbe8b0bb1e23edba280b7be30259fb347f51f4c415b1f33b3052fafbd9ceeb886ee3b60a1ac2bedd4bee98730693e77ada746894dd427ed967d1aa62bae8f
|
|
7
|
+
data.tar.gz: ff6513f7e571fc101ace25a01fc81e14251e23b5fc6c11b44089c754384b0462d620f95b38324cfe9e888fd86390fd85beb3a416f85644cf5cc5f08d0011c292
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,49 @@ follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
8
8
|
|
|
9
9
|
Nothing yet.
|
|
10
10
|
|
|
11
|
+
## [2.0.0] - 2026-09-05
|
|
12
|
+
|
|
13
|
+
### Breaking changes
|
|
14
|
+
|
|
15
|
+
- Non-streaming requests no longer automatically retry transport failures.
|
|
16
|
+
This prevents replay with consumed request input; applications that require
|
|
17
|
+
retries must implement them with a fresh body and an explicit retry policy.
|
|
18
|
+
- Rack 3 response hooks receive arrays for repeated header values, including
|
|
19
|
+
`Set-Cookie`, and non-streaming statuses are integers. Update hooks that
|
|
20
|
+
assumed newline-separated headers or string statuses. Rack 2 retains its
|
|
21
|
+
newline-separated header representation.
|
|
22
|
+
- Invalid or ambiguous HTTP framing is now rejected, incomplete responses fail,
|
|
23
|
+
and uploads without a known length use fresh chunked framing. Backends must
|
|
24
|
+
accept properly framed HTTP/1.1 requests and send valid, complete responses.
|
|
25
|
+
|
|
26
|
+
### Security
|
|
27
|
+
|
|
28
|
+
- Reframe decoded request bodies without a `CONTENT_LENGTH` using chunked
|
|
29
|
+
encoding instead of sending body bytes after `Content-Length: 0`. Bound
|
|
30
|
+
known-length input streams so excess bytes cannot become another backend
|
|
31
|
+
request; malformed lengths and prematurely ended uploads return `400`.
|
|
32
|
+
- Reject ambiguous backend framing (`Transfer-Encoding` with `Content-Length`,
|
|
33
|
+
conflicting or invalid lengths, and unsupported transfer codings) with `502`.
|
|
34
|
+
- Strip response headers named by the backend's `Connection` fields as well as
|
|
35
|
+
the standard hop-by-hop headers.
|
|
36
|
+
- Enforce `max_response_length` before buffering each chunk in non-streaming
|
|
37
|
+
mode, including rejecting declared oversized responses before reading them.
|
|
38
|
+
- Detect premature EOF in fixed-length responses: return `502` before sending
|
|
39
|
+
headers, or raise while streaming so the server aborts the incomplete transfer.
|
|
40
|
+
- Disable Net::HTTP transport retries in non-streaming mode as well as streaming
|
|
41
|
+
mode, preventing replay with an already-consumed request body.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Return integer statuses in non-streaming mode and preserve multiple response
|
|
46
|
+
header values as arrays on Rack 3 (including `Set-Cookie`). Rack 2 keeps its
|
|
47
|
+
newline-separated representation.
|
|
48
|
+
- Remove entity headers forbidden by Rack on 1xx/204/304 responses. HEAD and
|
|
49
|
+
304 representation sizes no longer incorrectly trigger the response body cap.
|
|
50
|
+
- Replace a remaining live-host test with a local fixture and simulate failed
|
|
51
|
+
DNS resolution without sending external DNS queries. Add offline adversarial
|
|
52
|
+
framing, size-limit, retry, and `Rack::Lint` regression checks.
|
|
53
|
+
|
|
11
54
|
## [1.0.2] - 2026-09-01
|
|
12
55
|
|
|
13
56
|
Housekeeping — no library behavior changes. **No action is needed by users:**
|
|
@@ -195,7 +238,8 @@ or a compatible fix. See the README's "Upgrading" section for migration steps.
|
|
|
195
238
|
|
|
196
239
|
Older releases (≤ 0.7.8) predate this changelog; see the git history and tags.
|
|
197
240
|
|
|
198
|
-
[Unreleased]: https://github.com/ncr/rack-proxy/compare/
|
|
241
|
+
[Unreleased]: https://github.com/ncr/rack-proxy/compare/v2.0.0...HEAD
|
|
242
|
+
[2.0.0]: https://github.com/ncr/rack-proxy/compare/v1.0.2...v2.0.0
|
|
199
243
|
[1.0.2]: https://github.com/ncr/rack-proxy/compare/v1.0.1...v1.0.2
|
|
200
244
|
[1.0.1]: https://github.com/ncr/rack-proxy/compare/v1.0.0...v1.0.1
|
|
201
245
|
[1.0.0]: https://github.com/ncr/rack-proxy/compare/v0.8.3...v1.0.0
|
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Typical uses:
|
|
|
37
37
|
Requires Ruby >= 3.0 and Rack 2.x or 3.x. Add to your `Gemfile`:
|
|
38
38
|
|
|
39
39
|
```ruby
|
|
40
|
-
gem "rack-proxy", "~>
|
|
40
|
+
gem "rack-proxy", "~> 2.0"
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
## Quick start
|
|
@@ -116,7 +116,7 @@ Pass options when instantiating (`Rack::Proxy.new(backend: ...)`) or mounting mi
|
|
|
116
116
|
- `:read_timeout` — per-read timeout in seconds (default `60`).
|
|
117
117
|
- `:open_timeout` — connection-open timeout in seconds.
|
|
118
118
|
- `:write_timeout` — per-write timeout in seconds.
|
|
119
|
-
- `:max_response_length` — cap (in bytes) on the backend response
|
|
119
|
+
- `:max_response_length` — cap (in bytes) on the backend response body. Oversized declared lengths are refused before reading the body, and each chunk is checked before buffering or forwarding it. Non-streaming responses return `502` on overflow; streaming responses abort if overflow is discovered after sending the headers. HEAD/304 representation lengths do not count as body bytes.
|
|
120
120
|
|
|
121
121
|
### Request shaping
|
|
122
122
|
|
|
@@ -240,7 +240,7 @@ From [`examples/example_service_proxy.rb`](examples/example_service_proxy.rb):
|
|
|
240
240
|
# 1. rails new test_app
|
|
241
241
|
# 2. cd test_app
|
|
242
242
|
# 3. install Rack-Proxy in `Gemfile`
|
|
243
|
-
# a. `gem 'rack-proxy', '~>
|
|
243
|
+
# a. `gem 'rack-proxy', '~> 2.0'`
|
|
244
244
|
# 4. install gem: `bundle install`
|
|
245
245
|
# 5. copy the class into your app and mount it from `config/initializers/proxy.rb`
|
|
246
246
|
# 6. run: `SERVICE_URL=http://guides.rubyonrails.org rails server`
|
|
@@ -382,6 +382,17 @@ end
|
|
|
382
382
|
|
|
383
383
|
## Upgrading
|
|
384
384
|
|
|
385
|
+
### 1.x → 2.0.0
|
|
386
|
+
|
|
387
|
+
2.0.0 hardens HTTP framing and response limits. Ruby and Rack requirements are unchanged.
|
|
388
|
+
|
|
389
|
+
- **Automatic transport retries are disabled in both modes.** Non-streaming requests previously inherited Net::HTTP's retry behavior. If your application needs retries, use an explicit policy that considers whether the operation is safe to replay and provides a fresh request body for each attempt.
|
|
390
|
+
- **Response hooks follow Rack's types.** Statuses are integers in both modes. On Rack 3, repeated headers such as `Set-Cookie` are arrays of strings; update hooks that split these values on newlines. Rack 2 still uses newline-separated strings.
|
|
391
|
+
- **Framing errors fail explicitly.** Malformed request lengths and incomplete uploads return `400`. Ambiguous backend framing returns `502`. A truncated backend body returns `502` before headers are sent, or raises during streaming so the server aborts the transfer. Uploads without `CONTENT_LENGTH` are forwarded with chunked encoding; backends must support HTTP/1.1 chunked requests.
|
|
392
|
+
- **Response limits apply before buffering.** `max_response_length` now bounds non-streaming accumulation. HEAD and 304 representation lengths do not count toward the body cap.
|
|
393
|
+
|
|
394
|
+
Update your Gemfile to `gem "rack-proxy", "~> 2.0"` and run `bundle update rack-proxy`. Existing explicit backend and TLS options continue to work.
|
|
395
|
+
|
|
385
396
|
### 0.8.x → 1.0.0
|
|
386
397
|
|
|
387
398
|
1.0.0 is a breaking release; the full list is in [CHANGELOG.md](CHANGELOG.md). The changes most likely to need action:
|
data/SECURITY.md
CHANGED
|
@@ -7,13 +7,14 @@ threat model below alongside the "Security considerations" section of the README
|
|
|
7
7
|
|
|
8
8
|
## Supported versions
|
|
9
9
|
|
|
10
|
-
Security fixes are released for the latest major series.
|
|
11
|
-
|
|
12
|
-
upgrade to `
|
|
10
|
+
Security fixes are released for the latest major series. Older supported
|
|
11
|
+
series receive critical fixes only during their transition period — please
|
|
12
|
+
upgrade to `2.x`.
|
|
13
13
|
|
|
14
14
|
| Version | Supported |
|
|
15
15
|
| ------- | --------- |
|
|
16
|
-
|
|
|
16
|
+
| 2.0.x | ✅ |
|
|
17
|
+
| 1.0.x | critical fixes only |
|
|
17
18
|
| 0.8.x | critical fixes only |
|
|
18
19
|
| < 0.8 | ❌ |
|
|
19
20
|
|
|
@@ -135,7 +135,16 @@ module Rack
|
|
|
135
135
|
@fiber = Fiber.new do
|
|
136
136
|
session.request(request) do |res|
|
|
137
137
|
Fiber.yield res
|
|
138
|
-
|
|
138
|
+
bytes = 0
|
|
139
|
+
res.read_body do |chunk|
|
|
140
|
+
bytes += chunk.bytesize
|
|
141
|
+
Fiber.yield chunk
|
|
142
|
+
end
|
|
143
|
+
# Net::HTTP tolerates premature EOF for Content-Length bodies on
|
|
144
|
+
# supported Ruby versions. A proxy must signal an incomplete body.
|
|
145
|
+
if request.response_body_permitted? && res.class.body_permitted? && res.content_length && bytes != res.content_length
|
|
146
|
+
raise EOFError, "backend response is shorter than Content-Length"
|
|
147
|
+
end
|
|
139
148
|
end
|
|
140
149
|
:done
|
|
141
150
|
end
|
data/lib/rack/proxy/version.rb
CHANGED
data/lib/rack/proxy.rb
CHANGED
|
@@ -37,6 +37,30 @@ module Rack
|
|
|
37
37
|
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError
|
|
38
38
|
].freeze
|
|
39
39
|
|
|
40
|
+
class InvalidRequest < StandardError; end
|
|
41
|
+
|
|
42
|
+
# Net::HTTP copies a body stream until EOF, even when Content-Length is
|
|
43
|
+
# smaller. Bound the input so extra bytes cannot become a second request.
|
|
44
|
+
class RequestBodyStream
|
|
45
|
+
def initialize(input, length)
|
|
46
|
+
@input, @remaining = input, length
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def read(length, buffer = nil)
|
|
50
|
+
if @remaining.zero?
|
|
51
|
+
buffer&.clear
|
|
52
|
+
return nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
data = @input.read([length, @remaining].min, buffer)
|
|
56
|
+
raise InvalidRequest, "request body is shorter than Content-Length" if data.nil? || data.empty?
|
|
57
|
+
|
|
58
|
+
@remaining -= data.bytesize
|
|
59
|
+
data
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
private_constant :InvalidRequest, :RequestBodyStream
|
|
63
|
+
|
|
40
64
|
class << self
|
|
41
65
|
def extract_http_request_headers(env)
|
|
42
66
|
headers = env.reject do |k, v|
|
|
@@ -63,7 +87,18 @@ module Rack
|
|
|
63
87
|
|
|
64
88
|
def normalize_headers(headers)
|
|
65
89
|
mapped = headers.map do |k, v|
|
|
66
|
-
|
|
90
|
+
value = if v.is_a?(Array)
|
|
91
|
+
if v.length == 1
|
|
92
|
+
v.first
|
|
93
|
+
elsif Rack.const_defined?(:Headers, false)
|
|
94
|
+
v
|
|
95
|
+
else
|
|
96
|
+
v.join("\n")
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
v
|
|
100
|
+
end
|
|
101
|
+
[titleize(k), value]
|
|
67
102
|
end
|
|
68
103
|
build_header_hash mapped.to_h
|
|
69
104
|
end
|
|
@@ -115,8 +150,8 @@ module Rack
|
|
|
115
150
|
@open_timeout = opts[:open_timeout]
|
|
116
151
|
@write_timeout = opts[:write_timeout]
|
|
117
152
|
# Optional cap (in bytes) on the backend response size, to bound memory
|
|
118
|
-
# against a hostile/huge backend.
|
|
119
|
-
#
|
|
153
|
+
# against a hostile/huge backend. Checked before buffering each chunk in
|
|
154
|
+
# either mode, as well as against declared lengths. Default: no cap.
|
|
120
155
|
@max_response_length = opts[:max_response_length]
|
|
121
156
|
# :ssl_version pins an exact protocol and is deprecated (it forbids TLS 1.3);
|
|
122
157
|
# prefer :min_version / :max_version, which map to Net::HTTP#min_version=/#max_version=.
|
|
@@ -223,12 +258,22 @@ module Rack
|
|
|
223
258
|
# its own content-encoding.
|
|
224
259
|
target_request.instance_variable_set(:@decode_content, false) if target_request.instance_variable_defined?(:@decode_content)
|
|
225
260
|
|
|
226
|
-
#
|
|
261
|
+
# Rack supplies decoded input. Generate framing for this hop instead
|
|
262
|
+
# of forwarding the client's Transfer-Encoding or guessing a zero size.
|
|
227
263
|
if target_request.request_body_permitted? && source_request.body
|
|
228
|
-
|
|
229
|
-
|
|
264
|
+
input = source_request.body
|
|
265
|
+
input.rewind if input.respond_to?(:rewind)
|
|
266
|
+
if (length = source_request.content_length)
|
|
267
|
+
raise InvalidRequest, "invalid Content-Length" unless /\A[0-9]+\z/.match?(length)
|
|
268
|
+
|
|
269
|
+
target_request.content_length = length.to_i
|
|
270
|
+
target_request.body_stream = RequestBodyStream.new(input, length.to_i)
|
|
271
|
+
else
|
|
272
|
+
target_request.delete("Content-Length")
|
|
273
|
+
target_request["Transfer-Encoding"] = "chunked"
|
|
274
|
+
target_request.body_stream = input
|
|
275
|
+
end
|
|
230
276
|
target_request.content_type = source_request.content_type if source_request.content_type
|
|
231
|
-
target_request.body_stream.rewind if target_request.body_stream.respond_to?(:rewind)
|
|
232
277
|
end
|
|
233
278
|
|
|
234
279
|
# Use basic auth if we have to
|
|
@@ -267,64 +312,93 @@ module Rack
|
|
|
267
312
|
configure_backend_connection(http, use_ssl: use_ssl, read_timeout: read_timeout)
|
|
268
313
|
end
|
|
269
314
|
target_response.logger = @logger if @logger
|
|
315
|
+
code = target_response.code
|
|
316
|
+
headers = prepare_response_headers(target_response.headers, code, target_request)
|
|
317
|
+
if response_body_permitted?(target_request, code)
|
|
318
|
+
target_response.max_response_length = @max_response_length
|
|
319
|
+
body = target_response
|
|
320
|
+
else
|
|
321
|
+
target_response.close
|
|
322
|
+
body = []
|
|
323
|
+
end
|
|
270
324
|
else
|
|
271
325
|
http = Net::HTTP.new(backend.host, backend.port)
|
|
272
326
|
configure_backend_connection(http, use_ssl: use_ssl, read_timeout: read_timeout)
|
|
273
327
|
|
|
274
|
-
|
|
275
|
-
http.request(target_request)
|
|
328
|
+
http.start do
|
|
329
|
+
http.request(target_request) do |response|
|
|
330
|
+
code = response.code.to_i
|
|
331
|
+
headers = prepare_response_headers(response.to_hash, code, target_request)
|
|
332
|
+
body = []
|
|
333
|
+
if response_body_permitted?(target_request, code)
|
|
334
|
+
buffered = +"".b
|
|
335
|
+
response.read_body do |chunk|
|
|
336
|
+
check_response_length!(buffered.bytesize + chunk.bytesize)
|
|
337
|
+
buffered << chunk
|
|
338
|
+
end
|
|
339
|
+
if response.content_length && buffered.bytesize != response.content_length
|
|
340
|
+
raise EOFError, "backend response is shorter than Content-Length"
|
|
341
|
+
end
|
|
342
|
+
body << buffered unless buffered.empty?
|
|
343
|
+
end
|
|
344
|
+
end
|
|
276
345
|
end
|
|
277
346
|
end
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
headers = self.class.normalize_headers(target_response.respond_to?(:headers) ? target_response.headers : target_response.to_hash)
|
|
281
|
-
body = target_response.body || []
|
|
282
|
-
body = [body] unless body.respond_to?(:each)
|
|
283
|
-
rescue URI::InvalidURIError
|
|
347
|
+
rescue URI::InvalidURIError, InvalidRequest
|
|
348
|
+
target_response&.close
|
|
284
349
|
return [400, {}, []]
|
|
285
|
-
rescue *BACKEND_ERRORS => e
|
|
350
|
+
rescue *BACKEND_ERRORS, HttpStreamingResponse::ResponseTooLarge => e
|
|
351
|
+
target_response&.close
|
|
286
352
|
@logger << "rack-proxy: backend request failed: #{e.class}: #{e.message}\n" if @logger.respond_to?(:<<)
|
|
287
353
|
return [502, {}, []]
|
|
288
354
|
end
|
|
289
355
|
|
|
290
|
-
# No entity body for status codes that don't allow one (1xx, 204, 304)
|
|
291
|
-
body = [] if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY[code.to_i]
|
|
292
|
-
|
|
293
|
-
# Remove hop-by-hop header fields from the response. Use #delete (not
|
|
294
|
-
# #reject!) so the returned HeaderHash's case-insensitive index stays
|
|
295
|
-
# consistent on Rack 2 for any downstream middleware.
|
|
296
|
-
headers.keys.each { |k| headers.delete(k) if HOP_BY_HOP_HEADERS[k.downcase] }
|
|
297
|
-
|
|
298
|
-
return [502, {}, []] if response_too_large?(target_response, headers, body)
|
|
299
|
-
|
|
300
356
|
[code, headers, body]
|
|
301
357
|
end
|
|
302
358
|
|
|
303
359
|
private
|
|
304
360
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
361
|
+
def response_body_permitted?(request, code)
|
|
362
|
+
request.response_body_permitted? && !Rack::Utils::STATUS_WITH_NO_ENTITY_BODY[code] && code != 205
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def check_response_length!(length)
|
|
366
|
+
if @max_response_length && length && length > @max_response_length
|
|
367
|
+
raise HttpStreamingResponse::ResponseTooLarge, "backend response exceeded max_response_length=#{@max_response_length}"
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# Validate framing before dropping hop-by-hop fields or reading a body.
|
|
372
|
+
# Net::HTTP dechunks responses but otherwise preserves their headers.
|
|
373
|
+
def prepare_response_headers(raw_headers, code, request)
|
|
374
|
+
# Rack 2 HeaderHash#each joins arrays with newlines. Read values directly
|
|
375
|
+
# so repeated framing/Connection fields stay distinct during validation.
|
|
376
|
+
headers = self.class.build_header_hash(raw_headers.keys.map { |key| [key, raw_headers[key]] })
|
|
377
|
+
transfer_encoding = headers["Transfer-Encoding"]
|
|
378
|
+
content_length = headers["Content-Length"]
|
|
379
|
+
if transfer_encoding
|
|
380
|
+
if content_length || Array(transfer_encoding).join(",").strip.downcase != "chunked"
|
|
381
|
+
raise Net::HTTPBadResponse, "ambiguous or unsupported backend transfer encoding"
|
|
321
382
|
end
|
|
322
|
-
target_response.max_response_length = @max_response_length
|
|
323
|
-
false
|
|
324
|
-
else
|
|
325
|
-
buffered = body.sum { |part| part.to_s.bytesize }
|
|
326
|
-
declared_oversize || buffered > @max_response_length
|
|
327
383
|
end
|
|
384
|
+
if content_length
|
|
385
|
+
lengths = Array(content_length).flat_map { |value| value.split(",", -1).map(&:strip) }
|
|
386
|
+
unless lengths.all? { |value| /\A[0-9]+\z/.match?(value) } && lengths.map(&:to_i).uniq.length == 1
|
|
387
|
+
raise Net::HTTPBadResponse, "invalid backend Content-Length"
|
|
388
|
+
end
|
|
389
|
+
headers["Content-Length"] = lengths.first.to_i.to_s
|
|
390
|
+
check_response_length!(lengths.first.to_i) if response_body_permitted?(request, code)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
connection_named = Array(headers["Connection"]).join(",").downcase.split(",").map(&:strip)
|
|
394
|
+
headers.keys.each do |key|
|
|
395
|
+
headers.delete(key) if HOP_BY_HOP_HEADERS[key.downcase] || connection_named.include?(key.downcase)
|
|
396
|
+
end
|
|
397
|
+
if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY[code]
|
|
398
|
+
headers.delete("Content-Length")
|
|
399
|
+
headers.delete("Content-Type")
|
|
400
|
+
end
|
|
401
|
+
self.class.normalize_headers(headers)
|
|
328
402
|
end
|
|
329
403
|
|
|
330
404
|
# Resolve the Net::HTTP request class for an HTTP method, or nil if there is
|
|
@@ -340,6 +414,9 @@ module Rack
|
|
|
340
414
|
# TLS option — notably the VERIFY_PEER default — can never land on only one.
|
|
341
415
|
def configure_backend_connection(conn, use_ssl:, read_timeout:)
|
|
342
416
|
conn.use_ssl = use_ssl
|
|
417
|
+
# Request input need not be rewindable. A transport retry could replay an
|
|
418
|
+
# operation with an empty or partial body, so neither path retries it.
|
|
419
|
+
conn.max_retries = 0
|
|
343
420
|
conn.read_timeout = read_timeout
|
|
344
421
|
conn.open_timeout = @open_timeout if @open_timeout
|
|
345
422
|
conn.write_timeout = @write_timeout if @write_timeout
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rack-proxy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jacek Becela
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|