ds9 1.0.0

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