ds9 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fa3ba996d357a12053551efdb6f4f60b89db4807
4
+ data.tar.gz: 24e779cd44010f0d19158199c75f849ff9db37c2
5
+ SHA512:
6
+ metadata.gz: 5cd6a8a0d6e38d1374c1c0cde4e3c34cd46639a28e76df77e024281322570e33e1ed38afd5f62371604693fa5e4306675311f8b5a9913d90c225306b3c88a828
7
+ data.tar.gz: ca98ca2db7715ac4ad8e7ec22b0eecdbceb76f31e5ba701026f5c450e0d38b0664c6e91030c2b624704213a2688446005c980bdad6cd335bba31d4128ae6b23d
@@ -0,0 +1,8 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.testlib = 'minitest/autorun'
7
+ at.find_directories = ARGV unless ARGV.empty?
8
+ end
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2015-06-23
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,13 @@
1
+ .autotest
2
+ CHANGELOG.md
3
+ Manifest.txt
4
+ README.md
5
+ Rakefile
6
+ ext/ds9/ds9.c
7
+ ext/ds9/ds9.h
8
+ ext/ds9/ds9_frames.c
9
+ ext/ds9/extconf.rb
10
+ lib/ds9.rb
11
+ test/helper.rb
12
+ test/test_client.rb
13
+ test/test_ds9.rb
@@ -0,0 +1,185 @@
1
+ # DS9
2
+
3
+ * https://github.com/tenderlove/ds9
4
+
5
+ ## DESCRIPTION:
6
+
7
+ This library allows you to write HTTP/2 clients and servers. It is a wrapper
8
+ around nghttp2.
9
+
10
+ ## INSTALLING
11
+
12
+ On OS X:
13
+
14
+ ```
15
+ $ brew install nghttp2
16
+ $ gem install ds9
17
+ ```
18
+
19
+ I'm not sure about other platforms, but it should Just Work if you install
20
+ libnghttp2.
21
+
22
+ ## FEATURES/PROBLEMS:
23
+
24
+ * Needs nghttp2
25
+
26
+ ## SYNOPSIS:
27
+
28
+ Here is a full client that supports server pushes:
29
+
30
+ ```ruby
31
+ ##
32
+ # ruby client.rb https://nghttp2.org/
33
+ # ruby client.rb https://www.google.com/
34
+
35
+ require 'ds9'
36
+ require 'socket'
37
+ require 'openssl'
38
+ require 'stringio'
39
+ require 'uri'
40
+ require 'thread'
41
+
42
+ class MySession < DS9::Client
43
+ def initialize sock
44
+ @sock = sock
45
+ @responses = Queue.new
46
+ @requests = []
47
+ @in_flight = {}
48
+ super()
49
+ end
50
+
51
+ def on_stream_close id, errcode
52
+ @responses << @in_flight.delete(id)
53
+ puts "FINISHED READING STREAM: #{id}"
54
+ if @requests.any?
55
+ submit_request @requests.pop
56
+ else
57
+ @responses << nil
58
+ @thread.join
59
+ terminate_session DS9::NO_ERROR
60
+ end
61
+ end
62
+
63
+ def on_begin_headers frame
64
+ @in_flight[frame.stream_id] = StringIO.new if frame.headers?
65
+ @requests << [] if frame.push_promise?
66
+ end
67
+
68
+ def on_header name, value, frame, flags
69
+ if frame.push_promise?
70
+ @requests.last << [name, value]
71
+ else
72
+ puts "HEADER: #{name}: #{value}"
73
+ end
74
+ end
75
+
76
+ def on_data_chunk_recv id, data, flags
77
+ @in_flight[id] << data
78
+ end
79
+
80
+ def send_event string
81
+ @sock.write_nonblock string
82
+ end
83
+
84
+ def recv_event length
85
+ case data = @sock.read_nonblock(length, nil, exception: false)
86
+ when :wait_readable then DS9::ERR_WOULDBLOCK
87
+ when nil then DS9::ERR_EOF
88
+ else
89
+ data
90
+ end
91
+ end
92
+
93
+ def run
94
+ @thread = Thread.new do
95
+ while response = @responses.pop
96
+ yield response
97
+ end
98
+ end
99
+
100
+ while want_read? || want_write?
101
+ rd, wr, _ = IO.select([@sock], [@sock])
102
+ receive
103
+ send
104
+ end
105
+ end
106
+ end
107
+
108
+ uri = URI.parse ARGV[0]
109
+ socket = TCPSocket.new uri.host, uri.port
110
+
111
+ ctx = OpenSSL::SSL::SSLContext.new
112
+ ctx.npn_protocols = [DS9::PROTO_VERSION_ID]
113
+ ctx.npn_select_cb = lambda do |protocols|
114
+ if protocols.include?(DS9::PROTO_VERSION_ID)
115
+ puts "The negotiated protocol: " + DS9::PROTO_VERSION_ID
116
+ DS9::PROTO_VERSION_ID
117
+ end
118
+ end
119
+
120
+ socket = OpenSSL::SSL::SSLSocket.new socket, ctx
121
+ socket.hostname = uri.hostname
122
+ socket.connect
123
+ socket.sync_close = true
124
+
125
+ session = MySession.new socket
126
+ session.submit_settings []
127
+
128
+ path = uri.path == '' ? '/' : uri.path
129
+ session.submit_request [
130
+ [':method', 'GET'],
131
+ [':path', path],
132
+ [':scheme', uri.scheme],
133
+ [':authority', [uri.host, uri.port].join(':')],
134
+ ['accept', '*/*'],
135
+ ['user-agent', 'test'],
136
+ ]
137
+
138
+ session.run do |res|
139
+ p res
140
+ end
141
+ ```
142
+
143
+ ## HACKING:
144
+
145
+ On OS X:
146
+
147
+ ```
148
+ $ brew install nghttp2
149
+ $ gem install hoe rake-compiler
150
+ $ rake compile test
151
+ ```
152
+
153
+ ## REQUIREMENTS:
154
+
155
+ * Needs nghttp2
156
+
157
+ ## INSTALL:
158
+
159
+ * brew install nghttp2
160
+ * gem install ds9
161
+
162
+ ## LICENSE:
163
+
164
+ (The MIT License)
165
+
166
+ Copyright (c) 2015 Aaron Patterson
167
+
168
+ Permission is hereby granted, free of charge, to any person obtaining
169
+ a copy of this software and associated documentation files (the
170
+ 'Software'), to deal in the Software without restriction, including
171
+ without limitation the rights to use, copy, modify, merge, publish,
172
+ distribute, sublicense, and/or sell copies of the Software, and to
173
+ permit persons to whom the Software is furnished to do so, subject to
174
+ the following conditions:
175
+
176
+ The above copyright notice and this permission notice shall be
177
+ included in all copies or substantial portions of the Software.
178
+
179
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
180
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
181
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
182
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
183
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
184
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
185
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.plugins.delete :rubyforge
7
+ Hoe.plugin :minitest
8
+ Hoe.plugin :gemspec # `gem install hoe-gemspec`
9
+ Hoe.plugin :git # `gem install hoe-git`
10
+
11
+ gem 'rake-compiler', '>= 0.4.1'
12
+ require "rake/extensiontask"
13
+
14
+ Hoe.spec 'ds9' do
15
+ developer('Aaron Patterson', 'aaron@tenderlovemaking.com')
16
+ self.readme_file = 'README.md'
17
+ self.history_file = 'CHANGELOG.md'
18
+ self.extra_rdoc_files = FileList['*.md']
19
+ extra_dev_deps << ['rake-compiler', '>= 0.4.1']
20
+
21
+ self.spec_extras = {
22
+ :required_ruby_version => '>= 2.2.2'
23
+ }
24
+
25
+ self.spec_extras[:extensions] = ["ext/ds9/extconf.rb"]
26
+ Rake::ExtensionTask.new "ds9", spec do |ext|
27
+ ext.lib_dir = File.join(*['lib', ENV['FAT_DIR']].compact)
28
+ end
29
+ end
30
+
31
+ # vim: syntax=ruby
@@ -0,0 +1,838 @@
1
+ #include <ds9.h>
2
+ #include <assert.h>
3
+
4
+ VALUE mDS9;
5
+ VALUE cDS9Session;
6
+ VALUE cDS9Client;
7
+ VALUE cDS9Server;
8
+ VALUE cDS9Callbacks;
9
+ VALUE eDS9Exception;
10
+ VALUE eDS9UninitializedException;
11
+
12
+ ID id_on_header,
13
+ id_on_begin_headers,
14
+ id_on_frame_not_send,
15
+ id_on_begin_frame,
16
+ id_on_invalid_frame_recv,
17
+ id_send_event,
18
+ id_recv_event,
19
+ id_on_frame_send,
20
+ id_on_frame_recv,
21
+ id_on_stream_close,
22
+ id_on_data_chunk_recv,
23
+ id_on_data_source_read,
24
+ id_before_frame_send;
25
+
26
+ #define CheckSelf(ptr) \
27
+ if (NULL == (ptr)) \
28
+ rb_raise(eDS9UninitializedException, "not initialized, call `super` from `initialize`");
29
+
30
+ static void * wrap_xmalloc(size_t size, void *mem_user_data)
31
+ {
32
+ return xmalloc(size);
33
+ }
34
+
35
+ static void wrap_xfree(void *ptr, void *mem_user_data)
36
+ {
37
+ xfree(ptr);
38
+ }
39
+
40
+ static void *wrap_xcalloc(size_t nmemb, size_t size, void *mem_user_data)
41
+ {
42
+ return xcalloc(nmemb, size);
43
+ }
44
+
45
+ static void *wrap_xrealloc(void *ptr, size_t size, void *mem_user_data)
46
+ {
47
+ return xrealloc(ptr, size);
48
+ }
49
+
50
+ static VALUE explode(int code) {
51
+ return rb_funcall(eDS9Exception, rb_intern("abort"), 1, INT2NUM(code));
52
+ }
53
+
54
+ static int before_frame_send_callback(nghttp2_session *session,
55
+ const nghttp2_frame *frame,
56
+ void *user_data)
57
+ {
58
+ VALUE self = (VALUE)user_data;
59
+ VALUE ret;
60
+
61
+ ret = rb_funcall(self, id_before_frame_send, 1, WrapDS9Frame(frame));
62
+
63
+ if (ret == Qfalse) return 1;
64
+
65
+ return 0;
66
+ }
67
+
68
+ static ssize_t send_callback(nghttp2_session * session,
69
+ const uint8_t *data,
70
+ size_t length,
71
+ int flags, void *user_data)
72
+ {
73
+ VALUE self = (VALUE)user_data;
74
+ VALUE ret;
75
+
76
+ ret = rb_funcall(self, id_send_event, 1, rb_str_new((const char *)data, length));
77
+
78
+ return NUM2INT(ret);
79
+ }
80
+
81
+ static int on_frame_recv_callback(nghttp2_session *session,
82
+ const nghttp2_frame *frame,
83
+ void *user_data)
84
+ {
85
+ VALUE self = (VALUE)user_data;
86
+ VALUE ret;
87
+
88
+ ret = rb_funcall(self, id_on_frame_recv, 1, WrapDS9Frame(frame));
89
+
90
+ if (ret == Qfalse) {
91
+ return 1;
92
+ }
93
+
94
+ return 0;
95
+ }
96
+
97
+ static int on_stream_close_callback(nghttp2_session *session,
98
+ int32_t stream_id,
99
+ uint32_t error_code,
100
+ void *user_data)
101
+ {
102
+ VALUE self = (VALUE)user_data;
103
+
104
+ VALUE ret = rb_funcall(self, id_on_stream_close, 2, INT2NUM(stream_id), INT2NUM(error_code));
105
+
106
+ if (ret == Qfalse) {
107
+ return 1;
108
+ }
109
+
110
+ return 0;
111
+ }
112
+
113
+ static int on_header_callback(nghttp2_session *session,
114
+ const nghttp2_frame *frame,
115
+ const uint8_t *name, size_t namelen,
116
+ const uint8_t *value, size_t valuelen,
117
+ uint8_t flags, void *user_data)
118
+ {
119
+ VALUE self = (VALUE)user_data;
120
+
121
+ VALUE ret = rb_funcall(self, id_on_header, 4,
122
+ rb_usascii_str_new((const char *)name, namelen),
123
+ rb_usascii_str_new((const char *)value, valuelen),
124
+ WrapDS9Frame(frame),
125
+ INT2NUM(flags));
126
+
127
+ if (ret == Qfalse) {
128
+ return 1;
129
+ }
130
+
131
+ return 0;
132
+ }
133
+
134
+ static int on_begin_headers_callback(nghttp2_session *session,
135
+ const nghttp2_frame *frame,
136
+ void *user_data)
137
+ {
138
+ VALUE self = (VALUE)user_data;
139
+
140
+ VALUE ret = rb_funcall(self, id_on_begin_headers, 1,
141
+ WrapDS9Frame(frame));
142
+
143
+ if (ret == Qfalse) {
144
+ return 1;
145
+ }
146
+
147
+ return 0;
148
+ }
149
+
150
+ static ssize_t recv_callback(nghttp2_session *session, uint8_t *buf,
151
+ size_t length, int flags,
152
+ void *user_data)
153
+ {
154
+ VALUE self = (VALUE)user_data;
155
+ VALUE ret;
156
+ ssize_t len;
157
+
158
+ ret = rb_funcall(self, id_recv_event, 1, INT2NUM(length));
159
+
160
+ if (FIXNUM_P(ret)) {
161
+ return NUM2INT(ret);
162
+ }
163
+
164
+ Check_Type(ret, T_STRING);
165
+ len = RSTRING_LEN(ret);
166
+
167
+ memcpy(buf, StringValuePtr(ret), len);
168
+
169
+ return len;
170
+ }
171
+
172
+ static int on_begin_frame_callback(nghttp2_session *session,
173
+ const nghttp2_frame_hd *hd,
174
+ void *user_data)
175
+ {
176
+ VALUE self = (VALUE)user_data;
177
+ VALUE ret;
178
+
179
+ ret = rb_funcall(self, id_on_begin_frame, 1, WrapDS9FrameHeader(hd));
180
+
181
+ if (ret == Qfalse) {
182
+ return 1;
183
+ }
184
+
185
+ return 0;
186
+ }
187
+
188
+ static int on_data_chunk_recv_callback(nghttp2_session *session,
189
+ uint8_t flags,
190
+ int32_t stream_id,
191
+ const uint8_t *data,
192
+ size_t len, void *user_data)
193
+ {
194
+ VALUE self = (VALUE)user_data;
195
+ VALUE ret;
196
+
197
+ ret = rb_funcall(self, id_on_data_chunk_recv, 3,
198
+ INT2NUM(stream_id),
199
+ rb_str_new((const char *)data, len),
200
+ INT2NUM(flags));
201
+
202
+ if (ret == Qfalse) {
203
+ return 1;
204
+ }
205
+
206
+ return 0;
207
+ }
208
+
209
+ static int on_invalid_frame_recv_callback(nghttp2_session *session,
210
+ const nghttp2_frame *frame, int lib_error_code,
211
+ void *user_data)
212
+ {
213
+ VALUE self = (VALUE)user_data;
214
+ VALUE ret;
215
+
216
+ ret = rb_funcall(self, id_on_invalid_frame_recv, 2,
217
+ WrapDS9Frame(frame),
218
+ INT2NUM(lib_error_code));
219
+
220
+ if (ret == Qfalse) {
221
+ return 1;
222
+ }
223
+
224
+ return 0;
225
+ }
226
+
227
+ static int on_frame_send_callback(nghttp2_session *session,
228
+ const nghttp2_frame *frame,
229
+ void *user_data)
230
+ {
231
+ VALUE self = (VALUE)user_data;
232
+ VALUE ret;
233
+
234
+ ret = rb_funcall(self, id_on_frame_send, 1, WrapDS9Frame(frame));
235
+
236
+ if (ret == Qfalse) {
237
+ return 1;
238
+ }
239
+
240
+ return 0;
241
+ }
242
+
243
+ static int on_frame_not_send_callback(nghttp2_session *session,
244
+ const nghttp2_frame *frame,
245
+ int lib_error_code,
246
+ void *user_data)
247
+ {
248
+ VALUE self = (VALUE)user_data;
249
+ VALUE reason = rb_str_new2(nghttp2_strerror(lib_error_code));
250
+ VALUE ret;
251
+
252
+ ret = rb_funcall(self, id_on_frame_not_send, 2, WrapDS9Frame(frame), reason);
253
+
254
+ if (ret == Qfalse) {
255
+ return 1;
256
+ }
257
+
258
+ return 0;
259
+ }
260
+
261
+ static ssize_t rb_data_read_callback(nghttp2_session *session,
262
+ int32_t stream_id, uint8_t *buf,
263
+ size_t length, uint32_t *data_flags,
264
+ nghttp2_data_source *source, void *user_data)
265
+ {
266
+ VALUE self = (VALUE)user_data;
267
+ VALUE ret;
268
+ ssize_t len;
269
+
270
+ ret = rb_funcall(self, id_on_data_source_read, 2, INT2NUM(stream_id),
271
+ INT2NUM(length));
272
+
273
+ if (NIL_P(ret)) {
274
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
275
+ return 0;
276
+ }
277
+
278
+ Check_Type(ret, T_STRING);
279
+ len = RSTRING_LEN(ret);
280
+ memcpy(buf, StringValuePtr(ret), len);
281
+
282
+ return len;
283
+ }
284
+
285
+ static void copy_list_to_nv(VALUE list, nghttp2_nv * head, size_t niv)
286
+ {
287
+ size_t i;
288
+
289
+ for(i = 0; i < niv; i++, head++) {
290
+ VALUE tuple = rb_ary_entry(list, (long)i);
291
+ VALUE name = rb_ary_entry(tuple, 0);
292
+ VALUE value = rb_ary_entry(tuple, 1);
293
+
294
+ head->name = (uint8_t *)StringValuePtr(name);
295
+ head->namelen = RSTRING_LEN(name);
296
+
297
+ head->value = (uint8_t *)StringValuePtr(value);
298
+ head->valuelen = RSTRING_LEN(value);
299
+ head->flags = NGHTTP2_NV_FLAG_NONE;
300
+ }
301
+ }
302
+
303
+ static VALUE allocate_session(VALUE klass)
304
+ {
305
+ return TypedData_Wrap_Struct(klass, &ds9_session_type, 0);
306
+ }
307
+
308
+ static VALUE client_init_internals(VALUE self, VALUE cb)
309
+ {
310
+ nghttp2_session_callbacks *callbacks;
311
+ nghttp2_session *session;
312
+ nghttp2_mem mem = {
313
+ NULL,
314
+ wrap_xmalloc,
315
+ wrap_xfree,
316
+ wrap_xcalloc,
317
+ wrap_xrealloc
318
+ };
319
+
320
+ TypedData_Get_Struct(cb, nghttp2_session_callbacks, &ds9_callbacks_type, callbacks);
321
+
322
+ nghttp2_session_client_new3(&session, callbacks, (void *)self, NULL, &mem);
323
+ DATA_PTR(self) = session;
324
+
325
+ return self;
326
+ }
327
+
328
+ static VALUE server_init_internals(VALUE self, VALUE cb)
329
+ {
330
+ nghttp2_session_callbacks *callbacks;
331
+ nghttp2_session *session;
332
+ nghttp2_mem mem = {
333
+ NULL,
334
+ wrap_xmalloc,
335
+ wrap_xfree,
336
+ wrap_xcalloc,
337
+ wrap_xrealloc
338
+ };
339
+
340
+ TypedData_Get_Struct(cb, nghttp2_session_callbacks, &ds9_callbacks_type, callbacks);
341
+
342
+ nghttp2_session_server_new3(&session, callbacks, (void *)self, NULL, &mem);
343
+ DATA_PTR(self) = session;
344
+
345
+ return self;
346
+ }
347
+
348
+ static VALUE session_want_write_p(VALUE self)
349
+ {
350
+ nghttp2_session *session;
351
+
352
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
353
+ CheckSelf(session);
354
+
355
+ if (nghttp2_session_want_write(session) == 0) {
356
+ return Qfalse;
357
+ }
358
+
359
+ return Qtrue;
360
+ }
361
+
362
+ static VALUE session_want_read_p(VALUE self)
363
+ {
364
+ nghttp2_session *session;
365
+
366
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
367
+ CheckSelf(session);
368
+
369
+ if (nghttp2_session_want_read(session) == 0) {
370
+ return Qfalse;
371
+ }
372
+
373
+ return Qtrue;
374
+ }
375
+
376
+ static VALUE session_submit_ping(VALUE self)
377
+ {
378
+ nghttp2_session *session;
379
+
380
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
381
+ CheckSelf(session);
382
+
383
+ if (nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL) == 0)
384
+ return Qtrue;
385
+
386
+ return Qfalse;
387
+ }
388
+
389
+ static VALUE session_submit_settings(VALUE self, VALUE settings)
390
+ {
391
+ size_t niv, i;
392
+ nghttp2_settings_entry *iv, *head;
393
+ nghttp2_session *session;
394
+ int rv;
395
+
396
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
397
+ CheckSelf(session);
398
+
399
+ niv = RARRAY_LEN(settings);
400
+ iv = xcalloc(niv, sizeof(nghttp2_settings_entry));
401
+ head = iv;
402
+
403
+ for(i = 0; i < niv; i++, head++) {
404
+ VALUE tuple = rb_ary_entry(settings, (long)i);
405
+ head->settings_id = NUM2INT(rb_ary_entry(tuple, 0));
406
+ head->value = NUM2INT(rb_ary_entry(tuple, 1));
407
+ }
408
+
409
+ rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
410
+
411
+ xfree(iv);
412
+
413
+ if (0 != rv) {
414
+ explode(rv);
415
+ }
416
+
417
+ return self;
418
+ }
419
+
420
+ static VALUE session_send(VALUE self)
421
+ {
422
+ int rv;
423
+ nghttp2_session *session;
424
+
425
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
426
+ CheckSelf(session);
427
+
428
+ rv = nghttp2_session_send(session);
429
+ if (rv != 0) {
430
+ explode(rv);
431
+ }
432
+
433
+ return self;
434
+ }
435
+
436
+ static VALUE session_receive(VALUE self)
437
+ {
438
+ int rv;
439
+ nghttp2_session *session;
440
+
441
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
442
+ CheckSelf(session);
443
+
444
+ assert(session);
445
+ rv = nghttp2_session_recv(session);
446
+ if (rv != 0) {
447
+ explode(rv);
448
+ }
449
+
450
+ return self;
451
+ }
452
+
453
+ static VALUE session_mem_receive(VALUE self, VALUE buf)
454
+ {
455
+ int rv;
456
+ nghttp2_session *session;
457
+
458
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
459
+ CheckSelf(session);
460
+
461
+ rv = nghttp2_session_mem_recv(session, (const uint8_t *)StringValuePtr(buf), RSTRING_LEN(buf));
462
+ if (rv < 0) {
463
+ explode(rv);
464
+ }
465
+
466
+ return INT2NUM(rv);
467
+ }
468
+
469
+ static VALUE session_mem_send(VALUE self)
470
+ {
471
+ ssize_t rv;
472
+ const uint8_t *data;
473
+ nghttp2_session *session;
474
+
475
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
476
+ CheckSelf(session);
477
+
478
+ rv = nghttp2_session_mem_send(session, &data);
479
+
480
+ if (rv == 0) {
481
+ return Qfalse;
482
+ }
483
+
484
+ if (rv < 0) {
485
+ explode(rv);
486
+ }
487
+
488
+ return rb_str_new((const char *)data, rv);
489
+ }
490
+
491
+ static VALUE session_outbound_queue_size(VALUE self)
492
+ {
493
+ nghttp2_session *session;
494
+
495
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
496
+ CheckSelf(session);
497
+
498
+ return INT2NUM(nghttp2_session_get_outbound_queue_size(session));
499
+ }
500
+
501
+ static VALUE session_submit_request(VALUE self, VALUE settings)
502
+ {
503
+ size_t niv, i;
504
+ nghttp2_nv *nva, *head;
505
+ nghttp2_session *session;
506
+ int rv;
507
+
508
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
509
+ CheckSelf(session);
510
+
511
+ Check_Type(settings, T_ARRAY);
512
+ niv = RARRAY_LEN(settings);
513
+ nva = xcalloc(niv, sizeof(nghttp2_nv));
514
+
515
+ copy_list_to_nv(settings, nva, niv);
516
+
517
+ rv = nghttp2_submit_request(session, NULL, nva, niv, NULL, NULL);
518
+
519
+ xfree(nva);
520
+
521
+ if (rv < 0) {
522
+ explode(rv);
523
+ }
524
+
525
+ return INT2NUM(rv);
526
+ }
527
+
528
+ static VALUE session_submit_shutdown(VALUE self)
529
+ {
530
+ int rv;
531
+ nghttp2_session *session;
532
+
533
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
534
+ CheckSelf(session);
535
+
536
+ rv = nghttp2_submit_shutdown_notice(session);
537
+
538
+ if (rv == 0)
539
+ return Qtrue;
540
+
541
+ return explode(rv);
542
+ }
543
+
544
+ static VALUE session_submit_goaway(VALUE self, VALUE last_stream_id, VALUE err)
545
+ {
546
+ int rv;
547
+ nghttp2_session *session;
548
+
549
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
550
+ CheckSelf(session);
551
+
552
+ rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE,
553
+ NUM2INT(last_stream_id), NUM2INT(err), NULL, 0);
554
+
555
+ if (rv == 0)
556
+ return Qtrue;
557
+
558
+ return explode(rv);
559
+ }
560
+
561
+ static VALUE session_stream_local_closed_p(VALUE self, VALUE streamid)
562
+ {
563
+ nghttp2_session *session;
564
+ int stream_id;
565
+
566
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
567
+ CheckSelf(session);
568
+
569
+ stream_id = NUM2INT(streamid);
570
+
571
+ switch(nghttp2_session_get_stream_local_close(session, stream_id))
572
+ {
573
+ case 1:
574
+ return Qtrue;
575
+ break;
576
+ case 0:
577
+ return Qfalse;
578
+ break;
579
+ default:
580
+ rb_raise(rb_eStandardError, "no such stream: %d", stream_id);
581
+ }
582
+
583
+ return Qfalse;
584
+ }
585
+
586
+ static VALUE session_stream_remote_closed_p(VALUE self, VALUE streamid)
587
+ {
588
+ nghttp2_session *session;
589
+ int stream_id;
590
+
591
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
592
+ CheckSelf(session);
593
+
594
+ stream_id = NUM2INT(streamid);
595
+
596
+ switch(nghttp2_session_get_stream_remote_close(session, stream_id))
597
+ {
598
+ case 1:
599
+ return Qtrue;
600
+ break;
601
+ case 0:
602
+ return Qfalse;
603
+ break;
604
+ default:
605
+ rb_raise(rb_eStandardError, "no such stream: %d", stream_id);
606
+ }
607
+
608
+ return Qfalse;
609
+ }
610
+
611
+ static VALUE session_terminate_session(VALUE self, VALUE err)
612
+ {
613
+ int rv;
614
+ nghttp2_session *session;
615
+
616
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
617
+ CheckSelf(session);
618
+
619
+ rv = nghttp2_session_terminate_session(session, NUM2INT(err));
620
+
621
+ if (rv != 0) {
622
+ explode(rv);
623
+ }
624
+
625
+ return self;
626
+ }
627
+
628
+ static VALUE server_submit_response(VALUE self, VALUE stream_id, VALUE headers)
629
+ {
630
+ nghttp2_session *session;
631
+ size_t niv;
632
+ nghttp2_nv *nva, *head;
633
+ nghttp2_data_provider provider;
634
+ int rv;
635
+
636
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
637
+ CheckSelf(session);
638
+
639
+ niv = RARRAY_LEN(headers);
640
+ nva = xcalloc(niv, sizeof(nghttp2_nv));
641
+
642
+ copy_list_to_nv(headers, nva, niv);
643
+
644
+ provider.read_callback = rb_data_read_callback;
645
+
646
+ rv = nghttp2_submit_response(session, NUM2INT(stream_id), nva, niv, &provider);
647
+
648
+ xfree(nva);
649
+
650
+ if (0 != rv) {
651
+ explode(rv);
652
+ }
653
+
654
+ return self;
655
+ }
656
+
657
+ static VALUE make_callbacks(VALUE self)
658
+ {
659
+ nghttp2_session_callbacks *callbacks;
660
+ nghttp2_session_callbacks_new(&callbacks);
661
+
662
+ if (rb_obj_respond_to(self, id_on_header, 1))
663
+ nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback);
664
+
665
+ if (rb_obj_respond_to(self, id_on_begin_headers, 1))
666
+ nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, on_begin_headers_callback);
667
+
668
+ if (rb_obj_respond_to(self, id_on_frame_not_send, 1))
669
+ nghttp2_session_callbacks_set_on_frame_not_send_callback(callbacks, on_frame_not_send_callback);
670
+
671
+ if (rb_obj_respond_to(self, id_on_begin_frame, 1))
672
+ nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks, on_begin_frame_callback);
673
+
674
+ if (rb_obj_respond_to(self, id_on_invalid_frame_recv, 1))
675
+ nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(callbacks, on_invalid_frame_recv_callback);
676
+
677
+ if (rb_obj_respond_to(self, id_send_event, 1))
678
+ nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
679
+
680
+ if (rb_obj_respond_to(self, id_recv_event, 1))
681
+ nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
682
+
683
+ if (rb_obj_respond_to(self, id_on_frame_send, 1))
684
+ nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback);
685
+
686
+ if (rb_obj_respond_to(self, id_on_frame_recv, 1))
687
+ nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback);
688
+
689
+ if (rb_obj_respond_to(self, id_on_stream_close, 1))
690
+ nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);
691
+
692
+ if (rb_obj_respond_to(self, id_on_data_chunk_recv, 1))
693
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback);
694
+
695
+ if (rb_obj_respond_to(self, id_before_frame_send, 1))
696
+ nghttp2_session_callbacks_set_before_frame_send_callback(callbacks, before_frame_send_callback);
697
+
698
+ return TypedData_Wrap_Struct(cDS9Callbacks, &ds9_callbacks_type, callbacks);
699
+ }
700
+
701
+ static VALUE server_submit_push_promise(VALUE self, VALUE stream_id, VALUE headers)
702
+ {
703
+ nghttp2_session *session;
704
+ nghttp2_nv *nva, *head;
705
+ size_t niv, i;
706
+ int rv;
707
+
708
+ TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
709
+ CheckSelf(session);
710
+
711
+ Check_Type(headers, T_ARRAY);
712
+ niv = RARRAY_LEN(headers);
713
+ nva = xcalloc(niv, sizeof(nghttp2_nv));
714
+ head = nva;
715
+
716
+ for(i = 0; i < niv; i++, head++) {
717
+ VALUE tuple = rb_ary_entry(headers, (long)i);
718
+ VALUE name = rb_ary_entry(tuple, 0);
719
+ VALUE value = rb_ary_entry(tuple, 1);
720
+
721
+ head->name = (uint8_t *)StringValuePtr(name);
722
+ head->namelen = RSTRING_LEN(name);
723
+
724
+ head->value = (uint8_t *)StringValuePtr(value);
725
+ head->valuelen = RSTRING_LEN(value);
726
+ head->flags = NGHTTP2_NV_FLAG_NONE;
727
+ }
728
+
729
+ rv = nghttp2_submit_push_promise(session, 0, NUM2INT(stream_id), nva, niv, NULL);
730
+
731
+ xfree(nva);
732
+
733
+ switch(rv) {
734
+ case NGHTTP2_ERR_NOMEM:
735
+ case NGHTTP2_ERR_PROTO:
736
+ case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
737
+ case NGHTTP2_ERR_INVALID_ARGUMENT:
738
+ return explode(rv);
739
+ break;
740
+ default:
741
+ return INT2NUM(rv);
742
+ }
743
+ }
744
+
745
+ static VALUE rb_nghttp_version(VALUE klass)
746
+ {
747
+ nghttp2_info * info = nghttp2_version(0);
748
+
749
+ return rb_usascii_str_new2(info->version_str);
750
+ }
751
+
752
+ static VALUE errors_to_string(VALUE mod, VALUE err)
753
+ {
754
+ return rb_usascii_str_new2(nghttp2_strerror(NUM2INT(err)));
755
+ }
756
+
757
+ void Init_ds9(void)
758
+ {
759
+ mDS9 = rb_define_module("DS9");
760
+
761
+ Init_ds9_frames(mDS9);
762
+
763
+ rb_define_const(mDS9, "PROTO_VERSION_ID", rb_str_new(NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN));
764
+
765
+ eDS9Exception = rb_define_class_under(mDS9, "Exception", rb_eStandardError);
766
+ eDS9UninitializedException = rb_define_class_under(mDS9, "UninitializedException", rb_eStandardError);
767
+
768
+ VALUE mDS9Settings = rb_define_module_under(mDS9, "Settings");
769
+ rb_define_const(mDS9Settings, "MAX_CONCURRENT_STREAMS", INT2NUM(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS));
770
+ rb_define_const(mDS9Settings, "INITIAL_WINDOW_SIZE", INT2NUM(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE));
771
+ rb_define_const(mDS9Settings, "HEADER_TABLE_SIZE", INT2NUM(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE));
772
+ rb_define_const(mDS9Settings, "ENABLE_PUSH", INT2NUM(NGHTTP2_SETTINGS_ENABLE_PUSH));
773
+ rb_define_const(mDS9Settings, "MAX_FRAME_SIZE", INT2NUM(NGHTTP2_SETTINGS_MAX_FRAME_SIZE));
774
+ rb_define_const(mDS9Settings, "MAX_HEADER_LIST_SIZE", INT2NUM(NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE));
775
+
776
+ rb_define_const(mDS9, "ERR_WOULDBLOCK", INT2NUM(NGHTTP2_ERR_WOULDBLOCK));
777
+ rb_define_const(mDS9, "ERR_EOF", INT2NUM(NGHTTP2_ERR_EOF));
778
+ rb_define_const(mDS9, "NO_ERROR", INT2NUM(NGHTTP2_NO_ERROR));
779
+ rb_define_const(mDS9, "DEFAULT_WEIGHT", INT2NUM(NGHTTP2_DEFAULT_WEIGHT));
780
+ rb_define_const(mDS9, "MAX_WEIGHT", INT2NUM(NGHTTP2_MAX_WEIGHT));
781
+ rb_define_const(mDS9, "MIN_WEIGHT", INT2NUM(NGHTTP2_MIN_WEIGHT));
782
+ rb_define_const(mDS9, "MAX_WINDOW_SIZE", INT2NUM(NGHTTP2_MAX_WINDOW_SIZE));
783
+ rb_define_const(mDS9, "INITIAL_WINDOW_SIZE", INT2NUM(NGHTTP2_INITIAL_WINDOW_SIZE));
784
+ rb_define_const(mDS9, "INITIAL_CONNECTION_WINDOW_SIZE", INT2NUM(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE));
785
+ rb_define_const(mDS9, "DEFAULT_HEADER_TABLE_SIZE", INT2NUM(NGHTTP2_DEFAULT_HEADER_TABLE_SIZE));
786
+ rb_define_const(mDS9, "CLIENT_MAGIC", rb_str_new(NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN));
787
+
788
+ rb_define_singleton_method(mDS9, "nghttp_version", rb_nghttp_version, 0);
789
+
790
+ cDS9Callbacks = rb_define_class_under(mDS9, "Callbacks", rb_cData);
791
+
792
+ cDS9Session = rb_define_class_under(mDS9, "Session", rb_cObject);
793
+ cDS9Client = rb_define_class_under(mDS9, "Client", cDS9Session);
794
+ cDS9Server = rb_define_class_under(mDS9, "Server", cDS9Session);
795
+
796
+ rb_define_alloc_func(cDS9Session, allocate_session);
797
+
798
+ rb_define_method(cDS9Session, "want_write?", session_want_write_p, 0);
799
+ rb_define_method(cDS9Session, "want_read?", session_want_read_p, 0);
800
+ rb_define_method(cDS9Session, "submit_settings", session_submit_settings, 1);
801
+ rb_define_method(cDS9Session, "submit_ping", session_submit_ping, 0);
802
+ rb_define_method(cDS9Session, "submit_goaway", session_submit_goaway, 2);
803
+ rb_define_method(cDS9Session, "send", session_send, 0);
804
+ rb_define_method(cDS9Session, "receive", session_receive, 0);
805
+ rb_define_method(cDS9Session, "mem_receive", session_mem_receive, 1);
806
+ rb_define_method(cDS9Session, "mem_send", session_mem_send, 0);
807
+ rb_define_method(cDS9Session, "outbound_queue_size", session_outbound_queue_size, 0);
808
+ rb_define_method(cDS9Session, "terminate_session", session_terminate_session, 1);
809
+ rb_define_method(cDS9Session, "stream_local_closed?", session_stream_local_closed_p, 1);
810
+ rb_define_method(cDS9Session, "stream_remote_closed?", session_stream_remote_closed_p, 1);
811
+
812
+ rb_define_method(cDS9Session, "submit_request", session_submit_request, 1);
813
+ rb_define_private_method(cDS9Session, "make_callbacks", make_callbacks, 0);
814
+ rb_define_private_method(cDS9Client, "init_internals", client_init_internals, 1);
815
+ rb_define_private_method(cDS9Server, "init_internals", server_init_internals, 1);
816
+
817
+ rb_define_method(cDS9Server, "submit_response", server_submit_response, 2);
818
+ rb_define_method(cDS9Server, "submit_push_promise", server_submit_push_promise, 2);
819
+ rb_define_method(cDS9Server, "submit_shutdown", session_submit_shutdown, 0);
820
+
821
+ rb_define_singleton_method(eDS9Exception, "to_string", errors_to_string, 1);
822
+
823
+ id_on_header = rb_intern("on_header");
824
+ id_on_begin_headers = rb_intern("on_begin_headers");
825
+ id_on_frame_not_send = rb_intern("on_frame_not_send");
826
+ id_on_begin_frame = rb_intern("on_begin_frame");
827
+ id_on_invalid_frame_recv = rb_intern("on_invalid_frame_recv");
828
+ id_send_event = rb_intern("send_event");
829
+ id_recv_event = rb_intern("recv_event");
830
+ id_on_frame_send = rb_intern("on_frame_send");
831
+ id_on_frame_recv = rb_intern("on_frame_recv");
832
+ id_on_stream_close = rb_intern("on_stream_close");
833
+ id_on_data_chunk_recv = rb_intern("on_data_chunk_recv");
834
+ id_before_frame_send = rb_intern("before_frame_send");
835
+ id_on_data_source_read = rb_intern("on_data_source_read");
836
+ }
837
+
838
+ /* vim: set noet sws=4 sw=4: */