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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fa7578335fe3be94277b5e3ddbb4417e72dd911a73cbc914607177b1b58107f
4
- data.tar.gz: e07b7f54b2056418051529db6a6f0e6dffddbbd87266f33cf15806fddb48485d
3
+ metadata.gz: 7794469f473ba02c2feb32c638c8659536b5b04b32e1b93479af7638e76e1019
4
+ data.tar.gz: 567e71e0a872ee5a5abcaa00208b149e2b59a69fe3e42f1e73687e3a79a036f9
5
5
  SHA512:
6
- metadata.gz: 54b5a8c620e014a83d2955a19de91ea6945ea6c456ec8040367778c44f33f99da6d079d00737cd68201a82461655f5dbd9c35ab34ee8da3a349fc8624d3fd4f1
7
- data.tar.gz: 0ca8cb386203f8c91fe7c5d62a0fe79f332863f5c97145f65459c12650033dc1bbf4781c988070868ad760a7844350f45ba645ec8ad2891e03e73b0b485504d5
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] = false
35
- opts[:log] = false
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)
@@ -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 << "0\r\n\r\n" if done
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, "Missing data"
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 serve_io(io, opts)
5
- # TODO: implement using UM
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
- raise NotImplementedError
10
+ adapter.respond_with_static_file(self, path, opts, cache_headers)
8
11
  end
9
12
  end
data/lib/tp2/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module TP2
2
- VERSION = '0.9'
2
+ VERSION = '0.11'
3
3
  end
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tp2
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.9'
4
+ version: '0.11'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner