ds9 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: fa3ba996d357a12053551efdb6f4f60b89db4807
4
- data.tar.gz: 24e779cd44010f0d19158199c75f849ff9db37c2
2
+ SHA256:
3
+ metadata.gz: 01fed28540c96e99ddb6513b914d305459d44518385ae18449a718089bf206ff
4
+ data.tar.gz: adee017da6138cbe133f12d33ce01b2d1e9dbd73f1fbb5b694b4f7ef02e377be
5
5
  SHA512:
6
- metadata.gz: 5cd6a8a0d6e38d1374c1c0cde4e3c34cd46639a28e76df77e024281322570e33e1ed38afd5f62371604693fa5e4306675311f8b5a9913d90c225306b3c88a828
7
- data.tar.gz: ca98ca2db7715ac4ad8e7ec22b0eecdbceb76f31e5ba701026f5c450e0d38b0664c6e91030c2b624704213a2688446005c980bdad6cd335bba31d4128ae6b23d
6
+ metadata.gz: 3bba90e6ae993d0f9fa1654482760da4696cbd8858e2a609b4e45ac206e812ddd8ad09835d04e3be61ffc7c28e1b8e4876e3b8752309fb735826269af04af6c2
7
+ data.tar.gz: 455f543137a742a2c0b9f10408ee02b9aaf7f1546e6fec7a18815928c3d184abcaf54d4f8299356e10578df0f5cb9b2e4a05d76feaa1b9d60f6bdf98a7f2bed2
@@ -1,3 +1,7 @@
1
+ ### 1.1.0
2
+
3
+ * Added support for post bodies
4
+
1
5
  === 1.0.0 / 2015-06-23
2
6
 
3
7
  * 1 major enhancement
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ gem 'rake-compiler', '>= 0.4.1'
12
12
  require "rake/extensiontask"
13
13
 
14
14
  Hoe.spec 'ds9' do
15
- developer('Aaron Patterson', 'aaron@tenderlovemaking.com')
15
+ developer('Aaron Patterson', 'tenderlove@ruby-lang.org')
16
16
  self.readme_file = 'README.md'
17
17
  self.history_file = 'CHANGELOG.md'
18
18
  self.extra_rdoc_files = FileList['*.md']
@@ -1,6 +1,8 @@
1
1
  #include <ds9.h>
2
2
  #include <assert.h>
3
3
 
4
+ typedef void (*copy_header_func_t)(VALUE, nghttp2_nv *, size_t);
5
+
4
6
  VALUE mDS9;
5
7
  VALUE cDS9Session;
6
8
  VALUE cDS9Client;
@@ -300,6 +302,35 @@ static void copy_list_to_nv(VALUE list, nghttp2_nv * head, size_t niv)
300
302
  }
301
303
  }
302
304
 
305
+ struct hash_copy_ctx {
306
+ nghttp2_nv * head;
307
+ };
308
+
309
+ static int
310
+ hash_copy_i(VALUE name, VALUE value, struct hash_copy_ctx * ctx)
311
+ {
312
+ nghttp2_nv * head = ctx->head;
313
+
314
+ head->name = (uint8_t *)StringValuePtr(name);
315
+ head->namelen = RSTRING_LEN(name);
316
+
317
+ head->value = (uint8_t *)StringValuePtr(value);
318
+ head->valuelen = RSTRING_LEN(value);
319
+ head->flags = NGHTTP2_NV_FLAG_NONE;
320
+
321
+ ctx->head = head + 1;
322
+
323
+ return ST_CONTINUE;
324
+ }
325
+
326
+ static void copy_hash_to_nv(VALUE hash, nghttp2_nv * head, size_t niv)
327
+ {
328
+ struct hash_copy_ctx copy_ctx;
329
+ copy_ctx.head = head;
330
+
331
+ rb_hash_foreach(hash, hash_copy_i, &copy_ctx);
332
+ }
333
+
303
334
  static VALUE allocate_session(VALUE klass)
304
335
  {
305
336
  return TypedData_Wrap_Struct(klass, &ds9_session_type, 0);
@@ -498,23 +529,89 @@ static VALUE session_outbound_queue_size(VALUE self)
498
529
  return INT2NUM(nghttp2_session_get_outbound_queue_size(session));
499
530
  }
500
531
 
501
- static VALUE session_submit_request(VALUE self, VALUE settings)
532
+ static ssize_t
533
+ ruby_read(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length,
534
+ uint32_t *data_flags, nghttp2_data_source *source, void *user_data)
535
+ {
536
+ VALUE ret = rb_funcall(source->ptr, rb_intern("read"), 1, INT2NUM(length));
537
+
538
+ if (NIL_P(ret)) {
539
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
540
+ return 0;
541
+ } else {
542
+ memcpy(buf, RSTRING_PTR(ret), RSTRING_LEN(ret));
543
+ return RSTRING_LEN(ret);
544
+ }
545
+ }
546
+
547
+ static ssize_t
548
+ file_read(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length,
549
+ uint32_t *data_flags, nghttp2_data_source *source, void *user_data)
550
+ {
551
+ ssize_t nread;
552
+ rb_io_t * fptr;
553
+
554
+ fptr = (rb_io_t *)source->ptr;
555
+ rb_io_check_readable(fptr);
556
+
557
+ nread = read(fptr->fd, buf, length);
558
+
559
+ if (nread == -1) {
560
+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
561
+ }
562
+
563
+ if (nread == 0) {
564
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
565
+ }
566
+ return nread;
567
+ }
568
+
569
+ static VALUE session_submit_request(VALUE self, VALUE settings, VALUE body)
502
570
  {
503
571
  size_t niv, i;
504
572
  nghttp2_nv *nva, *head;
505
573
  nghttp2_session *session;
574
+ nghttp2_data_provider provider;
506
575
  int rv;
576
+ copy_header_func_t copy_func;
507
577
 
508
578
  TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
509
579
  CheckSelf(session);
510
580
 
511
- Check_Type(settings, T_ARRAY);
512
- niv = RARRAY_LEN(settings);
513
- nva = xcalloc(niv, sizeof(nghttp2_nv));
581
+ switch(TYPE(settings))
582
+ {
583
+ case T_ARRAY:
584
+ niv = RARRAY_LEN(settings);
585
+ copy_func = copy_list_to_nv;
586
+ break;
587
+ case T_HASH:
588
+ niv = RHASH_SIZE(settings);
589
+ copy_func = copy_hash_to_nv;
590
+ break;
591
+ default:
592
+ Check_Type(settings, T_ARRAY);
593
+ }
514
594
 
515
- copy_list_to_nv(settings, nva, niv);
595
+ nva = xcalloc(niv, sizeof(nghttp2_nv));
516
596
 
517
- rv = nghttp2_submit_request(session, NULL, nva, niv, NULL, NULL);
597
+ copy_func(settings, nva, niv);
598
+
599
+ if (NIL_P(body)) {
600
+ rv = nghttp2_submit_request(session, NULL, nva, niv, NULL, NULL);
601
+ } else {
602
+ if (TYPE(body) == T_FILE) {
603
+ rb_io_t * rb_file;
604
+ GetOpenFile(body, rb_file);
605
+ /* Treat as a file descriptor */
606
+ provider.source.ptr = rb_file;
607
+ provider.read_callback = file_read;
608
+ } else {
609
+ provider.source.ptr = body;
610
+ provider.read_callback = ruby_read;
611
+ }
612
+
613
+ rv = nghttp2_submit_request(session, NULL, nva, niv, &provider, NULL);
614
+ }
518
615
 
519
616
  xfree(nva);
520
617
 
@@ -632,14 +729,28 @@ static VALUE server_submit_response(VALUE self, VALUE stream_id, VALUE headers)
632
729
  nghttp2_nv *nva, *head;
633
730
  nghttp2_data_provider provider;
634
731
  int rv;
732
+ copy_header_func_t copy_func;
635
733
 
636
734
  TypedData_Get_Struct(self, nghttp2_session, &ds9_session_type, session);
637
735
  CheckSelf(session);
638
736
 
639
- niv = RARRAY_LEN(headers);
737
+ switch(TYPE(headers))
738
+ {
739
+ case T_ARRAY:
740
+ niv = RARRAY_LEN(headers);
741
+ copy_func = copy_list_to_nv;
742
+ break;
743
+ case T_HASH:
744
+ niv = RHASH_SIZE(headers);
745
+ copy_func = copy_hash_to_nv;
746
+ break;
747
+ default:
748
+ Check_Type(headers, T_ARRAY);
749
+ }
750
+
640
751
  nva = xcalloc(niv, sizeof(nghttp2_nv));
641
752
 
642
- copy_list_to_nv(headers, nva, niv);
753
+ copy_func(headers, nva, niv);
643
754
 
644
755
  provider.read_callback = rb_data_read_callback;
645
756
 
@@ -809,7 +920,7 @@ void Init_ds9(void)
809
920
  rb_define_method(cDS9Session, "stream_local_closed?", session_stream_local_closed_p, 1);
810
921
  rb_define_method(cDS9Session, "stream_remote_closed?", session_stream_remote_closed_p, 1);
811
922
 
812
- rb_define_method(cDS9Session, "submit_request", session_submit_request, 1);
923
+ rb_define_private_method(cDS9Session, "submit_request", session_submit_request, 2);
813
924
  rb_define_private_method(cDS9Session, "make_callbacks", make_callbacks, 0);
814
925
  rb_define_private_method(cDS9Client, "init_internals", client_init_internals, 1);
815
926
  rb_define_private_method(cDS9Server, "init_internals", server_init_internals, 1);
@@ -2,6 +2,7 @@
2
2
  #define DS9_H
3
3
 
4
4
  #include <ruby.h>
5
+ #include <ruby/io.h>
5
6
  #include <nghttp2/nghttp2.h>
6
7
 
7
8
  static const rb_data_type_t ds9_session_type = {
data/lib/ds9.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  require 'ds9.so'
2
+ require 'stringio'
2
3
 
3
4
  module DS9
4
- VERSION = '1.0.0'
5
+ VERSION = '1.1.0'
5
6
 
6
7
  module Frames
7
8
  class Frame
@@ -103,4 +104,14 @@ module DS9
103
104
  super(str)
104
105
  end
105
106
  end
107
+
108
+ class Client
109
+ def submit_request headers, body = nil
110
+ case body
111
+ when String
112
+ body = StringIO.new body
113
+ end
114
+ super(headers, body)
115
+ end
116
+ end
106
117
  end
@@ -78,7 +78,7 @@ module DS9
78
78
  include IOEvents
79
79
 
80
80
  class Response
81
- attr_reader :stream_id, :body
81
+ attr_reader :stream_id, :body, :headers
82
82
 
83
83
  def initialize stream_id
84
84
  @stream_id = stream_id
@@ -131,6 +131,7 @@ module DS9
131
131
  def initialize read, write, app
132
132
  @app = app
133
133
  @read_streams = {}
134
+ @read_post_streams = {}
134
135
  @write_streams = {}
135
136
  super(read, write)
136
137
  end
@@ -159,10 +160,13 @@ module DS9
159
160
  end
160
161
 
161
162
  def on_header name, value, frame, flags
163
+ if name == ":method" && value == "POST"
164
+ @read_post_streams[frame.stream_id] = []
165
+ end
162
166
  @read_streams[frame.stream_id] << [name, value]
163
167
  end
164
168
 
165
- class Request < Struct.new :stream, :stream_id, :headers
169
+ class Request < Struct.new :stream, :stream_id, :headers, :body
166
170
  def path
167
171
  headers[':path']
168
172
  end
@@ -183,13 +187,20 @@ module DS9
183
187
  end
184
188
  end
185
189
 
190
+ def on_data_chunk_recv id, data, flags
191
+ @read_post_streams[id] << data
192
+ end
193
+
186
194
  def on_frame_recv frame
187
- return unless frame.headers?
195
+ return unless (frame.data? || frame.headers?) && frame.end_stream?
188
196
 
189
197
  req_headers = @read_streams[frame.stream_id]
190
198
 
191
199
  response = Response.new(self, frame.stream_id, [])
192
200
  request = Request.new(self, frame.stream_id, Hash[req_headers])
201
+ if @read_post_streams[frame.stream_id]
202
+ request.body = @read_post_streams[frame.stream_id].join
203
+ end
193
204
 
194
205
  @app.call request, response
195
206
 
@@ -131,6 +131,82 @@ class TestClient < DS9::TestCase
131
131
  assert_equal ["omglolwut", "lololol"], responses.map(&:body).map(&:string)
132
132
  end
133
133
 
134
+ def test_post
135
+ body = 'omglolwut'
136
+
137
+ server, client = pipe do |req, res|
138
+ case req.path
139
+ when '/'
140
+ res.submit_response [[":status", '200'],
141
+ ["server", 'test server'],
142
+ ["date", 'Sat, 27 Jun 2015 17:29:21 GMT'],
143
+ ["X-Whatever", "blah"]]
144
+ res.finish req.body
145
+ end
146
+ end
147
+
148
+ s = Thread.new { server.run }
149
+ c = Thread.new { client.run }
150
+
151
+ client.submit_request [
152
+ [':method', 'POST'],
153
+ [':path', '/'],
154
+ [':scheme', 'https'],
155
+ [':authority', ['localhost', '8080'].join(':')],
156
+ ['accept', '*/*'],
157
+ ['user-agent', 'test'],
158
+ ], body
159
+
160
+ responses = []
161
+ while response = client.responses.pop
162
+ responses << response
163
+ if responses.length == 1
164
+ client.terminate_session DS9::NO_ERROR
165
+ end
166
+ end
167
+
168
+ s.join
169
+ c.join
170
+ assert_equal [body], responses.map(&:body).map(&:string)
171
+ end
172
+
173
+ def test_post_file
174
+ server, client = pipe do |req, res|
175
+ case req.path
176
+ when '/'
177
+ res.submit_response [[":status", '200'],
178
+ ["server", 'test server'],
179
+ ["date", 'Sat, 27 Jun 2015 17:29:21 GMT'],
180
+ ["X-Whatever", "blah"]]
181
+ res.finish req.body
182
+ end
183
+ end
184
+
185
+ s = Thread.new { server.run }
186
+ c = Thread.new { client.run }
187
+
188
+ client.submit_request [
189
+ [':method', 'POST'],
190
+ [':path', '/'],
191
+ [':scheme', 'https'],
192
+ [':authority', ['localhost', '8080'].join(':')],
193
+ ['accept', '*/*'],
194
+ ['user-agent', 'test'],
195
+ ], File.open(__FILE__, "r")
196
+
197
+ responses = []
198
+ while response = client.responses.pop
199
+ responses << response
200
+ if responses.length == 1
201
+ client.terminate_session DS9::NO_ERROR
202
+ end
203
+ end
204
+
205
+ s.join
206
+ c.join
207
+ assert_equal File.read(__FILE__), responses.map(&:body).map(&:string).first
208
+ end
209
+
134
210
  def test_request
135
211
  body = 'omglolwut'
136
212
 
@@ -166,4 +242,49 @@ class TestClient < DS9::TestCase
166
242
  c.join
167
243
  assert_equal ["omglolwut"], responses.map(&:body).map(&:string)
168
244
  end
245
+
246
+ def test_request_response_with_hashes
247
+ body = 'omglolwut'
248
+
249
+ req_hash = {
250
+ ':method' => 'GET',
251
+ ':path' => '/',
252
+ ':scheme' => 'https',
253
+ ':authority' => ['localhost', '8080'].join(':'),
254
+ 'accept' => '*/*',
255
+ 'user-agent' => 'test',
256
+ }
257
+
258
+ res_hash = {
259
+ ":status" => '200',
260
+ "server" => 'test server',
261
+ "date" => 'Sat, 27 Jun 2015 17:29:21 GMT',
262
+ "x-whatever" => "blah"
263
+ }
264
+
265
+ server, client = pipe do |req, res|
266
+ assert_equal req_hash, req.headers
267
+
268
+ res.submit_response res_hash
269
+ res.finish body
270
+ end
271
+
272
+ client.submit_request req_hash
273
+
274
+ s = Thread.new { server.run }
275
+ c = Thread.new { client.run }
276
+
277
+ responses = []
278
+ while response = client.responses.pop
279
+ responses << response
280
+ if responses.length == 1
281
+ client.terminate_session DS9::NO_ERROR
282
+ end
283
+ end
284
+
285
+ s.join
286
+ c.join
287
+ assert_equal res_hash, responses.first.headers
288
+ assert_equal ["omglolwut"], responses.map(&:body).map(&:string)
289
+ end
169
290
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ds9
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-06 00:00:00.000000000 Z
11
+ date: 2018-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -16,61 +16,67 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.8'
19
+ version: '5.11'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.8'
26
+ version: '5.11'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rdoc
28
+ name: rake-compiler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '4.0'
33
+ version: 0.4.1
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '4.0'
40
+ version: 0.4.1
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake-compiler
42
+ name: rdoc
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.4.1
47
+ version: '4.0'
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: '6'
48
51
  type: :development
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
55
  - - ">="
53
56
  - !ruby/object:Gem::Version
54
- version: 0.4.1
57
+ version: '4.0'
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: '6'
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: hoe
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '3.14'
67
+ version: '3.17'
62
68
  type: :development
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: '3.14'
74
+ version: '3.17'
69
75
  description: |-
70
76
  This library allows you to write HTTP/2 clients and servers. It is a wrapper
71
77
  around nghttp2.
72
78
  email:
73
- - aaron@tenderlovemaking.com
79
+ - tenderlove@ruby-lang.org
74
80
  executables: []
75
81
  extensions:
76
82
  - ext/ds9/extconf.rb
@@ -114,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
120
  version: '0'
115
121
  requirements: []
116
122
  rubyforge_project:
117
- rubygems_version: 2.5.1
123
+ rubygems_version: 2.7.6
118
124
  signing_key:
119
125
  specification_version: 4
120
126
  summary: This library allows you to write HTTP/2 clients and servers