ebb 0.0.2 → 0.0.3
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.
- data/README +3 -5
- data/VERSION +1 -1
- data/bin/ebb_rails +9 -18
- data/ruby_lib/daemonizable.rb +34 -70
- data/ruby_lib/ebb.rb +16 -6
- data/src/ebb.c +82 -70
- data/src/ebb.h +29 -18
- data/src/ebb_ruby.c +69 -61
- data/src/extconf.rb +2 -1
- data/src/parser.c +871 -538
- data/src/parser.h +2 -2
- data/{benchmark/test.rb → test/basic_test.rb} +72 -19
- data/test/echo_server.rb +16 -0
- data/test/env_test.rb +114 -0
- metadata +12 -11
- data/src/parser.rl +0 -200
data/src/parser.h
CHANGED
@@ -17,6 +17,7 @@ typedef void (*field_cb)(void *data, const char *field, size_t flen, const char
|
|
17
17
|
|
18
18
|
typedef struct http_parser {
|
19
19
|
int cs;
|
20
|
+
int overflow_error;
|
20
21
|
size_t body_start;
|
21
22
|
int content_len;
|
22
23
|
size_t nread;
|
@@ -36,10 +37,9 @@ typedef struct http_parser {
|
|
36
37
|
element_cb http_version;
|
37
38
|
element_cb header_done;
|
38
39
|
element_cb content_length;
|
39
|
-
|
40
40
|
} http_parser;
|
41
41
|
|
42
|
-
|
42
|
+
void http_parser_init(http_parser *parser);
|
43
43
|
int http_parser_finish(http_parser *parser);
|
44
44
|
size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
|
45
45
|
int http_parser_has_error(http_parser *parser);
|
@@ -1,13 +1,17 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../ruby_lib/ebb'
|
2
2
|
require 'test/unit'
|
3
3
|
require 'net/http'
|
4
|
-
require '
|
4
|
+
require 'socket'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'json'
|
5
7
|
|
8
|
+
PORT = 4044
|
6
9
|
|
7
10
|
class EbbTest < Test::Unit::TestCase
|
8
11
|
def setup
|
9
12
|
@pid = fork do
|
10
|
-
server = Ebb::Server.new(self, :port =>
|
13
|
+
server = Ebb::Server.new(self, :port => PORT)
|
14
|
+
STDOUT.reopen "/dev/null", "a"
|
11
15
|
server.start
|
12
16
|
end
|
13
17
|
sleep 0.5
|
@@ -19,11 +23,11 @@ class EbbTest < Test::Unit::TestCase
|
|
19
23
|
end
|
20
24
|
|
21
25
|
def get(path)
|
22
|
-
Net::HTTP.get_response(URI.parse("http://0.0.0.0
|
26
|
+
Net::HTTP.get_response(URI.parse("http://0.0.0.0:#{PORT}#{path}"))
|
23
27
|
end
|
24
28
|
|
25
29
|
def post(path, data)
|
26
|
-
Net::HTTP.post_form(URI.parse("http://0.0.0.0
|
30
|
+
Net::HTTP.post_form(URI.parse("http://0.0.0.0:#{PORT}#{path}"), data)
|
27
31
|
end
|
28
32
|
|
29
33
|
@@responses = {}
|
@@ -35,12 +39,6 @@ class EbbTest < Test::Unit::TestCase
|
|
35
39
|
raise "bytes called with n <= 0" if n <= 0
|
36
40
|
body = @@responses[n] || "C"*n
|
37
41
|
status = 200
|
38
|
-
|
39
|
-
elsif commands.include?('env')
|
40
|
-
env.delete('rack.input') # delete this because it's hard to marshal
|
41
|
-
env.delete('rack.errors')
|
42
|
-
body = Base64.encode64(Marshal.dump(env))
|
43
|
-
status = 200
|
44
42
|
|
45
43
|
elsif commands.include?('test_post_length')
|
46
44
|
input_body = ""
|
@@ -89,7 +87,7 @@ class EbbTest < Test::Unit::TestCase
|
|
89
87
|
|
90
88
|
# this is rough but does detect major problems
|
91
89
|
def test_ab
|
92
|
-
r = %x{ab -n 1000 -c 50 -q http://0.0.0.0
|
90
|
+
r = %x{ab -n 1000 -c 50 -q http://0.0.0.0:#{PORT}/bytes/123}
|
93
91
|
assert r =~ /Requests per second:\s*(\d+)/, r
|
94
92
|
assert $1.to_i > 100, r
|
95
93
|
end
|
@@ -100,29 +98,84 @@ class EbbTest < Test::Unit::TestCase
|
|
100
98
|
assert_equal 200, response.code.to_i, response.body
|
101
99
|
end
|
102
100
|
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class EnvTest < Test::Unit::TestCase
|
104
|
+
def call(env)
|
105
|
+
env.delete('rack.input')
|
106
|
+
env.delete('rack.errors')
|
107
|
+
[200, {'Content-Type' => 'text/json'}, env.to_json]
|
108
|
+
end
|
103
109
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
110
|
+
def setup
|
111
|
+
@pid = fork do
|
112
|
+
server = Ebb::Server.new(self, :port => PORT)
|
113
|
+
STDOUT.reopen "/dev/null", "a"
|
114
|
+
server.start
|
115
|
+
end
|
116
|
+
sleep 0.5
|
117
|
+
end
|
118
|
+
|
119
|
+
def teardown
|
120
|
+
Process.kill('KILL', @pid)
|
121
|
+
sleep 0.5
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
### Tests from Mongrel http11
|
126
|
+
|
127
|
+
def request(request_string)
|
128
|
+
socket = TCPSocket.new("0.0.0.0", PORT)
|
129
|
+
socket.write(request_string)
|
130
|
+
lines = []
|
131
|
+
socket.read(500000).each_line { |l| lines << l }
|
132
|
+
env = JSON.parse(lines.last)
|
133
|
+
end
|
134
|
+
|
135
|
+
def assert_drops_connection(request_string)
|
136
|
+
socket = TCPSocket.new("0.0.0.0", PORT)
|
137
|
+
socket.write(request_string)
|
138
|
+
assert socket.closed?
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def test_parse_simple
|
143
|
+
env = request("GET / HTTP/1.1\r\n\r\n")
|
109
144
|
assert_equal 'HTTP/1.1', env['SERVER_PROTOCOL']
|
145
|
+
assert_equal '/', env['REQUEST_PATH']
|
146
|
+
assert_equal 'HTTP/1.1', env['HTTP_VERSION']
|
147
|
+
assert_equal '/', env['REQUEST_URI']
|
110
148
|
assert_equal 'CGI/1.2', env['GATEWAY_INTERFACE']
|
111
|
-
assert_equal '
|
112
|
-
|
113
|
-
|
149
|
+
assert_equal 'GET', env['REQUEST_METHOD']
|
150
|
+
assert_nil env['FRAGMENT']
|
151
|
+
assert_nil env['QUERY_STRING']
|
114
152
|
end
|
115
153
|
end
|
116
154
|
|
155
|
+
|
117
156
|
class EbbRailsTest < Test::Unit::TestCase
|
118
157
|
# just to make sure there isn't some load error
|
119
158
|
def test_ebb_rails_version
|
120
159
|
out = %x{ruby #{Ebb::LIBDIR}/../bin/ebb_rails -v}
|
121
160
|
assert_match %r{Ebb #{Ebb::VERSION}}, out
|
122
161
|
end
|
162
|
+
|
163
|
+
# def get(path)
|
164
|
+
# Net::HTTP.get_response(URI.parse("http://0.0.0.0:4043#{path}"))
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
# def test_starting_with_many_options
|
168
|
+
# %x{cd /tmp && rails ebb_test && ruby #{Ebb::LIBDIR}/../bin/ebb_rails start -d -e development -c /tmp/ebb_test -u www -g www -P /tmp/ebb.1.pid -p 4043 &}
|
169
|
+
# response = get('/')
|
170
|
+
# assert_equal 200, response.code
|
171
|
+
# ensure
|
172
|
+
# Process.kill('KILL', %x{cat /tmp/ebb.1.pid})
|
173
|
+
# end
|
123
174
|
end
|
124
175
|
|
125
176
|
|
177
|
+
|
178
|
+
|
126
179
|
#
|
127
180
|
# class SocketTest < Test::Unit::TestCase
|
128
181
|
# def test_socket_creation
|
data/test/echo_server.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
require File.dirname(__FILE__) + '/../ruby_lib/ebb'
|
4
|
+
|
5
|
+
|
6
|
+
class EchoApp
|
7
|
+
def call(env)
|
8
|
+
env['rack.input'] = env['rack.input'].read(1000000)
|
9
|
+
env.delete('rack.errors')
|
10
|
+
[200, {'Content-Type' => 'text/json'}, env.to_json]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
server = Ebb::Server.new(EchoApp.new, :port => 4037)
|
16
|
+
server.start
|
data/test/env_test.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'json'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
PORT = 4037
|
7
|
+
|
8
|
+
# This test depends on echo_server running at port 4037. I do this so that
|
9
|
+
# I can run a Python server at that port with a similar application and reuse
|
10
|
+
# these tests.
|
11
|
+
|
12
|
+
def send_request(request_string)
|
13
|
+
socket = TCPSocket.new("0.0.0.0", PORT)
|
14
|
+
socket.write(request_string)
|
15
|
+
lines = []
|
16
|
+
out = socket.read(5000000)
|
17
|
+
raise "Connection Closed on #{request_string.inspect}" if out.nil?
|
18
|
+
out.each_line { |l| lines << l }
|
19
|
+
env = JSON.parse(lines.last)
|
20
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE
|
21
|
+
return :fail
|
22
|
+
rescue RuntimeError => e
|
23
|
+
if e.message =~ /Connection Closed/
|
24
|
+
return :fail
|
25
|
+
else
|
26
|
+
raise e
|
27
|
+
end
|
28
|
+
rescue => e
|
29
|
+
puts "unknown exception: #{e.class}"
|
30
|
+
raise e
|
31
|
+
ensure
|
32
|
+
socket.close unless socket.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def drops_request?(request_string)
|
36
|
+
:fail == send_request(request_string)
|
37
|
+
end
|
38
|
+
|
39
|
+
class HttpParserTest < Test::Unit::TestCase
|
40
|
+
|
41
|
+
def test_parse_simple
|
42
|
+
env = send_request("GET / HTTP/1.1\r\n\r\n")
|
43
|
+
|
44
|
+
assert_equal 'HTTP/1.1', env['SERVER_PROTOCOL']
|
45
|
+
assert_equal '/', env['REQUEST_PATH']
|
46
|
+
assert_equal 'HTTP/1.1', env['HTTP_VERSION']
|
47
|
+
assert_equal '/', env['REQUEST_URI']
|
48
|
+
assert_equal 'CGI/1.2', env['GATEWAY_INTERFACE']
|
49
|
+
assert_equal 'GET', env['REQUEST_METHOD']
|
50
|
+
assert_nil env['FRAGMENT']
|
51
|
+
assert_nil env['QUERY_STRING']
|
52
|
+
assert_nil env['rack.input']
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_parse_dumbfuck_headers
|
56
|
+
should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
|
57
|
+
env = send_request(should_be_good)
|
58
|
+
assert_equal "++++++++++", env["HTTP_AAAAAAAAAAAAA"]
|
59
|
+
assert_nil env['rack.input']
|
60
|
+
|
61
|
+
nasty_pound_header = "GET / HTTP/1.1\r\nX-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
|
62
|
+
assert drops_request?(nasty_pound_header) # Correct?
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_parse_error
|
66
|
+
assert drops_request?("GET / SsUTF/1.1")
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_fragment_in_uri
|
70
|
+
env = send_request("GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n")
|
71
|
+
assert_equal '/forums/1/topics/2375?page=1', env['REQUEST_URI']
|
72
|
+
assert_equal 'posts-17408', env['FRAGMENT']
|
73
|
+
assert_nil env['rack.input']
|
74
|
+
end
|
75
|
+
|
76
|
+
# lame random garbage maker
|
77
|
+
def rand_data(min, max, readable=true)
|
78
|
+
count = min + ((rand(max)+1) *10).to_i
|
79
|
+
res = count.to_s + "/"
|
80
|
+
|
81
|
+
if readable
|
82
|
+
res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
|
83
|
+
else
|
84
|
+
res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
|
85
|
+
end
|
86
|
+
|
87
|
+
return res
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_horrible_queries
|
91
|
+
10.times do |c|
|
92
|
+
req = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
93
|
+
assert drops_request?(req), "large header names are caught"
|
94
|
+
end
|
95
|
+
|
96
|
+
# then that large mangled field values are caught
|
97
|
+
10.times do |c|
|
98
|
+
req = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
99
|
+
assert drops_request?(req), "large mangled field values are caught"
|
100
|
+
### XXX this is broken! fix me. this test should drop the request.
|
101
|
+
end
|
102
|
+
|
103
|
+
# then large headers are rejected too
|
104
|
+
req = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
105
|
+
req << "X-Test: test\r\n" * (80 * 1024)
|
106
|
+
assert drops_request?(req), "large headers are rejected"
|
107
|
+
|
108
|
+
# finally just that random garbage gets blocked all the time
|
109
|
+
10.times do |c|
|
110
|
+
req = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
111
|
+
assert drops_request?(req), "random garbage gets blocked all the time"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ebb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ry dahl
|
@@ -9,12 +9,12 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-03-03 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description: ""
|
17
|
-
email: ry
|
17
|
+
email: ry at tiny clouds dot org
|
18
18
|
executables:
|
19
19
|
- ebb_rails
|
20
20
|
extensions:
|
@@ -22,13 +22,10 @@ extensions:
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
-
- src/parser.rl
|
26
25
|
- src/ebb.c
|
27
|
-
- src/ebb_ruby.c
|
28
|
-
- src/parser.c
|
29
26
|
- src/ebb.h
|
27
|
+
- src/parser.c
|
30
28
|
- src/parser.h
|
31
|
-
- src/extconf.rb
|
32
29
|
- libev/ev.c
|
33
30
|
- libev/ev.h
|
34
31
|
- libev/ev_epoll.c
|
@@ -39,6 +36,10 @@ files:
|
|
39
36
|
- libev/ev_vars.h
|
40
37
|
- libev/ev_win32.c
|
41
38
|
- libev/ev_wrap.h
|
39
|
+
- VERSION
|
40
|
+
- README
|
41
|
+
- src/ebb_ruby.c
|
42
|
+
- src/extconf.rb
|
42
43
|
- ruby_lib/daemonizable.rb
|
43
44
|
- ruby_lib/ebb.rb
|
44
45
|
- ruby_lib/rack
|
@@ -47,12 +48,12 @@ files:
|
|
47
48
|
- benchmark/application.rb
|
48
49
|
- benchmark/bench_results.rb
|
49
50
|
- benchmark/server_test.rb
|
50
|
-
- benchmark/test.rb
|
51
51
|
- bin/ebb_rails
|
52
|
-
-
|
53
|
-
-
|
52
|
+
- test/basic_test.rb
|
53
|
+
- test/echo_server.rb
|
54
|
+
- test/env_test.rb
|
54
55
|
has_rdoc: false
|
55
|
-
homepage: http://
|
56
|
+
homepage: http://ebb.rubyforge.org
|
56
57
|
post_install_message:
|
57
58
|
rdoc_options: []
|
58
59
|
|
data/src/parser.rl
DELETED
@@ -1,200 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Copyright (c) 2005 Zed A. Shaw
|
3
|
-
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
-
*/
|
5
|
-
#include "parser.h"
|
6
|
-
#include <stdio.h>
|
7
|
-
#include <assert.h>
|
8
|
-
#include <stdlib.h>
|
9
|
-
#include <ctype.h>
|
10
|
-
#include <string.h>
|
11
|
-
|
12
|
-
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
13
|
-
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
14
|
-
#define PTR_TO(F) (buffer + parser->F)
|
15
|
-
|
16
|
-
/** machine **/
|
17
|
-
%%{
|
18
|
-
machine http_parser;
|
19
|
-
|
20
|
-
action mark {MARK(mark, fpc); }
|
21
|
-
|
22
|
-
|
23
|
-
action start_field { MARK(field_start, fpc); }
|
24
|
-
action write_field {
|
25
|
-
parser->field_len = LEN(field_start, fpc);
|
26
|
-
}
|
27
|
-
|
28
|
-
action start_value { MARK(mark, fpc); }
|
29
|
-
action write_value {
|
30
|
-
if(parser->http_field != NULL) {
|
31
|
-
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
32
|
-
}
|
33
|
-
}
|
34
|
-
|
35
|
-
action content_length {
|
36
|
-
if(parser->content_length != NULL) {
|
37
|
-
parser->content_length(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
38
|
-
}
|
39
|
-
}
|
40
|
-
|
41
|
-
action request_method {
|
42
|
-
if(parser->request_method != NULL)
|
43
|
-
parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
44
|
-
}
|
45
|
-
action request_uri {
|
46
|
-
if(parser->request_uri != NULL)
|
47
|
-
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
48
|
-
}
|
49
|
-
|
50
|
-
action start_query {MARK(query_start, fpc); }
|
51
|
-
action query_string {
|
52
|
-
if(parser->query_string != NULL)
|
53
|
-
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
|
54
|
-
}
|
55
|
-
|
56
|
-
action http_version {
|
57
|
-
if(parser->http_version != NULL)
|
58
|
-
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
59
|
-
}
|
60
|
-
|
61
|
-
action request_path {
|
62
|
-
if(parser->request_path != NULL)
|
63
|
-
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
|
64
|
-
}
|
65
|
-
|
66
|
-
action done {
|
67
|
-
parser->body_start = fpc - buffer + 1;
|
68
|
-
if(parser->header_done != NULL)
|
69
|
-
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
|
70
|
-
fbreak;
|
71
|
-
}
|
72
|
-
|
73
|
-
|
74
|
-
#### HTTP PROTOCOL GRAMMAR
|
75
|
-
# line endings
|
76
|
-
CRLF = "\r\n";
|
77
|
-
|
78
|
-
# character types
|
79
|
-
CTL = (cntrl | 127);
|
80
|
-
safe = ("$" | "-" | "_" | ".");
|
81
|
-
extra = ("!" | "*" | "'" | "(" | ")" | ",");
|
82
|
-
reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
|
83
|
-
unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
|
84
|
-
national = any -- (alpha | digit | reserved | extra | safe | unsafe);
|
85
|
-
unreserved = (alpha | digit | safe | extra | national);
|
86
|
-
escape = ("%" xdigit xdigit);
|
87
|
-
uchar = (unreserved | escape);
|
88
|
-
pchar = (uchar | ":" | "@" | "&" | "=" | "+");
|
89
|
-
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
90
|
-
|
91
|
-
# elements
|
92
|
-
token = (ascii -- (CTL | tspecials));
|
93
|
-
|
94
|
-
# URI schemes and absolute paths
|
95
|
-
scheme = ( alpha | digit | "+" | "-" | "." )* ;
|
96
|
-
absolute_uri = (scheme ":" (uchar | reserved )*);
|
97
|
-
|
98
|
-
path = (pchar+ ( "/" pchar* )*) ;
|
99
|
-
query = ( uchar | reserved )* %query_string ;
|
100
|
-
param = ( pchar | "/" )* ;
|
101
|
-
params = (param ( ";" param )*) ;
|
102
|
-
rel_path = (path? %request_path (";" params)?) ("?" %start_query query)?;
|
103
|
-
absolute_path = ("/"+ rel_path);
|
104
|
-
|
105
|
-
Request_URI = ("*" | absolute_uri | absolute_path) >mark %request_uri;
|
106
|
-
Method = (upper | digit | safe){1,20} >mark %request_method;
|
107
|
-
|
108
|
-
http_number = (digit+ "." digit+) ;
|
109
|
-
HTTP_Version = ("HTTP/" http_number) >mark %http_version ;
|
110
|
-
Request_Line = (Method " " Request_URI " " HTTP_Version CRLF) ;
|
111
|
-
|
112
|
-
field_name = (token -- ":")+ >start_field %write_field;
|
113
|
-
|
114
|
-
field_value = any* >start_value %write_value;
|
115
|
-
|
116
|
-
message_header = field_name ":" " "* field_value :> CRLF;
|
117
|
-
content_length = "Content-Length:"i " "* (field_value >mark %content_length) :> CRLF;
|
118
|
-
|
119
|
-
Request = Request_Line (content_length | message_header)* ( CRLF @done);
|
120
|
-
|
121
|
-
main := Request;
|
122
|
-
}%%
|
123
|
-
|
124
|
-
/** Data **/
|
125
|
-
%% write data;
|
126
|
-
|
127
|
-
int http_parser_init(http_parser *parser) {
|
128
|
-
int cs = 0;
|
129
|
-
%% write init;
|
130
|
-
parser->cs = cs;
|
131
|
-
parser->body_start = 0;
|
132
|
-
parser->content_len = 0;
|
133
|
-
parser->mark = 0;
|
134
|
-
parser->nread = 0;
|
135
|
-
parser->field_len = 0;
|
136
|
-
parser->field_start = 0;
|
137
|
-
|
138
|
-
return(1);
|
139
|
-
}
|
140
|
-
|
141
|
-
|
142
|
-
/** exec **/
|
143
|
-
size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
|
144
|
-
const char *p, *pe;
|
145
|
-
int cs = parser->cs;
|
146
|
-
|
147
|
-
assert(off <= len && "offset past end of buffer");
|
148
|
-
|
149
|
-
p = buffer+off;
|
150
|
-
pe = buffer+len;
|
151
|
-
|
152
|
-
assert(*pe == '\0' && "pointer does not end on NUL");
|
153
|
-
assert(pe - p == len - off && "pointers aren't same distance");
|
154
|
-
|
155
|
-
|
156
|
-
%% write exec;
|
157
|
-
|
158
|
-
parser->cs = cs;
|
159
|
-
parser->nread += p - (buffer + off);
|
160
|
-
|
161
|
-
assert(p <= pe && "buffer overflow after parsing execute");
|
162
|
-
assert(parser->nread <= len && "nread longer than length");
|
163
|
-
assert(parser->body_start <= len && "body starts after buffer end");
|
164
|
-
assert(parser->mark < len && "mark is after buffer end");
|
165
|
-
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
166
|
-
assert(parser->field_start < len && "field starts after buffer end");
|
167
|
-
|
168
|
-
if(parser->body_start) {
|
169
|
-
/* final \r\n combo encountered so stop right here */
|
170
|
-
%%write eof;
|
171
|
-
parser->nread++;
|
172
|
-
}
|
173
|
-
|
174
|
-
return(parser->nread);
|
175
|
-
}
|
176
|
-
|
177
|
-
int http_parser_finish(http_parser *parser)
|
178
|
-
{
|
179
|
-
int cs = parser->cs;
|
180
|
-
|
181
|
-
%%write eof;
|
182
|
-
|
183
|
-
parser->cs = cs;
|
184
|
-
|
185
|
-
if (http_parser_has_error(parser) ) {
|
186
|
-
return -1;
|
187
|
-
} else if (http_parser_is_finished(parser) ) {
|
188
|
-
return 1;
|
189
|
-
} else {
|
190
|
-
return 0;
|
191
|
-
}
|
192
|
-
}
|
193
|
-
|
194
|
-
int http_parser_has_error(http_parser *parser) {
|
195
|
-
return parser->cs == http_parser_error;
|
196
|
-
}
|
197
|
-
|
198
|
-
int http_parser_is_finished(http_parser *parser) {
|
199
|
-
return parser->cs == http_parser_first_final;
|
200
|
-
}
|