llhttp 0.0.1 → 0.3.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,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"