uringmachine 0.30.0 → 0.31.0
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 +12 -4
- data/README.md +46 -38
- data/TODO.md +56 -2
- data/benchmark/gets.rb +7 -7
- data/benchmark/gets_concurrent.rb +10 -10
- data/benchmark/http_parse.rb +14 -14
- data/benchmark/http_server_accept_queue.rb +11 -7
- data/benchmark/http_server_multi_accept.rb +7 -7
- data/benchmark/http_server_multi_ractor.rb +7 -7
- data/benchmark/http_server_single_thread.rb +7 -7
- data/benchmark/openssl.rb +50 -22
- data/docs/design/buffer_pool.md +1 -1
- data/examples/fiber_concurrency_io.rb +52 -0
- data/examples/fiber_concurrency_naive.rb +26 -0
- data/examples/fiber_concurrency_runqueue.rb +33 -0
- data/examples/io_uring_simple.c +24 -0
- data/examples/pg.rb +2 -2
- data/examples/stream.rb +2 -2
- data/ext/um/um.c +20 -3
- data/ext/um/um.h +24 -18
- data/ext/um/um_connection.c +775 -0
- data/ext/um/um_connection_class.c +394 -0
- data/ext/um/um_ssl.c +37 -2
- data/ext/um/um_utils.c +1 -1
- data/grant-2025/final-report.md +2 -0
- data/grant-2025/journal.md +1 -1
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +16 -16
- data/test/{test_stream.rb → test_connection.rb} +290 -153
- data/test/test_um.rb +18 -18
- metadata +10 -6
- data/ext/um/um_stream.c +0 -706
- data/ext/um/um_stream_class.c +0 -317
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c6af55ee77291dcf17471dde7dd9d68b731fd9a8dea4d1dccdcc09b325b9163
|
|
4
|
+
data.tar.gz: 516624d1a86287bbd0978d5aaf0cca540063ca7f6aa4cf2bab8cb9f23214d32d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e9d845525499342080228b44afb5cabb84af6bb0cb14081cc5e4e2eb627ab638312c0df818f5d109ab99d6e34a299bb21da23711f131f4a9641d688cee5cf35e
|
|
7
|
+
data.tar.gz: d67d15b398bac118b85219be3669a1479eba68e5d93ceb2e3f598238ae2fbefd7cf83dcffa132fe12ed0b0385d675c15e7f89a05150290da6fbc4d8bc40a1431
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# 0.31.0 2026-03-31
|
|
2
|
+
|
|
3
|
+
- Rework `Stream` into `Connection` class:
|
|
4
|
+
- Rename modes, improve SSL detection
|
|
5
|
+
- Rename and enhance different read methods
|
|
6
|
+
- Add `#write` method
|
|
7
|
+
- Add `#resp_write` method
|
|
8
|
+
|
|
1
9
|
# 0.30.0 2026-03-23
|
|
2
10
|
|
|
3
11
|
- Add `Stream#each`
|
|
@@ -30,7 +38,7 @@
|
|
|
30
38
|
|
|
31
39
|
# 0.28.2 2026-02-20
|
|
32
40
|
|
|
33
|
-
- Fix `Stream#
|
|
41
|
+
- Fix `Stream#read`
|
|
34
42
|
|
|
35
43
|
# 0.28.1 2026-02-20
|
|
36
44
|
|
|
@@ -169,9 +177,9 @@
|
|
|
169
177
|
|
|
170
178
|
# 2025-06-03 Version 0.12
|
|
171
179
|
|
|
172
|
-
- Add buffer, maxlen params to `Stream#
|
|
173
|
-
- Add buffer param to `Stream#
|
|
174
|
-
- Remove `Stream#
|
|
180
|
+
- Add buffer, maxlen params to `Stream#read_line`
|
|
181
|
+
- Add buffer param to `Stream#read`
|
|
182
|
+
- Remove `Stream#resp_read_line`, `Stream#resp_read` methods
|
|
175
183
|
|
|
176
184
|
# 2025-06-02 Version 0.11.1
|
|
177
185
|
|
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ implementation that allows integration with the entire Ruby ecosystem.
|
|
|
37
37
|
- Excellent performance characteristics for concurrent I/O-bound applications.
|
|
38
38
|
- `Fiber::Scheduler` implementation to automatically integrate with the Ruby
|
|
39
39
|
ecosystem in a transparent fashion.
|
|
40
|
-
-
|
|
40
|
+
- [Connection](#connections) class with automatic buffer management for reading.
|
|
41
41
|
- Optimized I/O for encrypted SSL connections.
|
|
42
42
|
|
|
43
43
|
## Design
|
|
@@ -286,64 +286,70 @@ fiber = Fiber.schedule do
|
|
|
286
286
|
end
|
|
287
287
|
```
|
|
288
288
|
|
|
289
|
-
##
|
|
289
|
+
## Connections
|
|
290
290
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
provide an API that is useful for
|
|
294
|
-
(frame-based) protocols.
|
|
291
|
+
`UringMachine::Connection` is a class designed for efficiently read from and
|
|
292
|
+
write to a socket or other file descriptor. Connections are ideal for
|
|
293
|
+
implementing the read side of protocols, and provide an API that is useful for
|
|
294
|
+
both line-based protocols and binary (frame-based) protocols.
|
|
295
295
|
|
|
296
|
-
A
|
|
297
|
-
(see also [
|
|
298
|
-
advantage of io_uring's registered
|
|
299
|
-
introduction of [incremental buffer
|
|
296
|
+
A connection is associated with a UringMachine instance and a target file
|
|
297
|
+
descriptor (or SSL socket, see also [connection modes](#connection-modes)
|
|
298
|
+
below). Behind the scenes, connections take advantage of io_uring's registered
|
|
299
|
+
buffers feature, and more recently, the introduction of [incremental buffer
|
|
300
300
|
consumption](https://github.com/axboe/liburing/wiki/What's-new-with-io_uring-in-6.11-and-6.12#incremental-provided-buffer-consumption).
|
|
301
301
|
|
|
302
|
-
When
|
|
302
|
+
When connections are used, UringMachine automatically manages the buffers it
|
|
303
303
|
provides to the kernel, maximizing buffer reuse and minimizing allocations.
|
|
304
304
|
UringMachine also responds to stress conditions (increased incoming traffic) by
|
|
305
305
|
automatically provisioning additional buffers.
|
|
306
306
|
|
|
307
|
-
To create a
|
|
307
|
+
To create a connection for a given fd, use `UM#connection`:
|
|
308
308
|
|
|
309
309
|
```ruby
|
|
310
|
-
|
|
310
|
+
conn = machine.connection(fd)
|
|
311
311
|
|
|
312
|
-
# you can also provide a block that will be passed the
|
|
313
|
-
machine.
|
|
312
|
+
# you can also provide a block that will be passed the connection instance:
|
|
313
|
+
machine.connection(fd) { |c| do_something_with(c) }
|
|
314
314
|
|
|
315
|
-
# you can also instantiate a
|
|
316
|
-
|
|
315
|
+
# you can also instantiate a connection directly:
|
|
316
|
+
conn = UM::Connection.new(machine, fd)
|
|
317
317
|
```
|
|
318
318
|
|
|
319
|
-
The following API is used to interact with the
|
|
319
|
+
The following API is used to interact with the connection:
|
|
320
320
|
|
|
321
321
|
```ruby
|
|
322
322
|
# Read until a newline character is encountered:
|
|
323
|
-
line =
|
|
323
|
+
line = conn.read_line(0)
|
|
324
324
|
|
|
325
325
|
# Read line with a maximum length of 13 bytes:
|
|
326
|
-
line =
|
|
326
|
+
line = conn.read_line(13)
|
|
327
327
|
|
|
328
328
|
# Read all data:
|
|
329
|
-
buf =
|
|
329
|
+
buf = conn.read(0)
|
|
330
330
|
|
|
331
331
|
# Read exactly 13 bytes:
|
|
332
|
-
buf =
|
|
332
|
+
buf = conn.read(13)
|
|
333
333
|
|
|
334
334
|
# Read up to 13 bytes:
|
|
335
|
-
buf =
|
|
335
|
+
buf = conn.read(-13)
|
|
336
|
+
|
|
337
|
+
# Read continuously until EOF
|
|
338
|
+
conn.read_each { |data| ... }
|
|
336
339
|
|
|
337
340
|
# Skip 3 bytes:
|
|
338
|
-
|
|
341
|
+
conn.skip(3)
|
|
342
|
+
|
|
343
|
+
# Write
|
|
344
|
+
conn.write('foo', 'bar', 'baz')
|
|
339
345
|
```
|
|
340
346
|
|
|
341
347
|
Here's an example of a how a basic HTTP request parser might be implemented
|
|
342
|
-
using a
|
|
348
|
+
using a connection:
|
|
343
349
|
|
|
344
350
|
```ruby
|
|
345
|
-
def parse_http_request_headers(
|
|
346
|
-
request_line =
|
|
351
|
+
def parse_http_request_headers(conn)
|
|
352
|
+
request_line = conn.read_line(0)
|
|
347
353
|
m = request_line.match(REQUEST_LINE_RE)
|
|
348
354
|
return nil if !m
|
|
349
355
|
|
|
@@ -354,7 +360,7 @@ def parse_http_request_headers(stream)
|
|
|
354
360
|
}
|
|
355
361
|
|
|
356
362
|
while true
|
|
357
|
-
line =
|
|
363
|
+
line = conn.read_line(0)
|
|
358
364
|
break if !line || line.empty?
|
|
359
365
|
|
|
360
366
|
m = line.match(HEADER_RE)
|
|
@@ -364,24 +370,26 @@ def parse_http_request_headers(stream)
|
|
|
364
370
|
end
|
|
365
371
|
```
|
|
366
372
|
|
|
367
|
-
###
|
|
373
|
+
### Connection modes
|
|
368
374
|
|
|
369
|
-
|
|
370
|
-
three modes:
|
|
375
|
+
Connection modes allow connections to be transport agnostic. Currently
|
|
376
|
+
connections support three modes:
|
|
371
377
|
|
|
372
|
-
- `:
|
|
378
|
+
- `:fd` - use the buffer pool, read data using multishot read
|
|
373
379
|
(this is the default mode).
|
|
374
|
-
- `:
|
|
380
|
+
- `:socket` - use the buffer pool, read data using multishot recv.
|
|
375
381
|
- `:ssl` - read from an `SSLSocket` object.
|
|
376
382
|
|
|
377
|
-
The mode is specified as an additional argument to `
|
|
383
|
+
The mode is specified as an additional argument to `Connection.new`:
|
|
378
384
|
|
|
379
385
|
```ruby
|
|
380
|
-
#
|
|
381
|
-
|
|
386
|
+
# using recv/send:
|
|
387
|
+
conn = machine.connection(fd, :socket)
|
|
382
388
|
|
|
383
|
-
#
|
|
384
|
-
|
|
389
|
+
# SSL I/O:
|
|
390
|
+
conn = machine.connection(ssl, :ssl)
|
|
391
|
+
# or simply:
|
|
392
|
+
conn = machine.connection(ssl)
|
|
385
393
|
```
|
|
386
394
|
|
|
387
395
|
## Performance
|
data/TODO.md
CHANGED
|
@@ -2,8 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
- Add tests for support for Set in `machine#await`
|
|
4
4
|
- Add tests for support for Set, Array in `machine#join`
|
|
5
|
-
- Add
|
|
6
|
-
- Add
|
|
5
|
+
- Add `UM#read_file` for reading entire file
|
|
6
|
+
- Add `UM#write_file` for writing entire file
|
|
7
|
+
- Rename stream methods: `:fd`, `:socket`, `:ssl`
|
|
8
|
+
|
|
9
|
+
## Improving streams
|
|
10
|
+
|
|
11
|
+
One wart of the stream API is that it's only for reading, so if we want to
|
|
12
|
+
implement a protocol where we read and write to a target fd, we also need to
|
|
13
|
+
keep the fd around or call `stream.target` every time we want to write to it,
|
|
14
|
+
*and* we don't have a transport-agnostic write op.
|
|
15
|
+
|
|
16
|
+
What if instead of `Stream` we had something called `Link`, which serves for
|
|
17
|
+
both reading and writing:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
conn = machine.connection(fd)
|
|
21
|
+
while l = conn.read_line
|
|
22
|
+
conn.write(l, '\n')
|
|
23
|
+
end
|
|
24
|
+
# or:
|
|
25
|
+
buf = conn.read(42)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
RESP:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
conn.resp_write(['foo', 'bar'])
|
|
32
|
+
reply = conn.resp_read
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
HTTP:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
r = conn.http_read_request
|
|
39
|
+
conn.http_write_response({ ':status' => 200 }, 'foo')
|
|
40
|
+
|
|
41
|
+
# or:
|
|
42
|
+
conn.http_write_request({ ':method' => 'GET', ':path' => '/foo' }, nil)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Plan of action:
|
|
46
|
+
|
|
47
|
+
- Rename methods:
|
|
48
|
+
- [v] rename `#read_line` to `#read_line`
|
|
49
|
+
- [v] rename `#read` to `#read`
|
|
50
|
+
- [v] rename `#read_to_delim` to `#read_to_delim`
|
|
51
|
+
- [v] rename `#each` to `#read_each`
|
|
52
|
+
- [v] rename `#resp_decode` to `#resp_read`
|
|
53
|
+
- Rename modes:
|
|
54
|
+
- [v] :fd to :fd
|
|
55
|
+
- [v] :socket to :socket
|
|
56
|
+
- [v] auto detect SSL
|
|
57
|
+
- Rename `Stream` to `Connection`
|
|
58
|
+
- Add methods:
|
|
59
|
+
- `#write(*bufs)`
|
|
60
|
+
- `#resp_write(obj)`
|
|
7
61
|
|
|
8
62
|
## Balancing I/O with the runqueue
|
|
9
63
|
|
data/benchmark/gets.rb
CHANGED
|
@@ -34,16 +34,16 @@ def um_read
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
@
|
|
38
|
-
@
|
|
39
|
-
def
|
|
40
|
-
@
|
|
37
|
+
@fd_connection = @machine.open('/dev/random', UM::O_RDONLY)
|
|
38
|
+
@conn = UM::Connection.new(@machine, @fd_connection)
|
|
39
|
+
def um_connection_read_line
|
|
40
|
+
@conn.read_line(0)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
Benchmark.ips do |x|
|
|
44
|
-
x.report('IO#gets')
|
|
45
|
-
x.report('UM#read+buf')
|
|
46
|
-
x.report('UM::
|
|
44
|
+
x.report('IO#gets') { io_gets }
|
|
45
|
+
x.report('UM#read+buf') { um_read }
|
|
46
|
+
x.report('UM::Connection') { um_connection_read_line }
|
|
47
47
|
|
|
48
48
|
x.compare!(order: :baseline)
|
|
49
49
|
end
|
|
@@ -83,40 +83,40 @@ ensure
|
|
|
83
83
|
stop_server
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
@
|
|
87
|
-
def
|
|
86
|
+
@total_connection = 0
|
|
87
|
+
def um_connection_do
|
|
88
88
|
# fd = @machine.open('/dev/random', UM::O_RDONLY)
|
|
89
89
|
fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
90
90
|
@machine.connect(fd, '127.0.0.1', 1234)
|
|
91
|
-
|
|
92
|
-
N.times { @
|
|
91
|
+
conn = UM::Connection.new(@machine, fd)
|
|
92
|
+
N.times { @total_connection += conn.read_line(0)&.bytesize || 0 }
|
|
93
93
|
rescue => e
|
|
94
94
|
p e
|
|
95
95
|
p e.backtrace
|
|
96
96
|
ensure
|
|
97
|
-
|
|
97
|
+
conn.clear
|
|
98
98
|
@machine.close(fd)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
def
|
|
101
|
+
def um_connection
|
|
102
102
|
start_server
|
|
103
103
|
ff = C.times.map {
|
|
104
104
|
@machine.snooze
|
|
105
|
-
@machine.spin {
|
|
105
|
+
@machine.spin { um_connection_do }
|
|
106
106
|
}
|
|
107
107
|
@machine.await(ff)
|
|
108
|
-
pp total: @
|
|
108
|
+
pp total: @total_connection
|
|
109
109
|
ensure
|
|
110
110
|
stop_server
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
p(C:, N:)
|
|
114
|
-
|
|
114
|
+
um_connection
|
|
115
115
|
pp @machine.metrics
|
|
116
116
|
exit
|
|
117
117
|
|
|
118
118
|
Benchmark.bm do
|
|
119
119
|
it.report('Thread/IO#gets') { io_gets }
|
|
120
120
|
it.report('Fiber/UM#read+buf') { um_read }
|
|
121
|
-
it.report('Fiber/UM::Stream') {
|
|
121
|
+
it.report('Fiber/UM::Stream') { um_connection }
|
|
122
122
|
end
|
data/benchmark/http_parse.rb
CHANGED
|
@@ -65,7 +65,7 @@ require 'stringio'
|
|
|
65
65
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/1\.1)/i
|
|
66
66
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
67
67
|
|
|
68
|
-
def
|
|
68
|
+
def read_line(fd, sio, buffer)
|
|
69
69
|
while true
|
|
70
70
|
line = sio.gets(chomp: true)
|
|
71
71
|
return line if line
|
|
@@ -76,7 +76,7 @@ def get_line(fd, sio, buffer)
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def get_request_line(fd, sio, buffer)
|
|
79
|
-
line =
|
|
79
|
+
line = read_line(fd, sio, buffer)
|
|
80
80
|
|
|
81
81
|
m = line.match(RE_REQUEST_LINE)
|
|
82
82
|
return nil if !m
|
|
@@ -96,7 +96,7 @@ def parse_headers(fd)
|
|
|
96
96
|
return nil if !headers
|
|
97
97
|
|
|
98
98
|
while true
|
|
99
|
-
line =
|
|
99
|
+
line = read_line(fd, sio, buffer)
|
|
100
100
|
break if line.empty?
|
|
101
101
|
|
|
102
102
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -129,15 +129,15 @@ ensure
|
|
|
129
129
|
($machine.close(wfd) rescue nil) if wfd
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
def
|
|
133
|
-
|
|
132
|
+
def connection_parse_headers(fd)
|
|
133
|
+
conn = UM::Connection.new($machine, fd)
|
|
134
134
|
|
|
135
135
|
buf = String.new(capacity: 65536)
|
|
136
|
-
headers =
|
|
136
|
+
headers = connection_get_request_line(conn, buf)
|
|
137
137
|
return nil if !headers
|
|
138
138
|
|
|
139
139
|
while true
|
|
140
|
-
line =
|
|
140
|
+
line = conn.read_line(0)
|
|
141
141
|
break if line.empty?
|
|
142
142
|
|
|
143
143
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -149,8 +149,8 @@ def stream_parse_headers(fd)
|
|
|
149
149
|
headers
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
def
|
|
153
|
-
line =
|
|
152
|
+
def connection_get_request_line(conn, buf)
|
|
153
|
+
line = conn.read_line(0)
|
|
154
154
|
|
|
155
155
|
m = line.match(RE_REQUEST_LINE)
|
|
156
156
|
return nil if !m
|
|
@@ -162,12 +162,12 @@ def stream_get_request_line(stream, buf)
|
|
|
162
162
|
}
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
-
def
|
|
165
|
+
def parse_http_connection
|
|
166
166
|
rfd, wfd = UM.pipe
|
|
167
167
|
queue = UM::Queue.new
|
|
168
168
|
|
|
169
169
|
$machine.spin do
|
|
170
|
-
headers =
|
|
170
|
+
headers = connection_parse_headers(rfd)
|
|
171
171
|
$machine.push(queue, headers)
|
|
172
172
|
rescue Exception => e
|
|
173
173
|
p e
|
|
@@ -188,7 +188,7 @@ def compare_allocs
|
|
|
188
188
|
p(
|
|
189
189
|
alloc_http_parser: alloc_count { x.times { parse_http_parser } },
|
|
190
190
|
alloc_stringio: alloc_count { x.times { parse_http_stringio } },
|
|
191
|
-
|
|
191
|
+
alloc_connection: alloc_count { x.times { parse_http_connection } }
|
|
192
192
|
)
|
|
193
193
|
ensure
|
|
194
194
|
GC.enable
|
|
@@ -213,8 +213,8 @@ def benchmark
|
|
|
213
213
|
x.config(:time => 5, :warmup => 3)
|
|
214
214
|
|
|
215
215
|
x.report("http_parser") { parse_http_parser }
|
|
216
|
-
x.report("stringio")
|
|
217
|
-
x.report("
|
|
216
|
+
x.report("stringio") { parse_http_stringio }
|
|
217
|
+
x.report("connection") { parse_http_connection }
|
|
218
218
|
|
|
219
219
|
x.compare!
|
|
220
220
|
end
|
|
@@ -12,8 +12,8 @@ require 'uringmachine'
|
|
|
12
12
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
13
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
line =
|
|
15
|
+
def connection_get_request_line(conn, buf)
|
|
16
|
+
line = conn.read_line(0)
|
|
17
17
|
m = line&.match(RE_REQUEST_LINE)
|
|
18
18
|
return nil if !m
|
|
19
19
|
|
|
@@ -26,12 +26,12 @@ end
|
|
|
26
26
|
|
|
27
27
|
class InvalidHeadersError < StandardError; end
|
|
28
28
|
|
|
29
|
-
def get_headers(
|
|
30
|
-
headers =
|
|
29
|
+
def get_headers(conn, buf)
|
|
30
|
+
headers = connection_get_request_line(conn, buf)
|
|
31
31
|
return nil if !headers
|
|
32
32
|
|
|
33
33
|
while true
|
|
34
|
-
line =
|
|
34
|
+
line = conn.read_line(0)
|
|
35
35
|
break if line.empty?
|
|
36
36
|
|
|
37
37
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -51,17 +51,21 @@ def send_response(machine, fd)
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def handle_connection(machine, fd)
|
|
54
|
-
|
|
54
|
+
conn = UM::Connection.new(machine, fd)
|
|
55
55
|
buf = String.new(capacity: 65536)
|
|
56
56
|
|
|
57
57
|
while true
|
|
58
|
-
headers = get_headers(
|
|
58
|
+
headers = get_headers(conn, buf)
|
|
59
59
|
break if !headers
|
|
60
60
|
|
|
61
61
|
send_response(machine, fd)
|
|
62
62
|
end
|
|
63
63
|
rescue InvalidHeadersError, SystemCallError => e
|
|
64
64
|
# ignore
|
|
65
|
+
rescue => e
|
|
66
|
+
p e
|
|
67
|
+
p e.backtrace
|
|
68
|
+
exit!
|
|
65
69
|
ensure
|
|
66
70
|
machine.close_async(fd)
|
|
67
71
|
end
|
|
@@ -12,8 +12,8 @@ require 'uringmachine'
|
|
|
12
12
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
13
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
line =
|
|
15
|
+
def connection_get_request_line(conn, buf)
|
|
16
|
+
line = conn.read_line(0)
|
|
17
17
|
m = line&.match(RE_REQUEST_LINE)
|
|
18
18
|
return nil if !m
|
|
19
19
|
|
|
@@ -26,12 +26,12 @@ end
|
|
|
26
26
|
|
|
27
27
|
class InvalidHeadersError < StandardError; end
|
|
28
28
|
|
|
29
|
-
def get_headers(
|
|
30
|
-
headers =
|
|
29
|
+
def get_headers(conn, buf)
|
|
30
|
+
headers = connection_get_request_line(conn, buf)
|
|
31
31
|
return nil if !headers
|
|
32
32
|
|
|
33
33
|
while true
|
|
34
|
-
line =
|
|
34
|
+
line = conn.read_line(0)
|
|
35
35
|
break if line.empty?
|
|
36
36
|
|
|
37
37
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -51,11 +51,11 @@ def send_response(machine, fd)
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def handle_connection(machine, fd)
|
|
54
|
-
|
|
54
|
+
conn = UM::Connection.new(machine, fd)
|
|
55
55
|
buf = String.new(capacity: 65536)
|
|
56
56
|
|
|
57
57
|
while true
|
|
58
|
-
headers = get_headers(
|
|
58
|
+
headers = get_headers(conn, buf)
|
|
59
59
|
break if !headers
|
|
60
60
|
|
|
61
61
|
send_response(machine, fd)
|
|
@@ -12,8 +12,8 @@ require 'uringmachine'
|
|
|
12
12
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
13
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
line =
|
|
15
|
+
def connection_get_request_line(conn, buf)
|
|
16
|
+
line = conn.read_line(0)
|
|
17
17
|
m = line&.match(RE_REQUEST_LINE)
|
|
18
18
|
return nil if !m
|
|
19
19
|
|
|
@@ -26,12 +26,12 @@ end
|
|
|
26
26
|
|
|
27
27
|
class InvalidHeadersError < StandardError; end
|
|
28
28
|
|
|
29
|
-
def get_headers(
|
|
30
|
-
headers =
|
|
29
|
+
def get_headers(conn, buf)
|
|
30
|
+
headers = connection_get_request_line(conn, buf)
|
|
31
31
|
return nil if !headers
|
|
32
32
|
|
|
33
33
|
while true
|
|
34
|
-
line =
|
|
34
|
+
line = conn.read_line(0)
|
|
35
35
|
break if line.empty?
|
|
36
36
|
|
|
37
37
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -53,11 +53,11 @@ end
|
|
|
53
53
|
|
|
54
54
|
def handle_connection(machine, fd)
|
|
55
55
|
machine.setsockopt(fd, UM::IPPROTO_TCP, UM::TCP_NODELAY, true)
|
|
56
|
-
|
|
56
|
+
conn = UM::Connection.new(machine, fd)
|
|
57
57
|
buf = String.new(capacity: 65536)
|
|
58
58
|
|
|
59
59
|
while true
|
|
60
|
-
headers = get_headers(
|
|
60
|
+
headers = get_headers(conn, buf)
|
|
61
61
|
break if !headers
|
|
62
62
|
|
|
63
63
|
send_response(machine, fd)
|
|
@@ -12,8 +12,8 @@ require 'uringmachine'
|
|
|
12
12
|
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
13
|
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
line =
|
|
15
|
+
def connection_get_request_line(conn, buf)
|
|
16
|
+
line = conn.read_line(0)
|
|
17
17
|
m = line&.match(RE_REQUEST_LINE)
|
|
18
18
|
return nil if !m
|
|
19
19
|
|
|
@@ -26,12 +26,12 @@ end
|
|
|
26
26
|
|
|
27
27
|
class InvalidHeadersError < StandardError; end
|
|
28
28
|
|
|
29
|
-
def get_headers(
|
|
30
|
-
headers =
|
|
29
|
+
def get_headers(conn, buf)
|
|
30
|
+
headers = connection_get_request_line(conn, buf)
|
|
31
31
|
return nil if !headers
|
|
32
32
|
|
|
33
33
|
while true
|
|
34
|
-
line =
|
|
34
|
+
line = conn.read_line(0)
|
|
35
35
|
break if line.empty?
|
|
36
36
|
|
|
37
37
|
m = line.match(RE_HEADER_LINE)
|
|
@@ -52,11 +52,11 @@ end
|
|
|
52
52
|
|
|
53
53
|
def handle_connection(machine, fd)
|
|
54
54
|
machine.setsockopt(fd, UM::IPPROTO_TCP, UM::TCP_NODELAY, true)
|
|
55
|
-
|
|
55
|
+
conn = UM::Connection.new(machine, fd)
|
|
56
56
|
buf = String.new(capacity: 65536)
|
|
57
57
|
|
|
58
58
|
while true
|
|
59
|
-
headers = get_headers(
|
|
59
|
+
headers = get_headers(conn, buf)
|
|
60
60
|
break if !headers
|
|
61
61
|
|
|
62
62
|
send_response(machine, fd)
|