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,30 @@
1
+ #ifndef DS9_H
2
+ #define DS9_H
3
+
4
+ #include <ruby.h>
5
+ #include <nghttp2/nghttp2.h>
6
+
7
+ static const rb_data_type_t ds9_session_type = {
8
+ "DS9/session",
9
+ {0, (void (*)(void *))nghttp2_session_del, 0,},
10
+ 0, 0,
11
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
12
+ RUBY_TYPED_FREE_IMMEDIATELY,
13
+ #endif
14
+ };
15
+
16
+ static const rb_data_type_t ds9_callbacks_type = {
17
+ "DS9/callbacks",
18
+ {0, (void (*)(void *))nghttp2_session_callbacks_del, 0,},
19
+ 0, 0,
20
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
21
+ RUBY_TYPED_FREE_IMMEDIATELY,
22
+ #endif
23
+ };
24
+
25
+ void Init_ds9_client(VALUE mDS9, VALUE cDS9Session);
26
+ void Init_ds9_frames(VALUE mDS9);
27
+ VALUE WrapDS9Frame(const nghttp2_frame *frame);
28
+ VALUE WrapDS9FrameHeader(const nghttp2_frame_hd *hd);
29
+
30
+ #endif
@@ -0,0 +1,164 @@
1
+ #include <ds9.h>
2
+
3
+ VALUE mDS9Frames;
4
+
5
+ VALUE cDS9FramesFrame;
6
+ VALUE mDS9FramesFlags;
7
+ VALUE cDS9FramesData;
8
+ VALUE cDS9FramesHeaders;
9
+ VALUE cDS9FramesPriority;
10
+ VALUE cDS9FramesRstStream;
11
+ VALUE cDS9FramesSettings;
12
+ VALUE cDS9FramesPushPromise;
13
+ VALUE cDS9FramesPing;
14
+ VALUE cDS9FramesGoaway;
15
+ VALUE cDS9FramesWindowUpdate;
16
+ VALUE cDS9FramesContinuation;
17
+
18
+ static const rb_data_type_t ds9_frame_type = {
19
+ "DS9/frame",
20
+ {0, xfree, 0,},
21
+ 0, 0,
22
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
23
+ RUBY_TYPED_FREE_IMMEDIATELY,
24
+ #endif
25
+ };
26
+
27
+ static VALUE frame_type_class(nghttp2_frame *frame)
28
+ {
29
+ switch(frame->hd.type) {
30
+ case NGHTTP2_DATA: return cDS9FramesData;
31
+ case NGHTTP2_HEADERS: return cDS9FramesHeaders;
32
+ case NGHTTP2_PRIORITY: return cDS9FramesPriority;
33
+ case NGHTTP2_RST_STREAM: return cDS9FramesRstStream;
34
+ case NGHTTP2_SETTINGS: return cDS9FramesSettings;
35
+ case NGHTTP2_PUSH_PROMISE: return cDS9FramesPushPromise;
36
+ case NGHTTP2_PING: return cDS9FramesPing;
37
+ case NGHTTP2_GOAWAY: return cDS9FramesGoaway;
38
+ case NGHTTP2_WINDOW_UPDATE: return cDS9FramesWindowUpdate;
39
+ case NGHTTP2_CONTINUATION: return cDS9FramesContinuation;
40
+ default: return cDS9FramesFrame;
41
+ }
42
+ }
43
+
44
+ VALUE WrapDS9FrameHeader(const nghttp2_frame_hd *hd)
45
+ {
46
+ VALUE klass = rb_const_get(cDS9FramesFrame, rb_intern("Header"));
47
+ return rb_funcall(klass, rb_intern("new"), 4,
48
+ INT2NUM(hd->length),
49
+ INT2NUM(hd->stream_id),
50
+ INT2NUM(hd->type),
51
+ INT2NUM(hd->flags));
52
+ }
53
+
54
+ VALUE WrapDS9Frame(const nghttp2_frame *frame)
55
+ {
56
+ /* dup the frame so Ruby can manage the struct's memory */
57
+ nghttp2_frame *dup = xmalloc(sizeof(nghttp2_frame));
58
+ memcpy(dup, frame, sizeof(nghttp2_frame));
59
+
60
+ return TypedData_Wrap_Struct(frame_type_class(dup), &ds9_frame_type, dup);
61
+ }
62
+
63
+ static VALUE frame_stream_id(VALUE self)
64
+ {
65
+ nghttp2_frame *frame;
66
+ TypedData_Get_Struct(self, nghttp2_frame, &ds9_frame_type, frame);
67
+
68
+ return INT2NUM(frame->hd.stream_id);
69
+ }
70
+
71
+ static VALUE frame_type(VALUE self)
72
+ {
73
+ nghttp2_frame *frame;
74
+ TypedData_Get_Struct(self, nghttp2_frame, &ds9_frame_type, frame);
75
+
76
+ return INT2NUM(frame->hd.type);
77
+ }
78
+
79
+ static VALUE frame_flags(VALUE self)
80
+ {
81
+ nghttp2_frame *frame;
82
+ TypedData_Get_Struct(self, nghttp2_frame, &ds9_frame_type, frame);
83
+
84
+ return INT2NUM(frame->hd.flags);
85
+ }
86
+
87
+ static VALUE promised_stream_id(VALUE self)
88
+ {
89
+ nghttp2_frame *frame;
90
+ TypedData_Get_Struct(self, nghttp2_frame, &ds9_frame_type, frame);
91
+
92
+ return INT2NUM(frame->push_promise.promised_stream_id);
93
+ }
94
+
95
+ static VALUE frame_header(VALUE self)
96
+ {
97
+ nghttp2_frame *frame;
98
+ TypedData_Get_Struct(self, nghttp2_frame, &ds9_frame_type, frame);
99
+
100
+ return WrapDS9FrameHeader((nghttp2_frame_hd *)frame);
101
+ }
102
+
103
+ static VALUE header_category(VALUE self)
104
+ {
105
+ nghttp2_frame *frame;
106
+ TypedData_Get_Struct(self, nghttp2_frame, &ds9_frame_type, frame);
107
+
108
+ return INT2NUM(frame->headers.cat);
109
+ }
110
+
111
+ void Init_ds9_frames(VALUE mDS9)
112
+ {
113
+ mDS9Frames = rb_define_module_under(mDS9, "Frames");
114
+ cDS9FramesFrame = rb_define_class_under(mDS9Frames, "Frame", rb_cData);
115
+
116
+ mDS9FramesFlags = rb_define_module_under(cDS9FramesFrame, "Flags");
117
+
118
+ cDS9FramesData = rb_define_class_under(mDS9Frames, "Data", cDS9FramesFrame);
119
+ cDS9FramesHeaders = rb_define_class_under(mDS9Frames, "Headers", cDS9FramesFrame);
120
+ cDS9FramesPriority = rb_define_class_under(mDS9Frames, "Priority", cDS9FramesFrame);
121
+ cDS9FramesRstStream = rb_define_class_under(mDS9Frames, "RstStream", cDS9FramesFrame);
122
+ cDS9FramesSettings = rb_define_class_under(mDS9Frames, "Settings", cDS9FramesFrame);
123
+ cDS9FramesPushPromise = rb_define_class_under(mDS9Frames, "PushPromise", cDS9FramesFrame);
124
+ cDS9FramesPing = rb_define_class_under(mDS9Frames, "Ping", cDS9FramesFrame);
125
+ cDS9FramesGoaway = rb_define_class_under(mDS9Frames, "Goaway", cDS9FramesFrame);
126
+ cDS9FramesWindowUpdate = rb_define_class_under(mDS9Frames, "WindowUpdate", cDS9FramesFrame);
127
+ cDS9FramesContinuation = rb_define_class_under(mDS9Frames, "Continuation", cDS9FramesFrame);
128
+
129
+ rb_define_method(cDS9FramesFrame, "stream_id", frame_stream_id, 0);
130
+ rb_define_method(cDS9FramesFrame, "type", frame_type, 0);
131
+ rb_define_method(cDS9FramesFrame, "flags", frame_flags, 0);
132
+ rb_define_method(cDS9FramesFrame, "header", frame_header, 0);
133
+ rb_define_method(cDS9FramesPushPromise, "promised_stream_id", promised_stream_id, 0);
134
+ rb_define_method(cDS9FramesHeaders, "category", header_category, 0);
135
+
136
+ rb_define_const(mDS9FramesFlags, "NONE", INT2NUM(NGHTTP2_FLAG_NONE));
137
+ rb_define_const(mDS9FramesFlags, "END_STREAM", INT2NUM(NGHTTP2_FLAG_END_STREAM));
138
+ rb_define_const(mDS9FramesFlags, "END_HEADERS", INT2NUM(NGHTTP2_FLAG_END_HEADERS));
139
+ rb_define_const(mDS9FramesFlags, "ACK", INT2NUM(NGHTTP2_FLAG_ACK));
140
+ rb_define_const(mDS9FramesFlags, "PADDED", INT2NUM(NGHTTP2_FLAG_PADDED));
141
+ rb_define_const(mDS9FramesFlags, "PRIORITY", INT2NUM(NGHTTP2_FLAG_PRIORITY));
142
+
143
+ rb_define_const(cDS9FramesGoaway, "NO_ERROR", INT2NUM(NGHTTP2_NO_ERROR));
144
+ rb_define_const(cDS9FramesGoaway, "PROTOCOL_ERROR", INT2NUM(NGHTTP2_PROTOCOL_ERROR));
145
+ rb_define_const(cDS9FramesGoaway, "INTERNAL_ERROR", INT2NUM(NGHTTP2_INTERNAL_ERROR));
146
+ rb_define_const(cDS9FramesGoaway, "FLOW_CONTROL_ERROR", INT2NUM(NGHTTP2_FLOW_CONTROL_ERROR));
147
+ rb_define_const(cDS9FramesGoaway, "SETTINGS_TIMEOUT", INT2NUM(NGHTTP2_SETTINGS_TIMEOUT));
148
+ rb_define_const(cDS9FramesGoaway, "STREAM_CLOSED", INT2NUM(NGHTTP2_STREAM_CLOSED));
149
+ rb_define_const(cDS9FramesGoaway, "FRAME_SIZE_ERROR", INT2NUM(NGHTTP2_FRAME_SIZE_ERROR));
150
+ rb_define_const(cDS9FramesGoaway, "REFUSED_STREAM", INT2NUM(NGHTTP2_REFUSED_STREAM));
151
+ rb_define_const(cDS9FramesGoaway, "CANCEL", INT2NUM(NGHTTP2_CANCEL));
152
+ rb_define_const(cDS9FramesGoaway, "COMPRESSION_ERROR", INT2NUM(NGHTTP2_COMPRESSION_ERROR));
153
+ rb_define_const(cDS9FramesGoaway, "CONNECT_ERROR", INT2NUM(NGHTTP2_CONNECT_ERROR));
154
+ rb_define_const(cDS9FramesGoaway, "ENHANCE_YOUR_CALM", INT2NUM(NGHTTP2_ENHANCE_YOUR_CALM));
155
+ rb_define_const(cDS9FramesGoaway, "INADEQUATE_SECURITY", INT2NUM(NGHTTP2_INADEQUATE_SECURITY));
156
+ rb_define_const(cDS9FramesGoaway, "HTTP_1_1_REQUIRED", INT2NUM(NGHTTP2_HTTP_1_1_REQUIRED));
157
+
158
+ rb_define_const(cDS9FramesHeaders, "REQUEST", INT2NUM(NGHTTP2_HCAT_REQUEST));
159
+ rb_define_const(cDS9FramesHeaders, "RESPONSE", INT2NUM(NGHTTP2_HCAT_RESPONSE));
160
+ rb_define_const(cDS9FramesHeaders, "PUSH_RESPONSE", INT2NUM(NGHTTP2_HCAT_PUSH_RESPONSE));
161
+ rb_define_const(cDS9FramesHeaders, "HEADERS", INT2NUM(NGHTTP2_HCAT_HEADERS));
162
+ }
163
+
164
+ /* vim: set noet sws=4 sw=4: */
@@ -0,0 +1,16 @@
1
+ require 'mkmf'
2
+
3
+ if with_config("static")
4
+ ldflags = pkg_config 'libnghttp2', 'libs-only-L'
5
+
6
+ archive = File.join ldflags.sub(/^-L/, ''), 'libnghttp2.a'
7
+ if File.exist? archive
8
+ $LDFLAGS << " #{archive}"
9
+ else
10
+ raise "couldn't find archive"
11
+ end
12
+ else
13
+ pkg_config 'libnghttp2'
14
+ end
15
+
16
+ create_makefile 'ds9'
@@ -0,0 +1,106 @@
1
+ require 'ds9.so'
2
+
3
+ module DS9
4
+ VERSION = '1.0.0'
5
+
6
+ module Frames
7
+ class Frame
8
+ Header = Struct.new :length, :stream_id, :type, :flags
9
+
10
+ def length
11
+ header.length
12
+ end
13
+
14
+ def settings?; false; end
15
+ def headers?; false; end
16
+ def data?; false; end
17
+ def push_promise?; false; end
18
+ def priority?; false; end
19
+ def rst_stream?; false; end
20
+ def ping?; false; end
21
+ def goaway?; false; end
22
+ def window_update?; false; end
23
+ def continuation?; false; end
24
+
25
+ def end_stream?
26
+ flags & Flags::END_STREAM > 0
27
+ end
28
+ end
29
+
30
+ class Continuation
31
+ def continuation?; true; end
32
+ end
33
+ class WindowUpdate
34
+ def window_update?; true; end
35
+ end
36
+
37
+ class Goaway
38
+ def goaway?; true; end
39
+ end
40
+
41
+ class Ping
42
+ def ping?; true; end
43
+ end
44
+
45
+ class Priority
46
+ def priority?; true; end
47
+ end
48
+
49
+ class RstStream
50
+ def rst_stream?; true; end
51
+ end
52
+
53
+ class PushPromise
54
+ def push_promise?; true; end
55
+ end
56
+
57
+ class Data
58
+ def data?; true; end
59
+ end
60
+
61
+ class Settings
62
+ def settings?; true; end
63
+ end
64
+
65
+ class Headers
66
+ def headers?; true; end
67
+ def request?; category == REQUEST; end
68
+ def response?; category == RESPONSE; end
69
+ def push_response?; category == PUSH_RESPONSE; end
70
+ end
71
+ end
72
+
73
+ class Session
74
+ def initialize
75
+ cbs = make_callbacks
76
+ init_internals cbs
77
+ end
78
+
79
+ private
80
+
81
+ def send_event string
82
+ raise NotImplementedError
83
+ end
84
+
85
+ def on_data_source_read stream_id, length
86
+ raise NotImplementedError
87
+ end
88
+
89
+ def recv_event length
90
+ raise NotImplementedError
91
+ end
92
+ end
93
+
94
+ class Exception < StandardError
95
+ def self.abort code
96
+ raise new(to_string(code), code)
97
+ end
98
+
99
+ attr_reader :code
100
+
101
+ def initialize str, code
102
+ @code = code
103
+ super(str)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,201 @@
1
+ require 'minitest/autorun'
2
+ require 'ds9'
3
+ require 'io/wait'
4
+ require 'thread'
5
+ require 'stringio'
6
+
7
+ Thread.abort_on_exception = true
8
+
9
+ trap("INFO") {
10
+ Thread.list.each do |k|
11
+ puts "#" * 90
12
+ puts k.backtrace
13
+ puts "#" * 90
14
+ end
15
+ }
16
+
17
+ module DS9
18
+ class TestCase < Minitest::Test
19
+ def pipe &block
20
+ rd1, wr1 = IO.pipe
21
+ rd2, wr2 = IO.pipe
22
+
23
+ server = Server.new rd1, wr2, block
24
+ server.submit_settings [
25
+ [DS9::Settings::MAX_CONCURRENT_STREAMS, 100],
26
+ ]
27
+
28
+ client = Client.new rd2, wr1, Queue.new
29
+ client.submit_settings [
30
+ [DS9::Settings::MAX_CONCURRENT_STREAMS, 100],
31
+ [DS9::Settings::INITIAL_WINDOW_SIZE, 65535],
32
+ ]
33
+ [server, client]
34
+ end
35
+
36
+ module IOEvents
37
+ attr_reader :reader, :writer
38
+
39
+ def initialize reader, writer
40
+ @reader = reader
41
+ @writer = writer
42
+ super()
43
+ end
44
+
45
+ def send_event string
46
+ writer.write_nonblock string
47
+ end
48
+
49
+ def recv_event length
50
+ case data = reader.read_nonblock(length, nil, exception: false)
51
+ when :wait_readable
52
+ DS9::ERR_WOULDBLOCK
53
+ when nil
54
+ DS9::ERR_EOF
55
+ else
56
+ data
57
+ end
58
+ rescue IOError
59
+ DS9::ERR_EOF
60
+ end
61
+
62
+ def run
63
+ while want_read? || want_write?
64
+ if want_write?
65
+ @writer.wait_writable 0.1
66
+ send
67
+ end
68
+
69
+ if want_read?
70
+ @reader.wait_readable 0.1
71
+ receive
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ class Client < DS9::Client
78
+ include IOEvents
79
+
80
+ class Response
81
+ attr_reader :stream_id, :body
82
+
83
+ def initialize stream_id
84
+ @stream_id = stream_id
85
+ @headers = {}
86
+ @body = StringIO.new
87
+ end
88
+
89
+ def [] k; @headers[k]; end
90
+ def []= k, v; @headers[k] = v; end
91
+ end
92
+
93
+ attr_reader :responses, :response_queue, :frames
94
+
95
+ def initialize read, write, response_queue
96
+ @response_streams = {}
97
+ @responses = response_queue
98
+ @frames = []
99
+ super(read, write)
100
+ end
101
+
102
+ def on_frame_recv frame
103
+ @frames << frame
104
+ end
105
+
106
+ def on_begin_headers frame
107
+ @response_streams[frame.stream_id] = Response.new(frame.stream_id)
108
+ end
109
+
110
+ def on_header name, value, frame, flags
111
+ @response_streams[frame.stream_id][name] = value
112
+ end
113
+
114
+ def on_stream_close id, err
115
+ @responses << @response_streams.delete(id)
116
+ end
117
+
118
+ def on_data_chunk_recv stream_id, data, flags
119
+ @response_streams[stream_id].body << data
120
+ end
121
+
122
+ def terminate_session err
123
+ super
124
+ @responses << nil
125
+ end
126
+ end
127
+
128
+ class Server < DS9::Server
129
+ include IOEvents
130
+
131
+ def initialize read, write, app
132
+ @app = app
133
+ @read_streams = {}
134
+ @write_streams = {}
135
+ super(read, write)
136
+ end
137
+
138
+ def before_frame_send frame
139
+ end
140
+
141
+ def on_begin_headers frame
142
+ @read_streams[frame.stream_id] = []
143
+ end
144
+
145
+ def on_data_source_read stream_id, length
146
+ @write_streams[stream_id].body.shift
147
+ end
148
+
149
+ def on_stream_close id, error_code
150
+ @read_streams.delete id
151
+ @write_streams.delete id
152
+ end
153
+
154
+ def submit_push_promise stream_id, headers
155
+ response = Response.new(self, super(stream_id, headers), [])
156
+ request = Request.new(self, stream_id, Hash[headers])
157
+ @app.call request, response
158
+ @write_streams[response.stream_id] = response
159
+ end
160
+
161
+ def on_header name, value, frame, flags
162
+ @read_streams[frame.stream_id] << [name, value]
163
+ end
164
+
165
+ class Request < Struct.new :stream, :stream_id, :headers
166
+ def path
167
+ headers[':path']
168
+ end
169
+ end
170
+
171
+ class Response < Struct.new :stream, :stream_id, :body
172
+ def push headers
173
+ stream.submit_push_promise stream_id, headers
174
+ end
175
+
176
+ def submit_response headers
177
+ stream.submit_response stream_id, headers
178
+ end
179
+
180
+ def finish str
181
+ body << str
182
+ body << nil
183
+ end
184
+ end
185
+
186
+ def on_frame_recv frame
187
+ return unless frame.headers?
188
+
189
+ req_headers = @read_streams[frame.stream_id]
190
+
191
+ response = Response.new(self, frame.stream_id, [])
192
+ request = Request.new(self, frame.stream_id, Hash[req_headers])
193
+
194
+ @app.call request, response
195
+
196
+ @write_streams[frame.stream_id] = response
197
+ end
198
+
199
+ end
200
+ end
201
+ end