Skip to content

Commit 6de0e56

Browse files
authored
Add support for as_json to avoid the default Rails implementation. (#53)
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>.
1 parent 5ec23da commit 6de0e56

File tree

8 files changed

+95
-0
lines changed

8 files changed

+95
-0
lines changed

lib/protocol/http/body/readable.rb

+10
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ def join
9292
return buffer
9393
end
9494
end
95+
96+
def as_json
97+
{
98+
class: self.class.name,
99+
length: self.length,
100+
stream: self.stream?,
101+
ready: self.ready?,
102+
empty: self.empty?
103+
}
104+
end
95105
end
96106
end
97107
end

lib/protocol/http/body/wrapper.rb

+7
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ def read
5151
@body.read
5252
end
5353

54+
def as_json
55+
{
56+
class: self.class.name,
57+
body: @body&.as_json
58+
}
59+
end
60+
5461
def inspect
5562
@body.inspect
5663
end

lib/protocol/http/headers.rb

+2
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ def to_h
299299
end
300300
end
301301

302+
alias as_json to_h
303+
302304
def inspect
303305
"#<#{self.class} #{@fields.inspect}>"
304306
end

lib/protocol/http/request.rb

+13
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,19 @@ def idempotent?
7373
@method != Methods::POST && (@body.nil? || @body.empty?)
7474
end
7575

76+
def as_json
77+
{
78+
scheme: @scheme,
79+
authority: @authority,
80+
method: @method,
81+
path: @path,
82+
version: @version,
83+
headers: @headers&.as_json,
84+
body: @body&.as_json,
85+
protocol: @protocol
86+
}
87+
end
88+
7689
def to_s
7790
"#{@scheme}://#{@authority}: #{@method} #{@path} #{@version}"
7891
end

lib/protocol/http/response.rb

+10
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ def self.for_exception(exception)
104104
Response[500, Headers['content-type' => 'text/plain'], ["#{exception.class}: #{exception.message}"]]
105105
end
106106

107+
def as_json
108+
{
109+
version: @version,
110+
status: @status,
111+
headers: @headers&.as_json,
112+
body: @body&.as_json,
113+
protocol: @protocol
114+
}
115+
end
116+
107117
def to_s
108118
"#{@status} #{@version}"
109119
end

test/protocol/http/body/wrapper.rb

+9
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,13 @@
6363
expect(message.body).to be_a(Protocol::HTTP::Body::Wrapper)
6464
end
6565
end
66+
67+
with "#as_json" do
68+
it "generates a JSON representation" do
69+
expect(body.as_json).to have_keys(
70+
class: be == "Protocol::HTTP::Body::Wrapper",
71+
body: be == source.as_json
72+
)
73+
end
74+
end
6675
end

test/protocol/http/request.rb

+15
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@
2525
)
2626
end
2727

28+
with "#as_json" do
29+
it "generates a JSON representation" do
30+
expect(request.as_json).to be == {
31+
scheme: "http",
32+
authority: "localhost",
33+
method: "GET",
34+
path: "/index.html",
35+
version: "HTTP/1.0",
36+
headers: headers.as_json,
37+
body: nil,
38+
protocol: nil
39+
}
40+
end
41+
end
42+
2843
it "should not be HEAD" do
2944
expect(request).not.to be(:head?)
3045
end

test/protocol/http/response.rb

+29
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
InformationalResponse = Sus::Shared("informational response") do
1414
it "should be informational" do
1515
expect(response).to be(:informational?)
16+
expect(response.as_json).to have_keys(status: be_within(100...200))
1617
end
1718

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

2830
it "should be final" do
@@ -45,6 +47,7 @@
4547

4648
it "should be a redirection" do
4749
expect(response).to be(:redirection?)
50+
expect(response.as_json).to have_keys(status: be_within(300...400))
4851
end
4952

5053
it "should not be informational" do
@@ -71,6 +74,7 @@
7174

7275
it "should be a failure" do
7376
expect(response).to be(:failure?)
77+
expect(response.as_json).to have_keys(status: be_within(400...600))
7478
end
7579
end
7680

@@ -99,6 +103,18 @@
99103
)
100104
end
101105

106+
with "#as_json" do
107+
it "generates a JSON representation" do
108+
expect(response.as_json).to have_keys(
109+
version: be == "HTTP/1.1",
110+
status: be == 100,
111+
headers: be == headers.as_json,
112+
body: be == nil,
113+
protocol: be == nil,
114+
)
115+
end
116+
end
117+
102118
it_behaves_like InformationalResponse
103119

104120
it "should be a continue" do
@@ -143,6 +159,7 @@
143159
end
144160

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

148165
it "should have attributes" do
@@ -155,6 +172,18 @@
155172
)
156173
end
157174

175+
with "#as_json" do
176+
it "generates a JSON representation" do
177+
expect(response.as_json).to have_keys(
178+
version: be == "HTTP/1.0",
179+
status: be == 200,
180+
headers: be == headers.as_json,
181+
body: be == body.as_json,
182+
protocol: be == nil,
183+
)
184+
end
185+
end
186+
158187
it_behaves_like SuccessfulResponse
159188

160189
it "should be ok" do

0 commit comments

Comments
 (0)