tp2 0.19.3 → 0.19.4

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: e0b5eac74e3e54f7dfbad1af92a110aa755c3e5ebe18229879c734baaa24e8cf
4
- data.tar.gz: ca1bf493c839517f11db5b0d346958c44bfd1aa194dbbd18e2300e9d843e8d9b
3
+ metadata.gz: 9e014b48f0e569963bc4a7c69735b7d3b2745e0300285be727e56a822302705d
4
+ data.tar.gz: 8a620cba8ce06490c6b4c4fd830ab2883b65a41a133aa86cf0f3c382610c0c1b
5
5
  SHA512:
6
- metadata.gz: bab1d252cbe06291116e6ddbb8e973e8a0b0acefd055acd52290d371629dc85abb6a4f4f5339a13bdbf20b2f1647b53517f30695ea356b0a5f9dcc30d01cb360
7
- data.tar.gz: 6c22dd2ebdb82c883f6805f2f0d456e76f5b86d84e57d2a1dfc3c8b66dbd2c36710e4f98d91607b6e73301390b3fbab078dda1e9a571cd3f832ed9e8b2efe2e7
6
+ metadata.gz: 03b13bf4d0529187cf96f862aa0bae5698e72337e39a2c820e319342dac8f9d8994f86bd66da2e9637947f330cac0c86ce0bec1fee299d92cc3430281231b3fc
7
+ data.tar.gz: 23c1881fc7db29bd5b78325a1fc7338674acb8a9db5b7cd735f0ea77e904e667312c6c49acf61b919743bdad1e375a4e519b9f517f4144b24347ab9c25d50356
@@ -8,7 +8,7 @@ jobs:
8
8
  fail-fast: false
9
9
  matrix:
10
10
  os: [ubuntu-latest]
11
- ruby: [3.4, 'head']
11
+ ruby: ['head']
12
12
 
13
13
  name: >-
14
14
  ${{matrix.os}}, ${{matrix.ruby}}
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.19.4 2026-01-26
2
+
3
+ - Fix and make graceful_shutdown idempotent
4
+ - Update dependencies
5
+
1
6
  # 0.19.3 2025-12-10
2
7
 
3
8
  - Update UringMachine
data/Gemfile CHANGED
@@ -1,12 +1,12 @@
1
- source 'https://rubygems.org'
1
+ source 'https://gem.coop'
2
2
 
3
3
  gemspec name: 'tp2'
4
4
 
5
5
  group :development do
6
- gem 'listen', '3.9.0'
7
- gem 'minitest', '5.25.4'
8
- gem 'rake', '13.2.1'
9
- gem 'rake-compiler', '1.2.9'
6
+ gem 'listen', '3.10.0'
7
+ gem 'minitest', '6.0.1'
8
+ gem 'rake', '13.3.1'
9
+ gem 'rake-compiler', '1.3.1'
10
10
  gem 'simplecov', '0.22.0'
11
- gem 'yard', '0.9.37'
11
+ gem 'yard', '0.9.38'
12
12
  end
data/examples/simple.rb CHANGED
@@ -8,9 +8,19 @@ end
8
8
 
9
9
  require 'tp2'
10
10
 
11
+ BODY = 'foobar' * 1000
11
12
  app = ->(req) {
12
- req.respond('foobar', ':status' => Qeweney::Status::TEAPOT)
13
+ req.respond(BODY)
13
14
  }
14
15
 
15
- TP2.config(&app)
16
- TP2.run
16
+ PORT = 1234
17
+
18
+ machine = UM.new
19
+ opts = {
20
+ banner: TP2::BANNER,
21
+ }
22
+ # opts[:logger] = TP2::Logger.new(machine, **opts)
23
+ env = { bind: "127.0.0.1:#{PORT}" }.merge(opts)
24
+ puts "Listening on #{env[:bind]}"
25
+ server = TP2::Server.new(machine, env) { app&.call(it) }
26
+ server.run
@@ -153,6 +153,11 @@ module TP2
153
153
 
154
154
  SEND_FLAGS = UM::MSG_NOSIGNAL | UM::MSG_WAITALL
155
155
 
156
+ EMPTY_CHUNK = "0\r\n\r\n"
157
+ EMPTY_CHUNK_LEN = EMPTY_CHUNK.bytesize
158
+
159
+ CHUNKED_ENCODING_POSTLUDE = "\r\n#{EMPTY_CHUNK}"
160
+
156
161
  # Sends response including headers and body. Waits for the request to complete
157
162
  # if not yet completed. The body is sent using chunked transfer encoding.
158
163
  # @param request [Qeweney::Request] HTTP request
@@ -165,8 +170,8 @@ module TP2
165
170
  @response_headers = headers
166
171
  request&.tx_incr(formatted_headers.bytesize + (body ? body.bytesize : 0))
167
172
  if body
168
- buf = "#{formatted_headers}#{body.bytesize.to_s(16)}\r\n#{body}\r\n#{EMPTY_CHUNK}"
169
- @machine.send(@fd, buf, buf.bytesize, SEND_FLAGS)
173
+ chunk_prelude = "#{body.bytesize.to_s(16)}\r\n"
174
+ @machine.sendv(@fd, formatted_headers, chunk_prelude, body, CHUNKED_ENCODING_POSTLUDE)
170
175
  else
171
176
  @machine.send(@fd, formatted_headers, formatted_headers.bytesize, SEND_FLAGS)
172
177
  end
@@ -187,9 +192,6 @@ module TP2
187
192
  @response_headers = headers
188
193
  end
189
194
 
190
- EMPTY_CHUNK = "0\r\n\r\n"
191
- EMPTY_CHUNK_LEN = EMPTY_CHUNK.bytesize
192
-
193
195
  # Sends a response body chunk. If no headers were sent, default headers are
194
196
  # sent using #send_headers. if the done option is true(thy), an empty chunk
195
197
  # will be sent to signal response completion to the client.
data/lib/tp2/server.rb CHANGED
@@ -50,11 +50,15 @@ module TP2
50
50
 
51
51
  def run
52
52
  setup
53
- @machine.join(*@accept_fibers)
53
+ @machine.await_fibers(@accept_fibers)
54
54
  rescue UM::Terminate
55
55
  graceful_shutdown
56
56
  end
57
57
 
58
+ def stop!
59
+ graceful_shutdown
60
+ end
61
+
58
62
  private
59
63
 
60
64
  def setup
@@ -69,7 +73,7 @@ module TP2
69
73
  setup_server_extensions
70
74
 
71
75
  # map fibers
72
- @connection_fiber_map = {}
76
+ @connection_fibers = Set.new
73
77
  end
74
78
 
75
79
  def get_bind_entries
@@ -124,47 +128,52 @@ module TP2
124
128
  end
125
129
 
126
130
  def accept_incoming(listen_fd)
127
- @machine.accept_each(listen_fd) do |fd|
128
- conn = Connection.new(self, @machine, fd, @env, &@app)
129
- f = @machine.spin(conn) do
130
- it.run
131
- ensure
132
- @connection_fiber_map.delete(f)
133
- end
134
- @connection_fiber_map[f] = true
135
- end
131
+ @machine.accept_each(listen_fd) { start_client_connection(it) }
136
132
  rescue UM::Terminate
137
133
  # terminated
138
- rescue Exception => e
139
- p e
140
- p e.backtrace
141
- exit
134
+ end
135
+
136
+ def start_client_connection(fd)
137
+ conn = Connection.new(self, @machine, fd, @env, &@app)
138
+ f = @machine.spin(conn) do
139
+ it.run
140
+ ensure
141
+ @connection_fibers.delete(f)
142
+ end
143
+ @connection_fibers << f
142
144
  end
143
145
 
144
146
  def close_all_server_fds
145
147
  @server_fds.each { @machine.close_async(it) }
146
148
  end
147
149
 
150
+ STOP = UM::Terminate.new
151
+
152
+ def stop_accept_fibers
153
+ @accept_fibers.each { @machine.schedule(it, STOP) if !it.done? }
154
+ @machine.await_fibers(@accept_fibers)
155
+ end
156
+
148
157
  def graceful_shutdown
149
158
  @env[:logger]&.info(message: 'Shutting down gracefully...')
150
159
 
151
160
  # stop listening
152
161
  close_all_server_fds
162
+ stop_accept_fibers
153
163
  @machine.snooze
154
164
 
155
- return if @connection_fiber_map.empty?
165
+ return if @connection_fibers.empty?
156
166
 
157
167
  # sleep for a bit, let requests finish
158
168
  @machine.sleep(PENDING_REQUESTS_GRACE_PERIOD)
159
- return if @connection_fiber_map.empty?
169
+ return if @connection_fibers.empty?
160
170
 
161
171
  # terminate pending fibers
162
- pending = @connection_fiber_map.keys
163
- signal = UM::Terminate.new
164
- pending.each { @machine.schedule(it, signal) }
172
+ pending = @connection_fibers.to_a
173
+ pending.each { @machine.schedule(it, STOP) }
165
174
 
166
175
  @machine.timeout(PENDING_REQUESTS_TIMEOUT_PERIOD, UM::Terminate) do
167
- @machine.join(*@connection_fiber_map.keys)
176
+ @machine.await_fibers(@connection_fibers)
168
177
  rescue UM::Terminate
169
178
  # timeout on waiting for adapters to finish running, do nothing
170
179
  end
data/lib/tp2/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module TP2
2
- VERSION = '0.19.3'
2
+ VERSION = '0.19.4'
3
3
  end
data/test/test_server.rb CHANGED
@@ -38,15 +38,13 @@ class ServerTest < Minitest::Test
38
38
 
39
39
  def run_server
40
40
  @server.run
41
- rescue STOP
42
- # ignore
43
41
  ensure
44
42
  @server_done = true
45
43
  end
46
44
 
47
45
  def teardown
48
46
  @machine.close(@client_fd) rescue nil
49
- @machine.schedule(@f_server, STOP.new)
47
+ @server.stop!
50
48
  @machine.snooze until @server_done
51
49
  end
52
50
 
@@ -113,7 +111,7 @@ class ServerTest < Minitest::Test
113
111
  write_http_request "GET /foo HTTP/1.1\r\nServer: foo.com\r\n\r\nSCHMET /bar HTTP/1.1\r\n\r\n"
114
112
 
115
113
  @machine.sleep(0.01)
116
- @machine.schedule(@f_server, UM::Terminate.new)
114
+ @server.stop!
117
115
  @machine.snooze
118
116
 
119
117
  response = read_client_side
@@ -282,9 +280,9 @@ class ServerTest < Minitest::Test
282
280
  @env[:logger] = TestLogger.new
283
281
  @app = ->(req) { reqs << req; req.respond('Hello, world!', {}) }
284
282
 
285
- write_http_request "GET / HTTP/1.0\r\n\r\n"
283
+ write_http_request "GET / HTTP/1.1\r\n\r\n"
286
284
  response = read_client_side
287
- expected = "HTTP/1.1 200\r\nContent-Length: 13\r\n\r\nHello, world!"
285
+ expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\nd\r\nHello, world!\r\n0\r\n\r\n"
288
286
  assert_equal(expected, response)
289
287
 
290
288
  entries = @env[:logger].entries
data/tp2.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.required_ruby_version = '>= 3.4'
21
21
  s.executables = ['tp2']
22
22
 
23
- s.add_dependency 'uringmachine', '~> 0.22.0'
23
+ s.add_dependency 'uringmachine', '~> 0.23.1'
24
24
  s.add_dependency 'qeweney', '~> 0.23'
25
25
  s.add_dependency 'rack', '~> 3.1.15'
26
26
  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.19.3
4
+ version: 0.19.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 0.22.0
18
+ version: 0.23.1
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 0.22.0
25
+ version: 0.23.1
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: qeweney
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -111,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
111
  - !ruby/object:Gem::Version
112
112
  version: '0'
113
113
  requirements: []
114
- rubygems_version: 4.0.0
114
+ rubygems_version: 4.0.3
115
115
  specification_version: 4
116
116
  summary: Experimental HTTP/1 server for UringMachine
117
117
  test_files: []