llhttp 0.0.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,314 @@
1
+ /*
2
+ This software is licensed under the MPL-2.0 License.
3
+
4
+ Copyright Bryan Powell, 2020.
5
+ */
6
+
7
+ #include <ruby/ruby.h>
8
+
9
+ #include "llhttp.h"
10
+
11
+ static VALUE mLLHttp, cParser, eError;
12
+
13
+ static ID rb_llhttp_callback_on_message_begin;
14
+ static ID rb_llhttp_callback_on_url;
15
+ static ID rb_llhttp_callback_on_status;
16
+ static ID rb_llhttp_callback_on_header_field;
17
+ static ID rb_llhttp_callback_on_header_value;
18
+ static ID rb_llhttp_callback_on_headers_complete;
19
+ static ID rb_llhttp_callback_on_body;
20
+ static ID rb_llhttp_callback_on_message_complete;
21
+ static ID rb_llhttp_callback_on_chunk_header;
22
+ static ID rb_llhttp_callback_on_chunk_complete;
23
+ static ID rb_llhttp_callback_on_url_complete;
24
+ static ID rb_llhttp_callback_on_status_complete;
25
+ static ID rb_llhttp_callback_on_header_field_complete;
26
+ static ID rb_llhttp_callback_on_header_value_complete;
27
+
28
+ static void rb_llhttp_free(llhttp_t *parser) {
29
+ if (parser) {
30
+ free(parser->settings);
31
+ free(parser);
32
+ }
33
+ }
34
+
35
+ VALUE rb_llhttp_allocate(VALUE klass) {
36
+ llhttp_t *parser = (llhttp_t *)malloc(sizeof(llhttp_t));
37
+ llhttp_settings_t *settings = (llhttp_settings_t *)malloc(sizeof(llhttp_settings_t));
38
+
39
+ llhttp_settings_init(settings);
40
+ llhttp_init(parser, HTTP_BOTH, settings);
41
+
42
+ return Data_Wrap_Struct(klass, 0, rb_llhttp_free, parser);
43
+ }
44
+
45
+ VALUE rb_llhttp_callback_call(VALUE delegate, ID method) {
46
+ return rb_funcall(delegate, method, 0);
47
+ }
48
+
49
+ void rb_llhttp_data_callback_call(VALUE delegate, ID method, char *data, size_t length) {
50
+ rb_funcall(delegate, method, 1, rb_str_new(data, length));
51
+ }
52
+
53
+ int rb_llhttp_on_message_begin(llhttp_t *parser) {
54
+ return NUM2INT(rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_message_begin));
55
+ }
56
+
57
+ int rb_llhttp_on_headers_complete(llhttp_t *parser) {
58
+ return NUM2INT(rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_headers_complete));
59
+ }
60
+
61
+ int rb_llhttp_on_message_complete(llhttp_t *parser) {
62
+ return NUM2INT(rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_message_complete));
63
+ }
64
+
65
+ int rb_llhttp_on_chunk_header(llhttp_t *parser) {
66
+ return NUM2INT(rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_chunk_header));
67
+ }
68
+
69
+ int rb_llhttp_on_url(llhttp_t *parser, char *data, size_t length) {
70
+ rb_llhttp_data_callback_call((VALUE)parser->data, rb_llhttp_callback_on_url, data, length);
71
+
72
+ return 0;
73
+ }
74
+
75
+ int rb_llhttp_on_status(llhttp_t *parser, char *data, size_t length) {
76
+ rb_llhttp_data_callback_call((VALUE)parser->data, rb_llhttp_callback_on_status, data, length);
77
+
78
+ return 0;
79
+ }
80
+
81
+ int rb_llhttp_on_header_field(llhttp_t *parser, char *data, size_t length) {
82
+ rb_llhttp_data_callback_call((VALUE)parser->data, rb_llhttp_callback_on_header_field, data, length);
83
+
84
+ return 0;
85
+ }
86
+
87
+ int rb_llhttp_on_header_value(llhttp_t *parser, char *data, size_t length) {
88
+ rb_llhttp_data_callback_call((VALUE)parser->data, rb_llhttp_callback_on_header_value, data, length);
89
+
90
+ return 0;
91
+ }
92
+
93
+ int rb_llhttp_on_body(llhttp_t *parser, char *data, size_t length) {
94
+ rb_llhttp_data_callback_call((VALUE)parser->data, rb_llhttp_callback_on_body, data, length);
95
+
96
+ return 0;
97
+ }
98
+
99
+ int rb_llhttp_on_chunk_complete(llhttp_t *parser) {
100
+ rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_chunk_complete);
101
+
102
+ return 0;
103
+ }
104
+
105
+ int rb_llhttp_on_url_complete(llhttp_t *parser) {
106
+ rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_url_complete);
107
+
108
+ return 0;
109
+ }
110
+
111
+ int rb_llhttp_on_status_complete(llhttp_t *parser) {
112
+ rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_status_complete);
113
+
114
+ return 0;
115
+ }
116
+
117
+ int rb_llhttp_on_header_field_complete(llhttp_t *parser) {
118
+ rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_header_field_complete);
119
+
120
+ return 0;
121
+ }
122
+
123
+ int rb_llhttp_on_header_value_complete(llhttp_t *parser) {
124
+ rb_llhttp_callback_call((VALUE)parser->data, rb_llhttp_callback_on_header_value_complete);
125
+
126
+ return 0;
127
+ }
128
+
129
+ VALUE rb_llhttp_parse(VALUE self, VALUE data) {
130
+ llhttp_t *parser;
131
+
132
+ Data_Get_Struct(self, llhttp_t, parser);
133
+
134
+ enum llhttp_errno err = llhttp_execute(parser, RSTRING_PTR(data), RSTRING_LEN(data));
135
+
136
+ if (err != HPE_OK) {
137
+ rb_raise(eError, "Error Parsing data: %s %s", llhttp_errno_name(err), parser->reason);
138
+ }
139
+
140
+ return Qtrue;
141
+ }
142
+
143
+ VALUE rb_llhttp_finish(VALUE self) {
144
+ llhttp_t *parser;
145
+
146
+ Data_Get_Struct(self, llhttp_t, parser);
147
+
148
+ enum llhttp_errno err = llhttp_finish(parser);
149
+
150
+ if (err != HPE_OK) {
151
+ rb_raise(eError, "Error Parsing data: %s %s", llhttp_errno_name(err), parser->reason);
152
+ }
153
+
154
+ return Qtrue;
155
+ }
156
+
157
+ VALUE rb_llhttp_content_length(VALUE self) {
158
+ llhttp_t *parser;
159
+
160
+ Data_Get_Struct(self, llhttp_t, parser);
161
+
162
+ return ULL2NUM(parser->content_length);
163
+ }
164
+
165
+ VALUE rb_llhttp_method_name(VALUE self) {
166
+ llhttp_t *parser;
167
+
168
+ Data_Get_Struct(self, llhttp_t, parser);
169
+
170
+ return rb_str_new_cstr(llhttp_method_name(parser->method));
171
+ }
172
+
173
+ VALUE rb_llhttp_status_code(VALUE self) {
174
+ llhttp_t *parser;
175
+
176
+ Data_Get_Struct(self, llhttp_t, parser);
177
+
178
+ return UINT2NUM(parser->status_code);
179
+ }
180
+
181
+ VALUE rb_llhttp_http_major(VALUE self) {
182
+ llhttp_t *parser;
183
+
184
+ Data_Get_Struct(self, llhttp_t, parser);
185
+
186
+ return UINT2NUM(parser->http_major);
187
+ }
188
+
189
+ VALUE rb_llhttp_http_minor(VALUE self) {
190
+ llhttp_t *parser;
191
+
192
+ Data_Get_Struct(self, llhttp_t, parser);
193
+
194
+ return UINT2NUM(parser->http_minor);
195
+ }
196
+
197
+ VALUE rb_llhttp_keep_alive(VALUE self) {
198
+ llhttp_t *parser;
199
+
200
+ Data_Get_Struct(self, llhttp_t, parser);
201
+
202
+ int ret = llhttp_should_keep_alive(parser);
203
+
204
+ return ret == 1 ? Qtrue : Qfalse;
205
+ }
206
+
207
+ static VALUE rb_llhttp_init(VALUE self, VALUE type) {
208
+ llhttp_t *parser;
209
+
210
+ Data_Get_Struct(self, llhttp_t, parser);
211
+
212
+ llhttp_settings_t *settings = parser->settings;
213
+
214
+ VALUE delegate = rb_iv_get(self, "@delegate");
215
+
216
+ rb_llhttp_callback_on_message_begin = rb_intern("internal_on_message_begin");
217
+ rb_llhttp_callback_on_headers_complete = rb_intern("internal_on_headers_complete");
218
+ rb_llhttp_callback_on_message_complete = rb_intern("internal_on_message_complete");
219
+ rb_llhttp_callback_on_chunk_header = rb_intern("internal_on_chunk_header");
220
+ rb_llhttp_callback_on_url = rb_intern("on_url");
221
+ rb_llhttp_callback_on_status = rb_intern("on_status");
222
+ rb_llhttp_callback_on_header_field = rb_intern("on_header_field");
223
+ rb_llhttp_callback_on_header_value = rb_intern("on_header_value");
224
+ rb_llhttp_callback_on_body = rb_intern("on_body");
225
+ rb_llhttp_callback_on_chunk_complete = rb_intern("on_chunk_complete");
226
+ rb_llhttp_callback_on_url_complete = rb_intern("on_url_complete");
227
+ rb_llhttp_callback_on_status_complete = rb_intern("on_status_complete");
228
+ rb_llhttp_callback_on_header_field_complete = rb_intern("on_header_field_complete");
229
+ rb_llhttp_callback_on_header_value_complete = rb_intern("on_header_value_complete");
230
+
231
+ if (rb_respond_to(delegate, rb_intern("on_message_begin"))) {
232
+ settings->on_message_begin = (llhttp_cb)rb_llhttp_on_message_begin;
233
+ }
234
+
235
+ if (rb_respond_to(delegate, rb_intern("on_headers_complete"))) {
236
+ settings->on_headers_complete = (llhttp_cb)rb_llhttp_on_headers_complete;
237
+ }
238
+
239
+ if (rb_respond_to(delegate, rb_intern("on_message_complete"))) {
240
+ settings->on_message_complete = (llhttp_cb)rb_llhttp_on_message_complete;
241
+ }
242
+
243
+ if (rb_respond_to(delegate, rb_intern("on_chunk_header"))) {
244
+ settings->on_chunk_header = (llhttp_cb)rb_llhttp_on_chunk_header;
245
+ }
246
+
247
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_url)) {
248
+ settings->on_url = (llhttp_data_cb)rb_llhttp_on_url;
249
+ }
250
+
251
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_status)) {
252
+ settings->on_status = (llhttp_data_cb)rb_llhttp_on_status;
253
+ }
254
+
255
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_header_field)) {
256
+ settings->on_header_field = (llhttp_data_cb)rb_llhttp_on_header_field;
257
+ }
258
+
259
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_header_value)) {
260
+ settings->on_header_value = (llhttp_data_cb)rb_llhttp_on_header_value;
261
+ }
262
+
263
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_body)) {
264
+ settings->on_body = (llhttp_data_cb)rb_llhttp_on_body;
265
+ }
266
+
267
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_chunk_complete)) {
268
+ settings->on_chunk_complete = (llhttp_cb)rb_llhttp_on_chunk_complete;
269
+ }
270
+
271
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_url_complete)) {
272
+ settings->on_url_complete = (llhttp_cb)rb_llhttp_on_url_complete;
273
+ }
274
+
275
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_status_complete)) {
276
+ settings->on_status_complete = (llhttp_cb)rb_llhttp_on_status_complete;
277
+ }
278
+
279
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_header_field_complete)) {
280
+ settings->on_header_field_complete = (llhttp_cb)rb_llhttp_on_header_field_complete;
281
+ }
282
+
283
+ if (rb_respond_to(delegate, rb_llhttp_callback_on_header_value_complete)) {
284
+ settings->on_header_value_complete = (llhttp_cb)rb_llhttp_on_header_value_complete;
285
+ }
286
+
287
+ llhttp_init(parser, FIX2INT(type), settings);
288
+
289
+ parser->data = (void*)delegate;
290
+
291
+ return Qtrue;
292
+ }
293
+
294
+ void Init_llhttp_ext(void) {
295
+ mLLHttp = rb_const_get(rb_cObject, rb_intern("LLHttp"));
296
+ cParser = rb_const_get(mLLHttp, rb_intern("Parser"));
297
+ eError = rb_const_get(mLLHttp, rb_intern("Error"));
298
+
299
+ rb_define_alloc_func(cParser, rb_llhttp_allocate);
300
+
301
+ rb_define_method(cParser, "<<", rb_llhttp_parse, 1);
302
+ rb_define_method(cParser, "parse", rb_llhttp_parse, 1);
303
+ rb_define_method(cParser, "finish", rb_llhttp_finish, 0);
304
+
305
+ rb_define_method(cParser, "content_length", rb_llhttp_content_length, 0);
306
+ rb_define_method(cParser, "method_name", rb_llhttp_method_name, 0);
307
+ rb_define_method(cParser, "status_code", rb_llhttp_status_code, 0);
308
+ rb_define_method(cParser, "http_major", rb_llhttp_http_major, 0);
309
+ rb_define_method(cParser, "http_minor", rb_llhttp_http_minor, 0);
310
+
311
+ rb_define_method(cParser, "keep_alive?", rb_llhttp_keep_alive, 0);
312
+
313
+ rb_define_private_method(cParser, "llhttp_init", rb_llhttp_init, 1);
314
+ }
@@ -1,37 +1,97 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LLHttp
4
- # Delegate for handling callbacks. Subclass this, implementing the necessary methods.
4
+ # [public] Delegate for handling callbacks. Subclass this object and implement necessary methods.
5
+ #
6
+ # class Delegate < LLHttp::Delegate
7
+ # def on_message_begin
8
+ # ...
9
+ # end
10
+ #
11
+ # def on_url(url)
12
+ # ...
13
+ # end
14
+ #
15
+ # def on_status(status)
16
+ # ...
17
+ # end
18
+ #
19
+ # def on_header_field(field)
20
+ # ...
21
+ # end
22
+ #
23
+ # def on_header_value(value)
24
+ # ...
25
+ # end
26
+ #
27
+ # def on_headers_complete
28
+ # ...
29
+ # end
30
+ #
31
+ # def on_body(body)
32
+ # ...
33
+ # end
34
+ #
35
+ # def on_message_complete
36
+ # ...
37
+ # end
38
+ #
39
+ # def on_chunk_header
40
+ # ...
41
+ # end
42
+ #
43
+ # def on_chunk_complete
44
+ # ...
45
+ # end
46
+ #
47
+ # def on_url_complete
48
+ # ...
49
+ # end
50
+ #
51
+ # def on_status_complete
52
+ # ...
53
+ # end
54
+ #
55
+ # def on_header_field_complete
56
+ # ...
57
+ # end
58
+ #
59
+ # def on_header_value_complete
60
+ # ...
61
+ # end
62
+ # end
5
63
  #
6
64
  class Delegate
7
- def on_message_begin
8
- end
65
+ private def internal_on_message_begin
66
+ on_message_begin
9
67
 
10
- def on_url(url)
68
+ 0
69
+ rescue
70
+ -1
11
71
  end
12
72
 
13
- def on_status(status)
14
- end
73
+ private def internal_on_headers_complete
74
+ on_headers_complete
15
75
 
16
- def on_header_field(field)
76
+ 0
77
+ rescue
78
+ -1
17
79
  end
18
80
 
19
- def on_header_value(value)
20
- end
81
+ private def internal_on_message_complete
82
+ on_message_complete
21
83
 
22
- def on_headers_complete
84
+ 0
85
+ rescue
86
+ -1
23
87
  end
24
88
 
25
- def on_body(body)
26
- end
27
-
28
- def on_message_complete
29
- end
30
-
31
- def on_chunk_header
32
- end
89
+ private def internal_on_chunk_header
90
+ on_chunk_header
33
91
 
34
- def on_chunk_complete
92
+ 0
93
+ rescue
94
+ -1
35
95
  end
36
96
  end
37
97
  end
data/lib/llhttp/error.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LLHttp
4
+ # [public] LLHttp's standard error object.
5
+ #
4
6
  class Error < StandardError
5
7
  end
6
8
  end
data/lib/llhttp/parser.rb CHANGED
@@ -1,19 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LLHttp
4
- # Wraps an llhttp context for parsing http requests and responses.
4
+ # [public] Wraps an llhttp context for parsing http requests and responses.
5
5
  #
6
- # = Finishing
6
+ # class Delegate < LLHttp::Delegate
7
+ # def on_message_begin
8
+ # ...
9
+ # end
7
10
  #
8
- # Call `LLHttp::Parser#finish` when processing is complete for the current request or response.
11
+ # ...
12
+ # end
9
13
  #
10
- # = Introspection
14
+ # parser = LLHttp::Parser.new(Delegate.new, type: :request)
15
+ # parser << "GET / HTTP/1.1\r\n\r\n"
16
+ # parser.finish
11
17
  #
12
- # `LLHttp::Parser#keep_alive?` returns `true` if there might be any other messages following the last that was successfuly parsed.
18
+ # ...
19
+ #
20
+ # Introspection
21
+ #
22
+ # * `LLHttp::Parser#content_length` returns the content length of the current request.
23
+ # * `LLHttp::Parser#method_name` returns the method name of the current response.
24
+ # * `LLHttp::Parser#status_code` returns the status code of the current response.
25
+ # * `LLHttp::Parser#http_major` returns the major http version of the current request/response.
26
+ # * `LLHttp::Parser#http_minor` returns the minor http version of the current request/response.
27
+ # * `LLHttp::Parser#keep_alive?` returns `true` if there might be more messages.
28
+ #
29
+ # Finishing
30
+ #
31
+ # Call `LLHttp::Parser#finish` when processing is complete for the current request or response.
13
32
  #
14
33
  class Parser
15
34
  LLHTTP_TYPES = {both: 0, request: 1, response: 2}.freeze
16
35
 
36
+ # [public] The parser type; one of: `both`, `request`, or `response`.
37
+ #
17
38
  attr_reader :type
18
39
 
19
40
  def initialize(delegate, type: :both)
@@ -24,4 +45,4 @@ module LLHttp
24
45
  end
25
46
  end
26
47
 
27
- require_relative "llhttp_ext"
48
+ require_relative "../llhttp_ext"