ds9 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: */