http3 0.0.3
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 +7 -0
- data/.clang-format +1 -0
- data/.vscode/extensions.json +6 -0
- data/.vscode/settings.json +10 -0
- data/CHANGELOG.md +34 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE-DEPENDENCIES.txt +66 -0
- data/LICENSE.txt +21 -0
- data/README.md +127 -0
- data/Rakefile +20 -0
- data/ext/http3/extconf.rb +48 -0
- data/ext/http3/nghttp3.c +347 -0
- data/ext/http3/nghttp3.h +105 -0
- data/ext/http3/nghttp3_callbacks.c +621 -0
- data/ext/http3/nghttp3_connection.c +1303 -0
- data/ext/http3/nghttp3_nv.c +87 -0
- data/ext/http3/nghttp3_qpack.c +656 -0
- data/ext/http3/nghttp3_settings.c +188 -0
- data/ext/http3/session.c +1017 -0
- data/lib/http3/rack_adapter.rb +162 -0
- data/lib/http3/request.rb +9 -0
- data/lib/http3/response.rb +21 -0
- data/lib/http3/server.rb +357 -0
- data/lib/http3/session.rb +193 -0
- data/lib/http3/version.rb +5 -0
- data/lib/http3.rb +16 -0
- data/sig/http3/nghttp3/callbacks.rbs +32 -0
- data/sig/http3/nghttp3/connection.rbs +101 -0
- data/sig/http3/nghttp3/error.rbs +48 -0
- data/sig/http3/nghttp3/info.rbs +9 -0
- data/sig/http3/nghttp3/nv.rbs +11 -0
- data/sig/http3/nghttp3/qpack.rbs +48 -0
- data/sig/http3/nghttp3/settings.rbs +27 -0
- data/sig/http3/rack_adapter.rbs +18 -0
- data/sig/http3/request.rbs +11 -0
- data/sig/http3/response.rbs +30 -0
- data/sig/http3/server.rbs +32 -0
- data/sig/http3/session.rbs +52 -0
- data/sig/http3.rbs +66 -0
- metadata +140 -0
|
@@ -0,0 +1,1303 @@
|
|
|
1
|
+
#include "nghttp3.h"
|
|
2
|
+
|
|
3
|
+
#include <time.h>
|
|
4
|
+
|
|
5
|
+
VALUE rb_cNghttp3Connection;
|
|
6
|
+
|
|
7
|
+
/* The body Strings handed to nghttp3 for one stream and not yet wholly
|
|
8
|
+
* acknowledged, oldest first. acked counts the bytes of the first of them
|
|
9
|
+
* the peer has acknowledged so far. */
|
|
10
|
+
typedef struct {
|
|
11
|
+
VALUE strings;
|
|
12
|
+
uint64_t acked;
|
|
13
|
+
} PendingStream;
|
|
14
|
+
|
|
15
|
+
static int pending_mark_i(st_data_t key, st_data_t value, st_data_t arg) {
|
|
16
|
+
const PendingStream *ps = (const PendingStream *)value;
|
|
17
|
+
/* rb_gc_mark, not rb_gc_mark_movable: nghttp3 holds pointers into these
|
|
18
|
+
* Strings, so neither they nor (for an embedded String) their bytes may
|
|
19
|
+
* move. */
|
|
20
|
+
rb_gc_mark(ps->strings);
|
|
21
|
+
if (!NIL_P(ps->strings)) {
|
|
22
|
+
for (long i = 0; i < RARRAY_LEN(ps->strings); i++) {
|
|
23
|
+
rb_gc_mark(RARRAY_AREF(ps->strings, i));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return ST_CONTINUE;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static int pending_free_i(st_data_t key, st_data_t value, st_data_t arg) {
|
|
30
|
+
xfree((void *)value);
|
|
31
|
+
return ST_CONTINUE;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static void pending_clear(ConnectionObj *obj) {
|
|
35
|
+
if (obj->pending == NULL) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
st_foreach(obj->pending, pending_free_i, 0);
|
|
39
|
+
st_clear(obj->pending);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static void connection_mark(void *ptr) {
|
|
43
|
+
ConnectionObj *obj = (ConnectionObj *)ptr;
|
|
44
|
+
rb_gc_mark(obj->settings);
|
|
45
|
+
rb_gc_mark(obj->callbacks);
|
|
46
|
+
rb_gc_mark(obj->stream_data_readers);
|
|
47
|
+
rb_gc_mark(obj->stream_user_data);
|
|
48
|
+
rb_gc_mark(obj->pending_exc);
|
|
49
|
+
if (obj->pending != NULL) {
|
|
50
|
+
st_foreach(obj->pending, pending_mark_i, 0);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static void connection_free(void *ptr) {
|
|
55
|
+
ConnectionObj *obj = (ConnectionObj *)ptr;
|
|
56
|
+
if (obj->conn != NULL && !obj->is_closed) {
|
|
57
|
+
nghttp3_conn_del(obj->conn);
|
|
58
|
+
obj->conn = NULL;
|
|
59
|
+
}
|
|
60
|
+
if (obj->pending != NULL) {
|
|
61
|
+
pending_clear(obj);
|
|
62
|
+
st_free_table(obj->pending);
|
|
63
|
+
}
|
|
64
|
+
xfree(ptr);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static size_t connection_memsize(const void *ptr) {
|
|
68
|
+
const ConnectionObj *obj = (const ConnectionObj *)ptr;
|
|
69
|
+
return sizeof(ConnectionObj) +
|
|
70
|
+
(obj->pending != NULL ? st_memsize(obj->pending) : 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* self is the object wrapping this struct, so it is not marked, but
|
|
74
|
+
* GC.compact may still move it. Everything else is pinned by dmark. */
|
|
75
|
+
static void connection_compact(void *ptr) {
|
|
76
|
+
ConnectionObj *obj = (ConnectionObj *)ptr;
|
|
77
|
+
obj->self = rb_gc_location(obj->self);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static const rb_data_type_t connection_data_type = {
|
|
81
|
+
.wrap_struct_name = "nghttp3_conn_rb",
|
|
82
|
+
.function =
|
|
83
|
+
{
|
|
84
|
+
.dmark = connection_mark,
|
|
85
|
+
.dfree = connection_free,
|
|
86
|
+
.dsize = connection_memsize,
|
|
87
|
+
.dcompact = connection_compact,
|
|
88
|
+
},
|
|
89
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
static VALUE connection_alloc(VALUE klass) {
|
|
93
|
+
ConnectionObj *obj;
|
|
94
|
+
VALUE self =
|
|
95
|
+
TypedData_Make_Struct(klass, ConnectionObj, &connection_data_type, obj);
|
|
96
|
+
obj->conn = NULL;
|
|
97
|
+
obj->self = self;
|
|
98
|
+
obj->settings = Qnil;
|
|
99
|
+
obj->callbacks = Qnil;
|
|
100
|
+
obj->stream_data_readers = Qnil;
|
|
101
|
+
obj->stream_user_data = Qnil;
|
|
102
|
+
obj->pending_exc = Qnil;
|
|
103
|
+
obj->is_closed = 0;
|
|
104
|
+
obj->is_server = 0;
|
|
105
|
+
obj->pending = st_init_numtable();
|
|
106
|
+
obj->stream_data_readers = rb_hash_new();
|
|
107
|
+
obj->stream_user_data = rb_hash_new();
|
|
108
|
+
return self;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
int nghttp3_rb_protect(ConnectionObj *obj, VALUE (*body)(VALUE), VALUE arg) {
|
|
112
|
+
int state = 0;
|
|
113
|
+
rb_protect(body, arg, &state);
|
|
114
|
+
if (state == 0) {
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
if (NIL_P(obj->pending_exc)) {
|
|
118
|
+
obj->pending_exc = rb_errinfo();
|
|
119
|
+
}
|
|
120
|
+
rb_set_errinfo(Qnil);
|
|
121
|
+
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Raise the exception a callback left in obj->pending_exc, if any. Called
|
|
125
|
+
* right after nghttp3 returns, before its return value is looked at: the
|
|
126
|
+
* NGHTTP3_ERR_CALLBACK_FAILURE it reports is the result of that
|
|
127
|
+
* exception. */
|
|
128
|
+
static void connection_raise_pending(ConnectionObj *obj) {
|
|
129
|
+
if (NIL_P(obj->pending_exc)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
VALUE exc = obj->pending_exc;
|
|
133
|
+
obj->pending_exc = Qnil;
|
|
134
|
+
rb_exc_raise(exc);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static PendingStream *pending_lookup(ConnectionObj *obj, int64_t stream_id) {
|
|
138
|
+
st_data_t value;
|
|
139
|
+
if (!st_lookup(obj->pending, (st_data_t)stream_id, &value)) {
|
|
140
|
+
return NULL;
|
|
141
|
+
}
|
|
142
|
+
return (PendingStream *)value;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Keep str, which nghttp3 is about to read from, until the peer has
|
|
146
|
+
* acknowledged all of it. */
|
|
147
|
+
static void pending_push(ConnectionObj *obj, int64_t stream_id, VALUE str) {
|
|
148
|
+
PendingStream *ps = pending_lookup(obj, stream_id);
|
|
149
|
+
if (ps == NULL) {
|
|
150
|
+
ps = ALLOC(PendingStream);
|
|
151
|
+
ps->strings = Qnil;
|
|
152
|
+
ps->acked = 0;
|
|
153
|
+
st_insert(obj->pending, (st_data_t)stream_id, (st_data_t)ps);
|
|
154
|
+
ps->strings = rb_ary_new();
|
|
155
|
+
}
|
|
156
|
+
rb_ary_push(ps->strings, str);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Drop the Strings at the front that the peer has now acknowledged to their
|
|
160
|
+
* end, as quic-ruby's quic_acked_stream_data_offset_cb does. A String
|
|
161
|
+
* acknowledged only in part stays. */
|
|
162
|
+
void nghttp3_rb_conn_acked(ConnectionObj *obj, int64_t stream_id,
|
|
163
|
+
uint64_t datalen) {
|
|
164
|
+
PendingStream *ps = pending_lookup(obj, stream_id);
|
|
165
|
+
if (ps == NULL) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
ps->acked += datalen;
|
|
169
|
+
while (RARRAY_LEN(ps->strings) > 0) {
|
|
170
|
+
uint64_t len = (uint64_t)RSTRING_LEN(RARRAY_AREF(ps->strings, 0));
|
|
171
|
+
if (ps->acked < len) {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
ps->acked -= len;
|
|
175
|
+
rb_ary_shift(ps->strings);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void nghttp3_rb_conn_stream_closed(ConnectionObj *obj, int64_t stream_id) {
|
|
180
|
+
st_data_t key = (st_data_t)stream_id;
|
|
181
|
+
st_data_t value;
|
|
182
|
+
if (st_delete(obj->pending, &key, &value)) {
|
|
183
|
+
xfree((void *)value);
|
|
184
|
+
}
|
|
185
|
+
rb_hash_delete(obj->stream_data_readers, LL2NUM(stream_id));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Prepare the nghttp3_callbacks for a new connection. A Callbacks object is
|
|
189
|
+
* checked here, so that the C callbacks can take it as one. */
|
|
190
|
+
static void connection_setup_callbacks(ConnectionObj *obj, VALUE rb_callbacks,
|
|
191
|
+
nghttp3_callbacks *callbacks) {
|
|
192
|
+
memset(callbacks, 0, sizeof(*callbacks));
|
|
193
|
+
if (!NIL_P(rb_callbacks)) {
|
|
194
|
+
rb_check_typeddata(rb_callbacks, &callbacks_data_type);
|
|
195
|
+
obj->callbacks = rb_callbacks;
|
|
196
|
+
}
|
|
197
|
+
nghttp3_rb_setup_callbacks(callbacks);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
* call-seq:
|
|
202
|
+
* Connection.client_new(settings = nil, callbacks = nil) -> Connection
|
|
203
|
+
*
|
|
204
|
+
* Creates a new client HTTP/3 connection.
|
|
205
|
+
* If settings is nil, default settings are used.
|
|
206
|
+
* If callbacks is provided, it will be used for HTTP/3 event notifications.
|
|
207
|
+
*/
|
|
208
|
+
static VALUE rb_nghttp3_connection_client_new(int argc, VALUE *argv,
|
|
209
|
+
VALUE klass) {
|
|
210
|
+
VALUE rb_settings, rb_callbacks;
|
|
211
|
+
ConnectionObj *obj;
|
|
212
|
+
nghttp3_settings settings;
|
|
213
|
+
nghttp3_settings *settings_ptr;
|
|
214
|
+
nghttp3_callbacks callbacks;
|
|
215
|
+
int rv;
|
|
216
|
+
VALUE self;
|
|
217
|
+
|
|
218
|
+
rb_scan_args(argc, argv, "02", &rb_settings, &rb_callbacks);
|
|
219
|
+
|
|
220
|
+
self = connection_alloc(klass);
|
|
221
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
222
|
+
|
|
223
|
+
if (NIL_P(rb_settings)) {
|
|
224
|
+
nghttp3_settings_default(&settings);
|
|
225
|
+
settings_ptr = &settings;
|
|
226
|
+
} else {
|
|
227
|
+
settings_ptr = nghttp3_rb_get_settings(rb_settings);
|
|
228
|
+
obj->settings = rb_settings;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
connection_setup_callbacks(obj, rb_callbacks, &callbacks);
|
|
232
|
+
|
|
233
|
+
rv = nghttp3_conn_client_new(&obj->conn, &callbacks, settings_ptr, NULL, obj);
|
|
234
|
+
|
|
235
|
+
if (rv != 0) {
|
|
236
|
+
nghttp3_rb_raise(rv, "Failed to create client connection");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
obj->is_server = 0;
|
|
240
|
+
return self;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/*
|
|
244
|
+
* call-seq:
|
|
245
|
+
* Connection.server_new(settings = nil, callbacks = nil) -> Connection
|
|
246
|
+
*
|
|
247
|
+
* Creates a new server HTTP/3 connection.
|
|
248
|
+
* If settings is nil, default settings are used.
|
|
249
|
+
* If callbacks is provided, it will be used for HTTP/3 event notifications.
|
|
250
|
+
*/
|
|
251
|
+
static VALUE rb_nghttp3_connection_server_new(int argc, VALUE *argv,
|
|
252
|
+
VALUE klass) {
|
|
253
|
+
VALUE rb_settings, rb_callbacks;
|
|
254
|
+
ConnectionObj *obj;
|
|
255
|
+
nghttp3_settings settings;
|
|
256
|
+
nghttp3_settings *settings_ptr;
|
|
257
|
+
nghttp3_callbacks callbacks;
|
|
258
|
+
int rv;
|
|
259
|
+
VALUE self;
|
|
260
|
+
|
|
261
|
+
rb_scan_args(argc, argv, "02", &rb_settings, &rb_callbacks);
|
|
262
|
+
|
|
263
|
+
self = connection_alloc(klass);
|
|
264
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
265
|
+
|
|
266
|
+
if (NIL_P(rb_settings)) {
|
|
267
|
+
nghttp3_settings_default(&settings);
|
|
268
|
+
settings_ptr = &settings;
|
|
269
|
+
} else {
|
|
270
|
+
settings_ptr = nghttp3_rb_get_settings(rb_settings);
|
|
271
|
+
obj->settings = rb_settings;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
connection_setup_callbacks(obj, rb_callbacks, &callbacks);
|
|
275
|
+
|
|
276
|
+
rv = nghttp3_conn_server_new(&obj->conn, &callbacks, settings_ptr, NULL, obj);
|
|
277
|
+
|
|
278
|
+
if (rv != 0) {
|
|
279
|
+
nghttp3_rb_raise(rv, "Failed to create server connection");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
obj->is_server = 1;
|
|
283
|
+
return self;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/*
|
|
287
|
+
* call-seq:
|
|
288
|
+
* connection.bind_control_stream(stream_id) -> self
|
|
289
|
+
*
|
|
290
|
+
* Binds the control stream to the given stream_id.
|
|
291
|
+
* This must be called before sending any HTTP frames.
|
|
292
|
+
*/
|
|
293
|
+
static VALUE rb_nghttp3_connection_bind_control_stream(VALUE self,
|
|
294
|
+
VALUE rb_stream_id) {
|
|
295
|
+
ConnectionObj *obj;
|
|
296
|
+
int64_t stream_id;
|
|
297
|
+
int rv;
|
|
298
|
+
|
|
299
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
300
|
+
|
|
301
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
302
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
306
|
+
rv = nghttp3_conn_bind_control_stream(obj->conn, stream_id);
|
|
307
|
+
|
|
308
|
+
if (rv != 0) {
|
|
309
|
+
nghttp3_rb_raise(rv, "Failed to bind control stream");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return self;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/*
|
|
316
|
+
* call-seq:
|
|
317
|
+
* connection.bind_qpack_streams(encoder_stream_id, decoder_stream_id) -> self
|
|
318
|
+
*
|
|
319
|
+
* Binds the QPACK encoder and decoder streams.
|
|
320
|
+
* This must be called before sending any HTTP frames.
|
|
321
|
+
*/
|
|
322
|
+
static VALUE rb_nghttp3_connection_bind_qpack_streams(VALUE self,
|
|
323
|
+
VALUE rb_enc_stream_id,
|
|
324
|
+
VALUE rb_dec_stream_id) {
|
|
325
|
+
ConnectionObj *obj;
|
|
326
|
+
int64_t enc_stream_id, dec_stream_id;
|
|
327
|
+
int rv;
|
|
328
|
+
|
|
329
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
330
|
+
|
|
331
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
332
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
enc_stream_id = NUM2LL(rb_enc_stream_id);
|
|
336
|
+
dec_stream_id = NUM2LL(rb_dec_stream_id);
|
|
337
|
+
|
|
338
|
+
rv = nghttp3_conn_bind_qpack_streams(obj->conn, enc_stream_id, dec_stream_id);
|
|
339
|
+
|
|
340
|
+
if (rv != 0) {
|
|
341
|
+
nghttp3_rb_raise(rv, "Failed to bind QPACK streams");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return self;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/*
|
|
348
|
+
* call-seq:
|
|
349
|
+
* connection.close -> nil
|
|
350
|
+
*
|
|
351
|
+
* Explicitly closes the connection and frees resources.
|
|
352
|
+
* After calling this, the connection object cannot be used.
|
|
353
|
+
*/
|
|
354
|
+
static VALUE rb_nghttp3_connection_close(VALUE self) {
|
|
355
|
+
ConnectionObj *obj;
|
|
356
|
+
|
|
357
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
358
|
+
|
|
359
|
+
if (obj->conn != NULL && !obj->is_closed) {
|
|
360
|
+
nghttp3_conn_del(obj->conn);
|
|
361
|
+
obj->conn = NULL;
|
|
362
|
+
obj->is_closed = 1;
|
|
363
|
+
/* nghttp3 reads none of the body Strings any more. */
|
|
364
|
+
pending_clear(obj);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return Qnil;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/*
|
|
371
|
+
* call-seq:
|
|
372
|
+
* connection.closed? -> true or false
|
|
373
|
+
*
|
|
374
|
+
* Returns true if the connection has been closed.
|
|
375
|
+
*/
|
|
376
|
+
static VALUE rb_nghttp3_connection_closed_p(VALUE self) {
|
|
377
|
+
ConnectionObj *obj;
|
|
378
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
379
|
+
return obj->is_closed ? Qtrue : Qfalse;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/*
|
|
383
|
+
* call-seq:
|
|
384
|
+
* connection.server? -> true or false
|
|
385
|
+
*
|
|
386
|
+
* Returns true if this is a server connection.
|
|
387
|
+
*/
|
|
388
|
+
static VALUE rb_nghttp3_connection_server_p(VALUE self) {
|
|
389
|
+
ConnectionObj *obj;
|
|
390
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
391
|
+
return obj->is_server ? Qtrue : Qfalse;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/*
|
|
395
|
+
* call-seq:
|
|
396
|
+
* connection.client? -> true or false
|
|
397
|
+
*
|
|
398
|
+
* Returns true if this is a client connection.
|
|
399
|
+
*/
|
|
400
|
+
static VALUE rb_nghttp3_connection_client_p(VALUE self) {
|
|
401
|
+
ConnectionObj *obj;
|
|
402
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
403
|
+
return obj->is_server ? Qfalse : Qtrue;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
static nghttp3_tstamp monotonic_now(void) {
|
|
407
|
+
struct timespec ts;
|
|
408
|
+
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
409
|
+
return (nghttp3_tstamp)ts.tv_sec * 1000000000ULL + (nghttp3_tstamp)ts.tv_nsec;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
struct read_stream_args {
|
|
413
|
+
ConnectionObj *obj;
|
|
414
|
+
int64_t stream_id;
|
|
415
|
+
VALUE data;
|
|
416
|
+
int fin;
|
|
417
|
+
nghttp3_tstamp ts;
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
static VALUE read_stream_body(VALUE arg) {
|
|
421
|
+
struct read_stream_args *a = (struct read_stream_args *)arg;
|
|
422
|
+
nghttp3_ssize rv = nghttp3_conn_read_stream2(
|
|
423
|
+
a->obj->conn, a->stream_id, (const uint8_t *)RSTRING_PTR(a->data),
|
|
424
|
+
(size_t)RSTRING_LEN(a->data), a->fin, a->ts);
|
|
425
|
+
connection_raise_pending(a->obj);
|
|
426
|
+
if (rv < 0) {
|
|
427
|
+
nghttp3_rb_raise((int)rv, "Failed to read stream");
|
|
428
|
+
}
|
|
429
|
+
return LL2NUM(rv);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
static VALUE read_stream_unlock(VALUE arg) {
|
|
433
|
+
rb_str_unlocktmp(((struct read_stream_args *)arg)->data);
|
|
434
|
+
return Qnil;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/*
|
|
438
|
+
* call-seq:
|
|
439
|
+
* connection.read_stream(stream_id, data, fin: false, ts: nil) -> Integer
|
|
440
|
+
*
|
|
441
|
+
* Reads data on a stream. This should be called when data is received from
|
|
442
|
+
* the QUIC layer. Returns the number of bytes consumed (for flow control).
|
|
443
|
+
* ts is the current time in nanoseconds on any monotonic clock, used for
|
|
444
|
+
* nghttp3's rate limits; it defaults to CLOCK_MONOTONIC. An exception
|
|
445
|
+
* raised by a callback comes out of here.
|
|
446
|
+
*/
|
|
447
|
+
static VALUE rb_nghttp3_connection_read_stream(int argc, VALUE *argv,
|
|
448
|
+
VALUE self) {
|
|
449
|
+
VALUE rb_stream_id, rb_data, rb_opts;
|
|
450
|
+
VALUE rb_fin = Qfalse;
|
|
451
|
+
VALUE rb_ts = Qnil;
|
|
452
|
+
ConnectionObj *obj;
|
|
453
|
+
|
|
454
|
+
rb_scan_args(argc, argv, "2:", &rb_stream_id, &rb_data, &rb_opts);
|
|
455
|
+
|
|
456
|
+
if (!NIL_P(rb_opts)) {
|
|
457
|
+
VALUE rb_fin_val = rb_hash_aref(rb_opts, ID2SYM(rb_intern("fin")));
|
|
458
|
+
if (!NIL_P(rb_fin_val)) {
|
|
459
|
+
rb_fin = rb_fin_val;
|
|
460
|
+
}
|
|
461
|
+
rb_ts = rb_hash_aref(rb_opts, ID2SYM(rb_intern("ts")));
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
465
|
+
|
|
466
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
467
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
Check_Type(rb_data, T_STRING);
|
|
471
|
+
|
|
472
|
+
struct read_stream_args args;
|
|
473
|
+
args.obj = obj;
|
|
474
|
+
args.stream_id = NUM2LL(rb_stream_id);
|
|
475
|
+
args.data = rb_data;
|
|
476
|
+
args.fin = RTEST(rb_fin) ? 1 : 0;
|
|
477
|
+
args.ts = NIL_P(rb_ts) ? monotonic_now() : (nghttp3_tstamp)NUM2ULL(rb_ts);
|
|
478
|
+
|
|
479
|
+
/* Callbacks run Ruby code, which must not change data under nghttp3. */
|
|
480
|
+
rb_str_locktmp(rb_data);
|
|
481
|
+
return rb_ensure(read_stream_body, (VALUE)&args, read_stream_unlock,
|
|
482
|
+
(VALUE)&args);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/*
|
|
486
|
+
* call-seq:
|
|
487
|
+
* connection.writev_stream -> Hash or nil
|
|
488
|
+
*
|
|
489
|
+
* Gets stream data to send to the QUIC layer.
|
|
490
|
+
* Returns a Hash with :stream_id, :fin, and :data keys, or nil if no data.
|
|
491
|
+
*/
|
|
492
|
+
static VALUE rb_nghttp3_connection_writev_stream(VALUE self) {
|
|
493
|
+
ConnectionObj *obj;
|
|
494
|
+
nghttp3_vec vec[16];
|
|
495
|
+
nghttp3_ssize rv;
|
|
496
|
+
int64_t stream_id;
|
|
497
|
+
int fin;
|
|
498
|
+
VALUE rb_result, rb_data;
|
|
499
|
+
size_t i, total_len;
|
|
500
|
+
|
|
501
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
502
|
+
|
|
503
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
504
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
rv = nghttp3_conn_writev_stream(obj->conn, &stream_id, &fin, vec, 16);
|
|
508
|
+
connection_raise_pending(obj);
|
|
509
|
+
|
|
510
|
+
if (rv < 0) {
|
|
511
|
+
nghttp3_rb_raise((int)rv, "Failed to writev stream");
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (rv == 0 && stream_id == -1) {
|
|
515
|
+
return Qnil;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* Calculate total length and concatenate data */
|
|
519
|
+
total_len = 0;
|
|
520
|
+
for (i = 0; i < (size_t)rv; i++) {
|
|
521
|
+
total_len += vec[i].len;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
rb_data = rb_str_buf_new(total_len);
|
|
525
|
+
for (i = 0; i < (size_t)rv; i++) {
|
|
526
|
+
rb_str_buf_cat(rb_data, (const char *)vec[i].base, vec[i].len);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
rb_result = rb_hash_new();
|
|
530
|
+
rb_hash_aset(rb_result, ID2SYM(rb_intern("stream_id")), LL2NUM(stream_id));
|
|
531
|
+
rb_hash_aset(rb_result, ID2SYM(rb_intern("fin")), fin ? Qtrue : Qfalse);
|
|
532
|
+
rb_hash_aset(rb_result, ID2SYM(rb_intern("data")), rb_data);
|
|
533
|
+
|
|
534
|
+
return rb_result;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/*
|
|
538
|
+
* call-seq:
|
|
539
|
+
* connection.add_write_offset(stream_id, n) -> self
|
|
540
|
+
*
|
|
541
|
+
* Tells the connection that n bytes have been accepted by the QUIC layer.
|
|
542
|
+
*/
|
|
543
|
+
static VALUE rb_nghttp3_connection_add_write_offset(VALUE self,
|
|
544
|
+
VALUE rb_stream_id,
|
|
545
|
+
VALUE rb_n) {
|
|
546
|
+
ConnectionObj *obj;
|
|
547
|
+
int rv;
|
|
548
|
+
int64_t stream_id;
|
|
549
|
+
size_t n;
|
|
550
|
+
|
|
551
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
552
|
+
|
|
553
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
554
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
558
|
+
n = NUM2SIZET(rb_n);
|
|
559
|
+
|
|
560
|
+
rv = nghttp3_conn_add_write_offset(obj->conn, stream_id, n);
|
|
561
|
+
connection_raise_pending(obj);
|
|
562
|
+
|
|
563
|
+
if (rv != 0) {
|
|
564
|
+
nghttp3_rb_raise(rv, "Failed to add write offset");
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return self;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/*
|
|
571
|
+
* call-seq:
|
|
572
|
+
* connection.add_ack_offset(stream_id, n) -> self
|
|
573
|
+
*
|
|
574
|
+
* Tells the connection that n bytes have been acknowledged by the remote peer.
|
|
575
|
+
*/
|
|
576
|
+
static VALUE rb_nghttp3_connection_add_ack_offset(VALUE self,
|
|
577
|
+
VALUE rb_stream_id,
|
|
578
|
+
VALUE rb_n) {
|
|
579
|
+
ConnectionObj *obj;
|
|
580
|
+
int rv;
|
|
581
|
+
int64_t stream_id;
|
|
582
|
+
uint64_t n;
|
|
583
|
+
|
|
584
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
585
|
+
|
|
586
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
587
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
591
|
+
n = NUM2ULL(rb_n);
|
|
592
|
+
|
|
593
|
+
rv = nghttp3_conn_add_ack_offset(obj->conn, stream_id, n);
|
|
594
|
+
connection_raise_pending(obj);
|
|
595
|
+
|
|
596
|
+
if (rv != 0) {
|
|
597
|
+
nghttp3_rb_raise(rv, "Failed to add ack offset");
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return self;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/*
|
|
604
|
+
* call-seq:
|
|
605
|
+
* connection.block_stream(stream_id) -> self
|
|
606
|
+
*
|
|
607
|
+
* Marks a stream as blocked due to QUIC flow control.
|
|
608
|
+
*/
|
|
609
|
+
static VALUE rb_nghttp3_connection_block_stream(VALUE self,
|
|
610
|
+
VALUE rb_stream_id) {
|
|
611
|
+
ConnectionObj *obj;
|
|
612
|
+
int64_t stream_id;
|
|
613
|
+
|
|
614
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
615
|
+
|
|
616
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
617
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
621
|
+
nghttp3_conn_block_stream(obj->conn, stream_id);
|
|
622
|
+
|
|
623
|
+
return self;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/*
|
|
627
|
+
* call-seq:
|
|
628
|
+
* connection.unblock_stream(stream_id) -> self
|
|
629
|
+
*
|
|
630
|
+
* Marks a stream as unblocked (no longer blocked by QUIC flow control).
|
|
631
|
+
*/
|
|
632
|
+
static VALUE rb_nghttp3_connection_unblock_stream(VALUE self,
|
|
633
|
+
VALUE rb_stream_id) {
|
|
634
|
+
ConnectionObj *obj;
|
|
635
|
+
int rv;
|
|
636
|
+
int64_t stream_id;
|
|
637
|
+
|
|
638
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
639
|
+
|
|
640
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
641
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
645
|
+
rv = nghttp3_conn_unblock_stream(obj->conn, stream_id);
|
|
646
|
+
connection_raise_pending(obj);
|
|
647
|
+
|
|
648
|
+
if (rv != 0) {
|
|
649
|
+
nghttp3_rb_raise(rv, "Failed to unblock stream");
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return self;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/*
|
|
656
|
+
* call-seq:
|
|
657
|
+
* connection.stream_writable?(stream_id) -> true or false
|
|
658
|
+
*
|
|
659
|
+
* Returns true if the stream is writable.
|
|
660
|
+
*/
|
|
661
|
+
static VALUE rb_nghttp3_connection_stream_writable_p(VALUE self,
|
|
662
|
+
VALUE rb_stream_id) {
|
|
663
|
+
ConnectionObj *obj;
|
|
664
|
+
int64_t stream_id;
|
|
665
|
+
int rv;
|
|
666
|
+
|
|
667
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
668
|
+
|
|
669
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
670
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
674
|
+
rv = nghttp3_conn_is_stream_writable(obj->conn, stream_id);
|
|
675
|
+
|
|
676
|
+
return rv ? Qtrue : Qfalse;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/*
|
|
680
|
+
* call-seq:
|
|
681
|
+
* connection.close_stream(stream_id, app_error_code) -> self
|
|
682
|
+
*
|
|
683
|
+
* Closes the stream with the given error code.
|
|
684
|
+
*/
|
|
685
|
+
static VALUE rb_nghttp3_connection_close_stream(VALUE self, VALUE rb_stream_id,
|
|
686
|
+
VALUE rb_error_code) {
|
|
687
|
+
ConnectionObj *obj;
|
|
688
|
+
int rv;
|
|
689
|
+
int64_t stream_id;
|
|
690
|
+
uint64_t app_error_code;
|
|
691
|
+
|
|
692
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
693
|
+
|
|
694
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
695
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
699
|
+
app_error_code = NUM2ULL(rb_error_code);
|
|
700
|
+
|
|
701
|
+
rv = nghttp3_conn_close_stream(obj->conn, stream_id, app_error_code);
|
|
702
|
+
connection_raise_pending(obj);
|
|
703
|
+
|
|
704
|
+
if (rv != 0) {
|
|
705
|
+
nghttp3_rb_raise(rv, "Failed to close stream");
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return self;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/*
|
|
712
|
+
* call-seq:
|
|
713
|
+
* connection.shutdown_stream_write(stream_id) -> self
|
|
714
|
+
*
|
|
715
|
+
* Prevents any further write operations on the stream.
|
|
716
|
+
*/
|
|
717
|
+
static VALUE rb_nghttp3_connection_shutdown_stream_write(VALUE self,
|
|
718
|
+
VALUE rb_stream_id) {
|
|
719
|
+
ConnectionObj *obj;
|
|
720
|
+
int64_t stream_id;
|
|
721
|
+
|
|
722
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
723
|
+
|
|
724
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
725
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
729
|
+
nghttp3_conn_shutdown_stream_write(obj->conn, stream_id);
|
|
730
|
+
connection_raise_pending(obj);
|
|
731
|
+
|
|
732
|
+
return self;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/*
|
|
736
|
+
* call-seq:
|
|
737
|
+
* connection.resume_stream(stream_id) -> self
|
|
738
|
+
*
|
|
739
|
+
* Resumes a stream that was blocked for input data.
|
|
740
|
+
*/
|
|
741
|
+
static VALUE rb_nghttp3_connection_resume_stream(VALUE self,
|
|
742
|
+
VALUE rb_stream_id) {
|
|
743
|
+
ConnectionObj *obj;
|
|
744
|
+
int rv;
|
|
745
|
+
int64_t stream_id;
|
|
746
|
+
|
|
747
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
748
|
+
|
|
749
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
750
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
754
|
+
rv = nghttp3_conn_resume_stream(obj->conn, stream_id);
|
|
755
|
+
connection_raise_pending(obj);
|
|
756
|
+
|
|
757
|
+
if (rv != 0) {
|
|
758
|
+
nghttp3_rb_raise(rv, "Failed to resume stream");
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
return self;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
struct read_data_args {
|
|
765
|
+
ConnectionObj *obj;
|
|
766
|
+
int64_t stream_id;
|
|
767
|
+
nghttp3_vec *vec;
|
|
768
|
+
uint32_t *pflags;
|
|
769
|
+
nghttp3_ssize result;
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
static VALUE read_data_body(VALUE arg) {
|
|
773
|
+
struct read_data_args *a = (struct read_data_args *)arg;
|
|
774
|
+
ConnectionObj *obj = a->obj;
|
|
775
|
+
VALUE rb_stream_id = LL2NUM(a->stream_id);
|
|
776
|
+
VALUE reader = rb_hash_aref(obj->stream_data_readers, rb_stream_id);
|
|
777
|
+
VALUE data;
|
|
778
|
+
|
|
779
|
+
a->result = 0;
|
|
780
|
+
if (NIL_P(reader)) {
|
|
781
|
+
*a->pflags |= NGHTTP3_DATA_FLAG_EOF;
|
|
782
|
+
return Qnil;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (RB_TYPE_P(reader, T_STRING)) {
|
|
786
|
+
/* The whole body at once: already a frozen copy, made by submit_*. */
|
|
787
|
+
data = reader;
|
|
788
|
+
rb_hash_delete(obj->stream_data_readers, rb_stream_id);
|
|
789
|
+
*a->pflags |= NGHTTP3_DATA_FLAG_EOF;
|
|
790
|
+
} else {
|
|
791
|
+
/* Proc: call it to get data */
|
|
792
|
+
VALUE result = rb_funcall(reader, rb_intern("call"), 1, rb_stream_id);
|
|
793
|
+
|
|
794
|
+
if (NIL_P(result)) {
|
|
795
|
+
*a->pflags |= NGHTTP3_DATA_FLAG_EOF;
|
|
796
|
+
rb_hash_delete(obj->stream_data_readers, rb_stream_id);
|
|
797
|
+
return Qnil;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (SYMBOL_P(result) && SYM2ID(result) == rb_intern("wouldblock")) {
|
|
801
|
+
a->result = NGHTTP3_ERR_WOULDBLOCK;
|
|
802
|
+
return Qnil;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
StringValue(result);
|
|
806
|
+
data = rb_str_new_frozen(result);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/* nghttp3 reads from data until the peer acknowledges it. */
|
|
810
|
+
pending_push(obj, a->stream_id, data);
|
|
811
|
+
a->vec[0].base = (uint8_t *)RSTRING_PTR(data);
|
|
812
|
+
a->vec[0].len = (size_t)RSTRING_LEN(data);
|
|
813
|
+
a->result = 1;
|
|
814
|
+
return Qnil;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/*
|
|
818
|
+
* Callback function for providing body data to nghttp3. The Ruby side runs
|
|
819
|
+
* under rb_protect: an exception waits in pending_exc for writev_stream.
|
|
820
|
+
*/
|
|
821
|
+
static nghttp3_ssize read_data_callback(nghttp3_conn *conn, int64_t stream_id,
|
|
822
|
+
nghttp3_vec *vec, size_t veccnt,
|
|
823
|
+
uint32_t *pflags, void *conn_user_data,
|
|
824
|
+
void *stream_user_data) {
|
|
825
|
+
ConnectionObj *obj = conn_user_data;
|
|
826
|
+
struct read_data_args args = {obj, stream_id, vec, pflags, 0};
|
|
827
|
+
|
|
828
|
+
if (nghttp3_rb_protect(obj, read_data_body, (VALUE)&args) != 0) {
|
|
829
|
+
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
830
|
+
}
|
|
831
|
+
return args.result;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/* Static data_reader structure for use in submit functions */
|
|
835
|
+
static const nghttp3_data_reader data_reader = {.read_data =
|
|
836
|
+
read_data_callback};
|
|
837
|
+
|
|
838
|
+
/*
|
|
839
|
+
* call-seq:
|
|
840
|
+
* connection.submit_request(stream_id, headers, body: nil) -> self
|
|
841
|
+
* connection.submit_request(stream_id, headers) { |stream_id| ... } -> self
|
|
842
|
+
*
|
|
843
|
+
* Submits an HTTP request on the given stream.
|
|
844
|
+
* Headers should be an array of Nghttp3::NV objects.
|
|
845
|
+
*/
|
|
846
|
+
static VALUE rb_nghttp3_connection_submit_request(int argc, VALUE *argv,
|
|
847
|
+
VALUE self) {
|
|
848
|
+
VALUE rb_stream_id, rb_headers, rb_opts, rb_body;
|
|
849
|
+
ConnectionObj *obj;
|
|
850
|
+
int64_t stream_id;
|
|
851
|
+
nghttp3_nv *nva;
|
|
852
|
+
size_t nvlen, i;
|
|
853
|
+
int rv;
|
|
854
|
+
int has_body = 0;
|
|
855
|
+
|
|
856
|
+
rb_scan_args(argc, argv, "2:", &rb_stream_id, &rb_headers, &rb_opts);
|
|
857
|
+
|
|
858
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
859
|
+
|
|
860
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
861
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (obj->is_server) {
|
|
865
|
+
rb_raise(rb_eNghttp3InvalidStateError,
|
|
866
|
+
"submit_request can only be called on client connections");
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
870
|
+
Check_Type(rb_headers, T_ARRAY);
|
|
871
|
+
nvlen = RARRAY_LEN(rb_headers);
|
|
872
|
+
|
|
873
|
+
/* Allocate and convert headers */
|
|
874
|
+
nva = ALLOCA_N(nghttp3_nv, nvlen);
|
|
875
|
+
for (i = 0; i < nvlen; i++) {
|
|
876
|
+
VALUE rb_nv = rb_ary_entry(rb_headers, i);
|
|
877
|
+
nva[i] = nghttp3_rb_nv_to_c(rb_nv);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/* Check for body */
|
|
881
|
+
rb_body = Qnil;
|
|
882
|
+
if (!NIL_P(rb_opts)) {
|
|
883
|
+
rb_body = rb_hash_aref(rb_opts, ID2SYM(rb_intern("body")));
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (!NIL_P(rb_body)) {
|
|
887
|
+
StringValue(rb_body);
|
|
888
|
+
/* A frozen copy, so that the application cannot change the bytes
|
|
889
|
+
* nghttp3 will read. */
|
|
890
|
+
rb_hash_aset(obj->stream_data_readers, LL2NUM(stream_id),
|
|
891
|
+
rb_str_new_frozen(rb_body));
|
|
892
|
+
has_body = 1;
|
|
893
|
+
} else if (rb_block_given_p()) {
|
|
894
|
+
VALUE block = rb_block_proc();
|
|
895
|
+
rb_hash_aset(obj->stream_data_readers, LL2NUM(stream_id), block);
|
|
896
|
+
has_body = 1;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
rv = nghttp3_conn_submit_request(obj->conn, stream_id, nva, nvlen,
|
|
900
|
+
has_body ? &data_reader : NULL, NULL);
|
|
901
|
+
connection_raise_pending(obj);
|
|
902
|
+
|
|
903
|
+
if (rv != 0) {
|
|
904
|
+
rb_hash_delete(obj->stream_data_readers, LL2NUM(stream_id));
|
|
905
|
+
nghttp3_rb_raise(rv, "Failed to submit request");
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
return self;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/*
|
|
912
|
+
* call-seq:
|
|
913
|
+
* connection.submit_response(stream_id, headers, body: nil) -> self
|
|
914
|
+
* connection.submit_response(stream_id, headers) { |stream_id| ... } -> self
|
|
915
|
+
*
|
|
916
|
+
* Submits an HTTP response on the given stream.
|
|
917
|
+
* Headers should be an array of Nghttp3::NV objects.
|
|
918
|
+
*/
|
|
919
|
+
static VALUE rb_nghttp3_connection_submit_response(int argc, VALUE *argv,
|
|
920
|
+
VALUE self) {
|
|
921
|
+
VALUE rb_stream_id, rb_headers, rb_opts, rb_body;
|
|
922
|
+
ConnectionObj *obj;
|
|
923
|
+
int64_t stream_id;
|
|
924
|
+
nghttp3_nv *nva;
|
|
925
|
+
size_t nvlen, i;
|
|
926
|
+
int rv;
|
|
927
|
+
int has_body = 0;
|
|
928
|
+
|
|
929
|
+
rb_scan_args(argc, argv, "2:", &rb_stream_id, &rb_headers, &rb_opts);
|
|
930
|
+
|
|
931
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
932
|
+
|
|
933
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
934
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (!obj->is_server) {
|
|
938
|
+
rb_raise(rb_eNghttp3InvalidStateError,
|
|
939
|
+
"submit_response can only be called on server connections");
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
943
|
+
Check_Type(rb_headers, T_ARRAY);
|
|
944
|
+
nvlen = RARRAY_LEN(rb_headers);
|
|
945
|
+
|
|
946
|
+
/* Allocate and convert headers */
|
|
947
|
+
nva = ALLOCA_N(nghttp3_nv, nvlen);
|
|
948
|
+
for (i = 0; i < nvlen; i++) {
|
|
949
|
+
VALUE rb_nv = rb_ary_entry(rb_headers, i);
|
|
950
|
+
nva[i] = nghttp3_rb_nv_to_c(rb_nv);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/* Check for body */
|
|
954
|
+
rb_body = Qnil;
|
|
955
|
+
if (!NIL_P(rb_opts)) {
|
|
956
|
+
rb_body = rb_hash_aref(rb_opts, ID2SYM(rb_intern("body")));
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (!NIL_P(rb_body)) {
|
|
960
|
+
StringValue(rb_body);
|
|
961
|
+
/* A frozen copy, so that the application cannot change the bytes
|
|
962
|
+
* nghttp3 will read. */
|
|
963
|
+
rb_hash_aset(obj->stream_data_readers, LL2NUM(stream_id),
|
|
964
|
+
rb_str_new_frozen(rb_body));
|
|
965
|
+
has_body = 1;
|
|
966
|
+
} else if (rb_block_given_p()) {
|
|
967
|
+
VALUE block = rb_block_proc();
|
|
968
|
+
rb_hash_aset(obj->stream_data_readers, LL2NUM(stream_id), block);
|
|
969
|
+
has_body = 1;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
rv = nghttp3_conn_submit_response(obj->conn, stream_id, nva, nvlen,
|
|
973
|
+
has_body ? &data_reader : NULL);
|
|
974
|
+
connection_raise_pending(obj);
|
|
975
|
+
|
|
976
|
+
if (rv != 0) {
|
|
977
|
+
rb_hash_delete(obj->stream_data_readers, LL2NUM(stream_id));
|
|
978
|
+
nghttp3_rb_raise(rv, "Failed to submit response");
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
return self;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/*
|
|
985
|
+
* call-seq:
|
|
986
|
+
* connection.submit_info(stream_id, headers) -> self
|
|
987
|
+
*
|
|
988
|
+
* Submits a 1xx informational response on the given stream.
|
|
989
|
+
* Headers should be an array of Nghttp3::NV objects.
|
|
990
|
+
*/
|
|
991
|
+
static VALUE rb_nghttp3_connection_submit_info(VALUE self, VALUE rb_stream_id,
|
|
992
|
+
VALUE rb_headers) {
|
|
993
|
+
ConnectionObj *obj;
|
|
994
|
+
int64_t stream_id;
|
|
995
|
+
nghttp3_nv *nva;
|
|
996
|
+
size_t nvlen, i;
|
|
997
|
+
int rv;
|
|
998
|
+
|
|
999
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1000
|
+
|
|
1001
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
1002
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
1006
|
+
Check_Type(rb_headers, T_ARRAY);
|
|
1007
|
+
nvlen = RARRAY_LEN(rb_headers);
|
|
1008
|
+
|
|
1009
|
+
nva = ALLOCA_N(nghttp3_nv, nvlen);
|
|
1010
|
+
for (i = 0; i < nvlen; i++) {
|
|
1011
|
+
VALUE rb_nv = rb_ary_entry(rb_headers, i);
|
|
1012
|
+
nva[i] = nghttp3_rb_nv_to_c(rb_nv);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
rv = nghttp3_conn_submit_info(obj->conn, stream_id, nva, nvlen);
|
|
1016
|
+
connection_raise_pending(obj);
|
|
1017
|
+
|
|
1018
|
+
if (rv != 0) {
|
|
1019
|
+
nghttp3_rb_raise(rv, "Failed to submit info");
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
return self;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/*
|
|
1026
|
+
* call-seq:
|
|
1027
|
+
* connection.submit_trailers(stream_id, trailers) -> self
|
|
1028
|
+
*
|
|
1029
|
+
* Submits trailer headers on the given stream.
|
|
1030
|
+
* This implicitly ends the stream.
|
|
1031
|
+
* Trailers should be an array of Nghttp3::NV objects.
|
|
1032
|
+
*/
|
|
1033
|
+
static VALUE rb_nghttp3_connection_submit_trailers(VALUE self,
|
|
1034
|
+
VALUE rb_stream_id,
|
|
1035
|
+
VALUE rb_trailers) {
|
|
1036
|
+
ConnectionObj *obj;
|
|
1037
|
+
int64_t stream_id;
|
|
1038
|
+
nghttp3_nv *nva;
|
|
1039
|
+
size_t nvlen, i;
|
|
1040
|
+
int rv;
|
|
1041
|
+
|
|
1042
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1043
|
+
|
|
1044
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
1045
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
1049
|
+
Check_Type(rb_trailers, T_ARRAY);
|
|
1050
|
+
nvlen = RARRAY_LEN(rb_trailers);
|
|
1051
|
+
|
|
1052
|
+
nva = ALLOCA_N(nghttp3_nv, nvlen);
|
|
1053
|
+
for (i = 0; i < nvlen; i++) {
|
|
1054
|
+
VALUE rb_nv = rb_ary_entry(rb_trailers, i);
|
|
1055
|
+
nva[i] = nghttp3_rb_nv_to_c(rb_nv);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
rv = nghttp3_conn_submit_trailers(obj->conn, stream_id, nva, nvlen);
|
|
1059
|
+
connection_raise_pending(obj);
|
|
1060
|
+
|
|
1061
|
+
if (rv != 0) {
|
|
1062
|
+
nghttp3_rb_raise(rv, "Failed to submit trailers");
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
return self;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/*
|
|
1069
|
+
* call-seq:
|
|
1070
|
+
* connection.submit_shutdown_notice -> self
|
|
1071
|
+
*
|
|
1072
|
+
* Signals the intention to shut down the connection gracefully.
|
|
1073
|
+
* After calling this, no new streams will be accepted.
|
|
1074
|
+
*/
|
|
1075
|
+
static VALUE rb_nghttp3_connection_submit_shutdown_notice(VALUE self) {
|
|
1076
|
+
ConnectionObj *obj;
|
|
1077
|
+
int rv;
|
|
1078
|
+
|
|
1079
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1080
|
+
|
|
1081
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
1082
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
rv = nghttp3_conn_submit_shutdown_notice(obj->conn);
|
|
1086
|
+
connection_raise_pending(obj);
|
|
1087
|
+
|
|
1088
|
+
if (rv != 0) {
|
|
1089
|
+
nghttp3_rb_raise(rv, "Failed to submit shutdown notice");
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
return self;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/*
|
|
1096
|
+
* call-seq:
|
|
1097
|
+
* connection.shutdown -> self
|
|
1098
|
+
*
|
|
1099
|
+
* Initiates graceful shutdown. Should be called after submit_shutdown_notice.
|
|
1100
|
+
*/
|
|
1101
|
+
static VALUE rb_nghttp3_connection_shutdown(VALUE self) {
|
|
1102
|
+
ConnectionObj *obj;
|
|
1103
|
+
int rv;
|
|
1104
|
+
|
|
1105
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1106
|
+
|
|
1107
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
1108
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
rv = nghttp3_conn_shutdown(obj->conn);
|
|
1112
|
+
connection_raise_pending(obj);
|
|
1113
|
+
|
|
1114
|
+
if (rv != 0) {
|
|
1115
|
+
nghttp3_rb_raise(rv, "Failed to shutdown connection");
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
return self;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/*
|
|
1122
|
+
* call-seq:
|
|
1123
|
+
* connection.shutdown_stream_read(stream_id) -> self
|
|
1124
|
+
*
|
|
1125
|
+
* Tells the connection that no more data will be read on the stream, so
|
|
1126
|
+
* that QPACK stops waiting on it.
|
|
1127
|
+
*/
|
|
1128
|
+
static VALUE rb_nghttp3_connection_shutdown_stream_read(VALUE self,
|
|
1129
|
+
VALUE rb_stream_id) {
|
|
1130
|
+
ConnectionObj *obj;
|
|
1131
|
+
int rv;
|
|
1132
|
+
|
|
1133
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1134
|
+
|
|
1135
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
1136
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
rv = nghttp3_conn_shutdown_stream_read(obj->conn, NUM2LL(rb_stream_id));
|
|
1140
|
+
connection_raise_pending(obj);
|
|
1141
|
+
|
|
1142
|
+
if (rv != 0) {
|
|
1143
|
+
nghttp3_rb_raise(rv, "Failed to shutdown stream read");
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
return self;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/*
|
|
1150
|
+
* call-seq:
|
|
1151
|
+
* connection.set_max_client_streams_bidi(max_streams) -> self
|
|
1152
|
+
*
|
|
1153
|
+
* Tells a server connection the cumulative number of bidirectional streams
|
|
1154
|
+
* the client may open, as QUIC's MAX_STREAMS allows. nghttp3 uses it for
|
|
1155
|
+
* graceful shutdown.
|
|
1156
|
+
*/
|
|
1157
|
+
static VALUE
|
|
1158
|
+
rb_nghttp3_connection_set_max_client_streams_bidi(VALUE self,
|
|
1159
|
+
VALUE rb_max_streams) {
|
|
1160
|
+
ConnectionObj *obj;
|
|
1161
|
+
|
|
1162
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1163
|
+
|
|
1164
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
1165
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
nghttp3_conn_set_max_client_streams_bidi(obj->conn, NUM2ULL(rb_max_streams));
|
|
1169
|
+
|
|
1170
|
+
return self;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/*
|
|
1174
|
+
* call-seq:
|
|
1175
|
+
* connection.pending_data(stream_id) -> Array
|
|
1176
|
+
*
|
|
1177
|
+
* The body Strings handed to nghttp3 on the stream that the peer has not
|
|
1178
|
+
* acknowledged in full yet, oldest first. A copy, for inspection.
|
|
1179
|
+
*/
|
|
1180
|
+
static VALUE rb_nghttp3_connection_pending_data(VALUE self,
|
|
1181
|
+
VALUE rb_stream_id) {
|
|
1182
|
+
ConnectionObj *obj;
|
|
1183
|
+
|
|
1184
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1185
|
+
|
|
1186
|
+
PendingStream *ps = pending_lookup(obj, NUM2LL(rb_stream_id));
|
|
1187
|
+
return ps == NULL ? rb_ary_new() : rb_ary_dup(ps->strings);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/*
|
|
1191
|
+
* call-seq:
|
|
1192
|
+
* connection.set_stream_user_data(stream_id, data) -> self
|
|
1193
|
+
*
|
|
1194
|
+
* Associates arbitrary data with the given stream.
|
|
1195
|
+
*/
|
|
1196
|
+
static VALUE rb_nghttp3_connection_set_stream_user_data(VALUE self,
|
|
1197
|
+
VALUE rb_stream_id,
|
|
1198
|
+
VALUE rb_data) {
|
|
1199
|
+
ConnectionObj *obj;
|
|
1200
|
+
|
|
1201
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1202
|
+
|
|
1203
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
1204
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
rb_hash_aset(obj->stream_user_data, rb_stream_id, rb_data);
|
|
1208
|
+
|
|
1209
|
+
return self;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/*
|
|
1213
|
+
* call-seq:
|
|
1214
|
+
* connection.get_stream_user_data(stream_id) -> Object or nil
|
|
1215
|
+
*
|
|
1216
|
+
* Returns the data associated with the given stream, or nil if none.
|
|
1217
|
+
*/
|
|
1218
|
+
static VALUE rb_nghttp3_connection_get_stream_user_data(VALUE self,
|
|
1219
|
+
VALUE rb_stream_id) {
|
|
1220
|
+
ConnectionObj *obj;
|
|
1221
|
+
|
|
1222
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
1223
|
+
|
|
1224
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
1225
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
return rb_hash_aref(obj->stream_user_data, rb_stream_id);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
void Init_nghttp3_connection(void) {
|
|
1232
|
+
rb_cNghttp3Connection =
|
|
1233
|
+
rb_define_class_under(rb_mNghttp3, "Connection", rb_cObject);
|
|
1234
|
+
|
|
1235
|
+
/* Disable direct instantiation with new */
|
|
1236
|
+
rb_undef_alloc_func(rb_cNghttp3Connection);
|
|
1237
|
+
|
|
1238
|
+
/* Class methods */
|
|
1239
|
+
rb_define_singleton_method(rb_cNghttp3Connection, "client_new",
|
|
1240
|
+
rb_nghttp3_connection_client_new, -1);
|
|
1241
|
+
rb_define_singleton_method(rb_cNghttp3Connection, "server_new",
|
|
1242
|
+
rb_nghttp3_connection_server_new, -1);
|
|
1243
|
+
|
|
1244
|
+
/* Instance methods */
|
|
1245
|
+
rb_define_method(rb_cNghttp3Connection, "bind_control_stream",
|
|
1246
|
+
rb_nghttp3_connection_bind_control_stream, 1);
|
|
1247
|
+
rb_define_method(rb_cNghttp3Connection, "bind_qpack_streams",
|
|
1248
|
+
rb_nghttp3_connection_bind_qpack_streams, 2);
|
|
1249
|
+
rb_define_method(rb_cNghttp3Connection, "close", rb_nghttp3_connection_close,
|
|
1250
|
+
0);
|
|
1251
|
+
rb_define_method(rb_cNghttp3Connection, "closed?",
|
|
1252
|
+
rb_nghttp3_connection_closed_p, 0);
|
|
1253
|
+
rb_define_method(rb_cNghttp3Connection, "server?",
|
|
1254
|
+
rb_nghttp3_connection_server_p, 0);
|
|
1255
|
+
rb_define_method(rb_cNghttp3Connection, "client?",
|
|
1256
|
+
rb_nghttp3_connection_client_p, 0);
|
|
1257
|
+
|
|
1258
|
+
/* Stream operation methods */
|
|
1259
|
+
rb_define_method(rb_cNghttp3Connection, "read_stream",
|
|
1260
|
+
rb_nghttp3_connection_read_stream, -1);
|
|
1261
|
+
rb_define_method(rb_cNghttp3Connection, "writev_stream",
|
|
1262
|
+
rb_nghttp3_connection_writev_stream, 0);
|
|
1263
|
+
rb_define_method(rb_cNghttp3Connection, "add_write_offset",
|
|
1264
|
+
rb_nghttp3_connection_add_write_offset, 2);
|
|
1265
|
+
rb_define_method(rb_cNghttp3Connection, "add_ack_offset",
|
|
1266
|
+
rb_nghttp3_connection_add_ack_offset, 2);
|
|
1267
|
+
rb_define_method(rb_cNghttp3Connection, "block_stream",
|
|
1268
|
+
rb_nghttp3_connection_block_stream, 1);
|
|
1269
|
+
rb_define_method(rb_cNghttp3Connection, "unblock_stream",
|
|
1270
|
+
rb_nghttp3_connection_unblock_stream, 1);
|
|
1271
|
+
rb_define_method(rb_cNghttp3Connection, "stream_writable?",
|
|
1272
|
+
rb_nghttp3_connection_stream_writable_p, 1);
|
|
1273
|
+
rb_define_method(rb_cNghttp3Connection, "close_stream",
|
|
1274
|
+
rb_nghttp3_connection_close_stream, 2);
|
|
1275
|
+
rb_define_method(rb_cNghttp3Connection, "shutdown_stream_write",
|
|
1276
|
+
rb_nghttp3_connection_shutdown_stream_write, 1);
|
|
1277
|
+
rb_define_method(rb_cNghttp3Connection, "resume_stream",
|
|
1278
|
+
rb_nghttp3_connection_resume_stream, 1);
|
|
1279
|
+
rb_define_method(rb_cNghttp3Connection, "shutdown_stream_read",
|
|
1280
|
+
rb_nghttp3_connection_shutdown_stream_read, 1);
|
|
1281
|
+
rb_define_method(rb_cNghttp3Connection, "set_max_client_streams_bidi",
|
|
1282
|
+
rb_nghttp3_connection_set_max_client_streams_bidi, 1);
|
|
1283
|
+
rb_define_method(rb_cNghttp3Connection, "pending_data",
|
|
1284
|
+
rb_nghttp3_connection_pending_data, 1);
|
|
1285
|
+
|
|
1286
|
+
/* HTTP operation methods */
|
|
1287
|
+
rb_define_method(rb_cNghttp3Connection, "submit_request",
|
|
1288
|
+
rb_nghttp3_connection_submit_request, -1);
|
|
1289
|
+
rb_define_method(rb_cNghttp3Connection, "submit_response",
|
|
1290
|
+
rb_nghttp3_connection_submit_response, -1);
|
|
1291
|
+
rb_define_method(rb_cNghttp3Connection, "submit_info",
|
|
1292
|
+
rb_nghttp3_connection_submit_info, 2);
|
|
1293
|
+
rb_define_method(rb_cNghttp3Connection, "submit_trailers",
|
|
1294
|
+
rb_nghttp3_connection_submit_trailers, 2);
|
|
1295
|
+
rb_define_method(rb_cNghttp3Connection, "submit_shutdown_notice",
|
|
1296
|
+
rb_nghttp3_connection_submit_shutdown_notice, 0);
|
|
1297
|
+
rb_define_method(rb_cNghttp3Connection, "shutdown",
|
|
1298
|
+
rb_nghttp3_connection_shutdown, 0);
|
|
1299
|
+
rb_define_method(rb_cNghttp3Connection, "set_stream_user_data",
|
|
1300
|
+
rb_nghttp3_connection_set_stream_user_data, 2);
|
|
1301
|
+
rb_define_method(rb_cNghttp3Connection, "get_stream_user_data",
|
|
1302
|
+
rb_nghttp3_connection_get_stream_user_data, 1);
|
|
1303
|
+
}
|