tp2 0.9 → 0.11
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/bin/tp2 +3 -2
- data/lib/tp2/http1_adapter.rb +43 -5
- data/lib/tp2/request_extensions.rb +6 -3
- data/lib/tp2/version.rb +1 -1
- data/test/test_http1_adapter.rb +53 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7794469f473ba02c2feb32c638c8659536b5b04b32e1b93479af7638e76e1019
|
4
|
+
data.tar.gz: 567e71e0a872ee5a5abcaa00208b149e2b59a69fe3e42f1e73687e3a79a036f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 21ffae9b2a103464dcfc23eae43020df47173c3ff0c8354ec57755dd45607ae56c9b0be14f95291f2b7ae1c2f768b3981553a3a2bee590ec83aa2497f1b2e631
|
7
|
+
data.tar.gz: fb203df62bba5e961bcbf380117eaec22c249dd7ed1615e4f6ce59d287ebb4f2901aad30dd8ac0759295882d4b6f9dbdea2e0f594298d9e4972d1e8a6e9a881f
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# Version 0.11 2025-06-13
|
2
|
+
|
3
|
+
- Improve logging for protocol errors
|
4
|
+
- Enable YJIT when running bin/tp2
|
5
|
+
|
6
|
+
# Version 0.10 2025-06-10
|
7
|
+
|
8
|
+
- Fix static file serving
|
9
|
+
- Fix silent mode
|
10
|
+
|
1
11
|
# Version 0.9 2025-06-09
|
2
12
|
|
3
13
|
- Use `#close_async` for closing sockets
|
data/bin/tp2
CHANGED
@@ -31,8 +31,8 @@ parser = OptionParser.new do |o|
|
|
31
31
|
}
|
32
32
|
|
33
33
|
o.on("-s", "--silent", "Silent mode") {
|
34
|
-
opts[:banner] =
|
35
|
-
opts[:log] =
|
34
|
+
opts[:banner] = nil
|
35
|
+
opts[:log] = nil
|
36
36
|
}
|
37
37
|
|
38
38
|
o.on("-h", "--help", "Show this help message") do
|
@@ -90,4 +90,5 @@ end
|
|
90
90
|
|
91
91
|
detect_app(opts) if !opts[:app_type]
|
92
92
|
|
93
|
+
RubyVM::YJIT.enable rescue nil
|
93
94
|
TP2.run(opts)
|
data/lib/tp2/http1_adapter.rb
CHANGED
@@ -32,6 +32,8 @@ module TP2
|
|
32
32
|
# we're done
|
33
33
|
when SystemCallError
|
34
34
|
@opts[:log]&.log("Encountered #{e.class}, closing connection")
|
35
|
+
when ProtocolError
|
36
|
+
@opts[:log]&.log("Protocol error (#{e.message}), closing connection")
|
35
37
|
when StandardError
|
36
38
|
@opts[:log]&.log("Unhandled exception #{e.class}: #{e.message}, closing connection")
|
37
39
|
@opts[:log]&.log(e.backtrace.join("\n"))
|
@@ -128,6 +130,9 @@ module TP2
|
|
128
130
|
@response_headers = headers
|
129
131
|
end
|
130
132
|
|
133
|
+
EMPTY_CHUNK = "0\r\n\r\n"
|
134
|
+
EMPTY_CHUNK_LEN = EMPTY_CHUNK.bytesize
|
135
|
+
|
131
136
|
# Sends a response body chunk. If no headers were sent, default headers are
|
132
137
|
# sent using #send_headers. if the done option is true(thy), an empty chunk
|
133
138
|
# will be sent to signal response completion to the client.
|
@@ -138,7 +143,7 @@ module TP2
|
|
138
143
|
def send_chunk(request, chunk, done: false)
|
139
144
|
data = +''
|
140
145
|
data << "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n" if chunk
|
141
|
-
data <<
|
146
|
+
data << EMPTY_CHUNK if done
|
142
147
|
return if data.empty?
|
143
148
|
|
144
149
|
request.tx_incr(data.bytesize)
|
@@ -149,9 +154,6 @@ module TP2
|
|
149
154
|
end
|
150
155
|
end
|
151
156
|
|
152
|
-
EMPTY_CHUNK = "0\r\n\r\n"
|
153
|
-
EMPTY_CHUNK_LEN = EMPTY_CHUNK.bytesize
|
154
|
-
|
155
157
|
# Finishes the response to the current request. If no headers were sent,
|
156
158
|
# default headers are sent using #send_headers.
|
157
159
|
# @return [void]
|
@@ -164,6 +166,42 @@ module TP2
|
|
164
166
|
end
|
165
167
|
end
|
166
168
|
|
169
|
+
def respond_with_static_file(req, path, opts, cache_headers)
|
170
|
+
fd = @machine.open(path, UM::O_RDONLY)
|
171
|
+
opts ||= {}
|
172
|
+
if opts[:headers]
|
173
|
+
opts[:headers].merge!(cache_headers)
|
174
|
+
else
|
175
|
+
opts[:headers] = cache_headers
|
176
|
+
end
|
177
|
+
|
178
|
+
maxlen = opts[:max_len] || 65536
|
179
|
+
buf = String.new(capacity: maxlen)
|
180
|
+
headers_sent = nil
|
181
|
+
while true
|
182
|
+
res = @machine.read(fd, buf, maxlen, 0)
|
183
|
+
if res < maxlen && !headers_sent
|
184
|
+
return respond(req, buf, opts[:headers])
|
185
|
+
elsif res == 0
|
186
|
+
return finish(req)
|
187
|
+
end
|
188
|
+
|
189
|
+
if !headers_sent
|
190
|
+
send_headers(req, opts[:headers])
|
191
|
+
headers_sent = true
|
192
|
+
end
|
193
|
+
done = res < maxlen
|
194
|
+
send_chunk(req, buf, done: done)
|
195
|
+
return if done
|
196
|
+
end
|
197
|
+
ensure
|
198
|
+
@machine.close(fd) if fd
|
199
|
+
end
|
200
|
+
|
201
|
+
def close
|
202
|
+
@machine.close_async(@fd)
|
203
|
+
end
|
204
|
+
|
167
205
|
private
|
168
206
|
|
169
207
|
def log(request, response_headers)
|
@@ -241,7 +279,7 @@ module TP2
|
|
241
279
|
def read(len, buf = nil, raise_on_eof = true)
|
242
280
|
str = @stream.get_string(buf, len)
|
243
281
|
if !str && raise_on_eof
|
244
|
-
raise ProtocolError,
|
282
|
+
raise ProtocolError, 'Missing data'
|
245
283
|
end
|
246
284
|
|
247
285
|
str
|
@@ -1,9 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Qeweney::Request
|
4
|
-
def
|
5
|
-
|
4
|
+
def respond_with_static_file(path, etag, last_modified, opts)
|
5
|
+
cache_headers = (etag || last_modified) ? {
|
6
|
+
'etag' => etag,
|
7
|
+
'last-modified' => last_modified,
|
8
|
+
} : {}
|
6
9
|
|
7
|
-
|
10
|
+
adapter.respond_with_static_file(self, path, opts, cache_headers)
|
8
11
|
end
|
9
12
|
end
|
data/lib/tp2/version.rb
CHANGED
data/test/test_http1_adapter.rb
CHANGED
@@ -520,4 +520,57 @@ class HTTP1AdapterTest < Minitest::Test
|
|
520
520
|
assert_equal expected, response
|
521
521
|
assert count >= chunk_count
|
522
522
|
end
|
523
|
+
|
524
|
+
def test_static_file_serving
|
525
|
+
fn = "/tmp/tp2-#{rand(1000)}"
|
526
|
+
IO.write(fn, 'foobar')
|
527
|
+
|
528
|
+
@hook = ->(req) do
|
529
|
+
req.respond_with_static_file(fn, nil, nil, nil)
|
530
|
+
req.adapter.close
|
531
|
+
end
|
532
|
+
|
533
|
+
response = +''
|
534
|
+
count = 0
|
535
|
+
|
536
|
+
write_client_side("GET / HTTP/1.1\r\n\r\n")
|
537
|
+
@machine.spin { @adapter.serve_request }
|
538
|
+
|
539
|
+
while (data = read_client_side(65536))
|
540
|
+
response << data
|
541
|
+
count += 1
|
542
|
+
@machine.snooze
|
543
|
+
end
|
544
|
+
|
545
|
+
content = IO.read(__FILE__)
|
546
|
+
file_size = content.bytesize
|
547
|
+
expected = "HTTP/1.1 200\r\nContent-Length: 6\r\n\r\nfoobar"
|
548
|
+
|
549
|
+
assert_equal expected, response
|
550
|
+
end
|
551
|
+
|
552
|
+
def test_static_file_serving_big
|
553
|
+
fn = "/tmp/tp2-#{rand(1000)}"
|
554
|
+
IO.write(fn, 'foobar')
|
555
|
+
|
556
|
+
@hook = ->(req) do
|
557
|
+
req.respond_with_static_file(fn, nil, nil, { max_len: 3 })
|
558
|
+
req.adapter.close
|
559
|
+
end
|
560
|
+
|
561
|
+
response = +''
|
562
|
+
count = 0
|
563
|
+
|
564
|
+
write_client_side("GET / HTTP/1.1\r\n\r\n")
|
565
|
+
@machine.spin { @adapter.serve_request }
|
566
|
+
|
567
|
+
while (data = read_client_side(65536))
|
568
|
+
response << data
|
569
|
+
count += 1
|
570
|
+
@machine.snooze
|
571
|
+
end
|
572
|
+
|
573
|
+
expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n"
|
574
|
+
assert_equal expected, response
|
575
|
+
end
|
523
576
|
end
|