@gjsify/http2-native 0.4.0 → 0.4.4
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.
- package/lib/types/index.d.ts +2 -0
- package/lib/types/session-bridge.gjs.spec.d.ts +1 -0
- package/package.json +60 -55
- package/prebuilds/linux-aarch64/GjsifyHttp2-1.0.gir +419 -0
- package/prebuilds/linux-aarch64/GjsifyHttp2-1.0.typelib +0 -0
- package/prebuilds/linux-aarch64/libgjsifyhttp2.so +0 -0
- package/prebuilds/linux-ppc64/GjsifyHttp2-1.0.gir +419 -0
- package/prebuilds/linux-ppc64/GjsifyHttp2-1.0.typelib +0 -0
- package/prebuilds/linux-ppc64/libgjsifyhttp2.so +0 -0
- package/prebuilds/linux-riscv64/GjsifyHttp2-1.0.gir +419 -0
- package/prebuilds/linux-riscv64/GjsifyHttp2-1.0.typelib +0 -0
- package/prebuilds/linux-riscv64/libgjsifyhttp2.so +0 -0
- package/prebuilds/linux-s390x/GjsifyHttp2-1.0.gir +419 -0
- package/prebuilds/linux-s390x/GjsifyHttp2-1.0.typelib +0 -0
- package/prebuilds/linux-s390x/libgjsifyhttp2.so +0 -0
- package/prebuilds/linux-x86_64/GjsifyHttp2-1.0.gir +419 -0
- package/prebuilds/linux-x86_64/GjsifyHttp2-1.0.typelib +0 -0
- package/prebuilds/linux-x86_64/libgjsifyhttp2.so +0 -0
- package/src/vala/frame-encoder.vala +241 -0
- package/src/vala/nghttp2-helpers.c +842 -0
- package/src/vala/nghttp2-helpers.h +327 -0
- package/src/vala/session-bridge.vala +468 -0
- package/src/vala/stream-id-allocator.vala +62 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tiny C shim around libnghttp2's HPACK encoder + frame helpers + session API.
|
|
3
|
+
*
|
|
4
|
+
* Vala can bind nghttp2 directly, but the calling conventions for opaque
|
|
5
|
+
* out-pointer constructors (nghttp2_hd_deflate_new), pointer-array structs
|
|
6
|
+
* (nghttp2_nv) and the session-callback machinery are awkward to express
|
|
7
|
+
* across a VAPI we have to maintain by hand. Wrapping them in plain C
|
|
8
|
+
* functions returning GBytes / opaque pointers is far simpler — and keeps
|
|
9
|
+
* every pointer hand-off inside C, which is exactly the pattern
|
|
10
|
+
* @gjsify/http-soup-bridge / @gjsify/webrtc-native use to keep
|
|
11
|
+
* SpiderMonkey GC out of the libnghttp2 boxed lifecycle.
|
|
12
|
+
*
|
|
13
|
+
* Reference: nghttp2/nghttp2.h (libnghttp2 ≥ 1.40).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
#ifndef GJSIFY_HTTP2_NGHTTP2_HELPERS_H
|
|
17
|
+
#define GJSIFY_HTTP2_NGHTTP2_HELPERS_H
|
|
18
|
+
|
|
19
|
+
#include <glib.h>
|
|
20
|
+
#include <glib-object.h>
|
|
21
|
+
#include <nghttp2/nghttp2.h>
|
|
22
|
+
|
|
23
|
+
G_BEGIN_DECLS
|
|
24
|
+
|
|
25
|
+
/* ────────────────────────────────────────────────────────────────────── *
|
|
26
|
+
* HPACK + standalone frame builders (used by FrameEncoder)
|
|
27
|
+
* ────────────────────────────────────────────────────────────────────── */
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* gjsify_http2_hpack_encode:
|
|
31
|
+
* @names: flat name array (length = n_pairs)
|
|
32
|
+
* @values: flat value array (length = n_pairs)
|
|
33
|
+
* @n_pairs: number of header pairs
|
|
34
|
+
*
|
|
35
|
+
* HPACK-encode @n_pairs name/value strings using a fresh nghttp2 deflater
|
|
36
|
+
* (default 4096-byte dynamic table). Returns a freshly-allocated #GBytes
|
|
37
|
+
* containing the encoded header block, or %NULL on error.
|
|
38
|
+
*
|
|
39
|
+
* Lower-cases nothing — caller is responsible for HTTP/2 lowercase rules.
|
|
40
|
+
*/
|
|
41
|
+
GBytes *gjsify_http2_hpack_encode (char **names,
|
|
42
|
+
char **values,
|
|
43
|
+
gsize n_pairs);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* gjsify_http2_pack_frame:
|
|
47
|
+
* @type: nghttp2 frame type (1=HEADERS, 5=PUSH_PROMISE, 0=DATA, ...)
|
|
48
|
+
* @flags: frame flags (END_STREAM=0x01, END_HEADERS=0x04, ...)
|
|
49
|
+
* @stream_id: stream identifier (31-bit)
|
|
50
|
+
* @payload: frame payload bytes (already HPACK-encoded for header frames,
|
|
51
|
+
* raw bytes for DATA / PUSH_PROMISE-with-promised-id-prefix)
|
|
52
|
+
*
|
|
53
|
+
* Builds a complete HTTP/2 frame: 9-byte fixed header (length, type, flags,
|
|
54
|
+
* stream-id) followed by @payload. Returns a freshly-allocated #GBytes.
|
|
55
|
+
*
|
|
56
|
+
* Caller is responsible for splitting payloads larger than 16384 bytes
|
|
57
|
+
* across CONTINUATION frames if needed (this helper does NOT segment).
|
|
58
|
+
*/
|
|
59
|
+
GBytes *gjsify_http2_pack_frame (guint8 type,
|
|
60
|
+
guint8 flags,
|
|
61
|
+
guint32 stream_id,
|
|
62
|
+
GBytes *payload);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* gjsify_http2_pack_push_promise:
|
|
66
|
+
* @associated_stream_id: the parent stream the push is associated with
|
|
67
|
+
* @promised_stream_id: the new even-numbered stream-id the server reserves
|
|
68
|
+
* @header_block: HPACK-encoded request headers for the push
|
|
69
|
+
*
|
|
70
|
+
* Convenience wrapper combining the 4-byte promised-stream-id prefix that
|
|
71
|
+
* PUSH_PROMISE requires with @header_block, then frames it. Sets
|
|
72
|
+
* END_HEADERS flag (single-frame header block — caller must keep
|
|
73
|
+
* @header_block ≤ remote SETTINGS_MAX_FRAME_SIZE − 4).
|
|
74
|
+
*/
|
|
75
|
+
GBytes *gjsify_http2_pack_push_promise (guint32 associated_stream_id,
|
|
76
|
+
guint32 promised_stream_id,
|
|
77
|
+
GBytes *header_block);
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* gjsify_http2_nghttp2_version:
|
|
81
|
+
*
|
|
82
|
+
* Returns the libnghttp2 runtime version string ("1.62.0", ...).
|
|
83
|
+
*/
|
|
84
|
+
const char *gjsify_http2_nghttp2_version (void);
|
|
85
|
+
|
|
86
|
+
/* ────────────────────────────────────────────────────────────────────── *
|
|
87
|
+
* Session API — opaque wrapper around an nghttp2_session + event queue.
|
|
88
|
+
*
|
|
89
|
+
* Events are captured C-side via nghttp2 callbacks and stored in a GQueue.
|
|
90
|
+
* The Vala-side wrapper drains the queue (gjsify_http2_session_drain_events)
|
|
91
|
+
* and re-emits each entry as a GLib signal via GLib.Idle.add() so JS-visible
|
|
92
|
+
* dispatch happens on the main loop thread, matching the webrtc-native
|
|
93
|
+
* streaming-thread → main-thread pattern.
|
|
94
|
+
*
|
|
95
|
+
* Threading model: nghttp2 callbacks run inside `gjsify_http2_session_recv`
|
|
96
|
+
* (called on the main loop in this codebase since I/O is GLib-driven), so
|
|
97
|
+
* we never need a cross-thread queue; the Idle.add() hop is purely to
|
|
98
|
+
* decouple JS dispatch from active I/O frames (avoid re-entrancy).
|
|
99
|
+
* ────────────────────────────────────────────────────────────────────── */
|
|
100
|
+
|
|
101
|
+
/* Opaque session handle. */
|
|
102
|
+
typedef struct _GjsifyHttp2Session GjsifyHttp2Session;
|
|
103
|
+
|
|
104
|
+
/* Mode flags for session_new. */
|
|
105
|
+
typedef enum {
|
|
106
|
+
GJSIFY_HTTP2_SESSION_MODE_SERVER = 0,
|
|
107
|
+
GJSIFY_HTTP2_SESSION_MODE_CLIENT = 1
|
|
108
|
+
} GjsifyHttp2SessionMode;
|
|
109
|
+
|
|
110
|
+
/* Event kinds — kept stable across versions; appended only. */
|
|
111
|
+
typedef enum {
|
|
112
|
+
GJSIFY_HTTP2_EVENT_HEADERS = 1, /* request/response HEADERS complete */
|
|
113
|
+
GJSIFY_HTTP2_EVENT_DATA = 2, /* DATA chunk for a stream */
|
|
114
|
+
GJSIFY_HTTP2_EVENT_STREAM_CLOSED = 3, /* stream finished (after END_STREAM or RST_STREAM) */
|
|
115
|
+
GJSIFY_HTTP2_EVENT_GOAWAY = 4, /* GOAWAY frame received */
|
|
116
|
+
GJSIFY_HTTP2_EVENT_SETTINGS = 5, /* SETTINGS frame received */
|
|
117
|
+
GJSIFY_HTTP2_EVENT_PUSH_PROMISE = 6 /* PUSH_PROMISE received (client side) */
|
|
118
|
+
} GjsifyHttp2EventKind;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* GjsifyHttp2Event:
|
|
122
|
+
*
|
|
123
|
+
* Opaque event record drained out of the session via
|
|
124
|
+
* gjsify_http2_session_drain_events(). Definition lives in
|
|
125
|
+
* nghttp2-helpers.c — accessed exclusively through the
|
|
126
|
+
* gjsify_http2_event_get_*() accessors below.
|
|
127
|
+
*
|
|
128
|
+
* Keeping the tag visible (forward declared here, full struct in .c)
|
|
129
|
+
* is what lets Vala emit a matching `typedef struct _GjsifyHttp2Event
|
|
130
|
+
* GjsifyHttp2Event;` forward declaration in its own generated header
|
|
131
|
+
* without redefining the tag.
|
|
132
|
+
*/
|
|
133
|
+
typedef struct _GjsifyHttp2Event GjsifyHttp2Event;
|
|
134
|
+
|
|
135
|
+
void gjsify_http2_event_free (GjsifyHttp2Event *event);
|
|
136
|
+
|
|
137
|
+
/* ── Event getters — Vala bindings call these instead of accessing struct
|
|
138
|
+
* fields directly, so the Vala C generator never needs to emit a
|
|
139
|
+
* typedef for GjsifyHttp2Event (which would clash with this header).
|
|
140
|
+
*
|
|
141
|
+
* Header arrays are returned as plain `char**` whose lifetime stays
|
|
142
|
+
* pinned to the event itself; do NOT free them — they vanish when the
|
|
143
|
+
* enclosing event is freed via gjsify_http2_event_free(). The data
|
|
144
|
+
* GBytes return retains a ref from inside the event; if the caller
|
|
145
|
+
* needs to outlive the event, take an explicit ref.
|
|
146
|
+
* ───────────────────────────────────────────────────────────────────── */
|
|
147
|
+
guint32 gjsify_http2_event_get_kind (GjsifyHttp2Event *event);
|
|
148
|
+
guint32 gjsify_http2_event_get_stream_id (GjsifyHttp2Event *event);
|
|
149
|
+
guint32 gjsify_http2_event_get_error_code (GjsifyHttp2Event *event);
|
|
150
|
+
guint32 gjsify_http2_event_get_last_stream_id (GjsifyHttp2Event *event);
|
|
151
|
+
gboolean gjsify_http2_event_get_end_stream (GjsifyHttp2Event *event);
|
|
152
|
+
guint32 gjsify_http2_event_get_promised_stream_id (GjsifyHttp2Event *event);
|
|
153
|
+
gsize gjsify_http2_event_get_header_count (GjsifyHttp2Event *event);
|
|
154
|
+
const char * gjsify_http2_event_get_header_name (GjsifyHttp2Event *event, gsize index);
|
|
155
|
+
const char * gjsify_http2_event_get_header_value (GjsifyHttp2Event *event, gsize index);
|
|
156
|
+
GBytes * gjsify_http2_event_get_data (GjsifyHttp2Event *event);
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* gjsify_http2_session_new:
|
|
160
|
+
* @mode: server or client
|
|
161
|
+
*
|
|
162
|
+
* Creates a new session. Returns NULL on allocation failure. Free with
|
|
163
|
+
* gjsify_http2_session_free().
|
|
164
|
+
*
|
|
165
|
+
* The session is created with default settings; the caller is expected
|
|
166
|
+
* to call gjsify_http2_session_submit_settings_local() with whatever
|
|
167
|
+
* server/client SETTINGS frame is appropriate (we always send a
|
|
168
|
+
* default-shaped SETTINGS on the next drain).
|
|
169
|
+
*/
|
|
170
|
+
GjsifyHttp2Session *gjsify_http2_session_new (GjsifyHttp2SessionMode mode);
|
|
171
|
+
|
|
172
|
+
void gjsify_http2_session_free (GjsifyHttp2Session *session);
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* gjsify_http2_session_feed:
|
|
176
|
+
* @session: the session
|
|
177
|
+
* @input: bytes received from the peer (may be %NULL only if len==0)
|
|
178
|
+
*
|
|
179
|
+
* Feed received bytes into nghttp2_session_mem_recv(). Returns the number
|
|
180
|
+
* of bytes consumed (always equal to the input length on success) or a
|
|
181
|
+
* negative nghttp2 error code on protocol/parse error.
|
|
182
|
+
*
|
|
183
|
+
* Side effect: events are appended to the internal event queue; drain
|
|
184
|
+
* via gjsify_http2_session_drain_events(). The function does NOT emit
|
|
185
|
+
* any output — call gjsify_http2_session_drain_output() afterwards.
|
|
186
|
+
*/
|
|
187
|
+
gssize gjsify_http2_session_feed (GjsifyHttp2Session *session,
|
|
188
|
+
GBytes *input);
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* gjsify_http2_session_drain_output:
|
|
192
|
+
* @session: the session
|
|
193
|
+
*
|
|
194
|
+
* Returns a #GBytes containing all pending output frames (concatenated)
|
|
195
|
+
* that nghttp2 wants to send. Empty bytes are returned if nothing is
|
|
196
|
+
* pending. Caller MUST write these bytes to the socket; only after the
|
|
197
|
+
* write completes should further frames be drained.
|
|
198
|
+
*/
|
|
199
|
+
GBytes *gjsify_http2_session_drain_output (GjsifyHttp2Session *session);
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* gjsify_http2_session_drain_events:
|
|
203
|
+
* @session: the session
|
|
204
|
+
*
|
|
205
|
+
* Drains every queued event into a NULL-terminated array of
|
|
206
|
+
* #GjsifyHttp2Event pointers. Caller owns the array AND each element
|
|
207
|
+
* (use gjsify_http2_event_free + g_free for the array container).
|
|
208
|
+
*
|
|
209
|
+
* Returns %NULL when the queue is empty.
|
|
210
|
+
*/
|
|
211
|
+
GjsifyHttp2Event **gjsify_http2_session_drain_events (GjsifyHttp2Session *session,
|
|
212
|
+
gsize *out_n);
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* gjsify_http2_session_submit_settings:
|
|
216
|
+
* @session: the session
|
|
217
|
+
*
|
|
218
|
+
* Queues a SETTINGS frame with the default values. Idempotent: calling
|
|
219
|
+
* twice queues two SETTINGS frames (rare; for the server-side initial
|
|
220
|
+
* handshake we call it once right after session creation).
|
|
221
|
+
*
|
|
222
|
+
* Returns 0 on success, < 0 nghttp2 error code.
|
|
223
|
+
*/
|
|
224
|
+
int gjsify_http2_session_submit_settings (GjsifyHttp2Session *session);
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* gjsify_http2_session_submit_response:
|
|
228
|
+
* @session: server session
|
|
229
|
+
* @stream_id: client-initiated stream we're responding to
|
|
230
|
+
* @names / @values: response header pairs (must include :status)
|
|
231
|
+
* @n_pairs: count of header pairs
|
|
232
|
+
* @end_stream: %TRUE if the response carries no body
|
|
233
|
+
*
|
|
234
|
+
* Submits HEADERS for the response. Returns 0 / < 0.
|
|
235
|
+
*/
|
|
236
|
+
int gjsify_http2_session_submit_response (GjsifyHttp2Session *session,
|
|
237
|
+
guint32 stream_id,
|
|
238
|
+
char **names,
|
|
239
|
+
char **values,
|
|
240
|
+
gsize n_pairs,
|
|
241
|
+
gboolean end_stream);
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* gjsify_http2_session_submit_request:
|
|
245
|
+
* @session: client session
|
|
246
|
+
* @names / @values: request header pairs (must include :method/:scheme/:path)
|
|
247
|
+
* @n_pairs: count of header pairs
|
|
248
|
+
* @end_stream: %TRUE if the request carries no body
|
|
249
|
+
*
|
|
250
|
+
* Returns the freshly-allocated odd client stream id (1, 3, 5, ...), or
|
|
251
|
+
* 0 on error. Symmetric to submit_response on the server side.
|
|
252
|
+
*/
|
|
253
|
+
guint32 gjsify_http2_session_submit_request (GjsifyHttp2Session *session,
|
|
254
|
+
char **names,
|
|
255
|
+
char **values,
|
|
256
|
+
gsize n_pairs,
|
|
257
|
+
gboolean end_stream);
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* gjsify_http2_session_submit_data:
|
|
261
|
+
* @session: the session
|
|
262
|
+
* @stream_id: target stream
|
|
263
|
+
* @data: payload bytes (may be %NULL only if end_stream is %TRUE)
|
|
264
|
+
* @end_stream: %TRUE marks last DATA on the stream
|
|
265
|
+
*
|
|
266
|
+
* Queues a DATA frame. Returns 0 on success, < 0 nghttp2 error code.
|
|
267
|
+
*
|
|
268
|
+
* Buffer ownership: nghttp2 reads via a data_source callback that the
|
|
269
|
+
* implementation backs with @data; we hold a ref on @data until the
|
|
270
|
+
* frame is fully transmitted (signalled via on_frame_send).
|
|
271
|
+
*/
|
|
272
|
+
int gjsify_http2_session_submit_data (GjsifyHttp2Session *session,
|
|
273
|
+
guint32 stream_id,
|
|
274
|
+
GBytes *data,
|
|
275
|
+
gboolean end_stream);
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* gjsify_http2_session_submit_push_promise:
|
|
279
|
+
* @session: server session
|
|
280
|
+
* @parent_id: client stream this push is associated with
|
|
281
|
+
* @names / @values / @n_pairs: request headers for the push
|
|
282
|
+
*
|
|
283
|
+
* Returns the promised (server-allocated) stream id, or 0 on error.
|
|
284
|
+
* The caller can then submit_response()/submit_data() on the returned id.
|
|
285
|
+
*/
|
|
286
|
+
guint32 gjsify_http2_session_submit_push_promise (GjsifyHttp2Session *session,
|
|
287
|
+
guint32 parent_id,
|
|
288
|
+
char **names,
|
|
289
|
+
char **values,
|
|
290
|
+
gsize n_pairs);
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* gjsify_http2_session_submit_goaway:
|
|
294
|
+
* @session: the session
|
|
295
|
+
* @last_stream_id: last processed stream id (set this BEFORE close)
|
|
296
|
+
* @error_code: nghttp2 error code (NO_ERROR=0, PROTOCOL_ERROR=1, ...)
|
|
297
|
+
*
|
|
298
|
+
* Queues a GOAWAY. Returns 0 / < 0 nghttp2 error code.
|
|
299
|
+
*/
|
|
300
|
+
int gjsify_http2_session_submit_goaway (GjsifyHttp2Session *session,
|
|
301
|
+
guint32 last_stream_id,
|
|
302
|
+
guint32 error_code);
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* gjsify_http2_session_submit_rst_stream:
|
|
306
|
+
* @session: the session
|
|
307
|
+
* @stream_id: stream to reset
|
|
308
|
+
* @error_code: nghttp2 error code
|
|
309
|
+
*
|
|
310
|
+
* Queues a RST_STREAM. Returns 0 / < 0.
|
|
311
|
+
*/
|
|
312
|
+
int gjsify_http2_session_submit_rst_stream (GjsifyHttp2Session *session,
|
|
313
|
+
guint32 stream_id,
|
|
314
|
+
guint32 error_code);
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* gjsify_http2_session_want_read / _want_write:
|
|
318
|
+
*
|
|
319
|
+
* Returns %TRUE if nghttp2 has more bytes to consume / emit. The driver
|
|
320
|
+
* uses these to decide whether to keep watching the socket for IN / OUT.
|
|
321
|
+
*/
|
|
322
|
+
gboolean gjsify_http2_session_want_read (GjsifyHttp2Session *session);
|
|
323
|
+
gboolean gjsify_http2_session_want_write (GjsifyHttp2Session *session);
|
|
324
|
+
|
|
325
|
+
G_END_DECLS
|
|
326
|
+
|
|
327
|
+
#endif /* GJSIFY_HTTP2_NGHTTP2_HELPERS_H */
|