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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 52450e9925c14c39e98b9e9a762a4f0ef69c92a1ff6c7f7898eb8d4e1c60c1e7
4
+ data.tar.gz: a18affd137347ef514d858c68df6be51f35683e82d13eb28abd0b89b05d55c0a
5
+ SHA512:
6
+ metadata.gz: 252b5eb914682b2797eab8b6b5c14e76efdb6f53ef7a2e527cc9358dbce83fd7953ffb7fe99e8d08ae6537f00ffb0d83dbf2aeb244bec372606e6cc98e47ae04
7
+ data.tar.gz: 6715a25f8dda241f3cd0029bfe8aa897524c30418723b5d249f71d2e0ae4e987e6656f213a0f49ebfb91f90d516eedaa1c3c1702b2be33b410998ad8a008b2dd
checksums.yaml.gz.sig ADDED
Binary file
data/ext/rakefile.rb ADDED
@@ -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
+ task :default do
7
+ ruby_library_directory = ENV.fetch("RUBYLIBDIR"){ENV.fetch("RUBYARCHDIR")}
8
+ build_environment = {"RUBYLIBDIR" => ruby_library_directory}
9
+
10
+ sh build_environment, "teapot", "fetch"
11
+ sh build_environment, "teapot", "scheduler-ruby-library", "Ruby/Protocol/HTTP3"
12
+ end
@@ -0,0 +1,82 @@
1
+ ---
2
+ platforms:
3
+ :commit: 53bfdd34201790980ba8f4c335dbf659d038eb06
4
+ :branch: master
5
+ build-make:
6
+ :commit: 50998307f3d1f462c2bda1999b42b2ea8aeb4b15
7
+ :branch: master
8
+ build-cmake:
9
+ :commit: 6558fdad5bbb475f0fa5584ff773de8f12ff232e
10
+ :branch: master
11
+ protocol-http3:
12
+ :commit: d9e1c8bc9cb301d430fb48c3aec3b938a6446277
13
+ :branch: main
14
+ ruby:
15
+ :commit: 685cdd5a421dae7749f368d08add9aa9709ad043
16
+ :branch: main
17
+ generate-template:
18
+ :commit: ad0dcfb6da64cc0508b71916b30e1050c82ecea7
19
+ :branch: master
20
+ generate-cpp-class:
21
+ :commit: b8996bd1404904a737b76d6893fc9f9bf78da540
22
+ :branch: master
23
+ build-compile-commands:
24
+ :commit: 1c4b18f86d45426d7dd284c28bc53837e5694b6c
25
+ :branch: main
26
+ variants:
27
+ :commit: f6a26a8ac3a47a23a213cbd15da2183239d394d1
28
+ :branch: master
29
+ platform-darwin-osx:
30
+ :commit: dbd14d21c11e85f6fc067529a63f81cf5e6604d2
31
+ :branch: master
32
+ platform-darwin-ios:
33
+ :commit: d0611a1afac738012ffb7c51185499292e9a87ba
34
+ :branch: master
35
+ build-clang:
36
+ :commit: 1c9be15a63c149f6decf8d0599b34755aa3b0979
37
+ :branch: master
38
+ build-darwin:
39
+ :commit: 0d68478a7754741dd5d360f70294161efdee030d
40
+ :branch: master
41
+ build-files:
42
+ :commit: 2e5392ef4e171885b621288c7829e5160f1255bf
43
+ :branch: master
44
+ executor-unix:
45
+ :commit: 91f433a59550b826f7bc69e36d1df9c6e5576575
46
+ :branch: master
47
+ executor-lldb:
48
+ :commit: 193ba0b15c532d2745b0f0bfb05cd9581e869555
49
+ :branch: master
50
+ protocol-quic:
51
+ :commit: 1328f66a91ba7b3e544304dde8ba7bc93d3462ec
52
+ :branch: main
53
+ scheduler:
54
+ :commit: 4d05a1631ac749106d268bda07425bf6ce0476dc
55
+ :branch: main
56
+ nghttp3:
57
+ :commit: 922c2aaca406840a63ce2c4a808d9c888538671d
58
+ :branch: main
59
+ ngtcp2:
60
+ :commit: c65334b60a04200b1691da3d85d38444c5ff951d
61
+ :branch: main
62
+ concurrent:
63
+ :commit: 5075d0f6c2c0e90dc152265ec5e236d56fd20c94
64
+ :branch: master
65
+ time:
66
+ :commit: c1ec0bb4fc9abca3de559a87884bdbc1413b3f1f
67
+ :branch: main
68
+ picotls:
69
+ :commit: 2bc03af57a8ea09f3b0bd11c47c876fd3bc1d9fc
70
+ :branch: main
71
+ coroutine:
72
+ :commit: 428204db9f5485d1565884c218933fd16833b439
73
+ :branch: master
74
+ openssl:
75
+ :commit: 2f48d686aaaf20dc6d7e8a277803657eb5951530
76
+ :branch: main
77
+ coroutine-arm64:
78
+ :commit: aada7d279a6c2ae79b6f8f3e45cfa44de3c432fe
79
+ :branch: master
80
+ scheduler-ruby:
81
+ :commit: 94e524e64912081aaa5a8cb54bad7ad73f01a51a
82
+ :branch: main
@@ -0,0 +1,363 @@
1
+ #include "Client.hpp"
2
+
3
+ #include "../QUIC/Bindings.hpp"
4
+
5
+ #include <Protocol/HTTP3/Stream.hpp>
6
+
7
+ #include <array>
8
+ #include <unordered_map>
9
+ #include <vector>
10
+
11
+ VALUE Ruby_Protocol_HTTP3_Client = Qnil;
12
+
13
+ namespace Ruby::Protocol::HTTP3 {
14
+
15
+ class Client final : public ::Protocol::QUIC::Client, public ::Protocol::HTTP3::Session {
16
+ public:
17
+ VALUE self;
18
+
19
+ private:
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
+ Client(VALUE self, VALUE configuration, VALUE tls_context, VALUE socket, VALUE remote_address, VALUE chosen_version) :
28
+ ::Protocol::QUIC::Client(*Ruby_Protocol_QUIC_Configuration_get(configuration), *Ruby_Protocol_QUIC_TLS_ClientContext_get(tls_context), *Ruby_Protocol_QUIC_Socket_get(socket), *Ruby_Protocol_QUIC_Address_get(remote_address), RB_NUM2UINT(chosen_version)),
29
+ ::Protocol::HTTP3::Session(::Protocol::HTTP3::Session::Role::CLIENT),
30
+ self(self),
31
+ _configuration(configuration),
32
+ _tls_context(tls_context),
33
+ _socket(socket),
34
+ _remote_address(remote_address)
35
+ {
36
+ }
37
+
38
+ void handshake_completed() override
39
+ {
40
+ auto control_stream = open_unidirectional_stream();
41
+ auto encoder_stream = open_unidirectional_stream();
42
+ auto decoder_stream = open_unidirectional_stream();
43
+
44
+ bind_control_stream(control_stream->stream_id());
45
+ bind_qpack_streams(encoder_stream->stream_id(), decoder_stream->stream_id());
46
+
47
+ send_packets();
48
+
49
+ if (rb_respond_to(self, rb_intern("handshake_completed"))) {
50
+ rb_funcall(self, rb_intern("handshake_completed"), 0);
51
+ }
52
+ }
53
+
54
+ ::Protocol::QUIC::Stream * create_stream(::Protocol::QUIC::StreamID stream_id) override
55
+ {
56
+ return new ::Protocol::HTTP3::Stream(*this, *this, stream_id);
57
+ }
58
+
59
+ ::Protocol::QUIC::Connection::Status send_stream_data() override
60
+ {
61
+ std::array<::Protocol::QUIC::Byte, 1024*64> packet;
62
+ std::array<nghttp3_vec, 16> http_vectors;
63
+
64
+ while (true) {
65
+ ::Protocol::QUIC::StreamID stream_id = -1;
66
+ bool is_final = false;
67
+
68
+ auto vector_count = write_stream_data(stream_id, is_final, http_vectors.data(), http_vectors.size());
69
+
70
+ if (stream_id < 0) {
71
+ break;
72
+ }
73
+
74
+ std::vector<ngtcp2_vec> stream_vectors;
75
+ stream_vectors.reserve(static_cast<std::size_t>(vector_count));
76
+
77
+ for (nghttp3_ssize index = 0; index < vector_count; ++index) {
78
+ stream_vectors.push_back(ngtcp2_vec{
79
+ .base = http_vectors[index].base,
80
+ .len = http_vectors[index].len,
81
+ });
82
+ }
83
+
84
+ ngtcp2_path_storage path_storage;
85
+ ngtcp2_path_storage_zero(&path_storage);
86
+ ngtcp2_pkt_info packet_info;
87
+ ngtcp2_ssize written_length = 0;
88
+ ::Protocol::QUIC::StreamDataFlags flags = is_final ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0;
89
+
90
+ auto result = ngtcp2_conn_writev_stream(::Protocol::QUIC::Client::native_handle(), &path_storage.path, &packet_info, packet.data(), packet.size(), &written_length, flags, stream_id, stream_vectors.data(), stream_vectors.size(), ::Protocol::QUIC::timestamp());
91
+
92
+ if (result == NGTCP2_ERR_STREAM_DATA_BLOCKED || result == NGTCP2_ERR_STREAM_SHUT_WR) {
93
+ block_stream(stream_id);
94
+ return Status(result);
95
+ }
96
+
97
+ if (result < 0) {
98
+ return Status(result);
99
+ }
100
+
101
+ add_write_offset(stream_id, static_cast<std::size_t>(written_length));
102
+
103
+ if (result > 0) {
104
+ send_packet(path_storage.path, packet_info, packet.data(), result);
105
+ }
106
+
107
+ if (result == 0 && written_length == 0) {
108
+ break;
109
+ }
110
+ }
111
+
112
+ return Status::OK;
113
+ }
114
+
115
+ 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
116
+ {
117
+ (void)token;
118
+ (void)flags;
119
+ (void)stream_data;
120
+
121
+ if (!rb_respond_to(self, rb_intern("header_received"))) {
122
+ return;
123
+ }
124
+
125
+ auto name_buffer = nghttp3_rcbuf_get_buf(name);
126
+ auto value_buffer = nghttp3_rcbuf_get_buf(value);
127
+
128
+ rb_funcall(
129
+ self,
130
+ rb_intern("header_received"),
131
+ 3,
132
+ RB_LL2NUM(stream_id),
133
+ rb_str_new(reinterpret_cast<const char *>(name_buffer.base), name_buffer.len),
134
+ rb_str_new(reinterpret_cast<const char *>(value_buffer.base), value_buffer.len)
135
+ );
136
+ }
137
+
138
+ void headers_finished(::Protocol::QUIC::StreamID stream_id, bool is_final, void *stream_data) override
139
+ {
140
+ (void)stream_data;
141
+
142
+ if (rb_respond_to(self, rb_intern("headers_finished"))) {
143
+ rb_funcall(self, rb_intern("headers_finished"), 2, RB_LL2NUM(stream_id), is_final ? Qtrue : Qfalse);
144
+ }
145
+ }
146
+
147
+ void settings_received(const nghttp3_proto_settings *settings) override
148
+ {
149
+ (void)settings;
150
+
151
+ if (rb_respond_to(self, rb_intern("settings_received"))) {
152
+ rb_funcall(self, rb_intern("settings_received"), 0);
153
+ }
154
+ }
155
+
156
+ void stream_finished(::Protocol::QUIC::StreamID stream_id, void *stream_data) override
157
+ {
158
+ (void)stream_data;
159
+
160
+ if (rb_respond_to(self, rb_intern("stream_finished"))) {
161
+ rb_funcall(self, rb_intern("stream_finished"), 1, RB_LL2NUM(stream_id));
162
+ }
163
+ }
164
+
165
+ void disconnect() override
166
+ {
167
+ ::Protocol::QUIC::Client::disconnect();
168
+ _streams.clear();
169
+ }
170
+
171
+ void mark()
172
+ {
173
+ rb_gc_mark_movable(self);
174
+ rb_gc_mark_movable(_configuration);
175
+ rb_gc_mark_movable(_tls_context);
176
+ rb_gc_mark_movable(_socket);
177
+ rb_gc_mark_movable(_remote_address);
178
+
179
+ for (auto & [stream_id, stream] : _streams) {
180
+ (void)stream_id;
181
+ rb_gc_mark_movable(stream);
182
+ }
183
+ }
184
+
185
+ void compact()
186
+ {
187
+ self = rb_gc_location(self);
188
+ _configuration = rb_gc_location(_configuration);
189
+ _tls_context = rb_gc_location(_tls_context);
190
+ _socket = rb_gc_location(_socket);
191
+ _remote_address = rb_gc_location(_remote_address);
192
+
193
+ for (auto & [stream_id, stream] : _streams) {
194
+ (void)stream_id;
195
+ stream = rb_gc_location(stream);
196
+ }
197
+ }
198
+ };
199
+
200
+ }
201
+
202
+ static void Ruby_Protocol_HTTP3_Client_mark(void *data)
203
+ {
204
+ if (data) {
205
+ reinterpret_cast<Ruby::Protocol::HTTP3::Client *>(data)->mark();
206
+ }
207
+ }
208
+
209
+ static void Ruby_Protocol_HTTP3_Client_compact(void *data)
210
+ {
211
+ if (data) {
212
+ reinterpret_cast<Ruby::Protocol::HTTP3::Client *>(data)->compact();
213
+ }
214
+ }
215
+
216
+ static void Ruby_Protocol_HTTP3_Client_free(void *data)
217
+ {
218
+ if (data) {
219
+ delete reinterpret_cast<Protocol::QUIC::Client *>(data);
220
+ }
221
+ }
222
+
223
+ static size_t Ruby_Protocol_HTTP3_Client_size(const void *data)
224
+ {
225
+ return sizeof(Ruby::Protocol::HTTP3::Client);
226
+ }
227
+
228
+ static const rb_data_type_t Ruby_Protocol_HTTP3_Client_type = {
229
+ .wrap_struct_name = "Protocol::HTTP3::Client",
230
+ .function = {
231
+ .dmark = Ruby_Protocol_HTTP3_Client_mark,
232
+ .dfree = Ruby_Protocol_HTTP3_Client_free,
233
+ .dsize = Ruby_Protocol_HTTP3_Client_size,
234
+ .dcompact = Ruby_Protocol_HTTP3_Client_compact,
235
+ },
236
+ .parent = &Ruby_Protocol_QUIC_Connection_type,
237
+ .data = NULL,
238
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
239
+ };
240
+
241
+ Protocol::QUIC::Client * Ruby_Protocol_HTTP3_Client_get(VALUE self)
242
+ {
243
+ Protocol::QUIC::Client *client;
244
+
245
+ TypedData_Get_Struct(self, Protocol::QUIC::Client, &Ruby_Protocol_HTTP3_Client_type, client);
246
+
247
+ return client;
248
+ }
249
+
250
+ static Ruby::Protocol::HTTP3::Client * Ruby_Protocol_HTTP3_Client_native_get(VALUE self)
251
+ {
252
+ return dynamic_cast<Ruby::Protocol::HTTP3::Client *>(Ruby_Protocol_HTTP3_Client_get(self));
253
+ }
254
+
255
+ static VALUE Ruby_Protocol_HTTP3_Client_allocate(VALUE klass)
256
+ {
257
+ return TypedData_Wrap_Struct(klass, &Ruby_Protocol_HTTP3_Client_type, NULL);
258
+ }
259
+
260
+ static VALUE Ruby_Protocol_HTTP3_Client_initialize(VALUE self, VALUE configuration, VALUE tls_context, VALUE socket, VALUE remote_address, VALUE chosen_version)
261
+ {
262
+ DATA_PTR(self) = new Ruby::Protocol::HTTP3::Client(self, configuration, tls_context, socket, remote_address, chosen_version);
263
+
264
+ return self;
265
+ }
266
+
267
+ static VALUE Ruby_Protocol_HTTP3_Client_connect(VALUE self)
268
+ {
269
+ auto client = Ruby_Protocol_HTTP3_Client_get(self);
270
+
271
+ client->connect();
272
+
273
+ return Qnil;
274
+ }
275
+
276
+ static VALUE Ruby_Protocol_HTTP3_Client_close(VALUE self)
277
+ {
278
+ auto client = Ruby_Protocol_HTTP3_Client_get(self);
279
+
280
+ client->close();
281
+
282
+ return Qnil;
283
+ }
284
+
285
+ static VALUE Ruby_Protocol_HTTP3_Client_send_packets(VALUE self)
286
+ {
287
+ auto client = Ruby_Protocol_HTTP3_Client_get(self);
288
+
289
+ client->send_packets();
290
+
291
+ return Qnil;
292
+ }
293
+
294
+ static VALUE Ruby_Protocol_HTTP3_Client_receive(VALUE self, VALUE ruby_socket)
295
+ {
296
+ auto client = Ruby_Protocol_HTTP3_Client_get(self);
297
+ auto socket = Ruby_Protocol_QUIC_Socket_get(ruby_socket);
298
+
299
+ auto path = ngtcp2_conn_get_path(client->native_handle());
300
+
301
+ if (!path) {
302
+ rb_raise(rb_eRuntimeError, "Could not get QUIC client path.");
303
+ }
304
+
305
+ auto status = client->receive_packets(*path, *socket);
306
+
307
+ switch (status) {
308
+ case Protocol::QUIC::Connection::Status::OK:
309
+ client->send_packets();
310
+ return Qtrue;
311
+ case Protocol::QUIC::Connection::Status::CLOSING:
312
+ case Protocol::QUIC::Connection::Status::DRAINING:
313
+ return Qfalse;
314
+ default:
315
+ rb_raise(rb_eRuntimeError, "Could not receive QUIC packet.");
316
+ }
317
+ }
318
+
319
+ static VALUE Ruby_Protocol_HTTP3_Client_submit_request(VALUE self, VALUE headers)
320
+ {
321
+ auto client = Ruby_Protocol_HTTP3_Client_native_get(self);
322
+ auto stream = client->open_bidirectional_stream();
323
+ auto stream_id = stream->stream_id();
324
+ auto count = RARRAY_LEN(headers);
325
+ std::vector<nghttp3_nv> native_headers;
326
+ native_headers.reserve(count);
327
+
328
+ for (long index = 0; index < count; ++index) {
329
+ VALUE pair = rb_ary_entry(headers, index);
330
+ VALUE name = rb_ary_entry(pair, 0);
331
+ VALUE value = rb_ary_entry(pair, 1);
332
+
333
+ name = rb_str_to_str(name);
334
+ value = rb_str_to_str(value);
335
+
336
+ native_headers.push_back(nghttp3_nv{
337
+ .name = reinterpret_cast<std::uint8_t *>(RSTRING_PTR(name)),
338
+ .value = reinterpret_cast<std::uint8_t *>(RSTRING_PTR(value)),
339
+ .namelen = static_cast<std::size_t>(RSTRING_LEN(name)),
340
+ .valuelen = static_cast<std::size_t>(RSTRING_LEN(value)),
341
+ .flags = NGHTTP3_NV_FLAG_NONE,
342
+ });
343
+ }
344
+
345
+ client->submit_request(stream_id, native_headers.data(), native_headers.size());
346
+ client->send_packets();
347
+
348
+ return RB_LL2NUM(stream_id);
349
+ }
350
+
351
+ void Init_Ruby_Protocol_HTTP3_Client(VALUE Protocol_HTTP3)
352
+ {
353
+ Ruby_Protocol_HTTP3_Client = rb_define_class_under(Protocol_HTTP3, "Client", rb_cObject);
354
+
355
+ rb_define_alloc_func(Ruby_Protocol_HTTP3_Client, Ruby_Protocol_HTTP3_Client_allocate);
356
+ rb_define_method(Ruby_Protocol_HTTP3_Client, "initialize", Ruby_Protocol_HTTP3_Client_initialize, 5);
357
+
358
+ rb_define_method(Ruby_Protocol_HTTP3_Client, "connect", Ruby_Protocol_HTTP3_Client_connect, 0);
359
+ rb_define_method(Ruby_Protocol_HTTP3_Client, "close", Ruby_Protocol_HTTP3_Client_close, 0);
360
+ rb_define_method(Ruby_Protocol_HTTP3_Client, "send_packets", Ruby_Protocol_HTTP3_Client_send_packets, 0);
361
+ rb_define_method(Ruby_Protocol_HTTP3_Client, "receive", Ruby_Protocol_HTTP3_Client_receive, 1);
362
+ rb_define_method(Ruby_Protocol_HTTP3_Client, "submit_request", Ruby_Protocol_HTTP3_Client_submit_request, 1);
363
+ }
@@ -0,0 +1,14 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ #include <Protocol/HTTP3/Session.hpp>
6
+ #include <Protocol/QUIC/Client.hpp>
7
+
8
+ extern "C" {
9
+ extern VALUE Ruby_Protocol_HTTP3_Client;
10
+
11
+ void Init_Ruby_Protocol_HTTP3_Client(VALUE Protocol_HTTP3);
12
+
13
+ Protocol::QUIC::Client * Ruby_Protocol_HTTP3_Client_get(VALUE self);
14
+ }