yakischloba-http-parser 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +30 -0
- data/http-parser.gemspec +30 -0
- data/lib/http-parser.rb +193 -0
- data/src/LICENSE +79 -0
- data/src/Makefile +27 -0
- data/src/README.md +129 -0
- data/src/http_parser.h +133 -0
- data/src/http_parser.rl +502 -0
- data/src/test.c +785 -0
- data/test/request.rb +82 -0
- data/test/response.rb +79 -0
- metadata +95 -0
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
task :default => [:clean, :build, :test]
|
5
|
+
|
6
|
+
task :clean do
|
7
|
+
chdir "src/" do
|
8
|
+
sh "make clean"
|
9
|
+
sh "rm -rf http_parser.#{FFI::Platform::LIBSUFFIX}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
task :build do
|
14
|
+
dynamicflag = (FFI::Platform::OS == "darwin" ? "-dynamiclib" : "-fPIC -shared")
|
15
|
+
chdir "src/" do
|
16
|
+
sh "make"
|
17
|
+
sh "gcc #{dynamicflag} -o http_parser.#{FFI::Platform::LIBSUFFIX} http_parser.o"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
task :test do
|
22
|
+
chdir "src/" do
|
23
|
+
sh "make test"
|
24
|
+
end
|
25
|
+
require 'lib/http-parser'
|
26
|
+
require 'bacon'
|
27
|
+
Bacon.summary_at_exit
|
28
|
+
require 'test/request.rb'
|
29
|
+
require 'test/response.rb'
|
30
|
+
end
|
data/http-parser.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "http-parser"
|
3
|
+
s.version = "0.0.2"
|
4
|
+
s.date = "2009-08-07"
|
5
|
+
s.authors = ["Ryan Dahl", "Zed Shaw", "Jake Douglas"]
|
6
|
+
s.email = "jakecdouglas@gmail.com"
|
7
|
+
s.has_rdoc = true
|
8
|
+
# We have rake as a dependency because of a JRuby thing that
|
9
|
+
# requires the actual rake gem to be installed.
|
10
|
+
s.add_dependency('rake')
|
11
|
+
s.add_dependency('ffi')
|
12
|
+
s.add_dependency('bacon')
|
13
|
+
s.summary = "FFI binding to ry's http-parser"
|
14
|
+
s.homepage = "http://www.github.com/yakischloba/http-parser-ffi"
|
15
|
+
s.description = "FFI binding to ry's http-parser"
|
16
|
+
s.extensions = ["Rakefile"]
|
17
|
+
s.files =
|
18
|
+
["http-parser.gemspec",
|
19
|
+
#"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"lib/http-parser.rb",
|
22
|
+
"src/http_parser.h",
|
23
|
+
"src/http_parser.rl",
|
24
|
+
"src/LICENSE",
|
25
|
+
"src/Makefile",
|
26
|
+
"src/README.md",
|
27
|
+
"src/test.c",
|
28
|
+
"test/request.rb",
|
29
|
+
"test/response.rb"]
|
30
|
+
end
|
data/lib/http-parser.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
class HttpParser
|
4
|
+
extend FFI::Library
|
5
|
+
ffi_lib File.dirname(File.expand_path(__FILE__)) + "/../src/" + "http_parser.#{FFI::Platform::LIBSUFFIX}"
|
6
|
+
|
7
|
+
callback :http_data_cb, [:pointer, :pointer, :size_t], :int
|
8
|
+
callback :http_cb, [:pointer], :int
|
9
|
+
|
10
|
+
# Request methods
|
11
|
+
HTTP_COPY = 0x0001
|
12
|
+
HTTP_DELETE = 0x0002
|
13
|
+
HTTP_GET = 0x0004
|
14
|
+
HTTP_HEAD = 0x0008
|
15
|
+
HTTP_LOCK = 0x0010
|
16
|
+
HTTP_MKCOL = 0x0020
|
17
|
+
HTTP_MOVE = 0x0040
|
18
|
+
HTTP_OPTIONS = 0x0080
|
19
|
+
HTTP_POST = 0x0100
|
20
|
+
HTTP_PROPFIND = 0x0200
|
21
|
+
HTTP_PROPPATCH = 0x0400
|
22
|
+
HTTP_PUT = 0x0800
|
23
|
+
HTTP_TRACE = 0x1000
|
24
|
+
HTTP_UNLOCK = 0x2000
|
25
|
+
|
26
|
+
# Transfer encodings
|
27
|
+
HTTP_IDENTITY = 0x01
|
28
|
+
HTTP_CHUNKED = 0x02
|
29
|
+
|
30
|
+
enum :http_parser_type, [:HTTP_REQUEST, :HTTP_RESPONSE]
|
31
|
+
|
32
|
+
class HttpParserStruct < FFI::Struct
|
33
|
+
# PRIVATE
|
34
|
+
layout :cs, :int,
|
35
|
+
:http_parser_type, :int,
|
36
|
+
:chunk_size, :size_t,
|
37
|
+
:eating, :int,
|
38
|
+
:error, :int,
|
39
|
+
:body_read, :size_t,
|
40
|
+
:header_field_mark, :string,
|
41
|
+
:header_field_size, :size_t,
|
42
|
+
:header_value_mark, :string,
|
43
|
+
:header_value_size, :size_t,
|
44
|
+
:query_string_mark, :string,
|
45
|
+
:query_string_size, :size_t,
|
46
|
+
:path_mark, :string,
|
47
|
+
:path_size, :size_t,
|
48
|
+
:uri_mark, :string,
|
49
|
+
:uri_size, :size_t,
|
50
|
+
:fragment_mark, :string,
|
51
|
+
:fragment_size, :size_t,
|
52
|
+
# READ-ONLY
|
53
|
+
:status_code, :ushort, # responses only
|
54
|
+
:method, :ushort, # requests only
|
55
|
+
:transfer_encoding, :short,
|
56
|
+
:version_major, :ushort,
|
57
|
+
:version_minor, :ushort,
|
58
|
+
:keep_alive, :short,
|
59
|
+
:content_length, :size_t,
|
60
|
+
# PUBLIC
|
61
|
+
:data, :pointer, # A pointer to get hook to the "connection" or "socket" object
|
62
|
+
:on_message_begin, :http_cb,
|
63
|
+
:on_path, :http_data_cb,
|
64
|
+
:on_query_string, :http_data_cb,
|
65
|
+
:on_uri, :http_data_cb,
|
66
|
+
:on_fragment, :http_data_cb,
|
67
|
+
:on_header_field, :http_data_cb,
|
68
|
+
:on_header_value, :http_data_cb,
|
69
|
+
:on_headers_complete, :http_cb,
|
70
|
+
:on_body, :http_data_cb,
|
71
|
+
:on_message_complete, :http_cb
|
72
|
+
end
|
73
|
+
|
74
|
+
attach_function :http_parser_init, [:pointer, :http_parser_type], :void
|
75
|
+
attach_function :http_parser_execute, [:pointer, :string, :size_t], :size_t
|
76
|
+
attach_function :http_parser_has_error, [:pointer], :int
|
77
|
+
attach_function :http_parser_should_keep_alive, [:pointer], :int
|
78
|
+
|
79
|
+
def initialize(type)
|
80
|
+
@type = type
|
81
|
+
@parser = HttpParserStruct.new
|
82
|
+
HttpParser.http_parser_init(@parser, HttpParser.enum_value(@type))
|
83
|
+
end
|
84
|
+
|
85
|
+
def execute(data)
|
86
|
+
@abort = false
|
87
|
+
HttpParser.http_parser_execute(@parser, data, data.length)
|
88
|
+
end
|
89
|
+
|
90
|
+
def has_error?
|
91
|
+
HttpParser.http_parser_has_error(@parser).zero? ? false : true
|
92
|
+
end
|
93
|
+
|
94
|
+
def should_keep_alive?
|
95
|
+
HttpParser.http_parser_should_keep_alive(@parser).zero? ? false : true
|
96
|
+
end
|
97
|
+
|
98
|
+
# You can call abort! from inside your callbacks which will halt the processing.
|
99
|
+
def abort!
|
100
|
+
@abort = true
|
101
|
+
end
|
102
|
+
|
103
|
+
def on_message_begin(proc=nil, &blk)
|
104
|
+
proc ||= blk
|
105
|
+
@on_message_begin = Proc.new do |ptr|
|
106
|
+
proc.call
|
107
|
+
@abort ? 1 : 0
|
108
|
+
end
|
109
|
+
@parser[:on_message_begin] = @on_message_begin
|
110
|
+
end
|
111
|
+
|
112
|
+
def on_path(proc=nil, &blk)
|
113
|
+
proc ||= blk
|
114
|
+
@on_path = Proc.new do |ptr,data,len|
|
115
|
+
proc.call(data.get_string(0, len))
|
116
|
+
@abort ? 1 : 0
|
117
|
+
end
|
118
|
+
@parser[:on_path] = @on_path
|
119
|
+
end
|
120
|
+
|
121
|
+
def on_query_string(proc=nil, &blk)
|
122
|
+
proc ||= blk
|
123
|
+
@on_query_string = Proc.new do |ptr,data,len|
|
124
|
+
proc.call(data.get_string(0, len))
|
125
|
+
@abort ? 1 : 0
|
126
|
+
end
|
127
|
+
@parser[:on_query_string] = @on_query_string
|
128
|
+
end
|
129
|
+
|
130
|
+
def on_uri(proc=nil, &blk)
|
131
|
+
proc ||= blk
|
132
|
+
@on_uri = Proc.new do |ptr,data,len|
|
133
|
+
proc.call(data.get_string(0, len))
|
134
|
+
@abort ? 1 : 0
|
135
|
+
end
|
136
|
+
@parser[:on_uri] = @on_uri
|
137
|
+
end
|
138
|
+
|
139
|
+
def on_fragment(proc=nil, &blk)
|
140
|
+
proc ||= blk
|
141
|
+
@on_fragment = Proc.new do |ptr,data,len|
|
142
|
+
proc.call(data.get_string(0, len))
|
143
|
+
@abort ? 1 : 0
|
144
|
+
end
|
145
|
+
@parser[:on_fragment] = @on_fragment
|
146
|
+
end
|
147
|
+
|
148
|
+
def on_header_field(proc=nil, &blk)
|
149
|
+
proc ||= blk
|
150
|
+
@on_header_field = Proc.new do |ptr,data,len|
|
151
|
+
proc.call(data.get_string(0, len))
|
152
|
+
@abort ? 1 : 0
|
153
|
+
end
|
154
|
+
@parser[:on_header_field] = @on_header_field
|
155
|
+
end
|
156
|
+
|
157
|
+
def on_header_value(proc=nil, &blk)
|
158
|
+
proc ||= blk
|
159
|
+
@on_header_value= Proc.new do |ptr,data,len|
|
160
|
+
proc.call(data.get_string(0, len))
|
161
|
+
@abort ? 1 : 0
|
162
|
+
end
|
163
|
+
@parser[:on_header_value] = @on_header_value
|
164
|
+
end
|
165
|
+
|
166
|
+
def on_headers_complete(proc=nil, &blk)
|
167
|
+
proc ||= blk
|
168
|
+
@on_headers_complete = Proc.new do |ptr|
|
169
|
+
proc.call
|
170
|
+
@abort ? 1 : 0
|
171
|
+
end
|
172
|
+
@parser[:on_headers_complete] = @on_headers_complete
|
173
|
+
end
|
174
|
+
|
175
|
+
def on_body(proc=nil, &blk)
|
176
|
+
proc ||= blk
|
177
|
+
@on_body = Proc.new do |ptr,data,len|
|
178
|
+
proc.call(data.get_string(0, len))
|
179
|
+
@abort ? 1 : 0
|
180
|
+
end
|
181
|
+
@parser[:on_body] = @on_body
|
182
|
+
end
|
183
|
+
|
184
|
+
def on_message_complete(proc=nil, &blk)
|
185
|
+
proc ||= blk
|
186
|
+
@on_message_complete = Proc.new do |ptr|
|
187
|
+
proc.call
|
188
|
+
@abort ? 1 : 0
|
189
|
+
end
|
190
|
+
@parser[:on_message_complete] = @on_message_complete
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
data/src/LICENSE
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
Copyright 2009, Ryan Lienhart Dahl. All rights reserved.
|
2
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
3
|
+
of this software and associated documentation files (the "Software"), to
|
4
|
+
deal in the Software without restriction, including without limitation the
|
5
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
6
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
7
|
+
furnished to do so, subject to the following conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be included in
|
10
|
+
all copies or substantial portions of the Software.
|
11
|
+
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
13
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
14
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
15
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
16
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
17
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
18
|
+
IN THE SOFTWARE.
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
http_parser is based on Zed Shaw's Mongrel. Mongrel's license is as follows.
|
24
|
+
|
25
|
+
-- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT --
|
26
|
+
Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
|
27
|
+
<zedshaw at zedshaw dot com> and contributors. You can redistribute it
|
28
|
+
and/or modify it under either the terms of the GPL2 or the conditions below:
|
29
|
+
|
30
|
+
1. You may make and give away verbatim copies of the source form of the
|
31
|
+
software without restriction, provided that you duplicate all of the
|
32
|
+
original copyright notices and associated disclaimers.
|
33
|
+
|
34
|
+
2. You may modify your copy of the software in any way, provided that
|
35
|
+
you do at least ONE of the following:
|
36
|
+
|
37
|
+
a) place your modifications in the Public Domain or otherwise make them
|
38
|
+
Freely Available, such as by posting said modifications to Usenet or an
|
39
|
+
equivalent medium, or by allowing the author to include your
|
40
|
+
modifications in the software.
|
41
|
+
|
42
|
+
b) use the modified software only within your corporation or
|
43
|
+
organization.
|
44
|
+
|
45
|
+
c) rename any non-standard executables so the names do not conflict with
|
46
|
+
standard executables, which must also be provided.
|
47
|
+
|
48
|
+
d) make other distribution arrangements with the author.
|
49
|
+
|
50
|
+
3. You may distribute the software in object code or executable
|
51
|
+
form, provided that you do at least ONE of the following:
|
52
|
+
|
53
|
+
a) distribute the executables and library files of the software,
|
54
|
+
together with instructions (in the manual page or equivalent) on where
|
55
|
+
to get the original distribution.
|
56
|
+
|
57
|
+
b) accompany the distribution with the machine-readable source of the
|
58
|
+
software.
|
59
|
+
|
60
|
+
c) give non-standard executables non-standard names, with
|
61
|
+
instructions on where to get the original software distribution.
|
62
|
+
|
63
|
+
d) make other distribution arrangements with the author.
|
64
|
+
|
65
|
+
4. You may modify and include the part of the software into any other
|
66
|
+
software (possibly commercial). But some files in the distribution
|
67
|
+
are not written by the author, so that they are not under this terms.
|
68
|
+
|
69
|
+
5. The scripts and library files supplied as input to or produced as
|
70
|
+
output from the software do not automatically fall under the
|
71
|
+
copyright of the software, but belong to whomever generated them,
|
72
|
+
and may be sold commercially, and may be aggregated with this
|
73
|
+
software.
|
74
|
+
|
75
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
76
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
77
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
78
|
+
PURPOSE.
|
79
|
+
-- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT --
|
data/src/Makefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#OPT=-O0 -g -Wall -Wextra -Werror
|
2
|
+
OPT=-O2
|
3
|
+
|
4
|
+
test: http_parser.o test.c
|
5
|
+
gcc $(OPT) http_parser.o test.c -o $@
|
6
|
+
|
7
|
+
http_parser.o: http_parser.c http_parser.h Makefile
|
8
|
+
gcc $(OPT) -c http_parser.c
|
9
|
+
|
10
|
+
http_parser.c: http_parser.rl Makefile
|
11
|
+
ragel -s -G2 http_parser.rl -o $@
|
12
|
+
|
13
|
+
tags: http_parser.rl http_parser.h test.c
|
14
|
+
ctags $^
|
15
|
+
|
16
|
+
clean:
|
17
|
+
rm -f *.o http_parser.c test http_parser.tar
|
18
|
+
|
19
|
+
package: http_parser.c
|
20
|
+
@rm -rf /tmp/http_parser && mkdir /tmp/http_parser && \
|
21
|
+
cp LICENSE README.md Makefile http_parser.c http_parser.rl \
|
22
|
+
http_parser.h test.c /tmp/http_parser && \
|
23
|
+
cd /tmp && \
|
24
|
+
tar -cf http_parser.tar http_parser/
|
25
|
+
@echo /tmp/http_parser.tar
|
26
|
+
|
27
|
+
.PHONY: clean package
|
data/src/README.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
HTTP Parser
|
2
|
+
===========
|
3
|
+
|
4
|
+
This is a parser for HTTP messages written in C. It parses both requests
|
5
|
+
and responses. The parser is designed to be used in performance HTTP
|
6
|
+
applications. It does not make any allocations, it does not buffer data, and
|
7
|
+
it can be interrupted at anytime. It only requires about 128 bytes of data
|
8
|
+
per message stream (in a web server that is per connection).
|
9
|
+
|
10
|
+
Features:
|
11
|
+
|
12
|
+
* No dependencies
|
13
|
+
* Parses both requests and responses.
|
14
|
+
* Handles keep-alive streams.
|
15
|
+
* Decodes chunked encoding.
|
16
|
+
* Extracts the following data from a message
|
17
|
+
* header fields and values
|
18
|
+
* content-length
|
19
|
+
* request method
|
20
|
+
* response status code
|
21
|
+
* transfer-encoding
|
22
|
+
* http version
|
23
|
+
* request path, query string, fragment
|
24
|
+
* message body
|
25
|
+
* Defends against buffer overflow attacks.
|
26
|
+
|
27
|
+
Usage
|
28
|
+
-----
|
29
|
+
|
30
|
+
One `http_parser` object is used per TCP connection. Initialize the struct
|
31
|
+
using `http_parser_init()` and set the callbacks. That might look something
|
32
|
+
like this:
|
33
|
+
|
34
|
+
http_parser *parser = malloc(sizeof(http_parser));
|
35
|
+
http_parser_init(parser, HTTP_REQUEST);
|
36
|
+
parser->on_path = my_path_callback;
|
37
|
+
parser->on_header_field = my_header_field_callback;
|
38
|
+
parser->data = my_socket;
|
39
|
+
|
40
|
+
When data is received on the socket execute the parser and check for errors.
|
41
|
+
|
42
|
+
size_t len = 80*1024;
|
43
|
+
char buf[len];
|
44
|
+
ssize_t recved;
|
45
|
+
|
46
|
+
recved = read(fd, buf, len);
|
47
|
+
if (recved != 0) // handle error
|
48
|
+
|
49
|
+
http_parser_execute(parser, buf, recved);
|
50
|
+
|
51
|
+
if (http_parser_has_error(parser)) {
|
52
|
+
// handle error. usually just close the connection
|
53
|
+
}
|
54
|
+
|
55
|
+
Scalar valued message information such as `status_code`, `method`, and the
|
56
|
+
HTTP version are stored in the parser structure. This data is only
|
57
|
+
temporarlly stored in `http_parser` and gets reset on each new message. If
|
58
|
+
this information is needed later, copy it out of the structure during the
|
59
|
+
`headers_complete` callback.
|
60
|
+
|
61
|
+
The parser decodes the transfer-encoding for both requests and responses
|
62
|
+
transparently. That is, a chunked encoding is decoded before being sent to
|
63
|
+
the on_body callback.
|
64
|
+
|
65
|
+
It does not decode the content-encoding (gzip). Not all HTTP applications
|
66
|
+
need to inspect the body. Decoding gzip is non-neglagable amount of
|
67
|
+
processing (and requires making allocations). HTTP proxies using this
|
68
|
+
parser, for example, would not want such a feature.
|
69
|
+
|
70
|
+
Callbacks
|
71
|
+
---------
|
72
|
+
|
73
|
+
During the `http_parser_execute()` call, the callbacks set in `http_parser`
|
74
|
+
will be executed. The parser maintains state and never looks behind, so
|
75
|
+
buffering the data is not necessary. If you need to save certain data for
|
76
|
+
later usage, you can do that from the callbacks.
|
77
|
+
|
78
|
+
There are two types of callbacks:
|
79
|
+
|
80
|
+
* notification `typedef int (*http_cb) (http_parser*);`
|
81
|
+
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
|
82
|
+
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
|
83
|
+
Callbacks: (requests only) on_path, on_query_string, on_uri, on_fragment,
|
84
|
+
(common) on_header_field, on_header_value, on_body;
|
85
|
+
|
86
|
+
In case you parse HTTP message in chunks (i.e. `read()` request line
|
87
|
+
from socket, parse, read half headers, parse, etc) your data callbacks
|
88
|
+
may be called more than once. Http-parser guarantees that data pointer is only
|
89
|
+
valid for the lifetime of callback. You can also `read()` into a heap allocated
|
90
|
+
buffer to avoid copying memory around if this fits your application.
|
91
|
+
|
92
|
+
Reading headers may be a tricky task if you read/parse headers partially.
|
93
|
+
Basically, you need to remember whether last header callback was field or value
|
94
|
+
and apply following logic:
|
95
|
+
|
96
|
+
/* on_header_field and on_header_value shortened to on_h_*
|
97
|
+
------------------------ ------------ --------------------------------------------
|
98
|
+
| State (prev. callback) | Callback | Description/action |
|
99
|
+
------------------------ ------------ --------------------------------------------
|
100
|
+
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
|
101
|
+
| | | into it |
|
102
|
+
------------------------ ------------ --------------------------------------------
|
103
|
+
| value | on_h_field | New header started. |
|
104
|
+
| | | Copy current name,value buffers to headers |
|
105
|
+
| | | list and allocate new buffer for new name |
|
106
|
+
------------------------ ------------ --------------------------------------------
|
107
|
+
| field | on_h_field | Previous name continues. Reallocate name |
|
108
|
+
| | | buffer and append callback data to it |
|
109
|
+
------------------------ ------------ --------------------------------------------
|
110
|
+
| field | on_h_value | Value for current header started. Allocate |
|
111
|
+
| | | new buffer and copy callback data to it |
|
112
|
+
------------------------ ------------ --------------------------------------------
|
113
|
+
| value | on_h_value | Value continues. Reallocate value buffer |
|
114
|
+
| | | and append callback data to it |
|
115
|
+
------------------------ ------------ --------------------------------------------
|
116
|
+
*/
|
117
|
+
|
118
|
+
See examples of reading in headers:
|
119
|
+
|
120
|
+
* [partial example](http://gist.github.com/155877) in C
|
121
|
+
* [from http-parser tests](http://github.com/ry/http-parser/blob/37a0ff8928fb0d83cec0d0d8909c5a4abcd221af/test.c#L403) in C
|
122
|
+
* [from Node library](http://github.com/ry/node/blob/842eaf446d2fdcb33b296c67c911c32a0dabc747/src/http.js#L284) in Javascript
|
123
|
+
|
124
|
+
Releases
|
125
|
+
--------
|
126
|
+
|
127
|
+
* [0.1](http://s3.amazonaws.com/four.livejournal/20090427/http_parser-0.1.tar.gz)
|
128
|
+
|
129
|
+
The source repo is at [github](http://github.com/ry/http-parser).
|
data/src/http_parser.h
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
/* Copyright (c) 2008, 2009 Ryan Dahl (ry@tinyclouds.org)
|
2
|
+
* Based on Zed Shaw's Mongrel, copyright (c) Zed A. Shaw
|
3
|
+
*
|
4
|
+
* All rights reserved.
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
* a copy of this software and associated documentation files (the
|
8
|
+
* "Software"), to deal in the Software without restriction, including
|
9
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
* the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be
|
15
|
+
* included in all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#ifndef http_parser_h
|
26
|
+
#define http_parser_h
|
27
|
+
#ifdef __cplusplus
|
28
|
+
extern "C" {
|
29
|
+
#endif
|
30
|
+
|
31
|
+
#include <sys/types.h>
|
32
|
+
|
33
|
+
typedef struct http_parser http_parser;
|
34
|
+
|
35
|
+
/* Callbacks should return non-zero to indicate an error. The parse will
|
36
|
+
* then halt execution.
|
37
|
+
*
|
38
|
+
* http_data_cb does not return data chunks. It will be call arbitrarally
|
39
|
+
* many times for each string. E.G. you might get 10 callbacks for "on_path"
|
40
|
+
* each providing just a few characters more data.
|
41
|
+
*/
|
42
|
+
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
43
|
+
typedef int (*http_cb) (http_parser*);
|
44
|
+
|
45
|
+
/* Request Methods */
|
46
|
+
#define HTTP_COPY 0x0001
|
47
|
+
#define HTTP_DELETE 0x0002
|
48
|
+
#define HTTP_GET 0x0004
|
49
|
+
#define HTTP_HEAD 0x0008
|
50
|
+
#define HTTP_LOCK 0x0010
|
51
|
+
#define HTTP_MKCOL 0x0020
|
52
|
+
#define HTTP_MOVE 0x0040
|
53
|
+
#define HTTP_OPTIONS 0x0080
|
54
|
+
#define HTTP_POST 0x0100
|
55
|
+
#define HTTP_PROPFIND 0x0200
|
56
|
+
#define HTTP_PROPPATCH 0x0400
|
57
|
+
#define HTTP_PUT 0x0800
|
58
|
+
#define HTTP_TRACE 0x1000
|
59
|
+
#define HTTP_UNLOCK 0x2000
|
60
|
+
|
61
|
+
/* Transfer Encodings */
|
62
|
+
#define HTTP_IDENTITY 0x01
|
63
|
+
#define HTTP_CHUNKED 0x02
|
64
|
+
|
65
|
+
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE };
|
66
|
+
|
67
|
+
struct http_parser {
|
68
|
+
/** PRIVATE **/
|
69
|
+
int cs;
|
70
|
+
enum http_parser_type type;
|
71
|
+
|
72
|
+
size_t chunk_size;
|
73
|
+
int eating;
|
74
|
+
int error;
|
75
|
+
size_t body_read;
|
76
|
+
|
77
|
+
const char *header_field_mark;
|
78
|
+
size_t header_field_size;
|
79
|
+
const char *header_value_mark;
|
80
|
+
size_t header_value_size;
|
81
|
+
const char *query_string_mark;
|
82
|
+
size_t query_string_size;
|
83
|
+
const char *path_mark;
|
84
|
+
size_t path_size;
|
85
|
+
const char *uri_mark;
|
86
|
+
size_t uri_size;
|
87
|
+
const char *fragment_mark;
|
88
|
+
size_t fragment_size;
|
89
|
+
|
90
|
+
/** READ-ONLY **/
|
91
|
+
unsigned short status_code; /* responses only */
|
92
|
+
unsigned short method; /* requests only */
|
93
|
+
short transfer_encoding;
|
94
|
+
unsigned short version_major;
|
95
|
+
unsigned short version_minor;
|
96
|
+
short keep_alive;
|
97
|
+
size_t content_length;
|
98
|
+
|
99
|
+
/** PUBLIC **/
|
100
|
+
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
101
|
+
|
102
|
+
/* an ordered list of callbacks */
|
103
|
+
|
104
|
+
http_cb on_message_begin;
|
105
|
+
|
106
|
+
/* requests only */
|
107
|
+
http_data_cb on_path;
|
108
|
+
http_data_cb on_query_string;
|
109
|
+
http_data_cb on_uri;
|
110
|
+
http_data_cb on_fragment;
|
111
|
+
|
112
|
+
http_data_cb on_header_field;
|
113
|
+
http_data_cb on_header_value;
|
114
|
+
http_cb on_headers_complete;
|
115
|
+
http_data_cb on_body;
|
116
|
+
http_cb on_message_complete;
|
117
|
+
};
|
118
|
+
|
119
|
+
/* Initializes an http_parser structure. The second argument specifies if
|
120
|
+
* it will be parsing requests or responses.
|
121
|
+
*/
|
122
|
+
void http_parser_init (http_parser *parser, enum http_parser_type);
|
123
|
+
|
124
|
+
size_t http_parser_execute (http_parser *parser, const char *data, size_t len);
|
125
|
+
|
126
|
+
int http_parser_has_error (http_parser *parser);
|
127
|
+
|
128
|
+
int http_parser_should_keep_alive (http_parser *parser);
|
129
|
+
|
130
|
+
#ifdef __cplusplus
|
131
|
+
}
|
132
|
+
#endif
|
133
|
+
#endif
|