File: http11.rb

package info (click to toggle)
ruby-async-http 0.94.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 916 kB
  • sloc: ruby: 5,224; javascript: 40; makefile: 4
file content (185 lines) | stat: -rwxr-xr-x 3,966 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2017-2025, by Samuel Williams.
# Copyright, 2018, by Janko Marohnić.
# Copyright, 2023, by Thomas Morgan.
# Copyright, 2023, by Josh Huber.
# Copyright, 2024, by Anton Zhuravsky.

require "async/http/protocol/http11"
require "async/http/a_protocol"

# Custom error class to track in tests
class BodyWriteError < StandardError; end

# A custom body class that raises during enumeration.
class ErrorProneBody < ::Protocol::HTTP::Body::Readable
	def initialize(...)
		super(...)
		
		@error = nil
	end
	
	attr :error
	
	def close(error = nil)
		@error = error
		super()
	end
	
	def each
		super
		raise BodyWriteError, "error during write"
	end
end

describe Async::HTTP::Protocol::HTTP11 do
	it_behaves_like Async::HTTP::AProtocol
	
	with "#as_json" do
		include Sus::Fixtures::Async::HTTP::ServerContext
		let(:protocol) {subject}
		
		it "generates a JSON representation" do
			response = client.get("/")
			connection = response.connection
			
			expect(connection.as_json).to be =~ /Async::HTTP::Protocol::HTTP1::Client negotiated HTTP/
		ensure
			response&.close
		end
		
		it "generates a JSON string" do
			response = client.get("/")
			connection = response.connection
			
			expect(JSON.dump(connection)).to be == connection.to_json
		ensure
			response&.close
		end
	end
	
	with "server" do
		include Sus::Fixtures::Async::HTTP::ServerContext
		
		let(:protocol) {subject}
		
		with "error during body write" do
			let(:body) {ErrorProneBody.new}
			
			let(:app) do
				Protocol::HTTP::Middleware.for do |request|
					# Return a response with a body that will raise during enumeration:
					Protocol::HTTP::Response[200, {}, body]
				end
			end
			
			it "handles error in ensure block without NameError" do
				response = client.get("/")
				
				expect do
					response.read
				end.to raise_exception(EOFError)
				
				expect(body.error).to be_a(BodyWriteError)
			end
		end
		
		with "bad requests" do
			def around
				current = Console.logger.level
				Console.logger.fatal!
				
				super
			ensure
				Console.logger.level = current
			end
			
			it "should fail cleanly when path is empty" do
				response = client.get("")
				
				expect(response.status).to be == 400
			end
		end
		
		with "head request" do
			let(:app) do
				Protocol::HTTP::Middleware.for do |request|
					Protocol::HTTP::Response[200, {}, ["Hello", "World"]]
				end
			end
			
			it "doesn't reply with body" do
				5.times do
					response = client.head("/")
					
					expect(response).to be(:success?)
					expect(response.version).to be == "HTTP/1.1"
					expect(response.body).to be(:empty?)
					expect(response.reason).to be == "OK"
					
					response.read
				end
			end
		end
		
		with "raw response" do
			let(:app) do
				Protocol::HTTP::Middleware.for do |request|
					peer = request.hijack!
					
					peer.write(
						"#{request.version} 200 It worked!\r\n" +
						"connection: close\r\n" +
						"\r\n" +
						"Hello World!"
					)
					peer.close
					
					nil
				end
			end
			
			it "reads raw response" do
				response = client.get("/")
				
				expect(response.read).to be == "Hello World!"
			end
			
			it "has access to the http reason phrase" do
				response = client.head("/")
				
				expect(response.reason).to be == "It worked!"
			end
		end
		
		with "full hijack with empty response" do
			let(:body) {::Protocol::HTTP::Body::Buffered.new([], 0)}
			
			let(:app) do
				::Protocol::HTTP::Middleware.for do |request|
					peer = request.hijack!
					
					peer.write(
						"#{request.version} 200 It worked!\r\n" +
						"connection: close\r\n" +
						"\r\n" +
						"Hello World!"
					)
					peer.close
					
					::Protocol::HTTP::Response[-1, {}, body]
				end
			end
			
			it "works properly" do
				expect(body).to receive(:close)
				
				response = client.get("/")
				
				expect(response.read).to be == "Hello World!"
			end
		end
	end
end