llhttp-ffi 0.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,215 @@
1
+ /*
2
+ This software is licensed under the MPL-2.0 License.
3
+
4
+ Copyright Bryan Powell, 2020.
5
+ */
6
+
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+ #include "llhttp.h"
10
+
11
+ typedef struct rb_llhttp_callbacks_s rb_llhttp_callbacks_t;
12
+
13
+ typedef int (*rb_llhttp_data_cb)(const char *data, size_t length);
14
+ typedef int (*rb_llhttp_cb)();
15
+
16
+ struct rb_llhttp_callbacks_s {
17
+ rb_llhttp_cb on_message_begin;
18
+ rb_llhttp_data_cb on_url;
19
+ rb_llhttp_data_cb on_status;
20
+ rb_llhttp_data_cb on_header_field;
21
+ rb_llhttp_data_cb on_header_value;
22
+ rb_llhttp_cb on_headers_complete;
23
+ rb_llhttp_data_cb on_body;
24
+ rb_llhttp_cb on_message_complete;
25
+ rb_llhttp_cb on_chunk_header;
26
+ rb_llhttp_cb on_chunk_complete;
27
+ rb_llhttp_cb on_url_complete;
28
+ rb_llhttp_cb on_status_complete;
29
+ rb_llhttp_cb on_header_field_complete;
30
+ rb_llhttp_cb on_header_value_complete;
31
+ };
32
+
33
+ int rb_llhttp_on_message_begin(llhttp_t *parser) {
34
+ rb_llhttp_callbacks_t* callbacks = parser->data;
35
+ return callbacks->on_message_begin();
36
+ }
37
+
38
+ int rb_llhttp_on_url(llhttp_t *parser, char *data, size_t length) {
39
+ rb_llhttp_callbacks_t* callbacks = parser->data;
40
+ callbacks->on_url(data, length);
41
+ return 0;
42
+ }
43
+
44
+ int rb_llhttp_on_status(llhttp_t *parser, char *data, size_t length) {
45
+ rb_llhttp_callbacks_t* callbacks = parser->data;
46
+ callbacks->on_status(data, length);
47
+ return 0;
48
+ }
49
+
50
+ int rb_llhttp_on_header_field(llhttp_t *parser, char *data, size_t length) {
51
+ rb_llhttp_callbacks_t* callbacks = parser->data;
52
+ callbacks->on_header_field(data, length);
53
+ return 0;
54
+ }
55
+
56
+ int rb_llhttp_on_header_value(llhttp_t *parser, char *data, size_t length) {
57
+ rb_llhttp_callbacks_t* callbacks = parser->data;
58
+ callbacks->on_header_value(data, length);
59
+ return 0;
60
+ }
61
+
62
+ int rb_llhttp_on_headers_complete(llhttp_t *parser) {
63
+ rb_llhttp_callbacks_t* callbacks = parser->data;
64
+ return callbacks->on_headers_complete();
65
+ }
66
+
67
+ int rb_llhttp_on_body(llhttp_t *parser, char *data, size_t length) {
68
+ rb_llhttp_callbacks_t* callbacks = parser->data;
69
+ callbacks->on_body(data, length);
70
+ return 0;
71
+ }
72
+
73
+ int rb_llhttp_on_message_complete(llhttp_t *parser) {
74
+ rb_llhttp_callbacks_t* callbacks = parser->data;
75
+ return callbacks->on_message_complete();
76
+ }
77
+
78
+ int rb_llhttp_on_chunk_header(llhttp_t *parser) {
79
+ rb_llhttp_callbacks_t* callbacks = parser->data;
80
+ return callbacks->on_chunk_header();
81
+ }
82
+
83
+ int rb_llhttp_on_chunk_complete(llhttp_t *parser) {
84
+ rb_llhttp_callbacks_t* callbacks = parser->data;
85
+ callbacks->on_chunk_complete();
86
+ return 0;
87
+ }
88
+
89
+ int rb_llhttp_on_url_complete(llhttp_t *parser) {
90
+ rb_llhttp_callbacks_t* callbacks = parser->data;
91
+ callbacks->on_url_complete();
92
+ return 0;
93
+ }
94
+
95
+ int rb_llhttp_on_status_complete(llhttp_t *parser) {
96
+ rb_llhttp_callbacks_t* callbacks = parser->data;
97
+ callbacks->on_status_complete();
98
+ return 0;
99
+ }
100
+
101
+ int rb_llhttp_on_header_field_complete(llhttp_t *parser) {
102
+ rb_llhttp_callbacks_t* callbacks = parser->data;
103
+ callbacks->on_header_field_complete();
104
+ return 0;
105
+ }
106
+
107
+ int rb_llhttp_on_header_value_complete(llhttp_t *parser) {
108
+ rb_llhttp_callbacks_t* callbacks = parser->data;
109
+ callbacks->on_header_value_complete();
110
+ return 0;
111
+ }
112
+
113
+ llhttp_t* rb_llhttp_init(int type, rb_llhttp_callbacks_t* callbacks) {
114
+ llhttp_t *parser = (llhttp_t *)malloc(sizeof(llhttp_t));
115
+ llhttp_settings_t *settings = (llhttp_settings_t *)malloc(sizeof(llhttp_settings_t));
116
+
117
+ llhttp_settings_init(settings);
118
+
119
+ if (callbacks->on_message_begin) {
120
+ settings->on_message_begin = (llhttp_cb)rb_llhttp_on_message_begin;
121
+ }
122
+
123
+ if (callbacks->on_url) {
124
+ settings->on_url = (llhttp_data_cb)rb_llhttp_on_url;
125
+ }
126
+
127
+ if (callbacks->on_status) {
128
+ settings->on_status = (llhttp_data_cb)rb_llhttp_on_status;
129
+ }
130
+
131
+ if (callbacks->on_header_field) {
132
+ settings->on_header_field = (llhttp_data_cb)rb_llhttp_on_header_field;
133
+ }
134
+
135
+ if (callbacks->on_header_value) {
136
+ settings->on_header_value = (llhttp_data_cb)rb_llhttp_on_header_value;
137
+ }
138
+
139
+ if (callbacks->on_headers_complete) {
140
+ settings->on_headers_complete = (llhttp_cb)rb_llhttp_on_headers_complete;
141
+ }
142
+
143
+ if (callbacks->on_body) {
144
+ settings->on_body = (llhttp_data_cb)rb_llhttp_on_body;
145
+ }
146
+
147
+ if (callbacks->on_message_complete) {
148
+ settings->on_message_complete = (llhttp_cb)rb_llhttp_on_message_complete;
149
+ }
150
+
151
+ if (callbacks->on_chunk_header) {
152
+ settings->on_chunk_header = (llhttp_cb)rb_llhttp_on_chunk_header;
153
+ }
154
+
155
+ if (callbacks->on_chunk_complete) {
156
+ settings->on_chunk_complete = (llhttp_cb)rb_llhttp_on_chunk_complete;
157
+ }
158
+
159
+ if (callbacks->on_url_complete) {
160
+ settings->on_url_complete = (llhttp_cb)rb_llhttp_on_url_complete;
161
+ }
162
+
163
+ if (callbacks->on_status_complete) {
164
+ settings->on_status_complete = (llhttp_cb)rb_llhttp_on_status_complete;
165
+ }
166
+
167
+ if (callbacks->on_header_field_complete) {
168
+ settings->on_header_field_complete = (llhttp_cb)rb_llhttp_on_header_field_complete;
169
+ }
170
+
171
+ if (callbacks->on_header_value_complete) {
172
+ settings->on_header_value_complete = (llhttp_cb)rb_llhttp_on_header_value_complete;
173
+ }
174
+
175
+ llhttp_init(parser, type, settings);
176
+
177
+ parser->data = callbacks;
178
+
179
+ return parser;
180
+ }
181
+
182
+ void rb_llhttp_free(llhttp_t* parser) {
183
+ if (parser) {
184
+ free(parser->settings);
185
+ free(parser);
186
+ }
187
+ }
188
+
189
+ uint64_t rb_llhttp_content_length(llhttp_t* parser) {
190
+ return parser->content_length;
191
+ }
192
+
193
+ const char* rb_llhttp_method_name(llhttp_t* parser) {
194
+ return llhttp_method_name(parser->method);
195
+ }
196
+
197
+ uint16_t rb_llhttp_status_code(llhttp_t* parser) {
198
+ return parser->status_code;
199
+ }
200
+
201
+ uint16_t rb_llhttp_http_major(llhttp_t* parser) {
202
+ return parser->http_major;
203
+ }
204
+
205
+ uint16_t rb_llhttp_http_minor(llhttp_t* parser) {
206
+ return parser->http_minor;
207
+ }
208
+
209
+ const char* rb_llhttp_errno_name(llhttp_errno_t errno) {
210
+ return llhttp_errno_name(errno);
211
+ }
212
+
213
+ const char* rb_llhttp_error_reason(llhttp_t* parser) {
214
+ return parser->reason;
215
+ }
Binary file
Binary file
Binary file
data/lib/llhttp.rb ADDED
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+ require "ffi-compiler/loader"
5
+
6
+ module LLHttp
7
+ require_relative "llhttp/delegate"
8
+ require_relative "llhttp/error"
9
+ require_relative "llhttp/parser"
10
+ require_relative "llhttp/version"
11
+
12
+ extend FFI::Library
13
+ ffi_lib(FFI::Compiler::Loader.find("llhttp-ext"))
14
+
15
+ callback :llhttp_data_cb, [:pointer, :size_t], :void
16
+ callback :llhttp_cb, [], :int
17
+
18
+ class Callbacks < FFI::Struct
19
+ layout :on_message_begin, :llhttp_cb,
20
+ :on_url, :llhttp_data_cb,
21
+ :on_status, :llhttp_data_cb,
22
+ :on_header_field, :llhttp_data_cb,
23
+ :on_header_value, :llhttp_data_cb,
24
+ :on_headers_complete, :llhttp_cb,
25
+ :on_body, :llhttp_data_cb,
26
+ :on_message_complete, :llhttp_cb,
27
+ :on_chunk_header, :llhttp_cb,
28
+ :on_chunk_complete, :llhttp_cb,
29
+ :on_url_complete, :llhttp_cb,
30
+ :on_status_complete, :llhttp_cb,
31
+ :on_header_field_complete, :llhttp_cb,
32
+ :on_header_value_complete, :llhttp_cb
33
+ end
34
+
35
+ attach_function :rb_llhttp_init, [:int, Callbacks.by_ref], :pointer
36
+ attach_function :rb_llhttp_content_length, [:pointer], :uint64
37
+ attach_function :rb_llhttp_method_name, [:pointer], :string
38
+ attach_function :rb_llhttp_status_code, [:pointer], :uint16
39
+ attach_function :rb_llhttp_http_major, [:pointer], :uint16
40
+ attach_function :rb_llhttp_http_minor, [:pointer], :uint16
41
+ attach_function :rb_llhttp_free, [:pointer], :void
42
+
43
+ attach_function :llhttp_execute, [:pointer, :pointer, :size_t], :int
44
+ attach_function :llhttp_errno_name, [:int], :string
45
+ attach_function :llhttp_get_error_reason, [:pointer], :string
46
+ attach_function :llhttp_should_keep_alive, [:pointer], :int
47
+ attach_function :llhttp_finish, [:pointer], :int
48
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LLHttp
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
63
+ #
64
+ class Delegate
65
+ # private def internal_on_message_begin
66
+ # on_message_begin
67
+
68
+ # 0
69
+ # rescue
70
+ # -1
71
+ # end
72
+
73
+ # private def internal_on_headers_complete
74
+ # on_headers_complete
75
+
76
+ # 0
77
+ # rescue
78
+ # -1
79
+ # end
80
+
81
+ # private def internal_on_message_complete
82
+ # on_message_complete
83
+
84
+ # 0
85
+ # rescue
86
+ # -1
87
+ # end
88
+
89
+ # private def internal_on_chunk_header
90
+ # on_chunk_header
91
+
92
+ # 0
93
+ # rescue
94
+ # -1
95
+ # end
96
+ end
97
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LLHttp
4
+ # [public] LLHttp's standard error object.
5
+ #
6
+ class Error < StandardError
7
+ end
8
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LLHttp
4
+ # [public] Wraps an llhttp context for parsing http requests and responses.
5
+ #
6
+ # class Delegate < LLHttp::Delegate
7
+ # def on_message_begin
8
+ # ...
9
+ # end
10
+ #
11
+ # ...
12
+ # end
13
+ #
14
+ # parser = LLHttp::Parser.new(Delegate.new, type: :request)
15
+ # parser << "GET / HTTP/1.1\r\n\r\n"
16
+ # parser.finish
17
+ #
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.
32
+ #
33
+ class Parser
34
+ LLHTTP_TYPES = {both: 0, request: 1, response: 2}.freeze
35
+
36
+ CALLBACKS = %i[
37
+ on_message_begin
38
+ on_headers_complete
39
+ on_message_complete
40
+ on_chunk_header
41
+ on_chunk_complete
42
+ on_url_complete
43
+ on_status_complete
44
+ on_header_field_complete
45
+ on_header_value_complete
46
+ ].freeze
47
+
48
+ CALLBACKS_WITH_DATA = %i[
49
+ on_url
50
+ on_status
51
+ on_header_field
52
+ on_header_value
53
+ on_body
54
+ ].freeze
55
+
56
+ # [public] The parser type; one of: `:both`, `:request`, or `:response`.
57
+ #
58
+ attr_reader :type
59
+
60
+ def initialize(delegate, type: :both)
61
+ @type, @delegate = type.to_sym, delegate
62
+
63
+ @callbacks = Callbacks.new
64
+
65
+ (CALLBACKS + CALLBACKS_WITH_DATA).each do |callback|
66
+ if delegate.respond_to?(callback)
67
+ @callbacks[callback] = method(callback).to_proc
68
+ end
69
+ end
70
+
71
+ @pointer = LLHttp.rb_llhttp_init(LLHTTP_TYPES.fetch(@type), @callbacks)
72
+
73
+ ObjectSpace.define_finalizer(self, self.class.free(@pointer))
74
+ end
75
+
76
+ # [public] Parse the given data.
77
+ #
78
+ def parse(data)
79
+ errno = LLHttp.llhttp_execute(@pointer, data, data.length)
80
+ raise build_error(errno) if errno > 0
81
+ end
82
+ alias_method :<<, :parse
83
+
84
+ # [public] Get the content length of the current request.
85
+ #
86
+ def content_length
87
+ LLHttp.rb_llhttp_content_length(@pointer)
88
+ end
89
+
90
+ # [public] Get the method of the current response.
91
+ #
92
+ def method_name
93
+ LLHttp.rb_llhttp_method_name(@pointer)
94
+ end
95
+
96
+ # [public] Get the status code of the current response.
97
+ #
98
+ def status_code
99
+ LLHttp.rb_llhttp_status_code(@pointer)
100
+ end
101
+
102
+ # [public] Get the major http version of the current request/response.
103
+ #
104
+ def http_major
105
+ LLHttp.rb_llhttp_http_major(@pointer)
106
+ end
107
+
108
+ # [public] Get the minor http version of the current request/response.
109
+ #
110
+ def http_minor
111
+ LLHttp.rb_llhttp_http_minor(@pointer)
112
+ end
113
+
114
+ # [public] Returns `true` if there might be more messages.
115
+ #
116
+ def keep_alive?
117
+ LLHttp.llhttp_should_keep_alive(@pointer) == 1
118
+ end
119
+
120
+ # [public] Get ready to parse the next request.
121
+ #
122
+ def finish
123
+ LLHttp.llhttp_finish(@pointer)
124
+ end
125
+
126
+ CALLBACKS.each do |callback|
127
+ class_eval(
128
+ <<~RB, __FILE__, __LINE__ + 1
129
+ private def #{callback}
130
+ @delegate.#{callback}
131
+
132
+ 0
133
+ rescue
134
+ -1
135
+ end
136
+ RB
137
+ )
138
+ end
139
+
140
+ CALLBACKS_WITH_DATA.each do |callback|
141
+ class_eval(
142
+ <<~RB, __FILE__, __LINE__ + 1
143
+ private def #{callback}(buffer, length)
144
+ @delegate.#{callback}(buffer.get_bytes(0, length))
145
+ end
146
+ RB
147
+ )
148
+ end
149
+
150
+ private def build_error(errno)
151
+ Error.new("Error Parsing data: #{LLHttp.llhttp_errno_name(errno)} #{LLHttp.llhttp_get_error_reason(@pointer)}")
152
+ end
153
+
154
+ def self.free(pointer)
155
+ proc { LLHttp.llhttp_free(@pointer) }
156
+ end
157
+ end
158
+ end