Skip to content

Commit

Permalink
Add support for as_json to avoid the default Rails implementation.
Browse files Browse the repository at this point in the history
The default Rails implementation will try to convert all instance
variables `as_json` which is inappropriate in some cases and can cause
`Stack level too deep (SystemStackError)`.

Fixes <socketry/async-http#156>.
  • Loading branch information
ioquatix committed Apr 16, 2024
1 parent 5ec23da commit 3f12d69
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 0 deletions.
10 changes: 10 additions & 0 deletions lib/protocol/http/body/readable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ def join
return buffer
end
end

def as_json
{
class: self.class.name,
length: self.length,
stream: self.stream?,
ready: self.ready?,
empty: self.empty?
}
end
end
end
end
Expand Down
7 changes: 7 additions & 0 deletions lib/protocol/http/body/wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ def read
@body.read
end

def as_json
{
class: self.class.name,
body: @body&.as_json
}
end

def inspect
@body.inspect
end
Expand Down
2 changes: 2 additions & 0 deletions lib/protocol/http/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ def to_h
end
end

alias as_json to_h

def inspect
"#<#{self.class} #{@fields.inspect}>"
end
Expand Down
13 changes: 13 additions & 0 deletions lib/protocol/http/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ def idempotent?
@method != Methods::POST && (@body.nil? || @body.empty?)
end

def as_json
{
scheme: @scheme,
authority: @authority,
method: @method,
path: @path,
version: @version,
headers: @headers&.as_json,
body: @body&.as_json,
protocol: @protocol
}
end

def to_s
"#{@scheme}://#{@authority}: #{@method} #{@path} #{@version}"
end
Expand Down
10 changes: 10 additions & 0 deletions lib/protocol/http/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ def self.for_exception(exception)
Response[500, Headers['content-type' => 'text/plain'], ["#{exception.class}: #{exception.message}"]]
end

def as_json
{
version: @version,
status: @status,
headers: @headers&.as_json,
body: @body&.as_json,
protocol: @protocol
}
end

def to_s
"#{@status} #{@version}"
end
Expand Down
9 changes: 9 additions & 0 deletions test/protocol/http/body/wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,13 @@
expect(message.body).to be_a(Protocol::HTTP::Body::Wrapper)
end
end

with "#as_json" do
it "generates a JSON representation" do
expect(body.as_json).to have_keys(
class: be == "Protocol::HTTP::Body::Wrapper",
body: be == source.as_json
)
end
end
end
15 changes: 15 additions & 0 deletions test/protocol/http/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@
)
end

with "#as_json" do
it "generates a JSON representation" do
expect(request.as_json).to be == {
scheme: "http",
authority: "localhost",
method: "GET",
path: "/index.html",
version: "HTTP/1.0",
headers: headers.as_json,
body: nil,
protocol: nil
}
end
end

it "should not be HEAD" do
expect(request).not.to be(:head?)
end
Expand Down
29 changes: 29 additions & 0 deletions test/protocol/http/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
InformationalResponse = Sus::Shared("informational response") do
it "should be informational" do
expect(response).to be(:informational?)
expect(response.as_json).to have_keys(status: be_within(100...200))
end

it "should not be a failure" do
Expand All @@ -23,6 +24,7 @@
SuccessfulResponse = Sus::Shared("successful response") do
it "should be successful" do
expect(response).to be(:success?)
expect(response.as_json).to have_keys(status: be_within(200...300))
end

it "should be final" do
Expand All @@ -45,6 +47,7 @@

it "should be a redirection" do
expect(response).to be(:redirection?)
expect(response.as_json).to have_keys(status: be_within(300...400))
end

it "should not be informational" do
Expand All @@ -71,6 +74,7 @@

it "should be a failure" do
expect(response).to be(:failure?)
expect(response.as_json).to have_keys(status: be_within(400...600))
end
end

Expand Down Expand Up @@ -99,6 +103,18 @@
)
end

with "#as_json" do
it "generates a JSON representation" do
expect(response.as_json).to have_keys(
version: be == "HTTP/1.1",
status: be == 100,
headers: be == headers.as_json,
body: be == nil,
protocol: be == nil,
)
end
end

it_behaves_like InformationalResponse

it "should be a continue" do
Expand Down Expand Up @@ -143,6 +159,7 @@
end

with "200 OK" do
let(:body) {Protocol::HTTP::Body::Buffered.wrap("Hello, World!")}
let(:response) {subject.new("HTTP/1.0", 200, headers, body)}

it "should have attributes" do
Expand All @@ -155,6 +172,18 @@
)
end

with "#as_json" do
it "generates a JSON representation" do
expect(response.as_json).to have_keys(
version: be == "HTTP/1.0",
status: be == 200,
headers: be == headers.as_json,
body: be == body.as_json,
protocol: be == nil,
)
end
end

it_behaves_like SuccessfulResponse

it "should be ok" do
Expand Down

0 comments on commit 3f12d69

Please sign in to comment.