julewire-rack 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0505d309f127982b88645a56045d2f472d36acb14efe781f6f08290559f8763e
4
- data.tar.gz: 00b881c32d5761ab740773b80098c9c47950ceb574e65d38f1d4c1e14d36adbe
3
+ metadata.gz: eb74d11c52a34a94a086bce1adcb38f347f12665a3c48b31e09cb2e851a66346
4
+ data.tar.gz: e44437009a72f9bb50eb48908efaa3b4825c75129dc406d20fc58f998645a434
5
5
  SHA512:
6
- metadata.gz: '078de25613aef97e313246e95e5207771a61439ed22f4b5ca97c1276593bd00863285504486275505d187fab88ab63dedbe69913b617e0e9e4c29acdc7abde7a'
7
- data.tar.gz: c35d17e54f37c11328f0582497a44084faf7f3ac28f9d5418b637d905d8327441efe71b4af276c4a88c9bc60c3e47daa27893044d8353db3a260c1b2fb34556b
6
+ metadata.gz: 7e9f9858ef76ee7f26a8b9aa59fb4c2196082285f3e9416444a33d6c6f54332e2db9938fad1a00b3a2ea5d020e7d81e322d87abe4475f7e7035bb68a256bcf57
7
+ data.tar.gz: 4ef90c074f7a6adbb0942b646fa57efd8213581cf9c5710a4d879815ae8c3ea955adfa9d7f57ce21a79baeab82562658045788564f3ad2e8d14f9e412285399e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 1.1.0 - 2026-07-20
4
+
5
+ - Cap request body capture by observed bytes, even when `Content-Length` claims
6
+ a smaller body.
7
+ - Require julewire-core 1.1.0.
8
+
9
+ ## 1.0.1 - 2026-06-25
10
+
11
+ - Require julewire-core 1.0.1.
12
+
3
13
  ## 1.0.0 - 2026-06-21
4
14
 
5
15
  - Initial release: Rack-family request lifecycle support primitives for Julewire
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.executables = []
32
32
  spec.require_paths = ["lib"]
33
33
 
34
- spec.add_dependency "julewire-core", ">= 1.0"
34
+ spec.add_dependency "julewire-core", ">= 1.1.0"
35
35
  spec.add_dependency "rack", ">= 3.2"
36
36
  spec.add_dependency "zeitwerk", ">= 2.8.1"
37
37
  end
@@ -8,7 +8,7 @@ module Julewire
8
8
  def call(response, **) = new(response, **).summary_fields
9
9
  end
10
10
 
11
- def initialize(response, content_types:, limit:, mode: Settings::STRING_BODY)
11
+ def initialize(response, content_types:, limit:, mode: nil)
12
12
  @response = response
13
13
  @content_types = content_types
14
14
  @limit = limit
@@ -30,7 +30,7 @@ module Julewire
30
30
  end
31
31
 
32
32
  capture(body_parts)
33
- return {} if @total_bytes.zero? && @captured.empty?
33
+ return {} if @total_bytes.zero?
34
34
 
35
35
  JsonBody.fields(:response, @captured, bytes: @total_bytes, truncated: @truncated, mode: @mode)
36
36
  end
@@ -38,11 +38,8 @@ module Julewire
38
38
  private
39
39
 
40
40
  def response_body_parts
41
- return unless @response.respond_to?(:stream)
42
-
43
41
  stream = @response.stream
44
42
  return if stream.respond_to?(:to_path)
45
- return unless stream.respond_to?(:to_ary)
46
43
 
47
44
  stream.to_ary
48
45
  rescue StandardError
@@ -59,9 +56,7 @@ module Julewire
59
56
  end
60
57
 
61
58
  def body_string(part)
62
- return part.to_str if part.respond_to?(:to_str)
63
-
64
- nil
59
+ part.to_str
65
60
  rescue StandardError
66
61
  nil
67
62
  end
@@ -70,7 +65,7 @@ module Julewire
70
65
  return unless body_parts.length == 1
71
66
 
72
67
  body = body_string(body_parts.first)
73
- return {} unless body && !body.empty?
68
+ return unless body && !body.empty?
74
69
 
75
70
  JsonBody.fields(:response, body, bytes: body.bytesize, truncated: false, mode: @mode)
76
71
  end
@@ -82,11 +77,6 @@ module Julewire
82
77
  return @captured << body if @limit.nil?
83
78
 
84
79
  remaining = @limit - @captured.bytesize
85
- if remaining <= 0
86
- @truncated = true if bytesize.positive?
87
- return
88
- end
89
-
90
80
  @captured << body.byteslice(0, remaining)
91
81
  @truncated = true if bytesize > remaining
92
82
  end
@@ -15,21 +15,21 @@ module Julewire
15
15
 
16
16
  class << self
17
17
  def build(selector)
18
- return new(true) if selector == true
19
18
  return unless selector
20
19
 
21
- new(Array(selector).to_h { [normalize_name(it), true] })
20
+ allowed = Array(selector).to_h { [normalize_name(it), true] } unless selector == true
21
+ new(allowed)
22
22
  end
23
23
 
24
24
  def normalize_name(name) = name.to_s.tr("_", "-").downcase
25
25
  end
26
26
 
27
- def initialize(selection)
28
- @selection = selection
27
+ def initialize(allowed)
28
+ @allowed = allowed
29
29
  end
30
30
 
31
31
  def include?(name)
32
- @selection == true ? !SENSITIVE_HEADER_SET.key?(name) : @selection.key?(name)
32
+ @allowed ? @allowed.key?(name) : !SENSITIVE_HEADER_SET.key?(name)
33
33
  end
34
34
  end
35
35
  end
@@ -27,28 +27,46 @@ module Julewire
27
27
  private
28
28
 
29
29
  def capture_body
30
- body = bounded_body
31
- bytes = content_length || body.bytesize
30
+ body, bytes = bounded_body
32
31
  captured, truncated = capture(body, total_bytes: bytes)
33
32
  [captured, bytes, truncated]
34
33
  rescue StandardError
35
34
  nil
36
35
  end
37
36
 
37
+ def captured_body_bytes(body, trust_actual_bytes:)
38
+ actual_bytes = body.bytesize
39
+ return actual_bytes if trust_actual_bytes && actual_bytes <= @limit
40
+
41
+ [content_length, actual_bytes].compact.max
42
+ end
43
+
38
44
  def bounded_body
39
- return request_body if @limit.nil?
45
+ return uncapped_body unless @limit
40
46
 
41
47
  length = content_length
42
- return request_body if length && length <= @limit
48
+ return trusted_body if length && length <= @limit
49
+
50
+ body, position = read_body_stream(limit: @limit + 1)
51
+ [body, captured_body_bytes(body, trust_actual_bytes: position&.zero?)]
52
+ end
53
+
54
+ def uncapped_body
55
+ body = request_body
56
+ [body, body.bytesize]
57
+ end
43
58
 
44
- read_body_stream(limit: @limit + 1)
59
+ def trusted_body
60
+ body = request_body
61
+ [body, captured_body_bytes(body, trust_actual_bytes: true)]
45
62
  end
46
63
 
47
64
  def request_body
48
65
  # Rack/Rails may already buffer raw_post; the byte cap applies after that read.
49
66
  return @request.raw_post if @request.respond_to?(:raw_post)
50
67
 
51
- read_body_stream(limit: nil)
68
+ body, = read_body_stream(limit: nil)
69
+ body
52
70
  end
53
71
 
54
72
  def read_body_stream(limit:)
@@ -61,7 +79,7 @@ module Julewire
61
79
  ensure
62
80
  restore_body_stream(io, original_position)
63
81
  end
64
- body.to_str
82
+ [body.to_str, original_position]
65
83
  end
66
84
 
67
85
  def body_stream_position(io)
@@ -30,7 +30,7 @@ module Julewire
30
30
  private
31
31
 
32
32
  def validate_body(value)
33
- return value if CAPTURE_BODY_VALUES.include?(value)
33
+ return if CAPTURE_BODY_VALUES.include?(value)
34
34
 
35
35
  raise Error, "body must be false, true, or :json"
36
36
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Julewire
4
4
  module Rack
5
- VERSION = "1.0.0"
5
+ VERSION = "1.1.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: julewire-rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Grebennik
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '1.0'
18
+ version: 1.1.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '1.0'
25
+ version: 1.1.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rack
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -95,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  requirements: []
98
- rubygems_version: 4.0.14
98
+ rubygems_version: 4.0.16
99
99
  specification_version: 4
100
100
  summary: Rack request lifecycle support for Julewire.
101
101
  test_files: []