nghttp3 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 +7 -0
- data/.clang-format +1 -0
- data/.vscode/extensions.json +6 -0
- data/.vscode/settings.json +10 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +45 -0
- data/Rakefile +20 -0
- data/ext/nghttp3/extconf.rb +18 -0
- data/ext/nghttp3/nghttp3.c +300 -0
- data/ext/nghttp3/nghttp3.h +65 -0
- data/ext/nghttp3/nghttp3_callbacks.c +713 -0
- data/ext/nghttp3/nghttp3_connection.c +1070 -0
- data/ext/nghttp3/nghttp3_nv.c +87 -0
- data/ext/nghttp3/nghttp3_qpack.c +680 -0
- data/ext/nghttp3/nghttp3_settings.c +188 -0
- data/lib/nghttp3/client.rb +236 -0
- data/lib/nghttp3/headers.rb +113 -0
- data/lib/nghttp3/request.rb +147 -0
- data/lib/nghttp3/response.rb +126 -0
- data/lib/nghttp3/server.rb +253 -0
- data/lib/nghttp3/stream_manager.rb +116 -0
- data/lib/nghttp3/version.rb +5 -0
- data/lib/nghttp3.rb +16 -0
- data/sig/nghttp3/callbacks.rbs +30 -0
- data/sig/nghttp3/client.rbs +38 -0
- data/sig/nghttp3/connection.rbs +85 -0
- data/sig/nghttp3/error.rbs +46 -0
- data/sig/nghttp3/headers.rbs +37 -0
- data/sig/nghttp3/info.rbs +7 -0
- data/sig/nghttp3/nv.rbs +9 -0
- data/sig/nghttp3/qpack.rbs +46 -0
- data/sig/nghttp3/request.rbs +35 -0
- data/sig/nghttp3/response.rbs +34 -0
- data/sig/nghttp3/server.rbs +33 -0
- data/sig/nghttp3/settings.rbs +25 -0
- data/sig/nghttp3/stream_manager.rbs +26 -0
- data/sig/nghttp3.rbs +64 -0
- metadata +83 -0
|
@@ -0,0 +1,1070 @@
|
|
|
1
|
+
#include "nghttp3.h"
|
|
2
|
+
|
|
3
|
+
VALUE rb_cNghttp3Connection;
|
|
4
|
+
|
|
5
|
+
typedef struct {
|
|
6
|
+
nghttp3_conn *conn;
|
|
7
|
+
VALUE settings; /* Prevent Settings from being GC'd */
|
|
8
|
+
VALUE callbacks; /* Prevent Callbacks from being GC'd */
|
|
9
|
+
VALUE stream_data_readers; /* stream_id => Proc/String for body data */
|
|
10
|
+
VALUE stream_user_data; /* stream_id => arbitrary user data */
|
|
11
|
+
VALUE pending_data; /* stream_id => [String, ...] for ACK tracking */
|
|
12
|
+
int is_closed;
|
|
13
|
+
int is_server;
|
|
14
|
+
} ConnectionObj;
|
|
15
|
+
|
|
16
|
+
static void connection_mark(void *ptr) {
|
|
17
|
+
ConnectionObj *obj = (ConnectionObj *)ptr;
|
|
18
|
+
if (obj->settings != Qnil) {
|
|
19
|
+
rb_gc_mark(obj->settings);
|
|
20
|
+
}
|
|
21
|
+
if (obj->callbacks != Qnil) {
|
|
22
|
+
rb_gc_mark(obj->callbacks);
|
|
23
|
+
}
|
|
24
|
+
if (obj->stream_data_readers != Qnil) {
|
|
25
|
+
rb_gc_mark(obj->stream_data_readers);
|
|
26
|
+
}
|
|
27
|
+
if (obj->stream_user_data != Qnil) {
|
|
28
|
+
rb_gc_mark(obj->stream_user_data);
|
|
29
|
+
}
|
|
30
|
+
if (obj->pending_data != Qnil) {
|
|
31
|
+
rb_gc_mark(obj->pending_data);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static void connection_free(void *ptr) {
|
|
36
|
+
ConnectionObj *obj = (ConnectionObj *)ptr;
|
|
37
|
+
if (obj->conn != NULL && !obj->is_closed) {
|
|
38
|
+
nghttp3_conn_del(obj->conn);
|
|
39
|
+
obj->conn = NULL;
|
|
40
|
+
}
|
|
41
|
+
xfree(ptr);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static size_t connection_memsize(const void *ptr) {
|
|
45
|
+
return sizeof(ConnectionObj);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static const rb_data_type_t connection_data_type = {
|
|
49
|
+
.wrap_struct_name = "nghttp3_conn_rb",
|
|
50
|
+
.function =
|
|
51
|
+
{
|
|
52
|
+
.dmark = connection_mark,
|
|
53
|
+
.dfree = connection_free,
|
|
54
|
+
.dsize = connection_memsize,
|
|
55
|
+
},
|
|
56
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
static VALUE connection_alloc(VALUE klass) {
|
|
60
|
+
ConnectionObj *obj;
|
|
61
|
+
VALUE self =
|
|
62
|
+
TypedData_Make_Struct(klass, ConnectionObj, &connection_data_type, obj);
|
|
63
|
+
obj->conn = NULL;
|
|
64
|
+
obj->settings = Qnil;
|
|
65
|
+
obj->callbacks = Qnil;
|
|
66
|
+
obj->stream_data_readers = rb_hash_new();
|
|
67
|
+
obj->stream_user_data = rb_hash_new();
|
|
68
|
+
obj->pending_data = rb_hash_new();
|
|
69
|
+
obj->is_closed = 0;
|
|
70
|
+
obj->is_server = 0;
|
|
71
|
+
return self;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* Returns the callbacks object associated with the connection.
|
|
76
|
+
* For internal use by callback wrapper functions.
|
|
77
|
+
*/
|
|
78
|
+
VALUE nghttp3_rb_get_callbacks(VALUE rb_conn) {
|
|
79
|
+
ConnectionObj *obj;
|
|
80
|
+
TypedData_Get_Struct(rb_conn, ConnectionObj, &connection_data_type, obj);
|
|
81
|
+
return obj->callbacks;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* call-seq:
|
|
86
|
+
* Connection.client_new(settings = nil, callbacks = nil) -> Connection
|
|
87
|
+
*
|
|
88
|
+
* Creates a new client HTTP/3 connection.
|
|
89
|
+
* If settings is nil, default settings are used.
|
|
90
|
+
* If callbacks is provided, it will be used for HTTP/3 event notifications.
|
|
91
|
+
*/
|
|
92
|
+
static VALUE rb_nghttp3_connection_client_new(int argc, VALUE *argv,
|
|
93
|
+
VALUE klass) {
|
|
94
|
+
VALUE rb_settings, rb_callbacks;
|
|
95
|
+
ConnectionObj *obj;
|
|
96
|
+
nghttp3_settings settings;
|
|
97
|
+
nghttp3_settings *settings_ptr;
|
|
98
|
+
nghttp3_callbacks callbacks;
|
|
99
|
+
int rv;
|
|
100
|
+
VALUE self;
|
|
101
|
+
|
|
102
|
+
rb_scan_args(argc, argv, "02", &rb_settings, &rb_callbacks);
|
|
103
|
+
|
|
104
|
+
self = connection_alloc(klass);
|
|
105
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
106
|
+
|
|
107
|
+
if (NIL_P(rb_settings)) {
|
|
108
|
+
nghttp3_settings_default(&settings);
|
|
109
|
+
settings_ptr = &settings;
|
|
110
|
+
} else {
|
|
111
|
+
settings_ptr = nghttp3_rb_get_settings(rb_settings);
|
|
112
|
+
obj->settings = rb_settings;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Initialize callbacks structure */
|
|
116
|
+
memset(&callbacks, 0, sizeof(callbacks));
|
|
117
|
+
|
|
118
|
+
if (!NIL_P(rb_callbacks)) {
|
|
119
|
+
obj->callbacks = rb_callbacks;
|
|
120
|
+
nghttp3_rb_setup_callbacks(&callbacks);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
rv = nghttp3_conn_client_new(&obj->conn, &callbacks, settings_ptr, NULL,
|
|
124
|
+
(void *)self);
|
|
125
|
+
|
|
126
|
+
if (rv != 0) {
|
|
127
|
+
nghttp3_rb_raise(rv, "Failed to create client connection");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
obj->is_server = 0;
|
|
131
|
+
return self;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* call-seq:
|
|
136
|
+
* Connection.server_new(settings = nil, callbacks = nil) -> Connection
|
|
137
|
+
*
|
|
138
|
+
* Creates a new server HTTP/3 connection.
|
|
139
|
+
* If settings is nil, default settings are used.
|
|
140
|
+
* If callbacks is provided, it will be used for HTTP/3 event notifications.
|
|
141
|
+
*/
|
|
142
|
+
static VALUE rb_nghttp3_connection_server_new(int argc, VALUE *argv,
|
|
143
|
+
VALUE klass) {
|
|
144
|
+
VALUE rb_settings, rb_callbacks;
|
|
145
|
+
ConnectionObj *obj;
|
|
146
|
+
nghttp3_settings settings;
|
|
147
|
+
nghttp3_settings *settings_ptr;
|
|
148
|
+
nghttp3_callbacks callbacks;
|
|
149
|
+
int rv;
|
|
150
|
+
VALUE self;
|
|
151
|
+
|
|
152
|
+
rb_scan_args(argc, argv, "02", &rb_settings, &rb_callbacks);
|
|
153
|
+
|
|
154
|
+
self = connection_alloc(klass);
|
|
155
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
156
|
+
|
|
157
|
+
if (NIL_P(rb_settings)) {
|
|
158
|
+
nghttp3_settings_default(&settings);
|
|
159
|
+
settings_ptr = &settings;
|
|
160
|
+
} else {
|
|
161
|
+
settings_ptr = nghttp3_rb_get_settings(rb_settings);
|
|
162
|
+
obj->settings = rb_settings;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Initialize callbacks structure */
|
|
166
|
+
memset(&callbacks, 0, sizeof(callbacks));
|
|
167
|
+
|
|
168
|
+
if (!NIL_P(rb_callbacks)) {
|
|
169
|
+
obj->callbacks = rb_callbacks;
|
|
170
|
+
nghttp3_rb_setup_callbacks(&callbacks);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
rv = nghttp3_conn_server_new(&obj->conn, &callbacks, settings_ptr, NULL,
|
|
174
|
+
(void *)self);
|
|
175
|
+
|
|
176
|
+
if (rv != 0) {
|
|
177
|
+
nghttp3_rb_raise(rv, "Failed to create server connection");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
obj->is_server = 1;
|
|
181
|
+
return self;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/*
|
|
185
|
+
* call-seq:
|
|
186
|
+
* connection.bind_control_stream(stream_id) -> self
|
|
187
|
+
*
|
|
188
|
+
* Binds the control stream to the given stream_id.
|
|
189
|
+
* This must be called before sending any HTTP frames.
|
|
190
|
+
*/
|
|
191
|
+
static VALUE rb_nghttp3_connection_bind_control_stream(VALUE self,
|
|
192
|
+
VALUE rb_stream_id) {
|
|
193
|
+
ConnectionObj *obj;
|
|
194
|
+
int64_t stream_id;
|
|
195
|
+
int rv;
|
|
196
|
+
|
|
197
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
198
|
+
|
|
199
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
200
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
204
|
+
rv = nghttp3_conn_bind_control_stream(obj->conn, stream_id);
|
|
205
|
+
|
|
206
|
+
if (rv != 0) {
|
|
207
|
+
nghttp3_rb_raise(rv, "Failed to bind control stream");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return self;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* call-seq:
|
|
215
|
+
* connection.bind_qpack_streams(encoder_stream_id, decoder_stream_id) -> self
|
|
216
|
+
*
|
|
217
|
+
* Binds the QPACK encoder and decoder streams.
|
|
218
|
+
* This must be called before sending any HTTP frames.
|
|
219
|
+
*/
|
|
220
|
+
static VALUE rb_nghttp3_connection_bind_qpack_streams(VALUE self,
|
|
221
|
+
VALUE rb_enc_stream_id,
|
|
222
|
+
VALUE rb_dec_stream_id) {
|
|
223
|
+
ConnectionObj *obj;
|
|
224
|
+
int64_t enc_stream_id, dec_stream_id;
|
|
225
|
+
int rv;
|
|
226
|
+
|
|
227
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
228
|
+
|
|
229
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
230
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
enc_stream_id = NUM2LL(rb_enc_stream_id);
|
|
234
|
+
dec_stream_id = NUM2LL(rb_dec_stream_id);
|
|
235
|
+
|
|
236
|
+
rv = nghttp3_conn_bind_qpack_streams(obj->conn, enc_stream_id, dec_stream_id);
|
|
237
|
+
|
|
238
|
+
if (rv != 0) {
|
|
239
|
+
nghttp3_rb_raise(rv, "Failed to bind QPACK streams");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return self;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/*
|
|
246
|
+
* call-seq:
|
|
247
|
+
* connection.close -> nil
|
|
248
|
+
*
|
|
249
|
+
* Explicitly closes the connection and frees resources.
|
|
250
|
+
* After calling this, the connection object cannot be used.
|
|
251
|
+
*/
|
|
252
|
+
static VALUE rb_nghttp3_connection_close(VALUE self) {
|
|
253
|
+
ConnectionObj *obj;
|
|
254
|
+
|
|
255
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
256
|
+
|
|
257
|
+
if (obj->conn != NULL && !obj->is_closed) {
|
|
258
|
+
nghttp3_conn_del(obj->conn);
|
|
259
|
+
obj->conn = NULL;
|
|
260
|
+
obj->is_closed = 1;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return Qnil;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/*
|
|
267
|
+
* call-seq:
|
|
268
|
+
* connection.closed? -> true or false
|
|
269
|
+
*
|
|
270
|
+
* Returns true if the connection has been closed.
|
|
271
|
+
*/
|
|
272
|
+
static VALUE rb_nghttp3_connection_closed_p(VALUE self) {
|
|
273
|
+
ConnectionObj *obj;
|
|
274
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
275
|
+
return obj->is_closed ? Qtrue : Qfalse;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/*
|
|
279
|
+
* call-seq:
|
|
280
|
+
* connection.server? -> true or false
|
|
281
|
+
*
|
|
282
|
+
* Returns true if this is a server connection.
|
|
283
|
+
*/
|
|
284
|
+
static VALUE rb_nghttp3_connection_server_p(VALUE self) {
|
|
285
|
+
ConnectionObj *obj;
|
|
286
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
287
|
+
return obj->is_server ? Qtrue : Qfalse;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/*
|
|
291
|
+
* call-seq:
|
|
292
|
+
* connection.client? -> true or false
|
|
293
|
+
*
|
|
294
|
+
* Returns true if this is a client connection.
|
|
295
|
+
*/
|
|
296
|
+
static VALUE rb_nghttp3_connection_client_p(VALUE self) {
|
|
297
|
+
ConnectionObj *obj;
|
|
298
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
299
|
+
return obj->is_server ? Qfalse : Qtrue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/*
|
|
303
|
+
* call-seq:
|
|
304
|
+
* connection.read_stream(stream_id, data, fin: false) -> Integer
|
|
305
|
+
*
|
|
306
|
+
* Reads data on a stream. This should be called when data is received from
|
|
307
|
+
* the QUIC layer. Returns the number of bytes consumed (for flow control).
|
|
308
|
+
*/
|
|
309
|
+
static VALUE rb_nghttp3_connection_read_stream(int argc, VALUE *argv,
|
|
310
|
+
VALUE self) {
|
|
311
|
+
VALUE rb_stream_id, rb_data, rb_opts;
|
|
312
|
+
VALUE rb_fin = Qfalse;
|
|
313
|
+
ConnectionObj *obj;
|
|
314
|
+
nghttp3_ssize rv;
|
|
315
|
+
int64_t stream_id;
|
|
316
|
+
int fin;
|
|
317
|
+
|
|
318
|
+
rb_scan_args(argc, argv, "2:", &rb_stream_id, &rb_data, &rb_opts);
|
|
319
|
+
|
|
320
|
+
if (!NIL_P(rb_opts)) {
|
|
321
|
+
VALUE rb_fin_val = rb_hash_aref(rb_opts, ID2SYM(rb_intern("fin")));
|
|
322
|
+
if (!NIL_P(rb_fin_val)) {
|
|
323
|
+
rb_fin = rb_fin_val;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
328
|
+
|
|
329
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
330
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
Check_Type(rb_data, T_STRING);
|
|
334
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
335
|
+
fin = RTEST(rb_fin) ? 1 : 0;
|
|
336
|
+
|
|
337
|
+
rv = nghttp3_conn_read_stream(obj->conn, stream_id,
|
|
338
|
+
(const uint8_t *)RSTRING_PTR(rb_data),
|
|
339
|
+
RSTRING_LEN(rb_data), fin);
|
|
340
|
+
|
|
341
|
+
if (rv < 0) {
|
|
342
|
+
nghttp3_rb_raise((int)rv, "Failed to read stream");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return LL2NUM(rv);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/*
|
|
349
|
+
* call-seq:
|
|
350
|
+
* connection.writev_stream -> Hash or nil
|
|
351
|
+
*
|
|
352
|
+
* Gets stream data to send to the QUIC layer.
|
|
353
|
+
* Returns a Hash with :stream_id, :fin, and :data keys, or nil if no data.
|
|
354
|
+
*/
|
|
355
|
+
static VALUE rb_nghttp3_connection_writev_stream(VALUE self) {
|
|
356
|
+
ConnectionObj *obj;
|
|
357
|
+
nghttp3_vec vec[16];
|
|
358
|
+
nghttp3_ssize rv;
|
|
359
|
+
int64_t stream_id;
|
|
360
|
+
int fin;
|
|
361
|
+
VALUE rb_result, rb_data;
|
|
362
|
+
size_t i, total_len;
|
|
363
|
+
|
|
364
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
365
|
+
|
|
366
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
367
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
rv = nghttp3_conn_writev_stream(obj->conn, &stream_id, &fin, vec, 16);
|
|
371
|
+
|
|
372
|
+
if (rv < 0) {
|
|
373
|
+
nghttp3_rb_raise((int)rv, "Failed to writev stream");
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (rv == 0 && stream_id == -1) {
|
|
377
|
+
return Qnil;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* Calculate total length and concatenate data */
|
|
381
|
+
total_len = 0;
|
|
382
|
+
for (i = 0; i < (size_t)rv; i++) {
|
|
383
|
+
total_len += vec[i].len;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
rb_data = rb_str_buf_new(total_len);
|
|
387
|
+
for (i = 0; i < (size_t)rv; i++) {
|
|
388
|
+
rb_str_buf_cat(rb_data, (const char *)vec[i].base, vec[i].len);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
rb_result = rb_hash_new();
|
|
392
|
+
rb_hash_aset(rb_result, ID2SYM(rb_intern("stream_id")), LL2NUM(stream_id));
|
|
393
|
+
rb_hash_aset(rb_result, ID2SYM(rb_intern("fin")), fin ? Qtrue : Qfalse);
|
|
394
|
+
rb_hash_aset(rb_result, ID2SYM(rb_intern("data")), rb_data);
|
|
395
|
+
|
|
396
|
+
return rb_result;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/*
|
|
400
|
+
* call-seq:
|
|
401
|
+
* connection.add_write_offset(stream_id, n) -> self
|
|
402
|
+
*
|
|
403
|
+
* Tells the connection that n bytes have been accepted by the QUIC layer.
|
|
404
|
+
*/
|
|
405
|
+
static VALUE rb_nghttp3_connection_add_write_offset(VALUE self,
|
|
406
|
+
VALUE rb_stream_id,
|
|
407
|
+
VALUE rb_n) {
|
|
408
|
+
ConnectionObj *obj;
|
|
409
|
+
int rv;
|
|
410
|
+
int64_t stream_id;
|
|
411
|
+
size_t n;
|
|
412
|
+
|
|
413
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
414
|
+
|
|
415
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
416
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
420
|
+
n = NUM2SIZET(rb_n);
|
|
421
|
+
|
|
422
|
+
rv = nghttp3_conn_add_write_offset(obj->conn, stream_id, n);
|
|
423
|
+
|
|
424
|
+
if (rv != 0) {
|
|
425
|
+
nghttp3_rb_raise(rv, "Failed to add write offset");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return self;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/*
|
|
432
|
+
* call-seq:
|
|
433
|
+
* connection.add_ack_offset(stream_id, n) -> self
|
|
434
|
+
*
|
|
435
|
+
* Tells the connection that n bytes have been acknowledged by the remote peer.
|
|
436
|
+
*/
|
|
437
|
+
static VALUE rb_nghttp3_connection_add_ack_offset(VALUE self,
|
|
438
|
+
VALUE rb_stream_id,
|
|
439
|
+
VALUE rb_n) {
|
|
440
|
+
ConnectionObj *obj;
|
|
441
|
+
int rv;
|
|
442
|
+
int64_t stream_id;
|
|
443
|
+
uint64_t n;
|
|
444
|
+
|
|
445
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
446
|
+
|
|
447
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
448
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
452
|
+
n = NUM2ULL(rb_n);
|
|
453
|
+
|
|
454
|
+
rv = nghttp3_conn_add_ack_offset(obj->conn, stream_id, n);
|
|
455
|
+
|
|
456
|
+
if (rv != 0) {
|
|
457
|
+
nghttp3_rb_raise(rv, "Failed to add ack offset");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return self;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/*
|
|
464
|
+
* call-seq:
|
|
465
|
+
* connection.block_stream(stream_id) -> self
|
|
466
|
+
*
|
|
467
|
+
* Marks a stream as blocked due to QUIC flow control.
|
|
468
|
+
*/
|
|
469
|
+
static VALUE rb_nghttp3_connection_block_stream(VALUE self,
|
|
470
|
+
VALUE rb_stream_id) {
|
|
471
|
+
ConnectionObj *obj;
|
|
472
|
+
int64_t stream_id;
|
|
473
|
+
|
|
474
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
475
|
+
|
|
476
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
477
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
481
|
+
nghttp3_conn_block_stream(obj->conn, stream_id);
|
|
482
|
+
|
|
483
|
+
return self;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/*
|
|
487
|
+
* call-seq:
|
|
488
|
+
* connection.unblock_stream(stream_id) -> self
|
|
489
|
+
*
|
|
490
|
+
* Marks a stream as unblocked (no longer blocked by QUIC flow control).
|
|
491
|
+
*/
|
|
492
|
+
static VALUE rb_nghttp3_connection_unblock_stream(VALUE self,
|
|
493
|
+
VALUE rb_stream_id) {
|
|
494
|
+
ConnectionObj *obj;
|
|
495
|
+
int rv;
|
|
496
|
+
int64_t stream_id;
|
|
497
|
+
|
|
498
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
499
|
+
|
|
500
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
501
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
505
|
+
rv = nghttp3_conn_unblock_stream(obj->conn, stream_id);
|
|
506
|
+
|
|
507
|
+
if (rv != 0) {
|
|
508
|
+
nghttp3_rb_raise(rv, "Failed to unblock stream");
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return self;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/*
|
|
515
|
+
* call-seq:
|
|
516
|
+
* connection.stream_writable?(stream_id) -> true or false
|
|
517
|
+
*
|
|
518
|
+
* Returns true if the stream is writable.
|
|
519
|
+
*/
|
|
520
|
+
static VALUE rb_nghttp3_connection_stream_writable_p(VALUE self,
|
|
521
|
+
VALUE rb_stream_id) {
|
|
522
|
+
ConnectionObj *obj;
|
|
523
|
+
int64_t stream_id;
|
|
524
|
+
int rv;
|
|
525
|
+
|
|
526
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
527
|
+
|
|
528
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
529
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
533
|
+
rv = nghttp3_conn_is_stream_writable(obj->conn, stream_id);
|
|
534
|
+
|
|
535
|
+
return rv ? Qtrue : Qfalse;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/*
|
|
539
|
+
* call-seq:
|
|
540
|
+
* connection.close_stream(stream_id, app_error_code) -> self
|
|
541
|
+
*
|
|
542
|
+
* Closes the stream with the given error code.
|
|
543
|
+
*/
|
|
544
|
+
static VALUE rb_nghttp3_connection_close_stream(VALUE self, VALUE rb_stream_id,
|
|
545
|
+
VALUE rb_error_code) {
|
|
546
|
+
ConnectionObj *obj;
|
|
547
|
+
int rv;
|
|
548
|
+
int64_t stream_id;
|
|
549
|
+
uint64_t app_error_code;
|
|
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
|
+
app_error_code = NUM2ULL(rb_error_code);
|
|
559
|
+
|
|
560
|
+
rv = nghttp3_conn_close_stream(obj->conn, stream_id, app_error_code);
|
|
561
|
+
|
|
562
|
+
if (rv != 0) {
|
|
563
|
+
nghttp3_rb_raise(rv, "Failed to close stream");
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return self;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/*
|
|
570
|
+
* call-seq:
|
|
571
|
+
* connection.shutdown_stream_write(stream_id) -> self
|
|
572
|
+
*
|
|
573
|
+
* Prevents any further write operations on the stream.
|
|
574
|
+
*/
|
|
575
|
+
static VALUE rb_nghttp3_connection_shutdown_stream_write(VALUE self,
|
|
576
|
+
VALUE rb_stream_id) {
|
|
577
|
+
ConnectionObj *obj;
|
|
578
|
+
int64_t stream_id;
|
|
579
|
+
|
|
580
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
581
|
+
|
|
582
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
583
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
587
|
+
nghttp3_conn_shutdown_stream_write(obj->conn, stream_id);
|
|
588
|
+
|
|
589
|
+
return self;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/*
|
|
593
|
+
* call-seq:
|
|
594
|
+
* connection.resume_stream(stream_id) -> self
|
|
595
|
+
*
|
|
596
|
+
* Resumes a stream that was blocked for input data.
|
|
597
|
+
*/
|
|
598
|
+
static VALUE rb_nghttp3_connection_resume_stream(VALUE self,
|
|
599
|
+
VALUE rb_stream_id) {
|
|
600
|
+
ConnectionObj *obj;
|
|
601
|
+
int rv;
|
|
602
|
+
int64_t stream_id;
|
|
603
|
+
|
|
604
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
605
|
+
|
|
606
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
607
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
611
|
+
rv = nghttp3_conn_resume_stream(obj->conn, stream_id);
|
|
612
|
+
|
|
613
|
+
if (rv != 0) {
|
|
614
|
+
nghttp3_rb_raise(rv, "Failed to resume stream");
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return self;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/*
|
|
621
|
+
* Callback function for providing body data to nghttp3.
|
|
622
|
+
*/
|
|
623
|
+
static nghttp3_ssize read_data_callback(nghttp3_conn *conn, int64_t stream_id,
|
|
624
|
+
nghttp3_vec *vec, size_t veccnt,
|
|
625
|
+
uint32_t *pflags, void *conn_user_data,
|
|
626
|
+
void *stream_user_data) {
|
|
627
|
+
VALUE rb_conn = (VALUE)conn_user_data;
|
|
628
|
+
ConnectionObj *obj;
|
|
629
|
+
VALUE readers, reader, result;
|
|
630
|
+
VALUE rb_stream_id = LL2NUM(stream_id);
|
|
631
|
+
|
|
632
|
+
TypedData_Get_Struct(rb_conn, ConnectionObj, &connection_data_type, obj);
|
|
633
|
+
readers = obj->stream_data_readers;
|
|
634
|
+
reader = rb_hash_aref(readers, rb_stream_id);
|
|
635
|
+
|
|
636
|
+
if (NIL_P(reader)) {
|
|
637
|
+
*pflags |= NGHTTP3_DATA_FLAG_EOF;
|
|
638
|
+
return 0;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (RB_TYPE_P(reader, T_STRING)) {
|
|
642
|
+
/* String: return all data at once */
|
|
643
|
+
vec[0].base = (uint8_t *)RSTRING_PTR(reader);
|
|
644
|
+
vec[0].len = RSTRING_LEN(reader);
|
|
645
|
+
*pflags |= NGHTTP3_DATA_FLAG_EOF;
|
|
646
|
+
|
|
647
|
+
/* Keep string in pending_data until ACKed */
|
|
648
|
+
VALUE pending = rb_hash_aref(obj->pending_data, rb_stream_id);
|
|
649
|
+
if (NIL_P(pending)) {
|
|
650
|
+
pending = rb_ary_new();
|
|
651
|
+
rb_hash_aset(obj->pending_data, rb_stream_id, pending);
|
|
652
|
+
}
|
|
653
|
+
rb_ary_push(pending, reader);
|
|
654
|
+
rb_hash_delete(readers, rb_stream_id);
|
|
655
|
+
return 1;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/* Proc: call it to get data */
|
|
659
|
+
result = rb_funcall(reader, rb_intern("call"), 1, rb_stream_id);
|
|
660
|
+
|
|
661
|
+
if (NIL_P(result)) {
|
|
662
|
+
*pflags |= NGHTTP3_DATA_FLAG_EOF;
|
|
663
|
+
rb_hash_delete(readers, rb_stream_id);
|
|
664
|
+
return 0;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (SYMBOL_P(result) && SYM2ID(result) == rb_intern("wouldblock")) {
|
|
668
|
+
return NGHTTP3_ERR_WOULDBLOCK;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/* Result should be a String */
|
|
672
|
+
StringValue(result);
|
|
673
|
+
|
|
674
|
+
/* Store in pending_data for GC protection until ACKed */
|
|
675
|
+
VALUE pending = rb_hash_aref(obj->pending_data, rb_stream_id);
|
|
676
|
+
if (NIL_P(pending)) {
|
|
677
|
+
pending = rb_ary_new();
|
|
678
|
+
rb_hash_aset(obj->pending_data, rb_stream_id, pending);
|
|
679
|
+
}
|
|
680
|
+
rb_ary_push(pending, result);
|
|
681
|
+
|
|
682
|
+
vec[0].base = (uint8_t *)RSTRING_PTR(result);
|
|
683
|
+
vec[0].len = RSTRING_LEN(result);
|
|
684
|
+
|
|
685
|
+
return 1;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* Static data_reader structure for use in submit functions */
|
|
689
|
+
static const nghttp3_data_reader data_reader = {.read_data =
|
|
690
|
+
read_data_callback};
|
|
691
|
+
|
|
692
|
+
/*
|
|
693
|
+
* call-seq:
|
|
694
|
+
* connection.submit_request(stream_id, headers, body: nil) -> self
|
|
695
|
+
* connection.submit_request(stream_id, headers) { |stream_id| ... } -> self
|
|
696
|
+
*
|
|
697
|
+
* Submits an HTTP request on the given stream.
|
|
698
|
+
* Headers should be an array of Nghttp3::NV objects.
|
|
699
|
+
*/
|
|
700
|
+
static VALUE rb_nghttp3_connection_submit_request(int argc, VALUE *argv,
|
|
701
|
+
VALUE self) {
|
|
702
|
+
VALUE rb_stream_id, rb_headers, rb_opts, rb_body;
|
|
703
|
+
ConnectionObj *obj;
|
|
704
|
+
int64_t stream_id;
|
|
705
|
+
nghttp3_nv *nva;
|
|
706
|
+
size_t nvlen, i;
|
|
707
|
+
int rv;
|
|
708
|
+
int has_body = 0;
|
|
709
|
+
|
|
710
|
+
rb_scan_args(argc, argv, "2:", &rb_stream_id, &rb_headers, &rb_opts);
|
|
711
|
+
|
|
712
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
713
|
+
|
|
714
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
715
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (obj->is_server) {
|
|
719
|
+
rb_raise(rb_eNghttp3InvalidStateError,
|
|
720
|
+
"submit_request can only be called on client connections");
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
724
|
+
Check_Type(rb_headers, T_ARRAY);
|
|
725
|
+
nvlen = RARRAY_LEN(rb_headers);
|
|
726
|
+
|
|
727
|
+
/* Allocate and convert headers */
|
|
728
|
+
nva = ALLOCA_N(nghttp3_nv, nvlen);
|
|
729
|
+
for (i = 0; i < nvlen; i++) {
|
|
730
|
+
VALUE rb_nv = rb_ary_entry(rb_headers, i);
|
|
731
|
+
nva[i] = nghttp3_rb_nv_to_c(rb_nv);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/* Check for body */
|
|
735
|
+
rb_body = Qnil;
|
|
736
|
+
if (!NIL_P(rb_opts)) {
|
|
737
|
+
rb_body = rb_hash_aref(rb_opts, ID2SYM(rb_intern("body")));
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (!NIL_P(rb_body)) {
|
|
741
|
+
StringValue(rb_body);
|
|
742
|
+
rb_hash_aset(obj->stream_data_readers, LL2NUM(stream_id), rb_body);
|
|
743
|
+
has_body = 1;
|
|
744
|
+
} else if (rb_block_given_p()) {
|
|
745
|
+
VALUE block = rb_block_proc();
|
|
746
|
+
rb_hash_aset(obj->stream_data_readers, LL2NUM(stream_id), block);
|
|
747
|
+
has_body = 1;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
rv = nghttp3_conn_submit_request(obj->conn, stream_id, nva, nvlen,
|
|
751
|
+
has_body ? &data_reader : NULL, NULL);
|
|
752
|
+
|
|
753
|
+
if (rv != 0) {
|
|
754
|
+
rb_hash_delete(obj->stream_data_readers, LL2NUM(stream_id));
|
|
755
|
+
nghttp3_rb_raise(rv, "Failed to submit request");
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
return self;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/*
|
|
762
|
+
* call-seq:
|
|
763
|
+
* connection.submit_response(stream_id, headers, body: nil) -> self
|
|
764
|
+
* connection.submit_response(stream_id, headers) { |stream_id| ... } -> self
|
|
765
|
+
*
|
|
766
|
+
* Submits an HTTP response on the given stream.
|
|
767
|
+
* Headers should be an array of Nghttp3::NV objects.
|
|
768
|
+
*/
|
|
769
|
+
static VALUE rb_nghttp3_connection_submit_response(int argc, VALUE *argv,
|
|
770
|
+
VALUE self) {
|
|
771
|
+
VALUE rb_stream_id, rb_headers, rb_opts, rb_body;
|
|
772
|
+
ConnectionObj *obj;
|
|
773
|
+
int64_t stream_id;
|
|
774
|
+
nghttp3_nv *nva;
|
|
775
|
+
size_t nvlen, i;
|
|
776
|
+
int rv;
|
|
777
|
+
int has_body = 0;
|
|
778
|
+
|
|
779
|
+
rb_scan_args(argc, argv, "2:", &rb_stream_id, &rb_headers, &rb_opts);
|
|
780
|
+
|
|
781
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
782
|
+
|
|
783
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
784
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (!obj->is_server) {
|
|
788
|
+
rb_raise(rb_eNghttp3InvalidStateError,
|
|
789
|
+
"submit_response can only be called on server connections");
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
793
|
+
Check_Type(rb_headers, T_ARRAY);
|
|
794
|
+
nvlen = RARRAY_LEN(rb_headers);
|
|
795
|
+
|
|
796
|
+
/* Allocate and convert headers */
|
|
797
|
+
nva = ALLOCA_N(nghttp3_nv, nvlen);
|
|
798
|
+
for (i = 0; i < nvlen; i++) {
|
|
799
|
+
VALUE rb_nv = rb_ary_entry(rb_headers, i);
|
|
800
|
+
nva[i] = nghttp3_rb_nv_to_c(rb_nv);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/* Check for body */
|
|
804
|
+
rb_body = Qnil;
|
|
805
|
+
if (!NIL_P(rb_opts)) {
|
|
806
|
+
rb_body = rb_hash_aref(rb_opts, ID2SYM(rb_intern("body")));
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
if (!NIL_P(rb_body)) {
|
|
810
|
+
StringValue(rb_body);
|
|
811
|
+
rb_hash_aset(obj->stream_data_readers, LL2NUM(stream_id), rb_body);
|
|
812
|
+
has_body = 1;
|
|
813
|
+
} else if (rb_block_given_p()) {
|
|
814
|
+
VALUE block = rb_block_proc();
|
|
815
|
+
rb_hash_aset(obj->stream_data_readers, LL2NUM(stream_id), block);
|
|
816
|
+
has_body = 1;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
rv = nghttp3_conn_submit_response(obj->conn, stream_id, nva, nvlen,
|
|
820
|
+
has_body ? &data_reader : NULL);
|
|
821
|
+
|
|
822
|
+
if (rv != 0) {
|
|
823
|
+
rb_hash_delete(obj->stream_data_readers, LL2NUM(stream_id));
|
|
824
|
+
nghttp3_rb_raise(rv, "Failed to submit response");
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
return self;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/*
|
|
831
|
+
* call-seq:
|
|
832
|
+
* connection.submit_info(stream_id, headers) -> self
|
|
833
|
+
*
|
|
834
|
+
* Submits a 1xx informational response on the given stream.
|
|
835
|
+
* Headers should be an array of Nghttp3::NV objects.
|
|
836
|
+
*/
|
|
837
|
+
static VALUE rb_nghttp3_connection_submit_info(VALUE self, VALUE rb_stream_id,
|
|
838
|
+
VALUE rb_headers) {
|
|
839
|
+
ConnectionObj *obj;
|
|
840
|
+
int64_t stream_id;
|
|
841
|
+
nghttp3_nv *nva;
|
|
842
|
+
size_t nvlen, i;
|
|
843
|
+
int rv;
|
|
844
|
+
|
|
845
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
846
|
+
|
|
847
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
848
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
852
|
+
Check_Type(rb_headers, T_ARRAY);
|
|
853
|
+
nvlen = RARRAY_LEN(rb_headers);
|
|
854
|
+
|
|
855
|
+
nva = ALLOCA_N(nghttp3_nv, nvlen);
|
|
856
|
+
for (i = 0; i < nvlen; i++) {
|
|
857
|
+
VALUE rb_nv = rb_ary_entry(rb_headers, i);
|
|
858
|
+
nva[i] = nghttp3_rb_nv_to_c(rb_nv);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
rv = nghttp3_conn_submit_info(obj->conn, stream_id, nva, nvlen);
|
|
862
|
+
|
|
863
|
+
if (rv != 0) {
|
|
864
|
+
nghttp3_rb_raise(rv, "Failed to submit info");
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
return self;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/*
|
|
871
|
+
* call-seq:
|
|
872
|
+
* connection.submit_trailers(stream_id, trailers) -> self
|
|
873
|
+
*
|
|
874
|
+
* Submits trailer headers on the given stream.
|
|
875
|
+
* This implicitly ends the stream.
|
|
876
|
+
* Trailers should be an array of Nghttp3::NV objects.
|
|
877
|
+
*/
|
|
878
|
+
static VALUE rb_nghttp3_connection_submit_trailers(VALUE self,
|
|
879
|
+
VALUE rb_stream_id,
|
|
880
|
+
VALUE rb_trailers) {
|
|
881
|
+
ConnectionObj *obj;
|
|
882
|
+
int64_t stream_id;
|
|
883
|
+
nghttp3_nv *nva;
|
|
884
|
+
size_t nvlen, i;
|
|
885
|
+
int rv;
|
|
886
|
+
|
|
887
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
888
|
+
|
|
889
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
890
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
stream_id = NUM2LL(rb_stream_id);
|
|
894
|
+
Check_Type(rb_trailers, T_ARRAY);
|
|
895
|
+
nvlen = RARRAY_LEN(rb_trailers);
|
|
896
|
+
|
|
897
|
+
nva = ALLOCA_N(nghttp3_nv, nvlen);
|
|
898
|
+
for (i = 0; i < nvlen; i++) {
|
|
899
|
+
VALUE rb_nv = rb_ary_entry(rb_trailers, i);
|
|
900
|
+
nva[i] = nghttp3_rb_nv_to_c(rb_nv);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
rv = nghttp3_conn_submit_trailers(obj->conn, stream_id, nva, nvlen);
|
|
904
|
+
|
|
905
|
+
if (rv != 0) {
|
|
906
|
+
nghttp3_rb_raise(rv, "Failed to submit trailers");
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
return self;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/*
|
|
913
|
+
* call-seq:
|
|
914
|
+
* connection.submit_shutdown_notice -> self
|
|
915
|
+
*
|
|
916
|
+
* Signals the intention to shut down the connection gracefully.
|
|
917
|
+
* After calling this, no new streams will be accepted.
|
|
918
|
+
*/
|
|
919
|
+
static VALUE rb_nghttp3_connection_submit_shutdown_notice(VALUE self) {
|
|
920
|
+
ConnectionObj *obj;
|
|
921
|
+
int rv;
|
|
922
|
+
|
|
923
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
924
|
+
|
|
925
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
926
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
rv = nghttp3_conn_submit_shutdown_notice(obj->conn);
|
|
930
|
+
|
|
931
|
+
if (rv != 0) {
|
|
932
|
+
nghttp3_rb_raise(rv, "Failed to submit shutdown notice");
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
return self;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/*
|
|
939
|
+
* call-seq:
|
|
940
|
+
* connection.shutdown -> self
|
|
941
|
+
*
|
|
942
|
+
* Initiates graceful shutdown. Should be called after submit_shutdown_notice.
|
|
943
|
+
*/
|
|
944
|
+
static VALUE rb_nghttp3_connection_shutdown(VALUE self) {
|
|
945
|
+
ConnectionObj *obj;
|
|
946
|
+
int rv;
|
|
947
|
+
|
|
948
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
949
|
+
|
|
950
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
951
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
rv = nghttp3_conn_shutdown(obj->conn);
|
|
955
|
+
|
|
956
|
+
if (rv != 0) {
|
|
957
|
+
nghttp3_rb_raise(rv, "Failed to shutdown connection");
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
return self;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/*
|
|
964
|
+
* call-seq:
|
|
965
|
+
* connection.set_stream_user_data(stream_id, data) -> self
|
|
966
|
+
*
|
|
967
|
+
* Associates arbitrary data with the given stream.
|
|
968
|
+
*/
|
|
969
|
+
static VALUE rb_nghttp3_connection_set_stream_user_data(VALUE self,
|
|
970
|
+
VALUE rb_stream_id,
|
|
971
|
+
VALUE rb_data) {
|
|
972
|
+
ConnectionObj *obj;
|
|
973
|
+
|
|
974
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
975
|
+
|
|
976
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
977
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
rb_hash_aset(obj->stream_user_data, rb_stream_id, rb_data);
|
|
981
|
+
|
|
982
|
+
return self;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/*
|
|
986
|
+
* call-seq:
|
|
987
|
+
* connection.get_stream_user_data(stream_id) -> Object or nil
|
|
988
|
+
*
|
|
989
|
+
* Returns the data associated with the given stream, or nil if none.
|
|
990
|
+
*/
|
|
991
|
+
static VALUE rb_nghttp3_connection_get_stream_user_data(VALUE self,
|
|
992
|
+
VALUE rb_stream_id) {
|
|
993
|
+
ConnectionObj *obj;
|
|
994
|
+
|
|
995
|
+
TypedData_Get_Struct(self, ConnectionObj, &connection_data_type, obj);
|
|
996
|
+
|
|
997
|
+
if (obj->conn == NULL || obj->is_closed) {
|
|
998
|
+
rb_raise(rb_eNghttp3InvalidStateError, "Connection is closed");
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return rb_hash_aref(obj->stream_user_data, rb_stream_id);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
void Init_nghttp3_connection(void) {
|
|
1005
|
+
rb_cNghttp3Connection =
|
|
1006
|
+
rb_define_class_under(rb_mNghttp3, "Connection", rb_cObject);
|
|
1007
|
+
|
|
1008
|
+
/* Disable direct instantiation with new */
|
|
1009
|
+
rb_undef_alloc_func(rb_cNghttp3Connection);
|
|
1010
|
+
|
|
1011
|
+
/* Class methods */
|
|
1012
|
+
rb_define_singleton_method(rb_cNghttp3Connection, "client_new",
|
|
1013
|
+
rb_nghttp3_connection_client_new, -1);
|
|
1014
|
+
rb_define_singleton_method(rb_cNghttp3Connection, "server_new",
|
|
1015
|
+
rb_nghttp3_connection_server_new, -1);
|
|
1016
|
+
|
|
1017
|
+
/* Instance methods */
|
|
1018
|
+
rb_define_method(rb_cNghttp3Connection, "bind_control_stream",
|
|
1019
|
+
rb_nghttp3_connection_bind_control_stream, 1);
|
|
1020
|
+
rb_define_method(rb_cNghttp3Connection, "bind_qpack_streams",
|
|
1021
|
+
rb_nghttp3_connection_bind_qpack_streams, 2);
|
|
1022
|
+
rb_define_method(rb_cNghttp3Connection, "close", rb_nghttp3_connection_close,
|
|
1023
|
+
0);
|
|
1024
|
+
rb_define_method(rb_cNghttp3Connection, "closed?",
|
|
1025
|
+
rb_nghttp3_connection_closed_p, 0);
|
|
1026
|
+
rb_define_method(rb_cNghttp3Connection, "server?",
|
|
1027
|
+
rb_nghttp3_connection_server_p, 0);
|
|
1028
|
+
rb_define_method(rb_cNghttp3Connection, "client?",
|
|
1029
|
+
rb_nghttp3_connection_client_p, 0);
|
|
1030
|
+
|
|
1031
|
+
/* Stream operation methods */
|
|
1032
|
+
rb_define_method(rb_cNghttp3Connection, "read_stream",
|
|
1033
|
+
rb_nghttp3_connection_read_stream, -1);
|
|
1034
|
+
rb_define_method(rb_cNghttp3Connection, "writev_stream",
|
|
1035
|
+
rb_nghttp3_connection_writev_stream, 0);
|
|
1036
|
+
rb_define_method(rb_cNghttp3Connection, "add_write_offset",
|
|
1037
|
+
rb_nghttp3_connection_add_write_offset, 2);
|
|
1038
|
+
rb_define_method(rb_cNghttp3Connection, "add_ack_offset",
|
|
1039
|
+
rb_nghttp3_connection_add_ack_offset, 2);
|
|
1040
|
+
rb_define_method(rb_cNghttp3Connection, "block_stream",
|
|
1041
|
+
rb_nghttp3_connection_block_stream, 1);
|
|
1042
|
+
rb_define_method(rb_cNghttp3Connection, "unblock_stream",
|
|
1043
|
+
rb_nghttp3_connection_unblock_stream, 1);
|
|
1044
|
+
rb_define_method(rb_cNghttp3Connection, "stream_writable?",
|
|
1045
|
+
rb_nghttp3_connection_stream_writable_p, 1);
|
|
1046
|
+
rb_define_method(rb_cNghttp3Connection, "close_stream",
|
|
1047
|
+
rb_nghttp3_connection_close_stream, 2);
|
|
1048
|
+
rb_define_method(rb_cNghttp3Connection, "shutdown_stream_write",
|
|
1049
|
+
rb_nghttp3_connection_shutdown_stream_write, 1);
|
|
1050
|
+
rb_define_method(rb_cNghttp3Connection, "resume_stream",
|
|
1051
|
+
rb_nghttp3_connection_resume_stream, 1);
|
|
1052
|
+
|
|
1053
|
+
/* HTTP operation methods */
|
|
1054
|
+
rb_define_method(rb_cNghttp3Connection, "submit_request",
|
|
1055
|
+
rb_nghttp3_connection_submit_request, -1);
|
|
1056
|
+
rb_define_method(rb_cNghttp3Connection, "submit_response",
|
|
1057
|
+
rb_nghttp3_connection_submit_response, -1);
|
|
1058
|
+
rb_define_method(rb_cNghttp3Connection, "submit_info",
|
|
1059
|
+
rb_nghttp3_connection_submit_info, 2);
|
|
1060
|
+
rb_define_method(rb_cNghttp3Connection, "submit_trailers",
|
|
1061
|
+
rb_nghttp3_connection_submit_trailers, 2);
|
|
1062
|
+
rb_define_method(rb_cNghttp3Connection, "submit_shutdown_notice",
|
|
1063
|
+
rb_nghttp3_connection_submit_shutdown_notice, 0);
|
|
1064
|
+
rb_define_method(rb_cNghttp3Connection, "shutdown",
|
|
1065
|
+
rb_nghttp3_connection_shutdown, 0);
|
|
1066
|
+
rb_define_method(rb_cNghttp3Connection, "set_stream_user_data",
|
|
1067
|
+
rb_nghttp3_connection_set_stream_user_data, 2);
|
|
1068
|
+
rb_define_method(rb_cNghttp3Connection, "get_stream_user_data",
|
|
1069
|
+
rb_nghttp3_connection_get_stream_user_data, 1);
|
|
1070
|
+
}
|