http-parser 1.0.5 → 1.2.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.
- checksums.yaml +4 -4
- data/LICENSE +20 -20
- data/README.md +69 -69
- data/Rakefile +19 -19
- data/ext/Rakefile +8 -8
- data/ext/http-parser/http_parser.c +2470 -2234
- data/ext/http-parser/http_parser.h +362 -318
- data/http-parser.gemspec +35 -33
- data/lib/http-parser.rb +11 -9
- data/lib/http-parser/errors.rb +77 -74
- data/lib/http-parser/ext.rb +9 -7
- data/lib/http-parser/parser.rb +258 -224
- data/lib/http-parser/types.rb +335 -311
- data/lib/http-parser/version.rb +5 -3
- data/spec/error_spec.rb +45 -45
- data/spec/instance_spec.rb +78 -78
- data/spec/parser_spec.rb +313 -313
- metadata +51 -46
data/lib/http-parser/types.rb
CHANGED
@@ -1,311 +1,335 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
:
|
11
|
-
:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
|
32
|
-
:
|
33
|
-
|
34
|
-
:
|
35
|
-
|
36
|
-
:
|
37
|
-
|
38
|
-
:
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
|
45
|
-
:
|
46
|
-
:
|
47
|
-
:
|
48
|
-
:
|
49
|
-
|
50
|
-
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
|
55
|
-
|
56
|
-
:
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
:
|
62
|
-
:
|
63
|
-
|
64
|
-
:
|
65
|
-
|
66
|
-
:
|
67
|
-
:
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
:
|
89
|
-
:
|
90
|
-
|
91
|
-
|
92
|
-
:
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
#
|
179
|
-
#
|
180
|
-
#
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
#
|
190
|
-
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
end
|
223
|
-
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
# @return [
|
228
|
-
#
|
229
|
-
#
|
230
|
-
def error
|
231
|
-
error = (self[:error_upgrade] & 0b1111111)
|
232
|
-
return
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
#
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
#
|
252
|
-
#
|
253
|
-
#
|
254
|
-
# @return [
|
255
|
-
#
|
256
|
-
#
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
#
|
264
|
-
#
|
265
|
-
#
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
:
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HttpParser
|
4
|
+
HTTP_MAX_HEADER_SIZE = (80 * 1024)
|
5
|
+
|
6
|
+
#
|
7
|
+
# These share a byte of data as a bitmap
|
8
|
+
#
|
9
|
+
TYPES = enum :http_parser_type, [
|
10
|
+
:request, 0,
|
11
|
+
:response,
|
12
|
+
:both
|
13
|
+
]
|
14
|
+
FLAG = {
|
15
|
+
:CHUNKED => 1 << 2,
|
16
|
+
:CONNECTION_KEEP_ALIVE => 1 << 3,
|
17
|
+
:CONNECTION_CLOSE => 1 << 4,
|
18
|
+
:CONNECTION_UPGRADE => 1 << 5,
|
19
|
+
:TRAILING => 1 << 6,
|
20
|
+
:UPGRADE => 1 << 7,
|
21
|
+
:SKIPBODY => 1 << 8
|
22
|
+
}
|
23
|
+
|
24
|
+
#
|
25
|
+
# Request Methods
|
26
|
+
#
|
27
|
+
METHODS = enum :http_method, [
|
28
|
+
:DELETE, 0,
|
29
|
+
:GET,
|
30
|
+
:HEAD,
|
31
|
+
:POST,
|
32
|
+
:PUT,
|
33
|
+
# pathological
|
34
|
+
:CONNECT,
|
35
|
+
:OPTIONS,
|
36
|
+
:TRACE,
|
37
|
+
# webdav
|
38
|
+
:COPY,
|
39
|
+
:LOCK,
|
40
|
+
:MKCOL,
|
41
|
+
:MOVE,
|
42
|
+
:PROPFIND,
|
43
|
+
:PROPPATCH,
|
44
|
+
:SEARCH,
|
45
|
+
:UNLOCK,
|
46
|
+
:BIND,
|
47
|
+
:REBIND,
|
48
|
+
:UNBIND,
|
49
|
+
:ACL,
|
50
|
+
# subversion
|
51
|
+
:REPORT,
|
52
|
+
:MKACTIVITY,
|
53
|
+
:CHECKOUT,
|
54
|
+
:MERGE,
|
55
|
+
# upnp
|
56
|
+
:MSEARCH,
|
57
|
+
:NOTIFY,
|
58
|
+
:SUBSCRIBE,
|
59
|
+
:UNSUBSCRIBE,
|
60
|
+
# RFC-5789
|
61
|
+
:PATCH,
|
62
|
+
:PURGE,
|
63
|
+
# CalDAV
|
64
|
+
:MKCALENDAR,
|
65
|
+
# RFC-2068, section 19.6.1.2
|
66
|
+
:LINK,
|
67
|
+
:UNLINK
|
68
|
+
]
|
69
|
+
|
70
|
+
|
71
|
+
UrlFields = enum :http_parser_url_fields, [
|
72
|
+
:SCHEMA, 0,
|
73
|
+
:HOST,
|
74
|
+
:PORT,
|
75
|
+
:PATH,
|
76
|
+
:QUERY,
|
77
|
+
:FRAGMENT,
|
78
|
+
:USERINFO,
|
79
|
+
:MAX
|
80
|
+
]
|
81
|
+
|
82
|
+
|
83
|
+
#
|
84
|
+
# Effectively this represents a request instance
|
85
|
+
#
|
86
|
+
class Instance < FFI::Struct
|
87
|
+
layout :type_flags, :uchar,
|
88
|
+
:state, :uchar,
|
89
|
+
:header_state, :uchar,
|
90
|
+
:index, :uchar,
|
91
|
+
|
92
|
+
:nread, :uint32,
|
93
|
+
:content_length, :uint64,
|
94
|
+
|
95
|
+
# READ-ONLY
|
96
|
+
:http_major, :ushort,
|
97
|
+
:http_minor, :ushort,
|
98
|
+
:status_code, :ushort, # responses only
|
99
|
+
:method, :uchar, # requests only
|
100
|
+
:error_upgrade, :uchar, # errno = first 7bits, upgrade = last bit
|
101
|
+
|
102
|
+
# PUBLIC
|
103
|
+
:data, :pointer
|
104
|
+
|
105
|
+
|
106
|
+
def initialize(ptr = nil)
|
107
|
+
if ptr then super(ptr)
|
108
|
+
else
|
109
|
+
super()
|
110
|
+
self.type = :both
|
111
|
+
end
|
112
|
+
|
113
|
+
yield self if block_given?
|
114
|
+
|
115
|
+
::HttpParser.http_parser_init(self, self.type) unless ptr
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Resets the parser.
|
120
|
+
#
|
121
|
+
# @param [:request, :response, :both] new_type
|
122
|
+
# The new type for the parser.
|
123
|
+
#
|
124
|
+
def reset!(new_type = type)
|
125
|
+
::HttpParser.http_parser_init(self, new_type)
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# The type of the parser.
|
130
|
+
#
|
131
|
+
# @return [:request, :response, :both]
|
132
|
+
# The parser type.
|
133
|
+
#
|
134
|
+
def type
|
135
|
+
TYPES[self[:type_flags] & 0x3]
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Sets the type of the parser.
|
140
|
+
#
|
141
|
+
# @param [:request, :response, :both] new_type
|
142
|
+
# The new parser type.
|
143
|
+
#
|
144
|
+
def type=(new_type)
|
145
|
+
self[:type_flags] = (flags | TYPES[new_type])
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# Flags for the parser.
|
150
|
+
#
|
151
|
+
# @return [Integer]
|
152
|
+
# Parser flags.
|
153
|
+
#
|
154
|
+
def flags
|
155
|
+
(self[:type_flags] & 0xfc)
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# The parsed HTTP major version number.
|
160
|
+
#
|
161
|
+
# @return [Integer]
|
162
|
+
# The HTTP major version number.
|
163
|
+
#
|
164
|
+
def http_major
|
165
|
+
self[:http_major]
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# The parsed HTTP minor version number.
|
170
|
+
#
|
171
|
+
# @return [Integer]
|
172
|
+
# The HTTP minor version number.
|
173
|
+
#
|
174
|
+
def http_minor
|
175
|
+
self[:http_minor]
|
176
|
+
end
|
177
|
+
|
178
|
+
#
|
179
|
+
# The parsed HTTP version.
|
180
|
+
#
|
181
|
+
# @return [String]
|
182
|
+
# The HTTP version.
|
183
|
+
#
|
184
|
+
def http_version
|
185
|
+
"%d.%d" % [self[:http_major], self[:http_minor]]
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
# The parsed HTTP response Status Code.
|
190
|
+
#
|
191
|
+
# @return [Integer]
|
192
|
+
# The HTTP Status Code.
|
193
|
+
#
|
194
|
+
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1
|
195
|
+
#
|
196
|
+
def http_status
|
197
|
+
self[:status_code]
|
198
|
+
end
|
199
|
+
|
200
|
+
#
|
201
|
+
# The parsed HTTP Method.
|
202
|
+
#
|
203
|
+
# @return [Symbol]
|
204
|
+
# The HTTP Method name.
|
205
|
+
#
|
206
|
+
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
|
207
|
+
#
|
208
|
+
def http_method
|
209
|
+
METHODS[self[:method]]
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# Determines whether the `Upgrade` header has been parsed.
|
214
|
+
#
|
215
|
+
# @return [Boolean]
|
216
|
+
# Specifies whether the `Upgrade` header has been seen.
|
217
|
+
#
|
218
|
+
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.42
|
219
|
+
#
|
220
|
+
def upgrade?
|
221
|
+
(self[:error_upgrade] & 0b10000000) > 0
|
222
|
+
end
|
223
|
+
|
224
|
+
#
|
225
|
+
# Determines whether an error occurred during processing.
|
226
|
+
#
|
227
|
+
# @return [Boolean]
|
228
|
+
# Did a parsing error occur with the request?
|
229
|
+
#
|
230
|
+
def error?
|
231
|
+
error = (self[:error_upgrade] & 0b1111111)
|
232
|
+
return error != 0
|
233
|
+
end
|
234
|
+
|
235
|
+
#
|
236
|
+
# Returns the error that occurred during processing.
|
237
|
+
#
|
238
|
+
# @return [StandarError]
|
239
|
+
# Returns the error that occurred.
|
240
|
+
#
|
241
|
+
def error
|
242
|
+
error = (self[:error_upgrade] & 0b1111111)
|
243
|
+
return nil if error == 0
|
244
|
+
|
245
|
+
err = ::HttpParser.err_name(error)[4..-1] # HPE_ is at the start of all these errors
|
246
|
+
klass = ERRORS[err.to_sym]
|
247
|
+
err = "#{::HttpParser.err_desc(error)} (#{err})"
|
248
|
+
return klass.nil? ? Error::UNKNOWN.new(err) : klass.new(err)
|
249
|
+
end
|
250
|
+
|
251
|
+
#
|
252
|
+
# Additional data attached to the parser.
|
253
|
+
#
|
254
|
+
# @return [FFI::Pointer]
|
255
|
+
# Pointer to the additional data.
|
256
|
+
#
|
257
|
+
def data
|
258
|
+
self[:data]
|
259
|
+
end
|
260
|
+
|
261
|
+
#
|
262
|
+
# Determines whether the `Connection: keep-alive` header has been
|
263
|
+
# parsed.
|
264
|
+
#
|
265
|
+
# @return [Boolean]
|
266
|
+
# Specifies whether the Connection should be kept alive.
|
267
|
+
#
|
268
|
+
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10
|
269
|
+
#
|
270
|
+
def keep_alive?
|
271
|
+
::HttpParser.http_should_keep_alive(self) > 0
|
272
|
+
end
|
273
|
+
|
274
|
+
#
|
275
|
+
# Determines if a chunked response has completed
|
276
|
+
#
|
277
|
+
# @return [Boolean]
|
278
|
+
# Specifies whether the chunked response has completed
|
279
|
+
#
|
280
|
+
def final_chunk?
|
281
|
+
::HttpParser.http_body_is_final(self) > 0
|
282
|
+
end
|
283
|
+
|
284
|
+
#
|
285
|
+
# Halts the parser if called in a callback
|
286
|
+
#
|
287
|
+
def stop!
|
288
|
+
throw :return, 1
|
289
|
+
end
|
290
|
+
|
291
|
+
#
|
292
|
+
# Indicates an error has occurred when called in a callback
|
293
|
+
#
|
294
|
+
def error!
|
295
|
+
throw :return, -1
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class FieldData < FFI::Struct
|
300
|
+
layout :off, :uint16,
|
301
|
+
:len, :uint16
|
302
|
+
end
|
303
|
+
|
304
|
+
class HttpParserUrl < FFI::Struct
|
305
|
+
layout :field_set, :uint16,
|
306
|
+
:port, :uint16,
|
307
|
+
:field_data, [FieldData, UrlFields[:MAX]]
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
callback :http_data_cb, [Instance.ptr, :pointer, :size_t], :int
|
312
|
+
callback :http_cb, [Instance.ptr], :int
|
313
|
+
|
314
|
+
|
315
|
+
class Settings < FFI::Struct
|
316
|
+
layout :on_message_begin, :http_cb,
|
317
|
+
:on_url, :http_data_cb,
|
318
|
+
:on_status, :http_data_cb,
|
319
|
+
:on_header_field, :http_data_cb,
|
320
|
+
:on_header_value, :http_data_cb,
|
321
|
+
:on_headers_complete, :http_cb,
|
322
|
+
:on_body, :http_data_cb,
|
323
|
+
:on_message_complete, :http_cb,
|
324
|
+
:on_chunk_header, :http_cb,
|
325
|
+
:on_chunk_complete, :http_cb
|
326
|
+
end
|
327
|
+
|
328
|
+
|
329
|
+
attach_function :http_parser_init, [Instance.by_ref, :http_parser_type], :void
|
330
|
+
attach_function :http_parser_execute, [Instance.by_ref, Settings.by_ref, :pointer, :size_t], :size_t
|
331
|
+
attach_function :http_should_keep_alive, [Instance.by_ref], :int
|
332
|
+
|
333
|
+
# Checks if this is the final chunk of the body
|
334
|
+
attach_function :http_body_is_final, [Instance.by_ref], :int
|
335
|
+
end
|