Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
- Reuse `define_dynamic_method` and `define_maybe_yield` methods in `RageController::API` from `Rage::Internal` by [@numice](https://github.com/numice) (#273).
- Add the `form_actions` router configuration (#278).

### Fixed

- [OpenAPI] Fix SystemStackError in Alba parser with circular associations (#268).
- Rewind `rack.input` when parsing request body (#279).

## [1.23.0] - 2026-04-15

### Fixed
Expand All @@ -29,7 +34,6 @@

### Fixed

- [OpenAPI] Fix SystemStackError in Alba parser with circular associations.
- Only parse request body as multipart if the request is multipart by [p8](https://github.com/p8) (#256).

## [1.22.0] - 2026-03-12
Expand Down
4 changes: 2 additions & 2 deletions lib/rage/params_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ def self.prepare(env, url_params)
end

request_params = if content_type.start_with?("application/json")
json_parse(env["rack.input"].read)
json_parse(env["rack.input"].tap { |io| io.rewind }.read)
elsif content_type.start_with?("application/x-www-form-urlencoded")
Iodine::Rack::Utils.parse_urlencoded_nested_query(env["rack.input"].read)
Iodine::Rack::Utils.parse_urlencoded_nested_query(env["rack.input"].tap { |io| io.rewind }.read)
elsif content_type.start_with?("multipart/form-data")
Iodine::Rack::Utils.parse_multipart(env["rack.input"], content_type)
end
Expand Down
50 changes: 49 additions & 1 deletion spec/params_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"multipart/form-data; boundary=--aa123"
end
end
let(:rack_input) { instance_double(StringIO, read: body) }
let(:rack_input) { instance_double(StringIO, read: body, rewind: 0) }

let(:env) do
{
Expand Down Expand Up @@ -330,4 +330,52 @@
end
end
end

context "when rack.input has been consumed by middleware" do
let(:url_params) { {} }

context "with json body" do
let(:raw_body) { '{"id":5,"name":"test"}' }
let(:rack_input) { StringIO.new(raw_body) }
let(:env) do
{
"IODINE_HAS_BODY" => true,
"QUERY_STRING" => "",
"CONTENT_TYPE" => "application/json",
"rack.input" => rack_input
}
end

before do
rack_input.read
allow(JSON).to receive(:parse).with(raw_body, symbolize_names: true).and_return({ id: 5, name: "test" })
end

it "can still parse the body after rewind" do
expect(subject).to eq({ id: 5, name: "test" })
end
end

context "with urlencoded body" do
let(:raw_body) { "id=15&name=test" }
let(:rack_input) { StringIO.new(raw_body) }
let(:env) do
{
"IODINE_HAS_BODY" => true,
"QUERY_STRING" => "",
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
"rack.input" => rack_input
}
end

before do
rack_input.read
allow(Iodine::Rack::Utils).to receive(:parse_urlencoded_nested_query).with(raw_body).and_return({ id: "15", name: "test" })
end

it "can still parse the body after rewind" do
expect(subject).to eq({ id: "15", name: "test" })
end
end
end
end
Loading