protocol-http3 0.0.1

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.
@@ -0,0 +1,257 @@
1
+ #include "Dispatcher.hpp"
2
+
3
+ #include "Server.hpp"
4
+
5
+ #include "../QUIC/Bindings.hpp"
6
+
7
+ #include <array>
8
+ #include <unordered_map>
9
+
10
+ VALUE Ruby_Protocol_HTTP3_Dispatcher = Qnil;
11
+
12
+ namespace Ruby::Protocol::HTTP3 {
13
+
14
+ class Dispatcher final : public ::Protocol::QUIC::Dispatcher {
15
+ public:
16
+ VALUE self;
17
+
18
+ private:
19
+ VALUE _configuration;
20
+ VALUE _tls_context;
21
+ std::unordered_map<::Protocol::HTTP3::Server *, VALUE> _servers;
22
+ std::unordered_map<::Protocol::QUIC::Socket *, VALUE> _sockets;
23
+
24
+ public:
25
+ Dispatcher(VALUE self, VALUE configuration, VALUE tls_context) :
26
+ ::Protocol::QUIC::Dispatcher(*Ruby_Protocol_QUIC_Configuration_get(configuration), *Ruby_Protocol_QUIC_TLS_ServerContext_get(tls_context)),
27
+ self(self),
28
+ _configuration(configuration),
29
+ _tls_context(tls_context)
30
+ {
31
+ }
32
+
33
+ VALUE ruby_configuration() noexcept {return _configuration;}
34
+ VALUE ruby_tls_context() noexcept {return _tls_context;}
35
+
36
+ VALUE listen(VALUE ruby_socket)
37
+ {
38
+ auto socket = Ruby_Protocol_QUIC_Socket_get(ruby_socket);
39
+
40
+ _sockets[socket] = ruby_socket;
41
+
42
+ auto server = ::Protocol::QUIC::Dispatcher::listen(*socket);
43
+
44
+ if (server) {
45
+ auto iterator = _servers.find(static_cast<::Protocol::HTTP3::Server *>(server));
46
+
47
+ if (iterator == _servers.end()) {
48
+ rb_raise(rb_eRuntimeError, "Could not find Ruby server wrapper for native server.");
49
+ }
50
+
51
+ return iterator->second;
52
+ }
53
+
54
+ return Qnil;
55
+ }
56
+
57
+ VALUE receive(VALUE ruby_socket)
58
+ {
59
+ auto socket = Ruby_Protocol_QUIC_Socket_get(ruby_socket);
60
+ _sockets[socket] = ruby_socket;
61
+
62
+ std::array<::Protocol::QUIC::Byte, 1024*64> buffer;
63
+ ::Protocol::QUIC::Address remote_address;
64
+ ::Protocol::QUIC::ECN ecn = ::Protocol::QUIC::ECN::UNSPECIFIED;
65
+ auto length = socket->receive_packet(buffer.data(), buffer.size(), remote_address, ecn);
66
+
67
+ ngtcp2_version_cid version_cid;
68
+ auto result = ngtcp2_pkt_decode_version_cid(&version_cid, buffer.data(), length, ::Protocol::QUIC::DEFAULT_SCID_LENGTH);
69
+
70
+ if (result == 0) {
71
+ auto server = process_packet(*socket, remote_address, buffer.data(), length, ecn, version_cid);
72
+
73
+ if (server) {
74
+ auto iterator = _servers.find(static_cast<::Protocol::HTTP3::Server *>(server));
75
+
76
+ if (iterator == _servers.end()) {
77
+ rb_raise(rb_eRuntimeError, "Could not find Ruby server wrapper for native server.");
78
+ }
79
+
80
+ return iterator->second;
81
+ }
82
+ }
83
+ else if (result == NGTCP2_ERR_VERSION_NEGOTIATION) {
84
+ send_version_negotiation(*socket, version_cid, remote_address);
85
+ }
86
+ else {
87
+ rb_raise(rb_eRuntimeError, "Could not decode QUIC version/CID: %s", ngtcp2_strerror(result));
88
+ }
89
+
90
+ return Qnil;
91
+ }
92
+
93
+ ::Protocol::QUIC::Server * create_server(::Protocol::QUIC::Socket & socket, const ::Protocol::QUIC::Address & address, const ngtcp2_pkt_hd & packet_header) override
94
+ {
95
+ auto iterator = _sockets.find(&socket);
96
+
97
+ if (iterator == _sockets.end()) {
98
+ rb_raise(rb_eRuntimeError, "Could not find Ruby socket wrapper for native socket.");
99
+ }
100
+
101
+ VALUE ruby_socket = iterator->second;
102
+ VALUE ruby_address = Ruby_Protocol_QUIC_Address_wrap(Ruby_Protocol_QUIC_Address, address);
103
+
104
+ VALUE ruby_packet_header = Ruby_Protocol_QUIC_PacketHeader_allocate(Ruby_Protocol_QUIC_PacketHeader);
105
+ DATA_PTR(ruby_packet_header) = new ngtcp2_pkt_hd(packet_header);
106
+
107
+ VALUE server = rb_funcall(self, rb_intern("create_server"), 3, ruby_socket, ruby_address, ruby_packet_header);
108
+ auto native_server = Ruby_Protocol_HTTP3_Server_get(server);
109
+
110
+ _servers[native_server] = server;
111
+
112
+ return native_server;
113
+ }
114
+
115
+ void remove(::Protocol::QUIC::Server * server) override
116
+ {
117
+ ::Protocol::QUIC::Dispatcher::remove(server);
118
+ _servers.erase(static_cast<::Protocol::HTTP3::Server *>(server));
119
+ }
120
+
121
+ void mark()
122
+ {
123
+ rb_gc_mark_movable(self);
124
+ rb_gc_mark_movable(_configuration);
125
+ rb_gc_mark_movable(_tls_context);
126
+
127
+ for (auto & [server, ruby_server] : _servers) {
128
+ (void)server;
129
+ rb_gc_mark_movable(ruby_server);
130
+ }
131
+
132
+ for (auto & [socket, ruby_socket] : _sockets) {
133
+ (void)socket;
134
+ rb_gc_mark_movable(ruby_socket);
135
+ }
136
+ }
137
+
138
+ void compact()
139
+ {
140
+ self = rb_gc_location(self);
141
+ _configuration = rb_gc_location(_configuration);
142
+ _tls_context = rb_gc_location(_tls_context);
143
+
144
+ for (auto & [server, ruby_server] : _servers) {
145
+ (void)server;
146
+ ruby_server = rb_gc_location(ruby_server);
147
+ }
148
+
149
+ for (auto & [socket, ruby_socket] : _sockets) {
150
+ (void)socket;
151
+ ruby_socket = rb_gc_location(ruby_socket);
152
+ }
153
+ }
154
+ };
155
+
156
+ }
157
+
158
+ static void Ruby_Protocol_HTTP3_Dispatcher_mark(void *data)
159
+ {
160
+ if (data) {
161
+ reinterpret_cast<Ruby::Protocol::HTTP3::Dispatcher *>(data)->mark();
162
+ }
163
+ }
164
+
165
+ static void Ruby_Protocol_HTTP3_Dispatcher_compact(void *data)
166
+ {
167
+ if (data) {
168
+ reinterpret_cast<Ruby::Protocol::HTTP3::Dispatcher *>(data)->compact();
169
+ }
170
+ }
171
+
172
+ static void Ruby_Protocol_HTTP3_Dispatcher_free(void *data)
173
+ {
174
+ if (data) {
175
+ delete reinterpret_cast<Protocol::QUIC::Dispatcher *>(data);
176
+ }
177
+ }
178
+
179
+ static size_t Ruby_Protocol_HTTP3_Dispatcher_size(const void *data)
180
+ {
181
+ return sizeof(Ruby::Protocol::HTTP3::Dispatcher);
182
+ }
183
+
184
+ static const rb_data_type_t Ruby_Protocol_HTTP3_Dispatcher_type = {
185
+ .wrap_struct_name = "Protocol::HTTP3::Dispatcher",
186
+ .function = {
187
+ .dmark = Ruby_Protocol_HTTP3_Dispatcher_mark,
188
+ .dfree = Ruby_Protocol_HTTP3_Dispatcher_free,
189
+ .dsize = Ruby_Protocol_HTTP3_Dispatcher_size,
190
+ .dcompact = Ruby_Protocol_HTTP3_Dispatcher_compact,
191
+ },
192
+ .data = NULL,
193
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
194
+ };
195
+
196
+ Protocol::QUIC::Dispatcher * Ruby_Protocol_HTTP3_Dispatcher_get(VALUE self)
197
+ {
198
+ Protocol::QUIC::Dispatcher *dispatcher;
199
+
200
+ TypedData_Get_Struct(self, Protocol::QUIC::Dispatcher, &Ruby_Protocol_HTTP3_Dispatcher_type, dispatcher);
201
+
202
+ return dispatcher;
203
+ }
204
+
205
+ static VALUE Ruby_Protocol_HTTP3_Dispatcher_allocate(VALUE klass)
206
+ {
207
+ return TypedData_Wrap_Struct(klass, &Ruby_Protocol_HTTP3_Dispatcher_type, NULL);
208
+ }
209
+
210
+ static VALUE Ruby_Protocol_HTTP3_Dispatcher_initialize(VALUE self, VALUE configuration, VALUE tls_context)
211
+ {
212
+ DATA_PTR(self) = new Ruby::Protocol::HTTP3::Dispatcher(self, configuration, tls_context);
213
+
214
+ return self;
215
+ }
216
+
217
+ static VALUE Ruby_Protocol_HTTP3_Dispatcher_configuration(VALUE self)
218
+ {
219
+ auto dispatcher = dynamic_cast<Ruby::Protocol::HTTP3::Dispatcher *>(Ruby_Protocol_HTTP3_Dispatcher_get(self));
220
+
221
+ return dispatcher->ruby_configuration();
222
+ }
223
+
224
+ static VALUE Ruby_Protocol_HTTP3_Dispatcher_tls_context(VALUE self)
225
+ {
226
+ auto dispatcher = dynamic_cast<Ruby::Protocol::HTTP3::Dispatcher *>(Ruby_Protocol_HTTP3_Dispatcher_get(self));
227
+
228
+ return dispatcher->ruby_tls_context();
229
+ }
230
+
231
+ static VALUE Ruby_Protocol_HTTP3_Dispatcher_listen(VALUE self, VALUE socket)
232
+ {
233
+ auto dispatcher = dynamic_cast<Ruby::Protocol::HTTP3::Dispatcher *>(Ruby_Protocol_HTTP3_Dispatcher_get(self));
234
+
235
+ return dispatcher->listen(socket);
236
+ }
237
+
238
+ static VALUE Ruby_Protocol_HTTP3_Dispatcher_receive(VALUE self, VALUE socket)
239
+ {
240
+ auto dispatcher = dynamic_cast<Ruby::Protocol::HTTP3::Dispatcher *>(Ruby_Protocol_HTTP3_Dispatcher_get(self));
241
+
242
+ return dispatcher->receive(socket);
243
+ }
244
+
245
+ void Init_Ruby_Protocol_HTTP3_Dispatcher(VALUE Protocol_HTTP3)
246
+ {
247
+ Ruby_Protocol_HTTP3_Dispatcher = rb_define_class_under(Protocol_HTTP3, "Dispatcher", rb_cObject);
248
+
249
+ rb_define_alloc_func(Ruby_Protocol_HTTP3_Dispatcher, Ruby_Protocol_HTTP3_Dispatcher_allocate);
250
+ rb_define_method(Ruby_Protocol_HTTP3_Dispatcher, "initialize", Ruby_Protocol_HTTP3_Dispatcher_initialize, 2);
251
+
252
+ rb_define_method(Ruby_Protocol_HTTP3_Dispatcher, "configuration", Ruby_Protocol_HTTP3_Dispatcher_configuration, 0);
253
+ rb_define_method(Ruby_Protocol_HTTP3_Dispatcher, "tls_context", Ruby_Protocol_HTTP3_Dispatcher_tls_context, 0);
254
+
255
+ rb_define_method(Ruby_Protocol_HTTP3_Dispatcher, "listen", Ruby_Protocol_HTTP3_Dispatcher_listen, 1);
256
+ rb_define_method(Ruby_Protocol_HTTP3_Dispatcher, "receive", Ruby_Protocol_HTTP3_Dispatcher_receive, 1);
257
+ }
@@ -0,0 +1,13 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ #include <Protocol/QUIC/Dispatcher.hpp>
6
+
7
+ extern "C" {
8
+ extern VALUE Ruby_Protocol_HTTP3_Dispatcher;
9
+
10
+ void Init_Ruby_Protocol_HTTP3_Dispatcher(VALUE Protocol_HTTP3);
11
+
12
+ Protocol::QUIC::Dispatcher * Ruby_Protocol_HTTP3_Dispatcher_get(VALUE self);
13
+ }
@@ -0,0 +1,239 @@
1
+ #include "Server.hpp"
2
+
3
+ #include "Dispatcher.hpp"
4
+
5
+ #include "../QUIC/Bindings.hpp"
6
+
7
+ #include <unordered_map>
8
+ #include <vector>
9
+
10
+ VALUE Ruby_Protocol_HTTP3_Server = Qnil;
11
+
12
+ namespace Ruby::Protocol::HTTP3 {
13
+
14
+ class Server : public ::Protocol::HTTP3::Server {
15
+ public:
16
+ VALUE self;
17
+
18
+ private:
19
+ VALUE _dispatcher;
20
+ VALUE _configuration;
21
+ VALUE _tls_context;
22
+ VALUE _socket;
23
+ VALUE _remote_address;
24
+ std::unordered_map<::Protocol::QUIC::StreamID, VALUE> _streams;
25
+
26
+ public:
27
+ Server(VALUE self, VALUE dispatcher, VALUE configuration, VALUE tls_context, VALUE socket, VALUE remote_address, VALUE packet_header, VALUE original_connection_id) :
28
+ ::Protocol::HTTP3::Server(*Ruby_Protocol_HTTP3_Dispatcher_get(dispatcher), *Ruby_Protocol_QUIC_Configuration_get(configuration), *Ruby_Protocol_QUIC_TLS_ServerContext_get(tls_context), *Ruby_Protocol_QUIC_Socket_get(socket), *Ruby_Protocol_QUIC_Address_get(remote_address), *Ruby_Protocol_QUIC_PacketHeader_get(packet_header), nullptr),
29
+ self(self),
30
+ _dispatcher(dispatcher),
31
+ _configuration(configuration),
32
+ _tls_context(tls_context),
33
+ _socket(socket),
34
+ _remote_address(remote_address)
35
+ {
36
+ (void)original_connection_id;
37
+ }
38
+
39
+ virtual ~Server()
40
+ {
41
+ }
42
+
43
+ void header_received(::Protocol::QUIC::StreamID stream_id, std::int32_t token, nghttp3_rcbuf *name, nghttp3_rcbuf *value, std::uint8_t flags, void *stream_data) override
44
+ {
45
+ (void)token;
46
+ (void)flags;
47
+ (void)stream_data;
48
+
49
+ if (!rb_respond_to(self, rb_intern("header_received"))) {
50
+ return;
51
+ }
52
+
53
+ auto name_buffer = nghttp3_rcbuf_get_buf(name);
54
+ auto value_buffer = nghttp3_rcbuf_get_buf(value);
55
+
56
+ rb_funcall(
57
+ self,
58
+ rb_intern("header_received"),
59
+ 3,
60
+ RB_LL2NUM(stream_id),
61
+ rb_str_new(reinterpret_cast<const char *>(name_buffer.base), name_buffer.len),
62
+ rb_str_new(reinterpret_cast<const char *>(value_buffer.base), value_buffer.len)
63
+ );
64
+ }
65
+
66
+ void headers_finished(::Protocol::QUIC::StreamID stream_id, bool is_final, void *stream_data) override
67
+ {
68
+ (void)stream_data;
69
+
70
+ if (rb_respond_to(self, rb_intern("headers_finished"))) {
71
+ rb_funcall(self, rb_intern("headers_finished"), 2, RB_LL2NUM(stream_id), is_final ? Qtrue : Qfalse);
72
+ }
73
+ }
74
+
75
+ void settings_received(const nghttp3_proto_settings *settings) override
76
+ {
77
+ (void)settings;
78
+
79
+ if (rb_respond_to(self, rb_intern("settings_received"))) {
80
+ rb_funcall(self, rb_intern("settings_received"), 0);
81
+ }
82
+ }
83
+
84
+ void stream_finished(::Protocol::QUIC::StreamID stream_id, void *stream_data) override
85
+ {
86
+ (void)stream_data;
87
+
88
+ if (rb_respond_to(self, rb_intern("stream_finished"))) {
89
+ rb_funcall(self, rb_intern("stream_finished"), 1, RB_LL2NUM(stream_id));
90
+ }
91
+ }
92
+
93
+ void disconnect() override
94
+ {
95
+ ::Protocol::HTTP3::Server::disconnect();
96
+ _streams.clear();
97
+ }
98
+
99
+ void mark()
100
+ {
101
+ rb_gc_mark_movable(self);
102
+ rb_gc_mark_movable(_dispatcher);
103
+ rb_gc_mark_movable(_configuration);
104
+ rb_gc_mark_movable(_tls_context);
105
+ rb_gc_mark_movable(_socket);
106
+ rb_gc_mark_movable(_remote_address);
107
+
108
+ for (auto & [stream_id, stream] : _streams) {
109
+ (void)stream_id;
110
+ rb_gc_mark_movable(stream);
111
+ }
112
+ }
113
+
114
+ void compact()
115
+ {
116
+ self = rb_gc_location(self);
117
+ _dispatcher = rb_gc_location(_dispatcher);
118
+ _configuration = rb_gc_location(_configuration);
119
+ _tls_context = rb_gc_location(_tls_context);
120
+ _socket = rb_gc_location(_socket);
121
+ _remote_address = rb_gc_location(_remote_address);
122
+
123
+ for (auto & [stream_id, stream] : _streams) {
124
+ (void)stream_id;
125
+ stream = rb_gc_location(stream);
126
+ }
127
+ }
128
+ };
129
+
130
+ }
131
+
132
+ static void Ruby_Protocol_HTTP3_Server_mark(void *data)
133
+ {
134
+ if (data) {
135
+ reinterpret_cast<Ruby::Protocol::HTTP3::Server *>(data)->mark();
136
+ }
137
+ }
138
+
139
+ static void Ruby_Protocol_HTTP3_Server_compact(void *data)
140
+ {
141
+ if (data) {
142
+ reinterpret_cast<Ruby::Protocol::HTTP3::Server *>(data)->compact();
143
+ }
144
+ }
145
+
146
+ static void Ruby_Protocol_HTTP3_Server_free(void *data)
147
+ {
148
+ if (data) {
149
+ delete reinterpret_cast<Protocol::HTTP3::Server *>(data);
150
+ }
151
+ }
152
+
153
+ static size_t Ruby_Protocol_HTTP3_Server_size(const void *data)
154
+ {
155
+ return sizeof(Ruby::Protocol::HTTP3::Server);
156
+ }
157
+
158
+ static const rb_data_type_t Ruby_Protocol_HTTP3_Server_type = {
159
+ .wrap_struct_name = "Protocol::HTTP3::Server",
160
+ .function = {
161
+ .dmark = Ruby_Protocol_HTTP3_Server_mark,
162
+ .dfree = Ruby_Protocol_HTTP3_Server_free,
163
+ .dsize = Ruby_Protocol_HTTP3_Server_size,
164
+ .dcompact = Ruby_Protocol_HTTP3_Server_compact,
165
+ },
166
+ .data = NULL,
167
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
168
+ };
169
+
170
+ Protocol::HTTP3::Server * Ruby_Protocol_HTTP3_Server_get(VALUE self)
171
+ {
172
+ Protocol::HTTP3::Server *server;
173
+
174
+ TypedData_Get_Struct(self, Protocol::HTTP3::Server, &Ruby_Protocol_HTTP3_Server_type, server);
175
+
176
+ return server;
177
+ }
178
+
179
+ static VALUE Ruby_Protocol_HTTP3_Server_allocate(VALUE klass)
180
+ {
181
+ return TypedData_Wrap_Struct(klass, &Ruby_Protocol_HTTP3_Server_type, NULL);
182
+ }
183
+
184
+ static VALUE Ruby_Protocol_HTTP3_Server_initialize(VALUE self, VALUE dispatcher, VALUE configuration, VALUE tls_context, VALUE socket, VALUE remote_address, VALUE packet_header, VALUE original_connection_id)
185
+ {
186
+ DATA_PTR(self) = new Ruby::Protocol::HTTP3::Server(self, dispatcher, configuration, tls_context, socket, remote_address, packet_header, original_connection_id);
187
+
188
+ return self;
189
+ }
190
+
191
+ static VALUE Ruby_Protocol_HTTP3_Server_send_packets(VALUE self)
192
+ {
193
+ auto server = Ruby_Protocol_HTTP3_Server_get(self);
194
+
195
+ server->send_packets();
196
+
197
+ return Qnil;
198
+ }
199
+
200
+ static VALUE Ruby_Protocol_HTTP3_Server_submit_response(VALUE self, VALUE stream_id, VALUE headers)
201
+ {
202
+ auto server = Ruby_Protocol_HTTP3_Server_get(self);
203
+ auto count = RARRAY_LEN(headers);
204
+ std::vector<nghttp3_nv> native_headers;
205
+ native_headers.reserve(count);
206
+
207
+ for (long index = 0; index < count; ++index) {
208
+ VALUE pair = rb_ary_entry(headers, index);
209
+ VALUE name = rb_ary_entry(pair, 0);
210
+ VALUE value = rb_ary_entry(pair, 1);
211
+
212
+ name = rb_str_to_str(name);
213
+ value = rb_str_to_str(value);
214
+
215
+ native_headers.push_back(nghttp3_nv{
216
+ .name = reinterpret_cast<std::uint8_t *>(RSTRING_PTR(name)),
217
+ .value = reinterpret_cast<std::uint8_t *>(RSTRING_PTR(value)),
218
+ .namelen = static_cast<std::size_t>(RSTRING_LEN(name)),
219
+ .valuelen = static_cast<std::size_t>(RSTRING_LEN(value)),
220
+ .flags = NGHTTP3_NV_FLAG_NONE,
221
+ });
222
+ }
223
+
224
+ server->submit_response(RB_NUM2LL(stream_id), native_headers.data(), native_headers.size());
225
+ server->send_packets();
226
+
227
+ return Qnil;
228
+ }
229
+
230
+ void Init_Ruby_Protocol_HTTP3_Server(VALUE Protocol_HTTP3)
231
+ {
232
+ Ruby_Protocol_HTTP3_Server = rb_define_class_under(Protocol_HTTP3, "Server", rb_cObject);
233
+
234
+ rb_define_alloc_func(Ruby_Protocol_HTTP3_Server, Ruby_Protocol_HTTP3_Server_allocate);
235
+ rb_define_method(Ruby_Protocol_HTTP3_Server, "initialize", Ruby_Protocol_HTTP3_Server_initialize, 7);
236
+
237
+ rb_define_method(Ruby_Protocol_HTTP3_Server, "send_packets", Ruby_Protocol_HTTP3_Server_send_packets, 0);
238
+ rb_define_method(Ruby_Protocol_HTTP3_Server, "submit_response", Ruby_Protocol_HTTP3_Server_submit_response, 2);
239
+ }
@@ -0,0 +1,13 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ #include <Protocol/HTTP3/Server.hpp>
6
+
7
+ extern "C" {
8
+ extern VALUE Ruby_Protocol_HTTP3_Server;
9
+
10
+ void Init_Ruby_Protocol_HTTP3_Server(VALUE Protocol_HTTP3);
11
+
12
+ Protocol::HTTP3::Server * Ruby_Protocol_HTTP3_Server_get(VALUE self);
13
+ }
@@ -0,0 +1,28 @@
1
+ #include "HTTP3.hpp"
2
+
3
+ #include "HTTP3/Client.hpp"
4
+ #include "HTTP3/Dispatcher.hpp"
5
+ #include "HTTP3/Server.hpp"
6
+
7
+ #include "QUIC/Bindings.hpp"
8
+
9
+ VALUE Ruby_Protocol_HTTP3 = Qnil;
10
+
11
+ static VALUE Ruby_Protocol_HTTP3_file_descriptor(VALUE self, VALUE socket)
12
+ {
13
+ (void)self;
14
+
15
+ return INT2NUM(Ruby_Protocol_QUIC_Socket_get(socket)->descriptor());
16
+ }
17
+
18
+ void Init_Ruby_Protocol_HTTP3(void)
19
+ {
20
+ VALUE Protocol = rb_define_module("Protocol");
21
+ Ruby_Protocol_HTTP3 = rb_define_module_under(Protocol, "HTTP3");
22
+
23
+ rb_define_singleton_method(Ruby_Protocol_HTTP3, "file_descriptor", Ruby_Protocol_HTTP3_file_descriptor, 1);
24
+
25
+ Init_Ruby_Protocol_HTTP3_Client(Ruby_Protocol_HTTP3);
26
+ Init_Ruby_Protocol_HTTP3_Server(Ruby_Protocol_HTTP3);
27
+ Init_Ruby_Protocol_HTTP3_Dispatcher(Ruby_Protocol_HTTP3);
28
+ }
@@ -0,0 +1,9 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ extern "C" {
6
+ extern VALUE Ruby_Protocol_HTTP3;
7
+
8
+ void Init_Ruby_Protocol_HTTP3(void);
9
+ }
@@ -0,0 +1,35 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ #include <Protocol/QUIC/Address.hpp>
6
+ #include <Protocol/QUIC/Configuration.hpp>
7
+ #include <Protocol/QUIC/Client.hpp>
8
+ #include <Protocol/QUIC/Connection.hpp>
9
+ #include <Protocol/QUIC/Dispatcher.hpp>
10
+ #include <Protocol/QUIC/Socket.hpp>
11
+ #include <Protocol/QUIC/TLS/ClientContext.hpp>
12
+ #include <Protocol/QUIC/TLS/ServerContext.hpp>
13
+
14
+ #include <ngtcp2/ngtcp2.h>
15
+
16
+ extern "C" {
17
+ extern VALUE Ruby_Protocol_QUIC_Address;
18
+ extern VALUE Ruby_Protocol_QUIC_PacketHeader;
19
+ extern const rb_data_type_t Ruby_Protocol_QUIC_Connection_type;
20
+
21
+ Protocol::QUIC::Address * Ruby_Protocol_QUIC_Address_get(VALUE self);
22
+ VALUE Ruby_Protocol_QUIC_Address_wrap(VALUE klass, const Protocol::QUIC::Address & address);
23
+
24
+ Protocol::QUIC::Configuration * Ruby_Protocol_QUIC_Configuration_get(VALUE self);
25
+ Protocol::QUIC::Client * Ruby_Protocol_QUIC_Client_get(VALUE self);
26
+ Protocol::QUIC::Connection * Ruby_Protocol_QUIC_Connection_get(VALUE self);
27
+ Protocol::QUIC::Dispatcher * Ruby_Protocol_QUIC_Dispatcher_get(VALUE self);
28
+
29
+ ngtcp2_pkt_hd * Ruby_Protocol_QUIC_PacketHeader_get(VALUE self);
30
+ VALUE Ruby_Protocol_QUIC_PacketHeader_allocate(VALUE klass);
31
+
32
+ Protocol::QUIC::Socket * Ruby_Protocol_QUIC_Socket_get(VALUE self);
33
+ Protocol::QUIC::TLS::ClientContext * Ruby_Protocol_QUIC_TLS_ClientContext_get(VALUE self);
34
+ Protocol::QUIC::TLS::ServerContext * Ruby_Protocol_QUIC_TLS_ServerContext_get(VALUE self);
35
+ }
data/ext/teapot.rb ADDED
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ teapot_version "3.0"
7
+
8
+ define_project "ruby-protocol-http3" do |project|
9
+ project.title = "Protocol::HTTP3"
10
+ project.license = "MIT License"
11
+ end
12
+
13
+ define_target "ruby-protocol-http3" do |target|
14
+ target.depends "Language/C++17"
15
+
16
+ target.depends "Library/Protocol/HTTP3"
17
+ target.depends "Library/ruby"
18
+ target.depends "Build/Files"
19
+ target.depends "Build/Compile/Commands"
20
+
21
+ target.provides "Ruby/Protocol/HTTP3" do
22
+ source_root = target.package.path + "source"
23
+
24
+ library_path = build dynamic_library: "Ruby_Protocol_HTTP3", source_files: source_root.glob("**/*.{c,cpp}")
25
+
26
+ copy source: [library_path], prefix: environment[:ruby_install_path]
27
+
28
+ compile_commands destination_path: (source_root + "compile_commands.json")
29
+ end
30
+ end
31
+
32
+ define_configuration "ruby-protocol-http3" do |configuration|
33
+ configuration[:source] = "https://github.com/kurocha/"
34
+
35
+ configuration.require "platforms"
36
+
37
+ configuration.require "build-make"
38
+ configuration.require "build-cmake"
39
+
40
+ configuration.require "scheduler-ruby"
41
+ configuration.require "protocol-http3"
42
+ configuration.require "ruby"
43
+
44
+ configuration.require "generate-template"
45
+ configuration.require "generate-cpp-class"
46
+
47
+ configuration.require "build-compile-commands"
48
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023-2026, by Samuel Williams.
5
+
6
+ # @namespace
7
+ module Protocol
8
+ # @namespace
9
+ module HTTP3
10
+ VERSION = "0.0.1"
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023-2026, by Samuel Williams.
5
+
6
+ require "protocol/quic"
7
+
8
+ require_relative "http3/version"
9
+
10
+ # Native extension:
11
+ require "Ruby_Protocol_HTTP3"
data/license.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright, 2023-2026, by Samuel Williams.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.