http3 0.0.4 → 0.0.5
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 +46 -0
- data/LICENSE-DEPENDENCIES.txt +2 -2
- data/README.md +76 -17
- data/ext/http3/extconf.rb +8 -5
- data/ext/http3/nghttp3.c +1 -1
- data/ext/http3/nghttp3.h +3 -1
- data/ext/http3/nghttp3_callbacks.c +8 -10
- data/ext/http3/nghttp3_connection.c +1 -1
- data/ext/http3/nghttp3_qpack.c +2 -2
- data/ext/http3/session.c +234 -9
- data/lib/http3/body_pipe.rb +160 -0
- data/lib/http3/body_stream.rb +120 -0
- data/lib/http3/rack_adapter.rb +114 -47
- data/lib/http3/request.rb +4 -2
- data/lib/http3/server.rb +402 -62
- data/lib/http3/session.rb +458 -89
- data/lib/http3/version.rb +1 -1
- data/lib/http3/worker_pool.rb +71 -0
- data/lib/http3.rb +3 -0
- data/sig/http3/body_pipe.rbs +45 -0
- data/sig/http3/body_stream.rbs +41 -0
- data/sig/http3/rack_adapter.rbs +21 -11
- data/sig/http3/request.rbs +5 -3
- data/sig/http3/response.rbs +2 -2
- data/sig/http3/server.rbs +8 -1
- data/sig/http3/session.rbs +104 -25
- data/sig/http3/worker_pool.rbs +19 -0
- metadata +9 -6
- data/.clang-format +0 -1
- data/.vscode/extensions.json +0 -6
- data/.vscode/settings.json +0 -10
data/lib/http3/version.rb
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTP3
|
|
4
|
+
# Threads that run the jobs posted to them, one at a time each:
|
|
5
|
+
#
|
|
6
|
+
# pool = HTTP3::WorkerPool.new(3)
|
|
7
|
+
# pool.start
|
|
8
|
+
# pool.post { ... }
|
|
9
|
+
# pool.stop(1) # => the threads still running after a second
|
|
10
|
+
#
|
|
11
|
+
# The pool does not count the free threads: Server posts only as many
|
|
12
|
+
# jobs as there are, so none waits for long.
|
|
13
|
+
class WorkerPool
|
|
14
|
+
# The priority of the threads. MRI shortens a thread's time slice of
|
|
15
|
+
# 100 ms by its negative priority, halving it for each step, so at -3,
|
|
16
|
+
# the least, a worker busy on the CPU keeps the thread that drives the
|
|
17
|
+
# connections waiting for the GVL 12.5 ms at a time. Which thread gets
|
|
18
|
+
# the GVL next does not change.
|
|
19
|
+
WORKER_PRIORITY = -3
|
|
20
|
+
|
|
21
|
+
def initialize(size)
|
|
22
|
+
@size = size
|
|
23
|
+
@jobs = Thread::Queue.new
|
|
24
|
+
@threads = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Make the threads, named "http3-worker-1" and on.
|
|
28
|
+
def start
|
|
29
|
+
@threads = Array.new(@size) do |i|
|
|
30
|
+
Thread.new { work }.tap do |thread|
|
|
31
|
+
thread.name = "http3-worker-#{i + 1}"
|
|
32
|
+
thread.priority = WORKER_PRIORITY
|
|
33
|
+
thread.report_on_exception = false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Queue job for the next free thread, without waiting.
|
|
40
|
+
def post(&job)
|
|
41
|
+
@jobs << job
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Drop the jobs not started, let each thread end once its job is done,
|
|
46
|
+
# and wait up to timeout seconds for them all. Returns the number of
|
|
47
|
+
# threads still running.
|
|
48
|
+
def stop(timeout)
|
|
49
|
+
@jobs.clear
|
|
50
|
+
@jobs.close
|
|
51
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
|
|
52
|
+
@threads.count do |thread|
|
|
53
|
+
thread.join([deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC), 0].max).nil?
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def work
|
|
60
|
+
while (job = @jobs.pop)
|
|
61
|
+
begin
|
|
62
|
+
job.call
|
|
63
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
64
|
+
# A job reports its own failures. Even exit in one leaves the
|
|
65
|
+
# thread for the next.
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/http3.rb
CHANGED
|
@@ -5,8 +5,11 @@ require_relative "http3/http3"
|
|
|
5
5
|
|
|
6
6
|
require_relative "http3/request"
|
|
7
7
|
require_relative "http3/response"
|
|
8
|
+
require_relative "http3/body_pipe"
|
|
9
|
+
require_relative "http3/body_stream"
|
|
8
10
|
require_relative "http3/session"
|
|
9
11
|
require_relative "http3/rack_adapter"
|
|
12
|
+
require_relative "http3/worker_pool"
|
|
10
13
|
require_relative "http3/server"
|
|
11
14
|
|
|
12
15
|
module HTTP3
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module HTTP3
|
|
2
|
+
# The stream a response was being sent on is gone (reset by the client,
|
|
3
|
+
# or asked by it for no more (STOP_SENDING), its connection ended, the
|
|
4
|
+
# server stopped, or the response was aborted after waiting on its client
|
|
5
|
+
# for response_timeout).
|
|
6
|
+
class StreamClosed < IOError
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module Body
|
|
10
|
+
# A response body written by the thread that runs the application and
|
|
11
|
+
# taken by the server's I/O loop. wake is called, with no lock held,
|
|
12
|
+
# each time the body goes from having nothing to take to having
|
|
13
|
+
# something.
|
|
14
|
+
class Pipe
|
|
15
|
+
def initialize: (capacity: Integer, wake: ^() -> void) -> void
|
|
16
|
+
|
|
17
|
+
# Queue chunk, waiting while capacity bytes or more are queued.
|
|
18
|
+
# Returns its size. Raises StreamClosed once the body was given up on,
|
|
19
|
+
# and IOError once it has ended.
|
|
20
|
+
def write: (String chunk) -> Integer
|
|
21
|
+
|
|
22
|
+
# End the body and wait until all of it has been taken.
|
|
23
|
+
def finish: () -> nil
|
|
24
|
+
|
|
25
|
+
# End the body with error, once what is queued has been taken, and
|
|
26
|
+
# wait until it has. Returns true once error has been taken, and false
|
|
27
|
+
# when it never will: the body had ended or been given up on (fail
|
|
28
|
+
# does nothing then), or was given up on while fail waited.
|
|
29
|
+
def fail: (Exception error) -> bool
|
|
30
|
+
|
|
31
|
+
# Wait until every chunk queued has been taken.
|
|
32
|
+
def flush: () -> self
|
|
33
|
+
|
|
34
|
+
# A chunk; once they have all been taken, :eof, or the exception given
|
|
35
|
+
# to #fail; nil while there is nothing yet. Never waits.
|
|
36
|
+
def take: () -> (String | :eof | Exception | nil)
|
|
37
|
+
|
|
38
|
+
# True when #take has something other than nil to return.
|
|
39
|
+
def ready?: () -> bool
|
|
40
|
+
|
|
41
|
+
# Give up on the body: the writer's calls raise StreamClosed.
|
|
42
|
+
def cancel: () -> nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module HTTP3
|
|
2
|
+
module Body
|
|
3
|
+
# The stream a streaming body (Rack 3's call(stream)) is called with:
|
|
4
|
+
# writes go to a Body::Pipe, reads come from the request body. Its
|
|
5
|
+
# methods behave as IO's do. Once the stream has gone away (the client
|
|
6
|
+
# reset it or sent STOP_SENDING, or it was aborted after
|
|
7
|
+
# response_timeout), write and flush raise StreamClosed, and so do
|
|
8
|
+
# close_write and close, once;
|
|
9
|
+
# writing past length, the content-length, fails the pipe and raises
|
|
10
|
+
# StreamClosed too.
|
|
11
|
+
class Stream
|
|
12
|
+
def initialize: (pipe: Pipe, input: StringIO | ::File, length: Integer?) -> void
|
|
13
|
+
|
|
14
|
+
# Read from the request body, as IO#read does. Raises IOError after
|
|
15
|
+
# close_read.
|
|
16
|
+
def read: (?Integer? length, ?String outbuf) -> String?
|
|
17
|
+
|
|
18
|
+
# Write each of objects, as a String, waiting while the pipe is full.
|
|
19
|
+
# Returns the bytes written. Raises IOError after close_write.
|
|
20
|
+
def write: (*_ToS objects) -> Integer
|
|
21
|
+
|
|
22
|
+
def <<: (_ToS object) -> self
|
|
23
|
+
|
|
24
|
+
# Wait until what has been written has been taken to be sent. Raises
|
|
25
|
+
# IOError after close_write.
|
|
26
|
+
def flush: () -> self
|
|
27
|
+
|
|
28
|
+
# Close the request body; may be called again.
|
|
29
|
+
def close_read: () -> nil
|
|
30
|
+
|
|
31
|
+
# End the body, and wait until all of it has been taken; fail the pipe
|
|
32
|
+
# instead if it is short of length. May be called again, and does
|
|
33
|
+
# nothing then.
|
|
34
|
+
def close_write: () -> nil
|
|
35
|
+
|
|
36
|
+
def close: () -> nil
|
|
37
|
+
|
|
38
|
+
def closed?: () -> bool
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/sig/http3/rack_adapter.rbs
CHANGED
|
@@ -13,18 +13,28 @@ module HTTP3
|
|
|
13
13
|
# What :authority, or host, must look like.
|
|
14
14
|
AUTHORITY: Regexp
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
# A body read with each, and a streaming body, go through a Body::Pipe
|
|
17
|
+
# of pipe_capacity bytes, whose wake is wake.
|
|
18
|
+
def initialize: (untyped app, pipe_capacity: Integer, wake: ^() -> void) -> void
|
|
17
19
|
|
|
18
|
-
# Answers, without calling the application,
|
|
19
|
-
# 400 a request whose scheme is not http or
|
|
20
|
-
# fragment, or whose authority is malformed or
|
|
21
|
-
# field. Raises what the application raises, and
|
|
22
|
-
# answer that cannot be sent (among them a
|
|
23
|
-
# a content-length that is malformed or
|
|
24
|
-
# not have); the Rack body is closed
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
|
|
20
|
+
# Returns [response, fill]. Answers, without calling the application,
|
|
21
|
+
# CONNECT with 501, and with 400 a request whose scheme is not http or
|
|
22
|
+
# https, whose path has a fragment, or whose authority is malformed or
|
|
23
|
+
# differs from its host field. Raises what the application raises, and
|
|
24
|
+
# InvalidResponse for an answer that cannot be sent (among them a
|
|
25
|
+
# status outside 200..599, and a content-length that is malformed or
|
|
26
|
+
# that a file or array body does not have); the Rack body is closed
|
|
27
|
+
# either way. fill is nil, the Rack body closed already, unless the
|
|
28
|
+
# response sends a body that is read with each or is a streaming body
|
|
29
|
+
# (call): then the response has a Body::Pipe, and fill writes the body
|
|
30
|
+
# to it, fails it once the body passes content-length, ends short of
|
|
31
|
+
# it or raises (or, with each, yields a chunk that is not a String),
|
|
32
|
+
# and closes the Rack body. A streaming body is called with a
|
|
33
|
+
# Body::Stream whose read reads the request body, and ends when call
|
|
34
|
+
# returns. fill returns nil, or an exception the body raised that did
|
|
35
|
+
# not abort its stream, the body having ended or the server having
|
|
36
|
+
# given up on it, for the caller to log.
|
|
37
|
+
def call: (Request request, remote: Addrinfo) -> [Response, (^() -> Exception?)?]
|
|
28
38
|
|
|
29
39
|
# The 400s and 501s #call answered since the last call, as
|
|
30
40
|
# {status => count}; the count starts over.
|
data/sig/http3/request.rbs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
module HTTP3
|
|
2
2
|
# A request as a Session put it together. headers holds [name, value]
|
|
3
|
-
# pairs with lowercase names, the pseudo-headers first.
|
|
3
|
+
# pairs with lowercase names, the pseudo-headers first. input is the
|
|
4
|
+
# body, a binary IO at its start, which becomes rack.input: a StringIO,
|
|
5
|
+
# or a temporary File once the body is past request_body_buffer_size.
|
|
4
6
|
class Request < Data
|
|
5
7
|
attr_reader stream_id: Integer
|
|
6
8
|
attr_reader headers: Array[[String, String]]
|
|
7
|
-
attr_reader
|
|
9
|
+
attr_reader input: StringIO | ::File
|
|
8
10
|
|
|
9
|
-
def self.new: (stream_id: Integer, headers: Array[[String, String]],
|
|
11
|
+
def self.new: (stream_id: Integer, headers: Array[[String, String]], input: StringIO | ::File) -> Request
|
|
10
12
|
end
|
|
11
13
|
end
|
data/sig/http3/response.rbs
CHANGED
|
@@ -3,9 +3,9 @@ module HTTP3
|
|
|
3
3
|
class Response < Data
|
|
4
4
|
attr_reader status: Integer
|
|
5
5
|
attr_reader headers: Array[[String, String]]
|
|
6
|
-
attr_reader body: Body::File | Body::Chunks | nil
|
|
6
|
+
attr_reader body: Body::File | Body::Chunks | Body::Pipe | nil
|
|
7
7
|
|
|
8
|
-
def self.new: (status: Integer, headers: Array[[String, String]], body: Body::File | Body::Chunks | nil) -> Response
|
|
8
|
+
def self.new: (status: Integer, headers: Array[[String, String]], body: Body::File | Body::Chunks | Body::Pipe | nil) -> Response
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
module Body
|
data/sig/http3/server.rbs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module HTTP3
|
|
2
2
|
# An HTTP/3 server for a Rack 3 application on one UDP address. #run
|
|
3
3
|
# serves until SIGTERM or SIGINT, then drains; SIGHUP reloads the
|
|
4
|
-
# certificate and key.
|
|
4
|
+
# certificate and key. The application runs on threads worker threads.
|
|
5
5
|
class Server
|
|
6
6
|
ALPN: Array[String]
|
|
7
7
|
SIGNALS: Hash[String, String]
|
|
@@ -20,14 +20,21 @@ module HTTP3
|
|
|
20
20
|
?settings: QUIC::Settings?,
|
|
21
21
|
?qlog_dir: (String | _ToPath)?,
|
|
22
22
|
?max_connections: Integer,
|
|
23
|
+
?threads: Integer,
|
|
23
24
|
?max_request_body: Integer,
|
|
24
25
|
?max_field_section_size: Integer,
|
|
25
26
|
?max_connection_request_buffer: Integer,
|
|
26
27
|
?max_total_request_buffer: Integer,
|
|
28
|
+
?request_body_buffer_size: Integer,
|
|
29
|
+
?max_connection_request_spool: Integer,
|
|
30
|
+
?max_total_request_spool: Integer,
|
|
31
|
+
?max_connection_request_spool_files: Integer,
|
|
32
|
+
?max_total_request_spool_files: Integer,
|
|
27
33
|
?send_buffer_size: Integer,
|
|
28
34
|
?max_connection_send_buffer: Integer,
|
|
29
35
|
?max_total_send_buffer: Integer,
|
|
30
36
|
?request_timeout: (Integer | Float)?,
|
|
37
|
+
?response_timeout: (Integer | Float)?,
|
|
31
38
|
?refusal_log_interval: Integer | Float,
|
|
32
39
|
?shutdown_timeout: Numeric,
|
|
33
40
|
?logger: Logger
|
data/sig/http3/session.rbs
CHANGED
|
@@ -5,6 +5,9 @@ module HTTP3
|
|
|
5
5
|
# What .shared_buffer returns, to be passed to .attach as it is.
|
|
6
6
|
type shared_buffer = Object
|
|
7
7
|
|
|
8
|
+
# What .shared_spool returns, to be passed to .attach as it is.
|
|
9
|
+
type shared_spool = Object
|
|
10
|
+
|
|
8
11
|
# What a field counts for besides its name and value (RFC 9114 section
|
|
9
12
|
# 4.2.2).
|
|
10
13
|
FIELD_OVERHEAD: Integer
|
|
@@ -12,64 +15,127 @@ module HTTP3
|
|
|
12
15
|
# Take over quic_conn's streams. Requests over max_request_body bytes of
|
|
13
16
|
# body get 413, over max_field_section_size bytes of headers 431, as
|
|
14
17
|
# soon as they pass the limit; the rest of them is not read. Requests
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
18
|
+
# may hold max_connection_request_buffer bytes, and those of every
|
|
19
|
+
# Session sharing shared_buffer its limit; more gets 503. One not in
|
|
20
|
+
# full within request_timeout seconds of its headers gets 408, and one
|
|
21
|
+
# not handed out within request_timeout seconds of its end 503
|
|
22
|
+
# (#expire_requests); nil is no limit. A body past
|
|
23
|
+
# request_body_buffer_size bytes goes to a temporary file, all of it
|
|
24
|
+
# (.spool_file). The files may hold max_connection_request_spool bytes
|
|
25
|
+
# and be max_connection_request_spool_files many; those of every
|
|
26
|
+
# Session sharing shared_spool count against its limits. More, or a
|
|
27
|
+
# file that cannot be made or written, gets 503. A request counts until
|
|
28
|
+
# its stream closes, or, once handed out, until #finish, though its
|
|
29
|
+
# stream or connection has gone. A response whose body has waited on
|
|
30
|
+
# its client for response_timeout seconds is aborted
|
|
31
|
+
# (#expire_responses): a limit on a stall, not on the whole response;
|
|
32
|
+
# nil is no limit. The caller calls #expire_requests and
|
|
33
|
+
# #expire_responses every turn, and waits no longer than
|
|
34
|
+
# #next_request_deadline and #next_response_deadline.
|
|
19
35
|
def self.attach: (QUIC::Connection::Server quic_conn, max_request_body: Integer, max_field_section_size: Integer,
|
|
20
36
|
?max_connection_request_buffer: Integer, ?shared_buffer: shared_buffer?,
|
|
21
|
-
?request_timeout: (Integer | Float)?
|
|
37
|
+
?request_timeout: (Integer | Float)?, ?request_body_buffer_size: Integer,
|
|
38
|
+
?max_connection_request_spool: Integer, ?max_connection_request_spool_files: Integer,
|
|
39
|
+
?shared_spool: shared_spool?, ?response_timeout: (Integer | Float)?) -> Session
|
|
22
40
|
|
|
23
41
|
# A limit of limit bytes for the requests of every Session given it.
|
|
24
42
|
def self.shared_buffer: (Integer limit) -> shared_buffer
|
|
25
43
|
|
|
44
|
+
# Limits of limit bytes in temporary files, and of files open at once,
|
|
45
|
+
# for the requests of every Session given it.
|
|
46
|
+
def self.shared_spool: (Integer limit, files: Integer) -> shared_spool
|
|
47
|
+
|
|
48
|
+
# A temporary file without a name in Dir.tmpdir, in binary mode.
|
|
49
|
+
# Raises SystemCallError when none can be had.
|
|
50
|
+
def self.spool_file: () -> ::File
|
|
51
|
+
|
|
26
52
|
def self._attach: (QUIC::Connection::Server quic_conn, Integer max_field_section_size) -> Session
|
|
27
53
|
|
|
28
|
-
#
|
|
29
|
-
# (408, 413, 431, 503)
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
54
|
+
# The requests refused since the last call, each with the status to
|
|
55
|
+
# answer it with directly (408, 413, 431, 503). One comes out as soon
|
|
56
|
+
# as it is refused, with neither headers nor body.
|
|
57
|
+
def take_refusals: () -> Array[[Request, Integer]]
|
|
58
|
+
|
|
59
|
+
# The next request received in full, for the application, or nil. The
|
|
60
|
+
# caller closes its input, and calls #finish, once done with it; what
|
|
61
|
+
# it holds counts until then, even after its stream has closed.
|
|
62
|
+
def next_request: () -> Request?
|
|
63
|
+
|
|
64
|
+
# The caller is done with a request #next_request handed out: what it
|
|
65
|
+
# held no longer counts, though its stream or connection has gone.
|
|
66
|
+
# Calling it again does nothing.
|
|
67
|
+
def finish: (Integer stream_id) -> void
|
|
68
|
+
|
|
69
|
+
# True while the request on stream_id can still be answered: its stream
|
|
70
|
+
# is open, the client has neither reset it nor asked for no more of it
|
|
71
|
+
# (STOP_SENDING), it has not been refused, and it has not been answered.
|
|
72
|
+
def answerable?: (Integer stream_id) -> bool
|
|
73
|
+
|
|
74
|
+
# The first exception a temporary file raised since the last call (its
|
|
75
|
+
# request got 503), or nil.
|
|
76
|
+
def take_spool_error: () -> Exception?
|
|
33
77
|
|
|
34
78
|
# Refuse with 408 the requests still coming in whose request_timeout
|
|
35
|
-
# has passed at now (CLOCK_MONOTONIC)
|
|
79
|
+
# has passed at now (CLOCK_MONOTONIC), and with 503 those that have
|
|
80
|
+
# waited that long to be handed out.
|
|
36
81
|
def expire_requests: (Float now) -> void
|
|
37
82
|
|
|
38
|
-
# When the next request still coming in
|
|
83
|
+
# When the next request still coming in, or waiting to be handed out,
|
|
84
|
+
# runs out of time, or nil.
|
|
39
85
|
def next_request_deadline: () -> Float?
|
|
40
86
|
|
|
87
|
+
# Abort with H3_REQUEST_CANCELLED the responses whose body has waited
|
|
88
|
+
# on its client response_timeout seconds or more at now
|
|
89
|
+
# (CLOCK_MONOTONIC), cancelling a Body::Pipe, whose writer then gets
|
|
90
|
+
# StreamClosed. Returns how many.
|
|
91
|
+
def expire_responses: (Float now) -> Integer
|
|
92
|
+
|
|
93
|
+
# When the next response waiting on its client runs out of time, or nil.
|
|
94
|
+
def next_response_deadline: () -> Float?
|
|
95
|
+
|
|
41
96
|
# Send response. Yields the exception of a file body whose descriptor
|
|
42
97
|
# could not be duplicated; its stream is aborted. Raises
|
|
43
98
|
# Nghttp3::InvalidStateError for a request already answered.
|
|
44
99
|
def respond: (Request request, Response response) ?{ (SystemCallError error) -> void } -> void
|
|
45
100
|
|
|
46
|
-
# Queue more of each Body::Chunks body, up to limit
|
|
47
|
-
# stream, connection_limit on the connection, and room
|
|
48
|
-
# (nil for no limit); a chunk may pass a limit.
|
|
49
|
-
# queued. Yields the request (its pseudo-headers
|
|
50
|
-
# and exception of a body that raised.
|
|
101
|
+
# Queue more of each Body::Chunks and Body::Pipe body, up to limit
|
|
102
|
+
# unsent bytes per stream, connection_limit on the connection, and room
|
|
103
|
+
# bytes queued now (nil for no limit); a chunk may pass a limit.
|
|
104
|
+
# Returns the bytes queued. Yields the request (its pseudo-headers
|
|
105
|
+
# only, without body) and exception of a body that raised or failed.
|
|
51
106
|
def fill_bodies: (Integer limit, ?connection_limit: Integer?, ?room: Integer?) ?{ (Request request, Exception error) -> void } -> Integer
|
|
52
107
|
|
|
108
|
+
# True when a Body::Chunks body, or a Body::Pipe with something to
|
|
109
|
+
# take, could take more.
|
|
53
110
|
def wants_fill?: (Integer limit, ?connection_limit: Integer?, ?room: Integer?) -> bool
|
|
54
111
|
|
|
55
|
-
# Bytes of the Body::Chunks bodies queued but not taken
|
|
112
|
+
# Bytes of the Body::Chunks and Body::Pipe bodies queued but not taken
|
|
113
|
+
# by nghttp3 yet.
|
|
56
114
|
def unsent_total: () -> Integer
|
|
57
115
|
|
|
58
|
-
# True while a request stream is open
|
|
116
|
+
# True while a request stream is open, or a request waits to be handed
|
|
117
|
+
# out.
|
|
59
118
|
def busy?: () -> bool
|
|
60
119
|
|
|
61
|
-
# Let go of closed streams
|
|
62
|
-
# side of the requests the client reset
|
|
63
|
-
# (H3_REQUEST_INCOMPLETE)
|
|
120
|
+
# Let go of closed streams (a Body::Pipe is cancelled); yields a body
|
|
121
|
+
# close that raised. Resets our side of the requests the client reset
|
|
122
|
+
# before their end (H3_REQUEST_INCOMPLETE), and of those it reset once
|
|
123
|
+
# they had come in full, while they waited to be handed out or once
|
|
124
|
+
# handed out and not answered (H3_REQUEST_CANCELLED).
|
|
64
125
|
def release_closed: () ?{ (Integer stream_id, Exception error) -> void } -> void
|
|
65
126
|
|
|
66
|
-
#
|
|
127
|
+
# The connection is gone: let go of the nghttp3 connection and, of each
|
|
128
|
+
# body, of what nghttp3 has not taken (a file body's descriptor
|
|
129
|
+
# included), then close every body still held. Afterwards submit_response
|
|
130
|
+
# and stop_reading raise Nghttp3::InvalidStateError, as on a failed
|
|
131
|
+
# connection, and push_chunk, push_eof and attach_file as on an aborted
|
|
132
|
+
# stream. What the requests handed out hold counts until #finish.
|
|
67
133
|
def release_all: () ?{ (Integer stream_id, Exception error) -> void } -> void
|
|
68
134
|
|
|
69
135
|
# Called inside nghttp3, or inside the QUIC connection's packet
|
|
70
136
|
# handling, where submit_response, stop_reading, push_chunk, push_eof,
|
|
71
|
-
# attach_file, shutdown_notice and abort_stream (and so respond)
|
|
72
|
-
# Nghttp3::InvalidStateError.
|
|
137
|
+
# attach_file, shutdown_notice and abort_stream (and so respond), and
|
|
138
|
+
# release_all, raise Nghttp3::InvalidStateError.
|
|
73
139
|
def on_begin_headers: (Integer id) -> void
|
|
74
140
|
def on_header: (Integer id, String name, String value) -> void
|
|
75
141
|
def on_fields_too_large: (Integer id) -> void
|
|
@@ -78,19 +144,32 @@ module HTTP3
|
|
|
78
144
|
def on_end_stream: (Integer id) -> void
|
|
79
145
|
# The client reset its side of the stream.
|
|
80
146
|
def on_reset: (Integer id) -> void
|
|
147
|
+
# The client asked for no more of the response (STOP_SENDING).
|
|
148
|
+
def on_stop_sending: (Integer id) -> void
|
|
81
149
|
|
|
82
150
|
# The C part.
|
|
83
151
|
def submit_response: (Integer stream_id, Array[[String, String]] headers, bool has_body) -> self
|
|
84
152
|
# Read no more of the request on stream_id (STOP_SENDING with
|
|
85
153
|
# H3_NO_ERROR); for one answered before it has arrived in full.
|
|
86
154
|
def stop_reading: (Integer stream_id) -> self
|
|
155
|
+
# These three raise Nghttp3::InvalidStateError once the stream has been
|
|
156
|
+
# aborted.
|
|
87
157
|
def push_chunk: (Integer stream_id, String str) -> self
|
|
88
158
|
def push_eof: (Integer stream_id) -> self
|
|
89
159
|
def attach_file: (Integer stream_id, IO io, Integer offset, Integer length) -> self
|
|
90
160
|
def unsent_bytes: (Integer stream_id) -> Integer
|
|
161
|
+
# Since when (CLOCK_MONOTONIC seconds) the response body on stream_id
|
|
162
|
+
# has waited on the client with nothing moving: no acknowledgement of
|
|
163
|
+
# it, no wider flow control window for it, and, while that window does
|
|
164
|
+
# not hold it back, no acknowledgement of any body on the connection.
|
|
165
|
+
# nil while none of it waits, without a body, and once aborted.
|
|
166
|
+
def response_stalled_since: (Integer stream_id) -> Float?
|
|
91
167
|
def take_closed: () -> Array[Integer]
|
|
92
168
|
def take_error: () -> Exception?
|
|
93
169
|
def shutdown_notice: () -> self
|
|
170
|
+
# Stop the stream in both directions with app_error_code. What of the
|
|
171
|
+
# body nghttp3 has not taken is let go at once (unsent_bytes no longer
|
|
172
|
+
# counts it), and a file body's descriptor closed.
|
|
94
173
|
def abort_stream: (Integer stream_id, Integer app_error_code) -> self
|
|
95
174
|
end
|
|
96
175
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module HTTP3
|
|
2
|
+
# Threads that run the jobs posted to them, one at a time each.
|
|
3
|
+
class WorkerPool
|
|
4
|
+
# The priority of the threads, the least MRI has.
|
|
5
|
+
WORKER_PRIORITY: Integer
|
|
6
|
+
|
|
7
|
+
def initialize: (Integer size) -> void
|
|
8
|
+
|
|
9
|
+
# Make the threads, named "http3-worker-1" and on.
|
|
10
|
+
def start: () -> self
|
|
11
|
+
|
|
12
|
+
# Queue a job for the next free thread, without waiting.
|
|
13
|
+
def post: () { () -> void } -> self
|
|
14
|
+
|
|
15
|
+
# Drop the jobs not started, and wait up to timeout seconds for the
|
|
16
|
+
# threads to end. Returns the number still running.
|
|
17
|
+
def stop: (Numeric timeout) -> Integer
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: http3
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yusuke Nakamura
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.0.
|
|
32
|
+
version: 0.0.6
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.0.
|
|
39
|
+
version: 0.0.6
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: rack
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -75,9 +75,6 @@ extensions:
|
|
|
75
75
|
- ext/http3/extconf.rb
|
|
76
76
|
extra_rdoc_files: []
|
|
77
77
|
files:
|
|
78
|
-
- ".clang-format"
|
|
79
|
-
- ".vscode/extensions.json"
|
|
80
|
-
- ".vscode/settings.json"
|
|
81
78
|
- CHANGELOG.md
|
|
82
79
|
- CODE_OF_CONDUCT.md
|
|
83
80
|
- LICENSE-DEPENDENCIES.txt
|
|
@@ -94,13 +91,18 @@ files:
|
|
|
94
91
|
- ext/http3/nghttp3_settings.c
|
|
95
92
|
- ext/http3/session.c
|
|
96
93
|
- lib/http3.rb
|
|
94
|
+
- lib/http3/body_pipe.rb
|
|
95
|
+
- lib/http3/body_stream.rb
|
|
97
96
|
- lib/http3/rack_adapter.rb
|
|
98
97
|
- lib/http3/request.rb
|
|
99
98
|
- lib/http3/response.rb
|
|
100
99
|
- lib/http3/server.rb
|
|
101
100
|
- lib/http3/session.rb
|
|
102
101
|
- lib/http3/version.rb
|
|
102
|
+
- lib/http3/worker_pool.rb
|
|
103
103
|
- sig/http3.rbs
|
|
104
|
+
- sig/http3/body_pipe.rbs
|
|
105
|
+
- sig/http3/body_stream.rbs
|
|
104
106
|
- sig/http3/nghttp3/callbacks.rbs
|
|
105
107
|
- sig/http3/nghttp3/connection.rbs
|
|
106
108
|
- sig/http3/nghttp3/error.rbs
|
|
@@ -113,6 +115,7 @@ files:
|
|
|
113
115
|
- sig/http3/response.rbs
|
|
114
116
|
- sig/http3/server.rbs
|
|
115
117
|
- sig/http3/session.rbs
|
|
118
|
+
- sig/http3/worker_pool.rbs
|
|
116
119
|
homepage: https://github.com/unasuke/http3-ruby
|
|
117
120
|
licenses:
|
|
118
121
|
- MIT
|
data/.clang-format
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
BasedOnStyle: LLVM
|
data/.vscode/extensions.json
DELETED