llhttp-ffi 0.0.0

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