ry / ebb fork watch download tarball
public this repo is viewable by everyone
Description: web server
Homepage: http://ebb.rubyforge.org
Clone URL: git://github.com/ry/ebb.git
Clean up parser; add tests
ryah (author)
about 1 month ago
commit  d5014ad2da264096a076bbe5f6898095eff38749
tree    3b9da6803e9a4abafc644011bebc0eb292e88017
parent  573814245209b423878a0011ef40aa674e7c2c10
...
19
20
21
22
23
24
25
26
27
28
29
...
136
137
138
139
140
141
142
143
144
145
...
256
257
258
259
260
261
262
263
264
265
...
19
20
21
 
 
22
 
 
23
24
25
...
132
133
134
 
 
135
 
136
137
138
...
249
250
251
 
 
252
 
253
254
255
0
@@ -19,11 +19,7 @@ static VALUE global_request_method;
0
 static VALUE global_request_path;
0
 static VALUE global_request_uri;
0
 static VALUE global_server_port;
0
-static VALUE global_http_accept;
0
-static VALUE global_http_connection;
0
 static VALUE global_http_content_length;
0
-static VALUE global_http_content_type;
0
-static VALUE global_http_content_type;
0
 static VALUE global_http_prefix;
0
 static VALUE global_http_version;
0
 
0
@@ -136,10 +132,7 @@ VALUE env_field(struct ebb_env_item *item)
0
     return f;
0
   }
0
   switch(item->type) {
0
- case MONGREL_ACCEPT: return global_http_accept;
0
- case MONGREL_CONNECTION: return global_http_connection;
0
     case MONGREL_CONTENT_LENGTH: return global_http_content_length;
0
- case MONGREL_CONTENT_TYPE: return global_http_content_type;
0
     case MONGREL_FRAGMENT: return global_fragment;
0
     case MONGREL_HTTP_VERSION: return global_http_version;
0
     case MONGREL_QUERY_STRING: return global_query_string;
0
@@ -256,10 +249,7 @@ void Init_ebb_ext()
0
   DEF_GLOBAL(request_path, "REQUEST_PATH");
0
   DEF_GLOBAL(request_uri, "REQUEST_URI");
0
   DEF_GLOBAL(server_port, "SERVER_PORT");
0
- DEF_GLOBAL(http_accept, "HTTP_ACCEPT");
0
- DEF_GLOBAL(http_connection, "HTTP_CONNECTION");
0
   DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
0
- DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
0
   DEF_GLOBAL(http_prefix, "HTTP_");
0
   DEF_GLOBAL(http_version, "HTTP_VERSION");
0
   
...
12
13
14
15
16
17
18
19
 
20
21
22
...
25
26
27
28
29
30
 
31
32
33
...
43
44
45
46
47
48
 
49
50
51
...
12
13
14
 
 
 
 
 
15
16
17
18
...
21
22
23
 
24
 
25
26
27
28
...
38
39
40
 
 
 
41
42
43
44
0
@@ -12,11 +12,7 @@
0
 #include <stddef.h>
0
 #endif
0
 
0
-
0
-enum { MONGREL_ACCEPT
0
- , MONGREL_CONNECTION
0
- , MONGREL_CONTENT_LENGTH
0
- , MONGREL_CONTENT_TYPE
0
+enum { MONGREL_CONTENT_LENGTH
0
      , MONGREL_FRAGMENT
0
      , MONGREL_HTTP_VERSION
0
      , MONGREL_QUERY_STRING
0
@@ -25,9 +21,8 @@ enum { MONGREL_ACCEPT
0
      , MONGREL_REQUEST_URI
0
      };
0
 
0
-typedef void (*element_cb)(void *data, const char *at, size_t length);
0
 typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
0
-typedef void (*new_element_cb)(void *data, int type, const char *at, size_t length);
0
+typedef void (*element_cb)(void *data, int type, const char *at, size_t length);
0
 
0
 typedef struct http_parser {
0
   int cs;
0
@@ -43,9 +38,7 @@ typedef struct http_parser {
0
   void *data;
0
 
0
   field_cb http_field;
0
-
0
- element_cb header_done;
0
- new_element_cb on_element;
0
+ element_cb on_element;
0
 } http_parser;
0
 
0
 void http_parser_init(http_parser *parser);
...
14
15
16
17
18
19
20
...
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
...
151
152
153
154
155
156
157
158
159
 
 
160
161
 
162
163
164
...
167
168
169
 
 
 
 
 
 
 
 
 
 
 
 
 
170
171
172
...
218
219
220
 
 
 
 
221
222
223
...
14
15
16
 
17
18
19
...
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
...
122
123
124
 
 
 
 
 
 
125
126
127
 
128
129
130
131
...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
198
199
200
201
202
203
204
205
206
207
0
@@ -14,7 +14,6 @@
0
 #define LEN(AT, FPC) (FPC - buffer - parser->AT)
0
 #define MARK(M,FPC) (parser->M = (FPC) - buffer)
0
 #define PTR_TO(F) (buffer + parser->F)
0
-
0
 /** machine **/
0
 %%{
0
   machine http_parser;
0
@@ -38,73 +37,45 @@
0
     }
0
   }
0
   
0
- action http_accept {
0
- if(LEN(mark, fpc) > 1024) { parser->overflow_error = TRUE; fbreak; }
0
- parser->on_element(parser->data, MONGREL_ACCEPT, PTR_TO(mark), LEN(mark, fpc));
0
- }
0
- action http_connection {
0
- if(LEN(mark, fpc) > 1024) { parser->overflow_error = TRUE; fbreak; }
0
- parser->on_element(parser->data, MONGREL_CONNECTION, PTR_TO(mark), LEN(mark, fpc));
0
- }
0
   action http_content_length {
0
- if(LEN(mark, fpc) > 20) { parser->overflow_error = TRUE; fbreak; }
0
+ if(!apply_element(parser, MONGREL_CONTENT_LENGTH, PTR_TO(mark), fpc, 20))
0
+ fbreak;
0
     set_content_length(parser, PTR_TO(mark), LEN(mark, fpc));
0
- parser->on_element(parser->data, MONGREL_CONTENT_LENGTH, PTR_TO(mark), LEN(mark, fpc));
0
- }
0
-
0
- action http_content_type {
0
- if(LEN(mark, fpc) > 1024) { parser->overflow_error = TRUE; fbreak; }
0
- parser->on_element(parser->data, MONGREL_CONTENT_TYPE, PTR_TO(mark), LEN(mark, fpc));
0
   }
0
   
0
   action fragment {
0
- /* Don't know if this length is specified somewhere or not */
0
- if(LEN(mark, fpc) > 1024) { parser->overflow_error = TRUE; fbreak; }
0
- parser->on_element(parser->data, MONGREL_FRAGMENT, PTR_TO(mark), LEN(mark, fpc));
0
+ if(!apply_element(parser, MONGREL_FRAGMENT, PTR_TO(mark), fpc, 10*1024))
0
+ fbreak;
0
   }
0
   
0
   action http_version {
0
- parser->on_element(parser->data, MONGREL_HTTP_VERSION, PTR_TO(mark), LEN(mark, fpc));
0
+ if(!apply_element(parser, MONGREL_HTTP_VERSION, PTR_TO(mark), fpc, 10))
0
+ fbreak;
0
   }
0
   
0
   action request_path {
0
- if(LEN(mark, fpc) > 1024) {
0
- parser->overflow_error = TRUE;
0
+ if(!apply_element(parser, MONGREL_REQUEST_PATH, PTR_TO(mark), fpc, 1024))
0
       fbreak;
0
- }
0
- parser->on_element(parser->data, MONGREL_REQUEST_PATH, PTR_TO(mark), LEN(mark,fpc));
0
   }
0
   
0
- action request_method {
0
- parser->on_element(parser->data, MONGREL_REQUEST_METHOD, PTR_TO(mark), LEN(mark, fpc));
0
+ action request_method {
0
+ if(!apply_element(parser, MONGREL_REQUEST_METHOD, PTR_TO(mark), fpc, 1024))
0
+ fbreak;
0
   }
0
   
0
   action request_uri {
0
- if(LEN(mark, fpc) > 12 * 1024) {
0
- parser->overflow_error = TRUE;
0
+ if(!apply_element(parser, MONGREL_REQUEST_URI, PTR_TO(mark), fpc, 12*1024))
0
       fbreak;
0
- }
0
- parser->on_element(parser->data, MONGREL_REQUEST_URI, PTR_TO(mark), LEN(mark, fpc));
0
   }
0
   
0
   action start_query {MARK(query_start, fpc); }
0
- action query_string {
0
- if(LEN(query_start, fpc) > 10 * 1024) {
0
- parser->overflow_error = TRUE;
0
+ action query_string {
0
+ if(!apply_element(parser, MONGREL_QUERY_STRING, PTR_TO(query_start), fpc, 10*1024))
0
       fbreak;
0
- }
0
- parser->on_element(parser->data, MONGREL_QUERY_STRING, PTR_TO(query_start), LEN(query_start, fpc));
0
   }
0
   
0
-
0
   action done {
0
- if(parser->nread > 1024 * (80 + 32)) {
0
- parser->overflow_error = TRUE;
0
- fbreak;
0
- }
0
- parser->body_start = fpc - buffer + 1;
0
- if(parser->header_done != NULL)
0
- parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
0
+ parser->body_start = fpc - buffer + 1;
0
     fbreak;
0
   }
0
 
0
@@ -151,14 +122,10 @@
0
 
0
   field_value = any* >start_value %write_value;
0
   
0
- known_header = ( ("Accept:"i " "* (any* >mark %http_accept))
0
- | ("Connection:"i " "* (any* >mark %http_connection))
0
- | ("Content-Length:"i " "* (digit+ >mark %http_content_length))
0
- | ("Content-Type:"i " "* (any* >mark %http_content_type))
0
- ) :> CRLF;
0
- unknown_header = (field_name ":" " "* field_value :> CRLF) -- known_header;
0
+ content_length = "Content-Length:"i " "* (digit+ >mark %http_content_length) :> CRLF;
0
+ unknown_header = (field_name ":" " "* field_value :> CRLF) -- content_length;
0
   
0
- Request = Request_Line (known_header | unknown_header)* ( CRLF @done );
0
+ Request = Request_Line (content_length | unknown_header)* ( CRLF @done );
0
 
0
 main := Request;
0
 
0
@@ -167,6 +134,19 @@ main := Request;
0
 /** Data **/
0
 %% write data;
0
 
0
+/* returns TRUE if applied, FALSE if there was an error */
0
+static int apply_element(http_parser *parser, int type, const char *begin, const char *end, int max_length)
0
+{
0
+ int len = (int)(end-begin);
0
+ if(len > max_length) {
0
+ parser->overflow_error = TRUE;
0
+ return FALSE;
0
+ }
0
+ if(parser->on_element)
0
+ parser->on_element(parser->data, type, begin, len);
0
+ return TRUE;
0
+}
0
+
0
 static void set_content_length(http_parser *parser, const char *at, int length)
0
 {
0
   /* atoi_length - why isn't this in the statndard library? i hate c */
0
@@ -218,6 +198,10 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len,
0
   assert(parser->field_len <= len && "field has length longer than whole buffer");
0
   assert(parser->field_start < len && "field starts after buffer end");
0
   
0
+ if(parser->nread > 1024 * (80 + 32))
0
+ parser->overflow_error = TRUE;
0
+
0
+
0
   /* Ragel 6 does not use write eof; no need for this
0
   if(parser->body_start) {
0
     // final \r\n combo encountered so stop right here
...
5
6
7
8
9
 
 
10
11
12
13
14
15
16
17
18
...
5
6
7
 
 
8
9
10
11
12
13
14
 
15
16
17
0
@@ -5,14 +5,13 @@ TARGETS = parser_test
0
 
0
 all: $(TARGETS)
0
 
0
-parser_test: parser_test.c
0
-  $(CC) $(CFLAGS) -o $@ $^ -I../src ../src/parser.o
0
+parser_test: parser_test.c
0
+  $(CC) $(CFLAGS) -o $@ $^ `pkg-config --libs --cflags glib-2.0` -I../src ../src/parser.o
0
 
0
 check: parser_test
0
   -./parser_test
0
 
0
 clean:
0
-  rm -f parser_test
0
   rm -f $(TARGETS)
0
 
0
 .PHONY: all clean check
...
35
36
37
38
 
39
40
41
42
 
43
44
45
...
48
49
50
51
 
52
53
54
...
62
63
64
65
 
66
67
68
...
91
92
93
94
 
95
96
97
...
35
36
37
 
38
39
40
41
 
42
43
44
45
...
48
49
50
 
51
52
53
54
...
62
63
64
 
65
66
67
68
...
91
92
93
 
94
95
96
97
0
@@ -35,11 +35,11 @@ end
0
 class HttpParserTest < ServerTest
0
   
0
   def test_parse_simple
0
- env = send_request("GET / HTTP/1.1\r\n\r\n")
0
+ env = send_request("GET / HTTP/1.0\r\n\r\n")
0
     
0
     assert_equal 'HTTP/1.1', env['SERVER_PROTOCOL']
0
     assert_equal '/', env['REQUEST_PATH']
0
- assert_equal 'HTTP/1.1', env['HTTP_VERSION']
0
+ assert_equal 'HTTP/1.0', env['HTTP_VERSION']
0
     assert_equal '/', env['REQUEST_URI']
0
     assert_equal 'GET', env['REQUEST_METHOD']
0
     assert_nil env['FRAGMENT']
0
@@ -48,7 +48,7 @@ class HttpParserTest < ServerTest
0
   end
0
   
0
   def test_parse_dumbfuck_headers
0
- should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
0
+ should_be_good = "GET / HTTP/1.0\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
0
     env = send_request(should_be_good)
0
     assert_equal "++++++++++", env["HTTP_AAAAAAAAAAAAA"]
0
     assert_equal "", env['rack.input']
0
@@ -62,7 +62,7 @@ class HttpParserTest < ServerTest
0
   end
0
 
0
   def test_fragment_in_uri
0
- env = send_request("GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n")
0
+ env = send_request("GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.0\r\n\r\n")
0
     assert_equal '/forums/1/topics/2375?page=1', env['REQUEST_URI']
0
     assert_equal 'posts-17408', env['FRAGMENT']
0
     assert_equal "", env['rack.input']
0
@@ -91,7 +91,7 @@ class HttpParserTest < ServerTest
0
     # then that large mangled field values are caught
0
     10.times do |c|
0
       req = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
0
- #assert drops_request?(req), "large mangled field values are caught"
0
+ assert drops_request?(req), "large mangled field values are caught"
0
       ### XXX this is broken! fix me. this test should drop the request.
0
     end
0
     
...
3
4
5
6
7
 
8
9
10
...
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
...
3
4
5
 
 
6
7
8
9
...
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
223
224
225
226
227
228
0
@@ -3,8 +3,7 @@
0
 #include <string.h>
0
 #include "parser.h"
0
 #include <assert.h>
0
-#define TRUE (1)
0
-#define FALSE (0)
0
+#include <glib.h>
0
 #define assertFalse(x) assert(!(x)); printf(".")
0
 #define assertTrue(x) assert(x); printf(".")
0
 #define assertEquals(x, y) assert(x == y); printf(".")
0
@@ -167,52 +166,63 @@ char *rand_data(int min, int max, int readable)
0
 
0
 void horrible_queries_test(void) {
0
   int i;
0
- char req[1000 * 1024];
0
+ GString *req = g_string_new("");
0
   
0
   for(i = 0; i < 10; i++) {
0
- sprintf(req, "GET /%s HTTP/1.1\r\nX-%s: Test\r\n\r\n"
0
- , rand_data(10, 120, TRUE)
0
- , rand_data(1024, 1024+i*1024, TRUE)
0
- );
0
+ g_string_append_printf(req, "GET /%s HTTP/1.1\r\nX-%s: Test\r\n\r\n"
0
+ , rand_data(10, 120, TRUE)
0
+ , rand_data(1024, 1024+i*1024, TRUE)
0
+ );
0
     http_parser_init(&parser);
0
     parser.on_element = donothing_element_cb;
0
     parser.http_field = 0;
0
- http_parser_execute(&parser, req, strlen(req), 0);
0
+ http_parser_execute(&parser, req->str, req->len, 0);
0
     assertTrue(http_parser_has_error(&parser));
0
   }
0
+ req->len = 0;
0
   
0
   /* then that large mangled field values are caught */
0
   for(i = 0; i < 10; i++) {
0
- sprintf(req, "GET /%s HTTP/1.1\r\nX-Test: %s\r\n\r\n"
0
- , rand_data(10,120, TRUE)
0
- , rand_data(1024, 1024+(i*1024), FALSE)
0
- );
0
+ g_string_append_printf(req, "GET /%s HTTP/1.1\r\nX-Test: %s\r\n\r\n"
0
+ , rand_data(10,120, TRUE)
0
+ , rand_data(1024, 1024+(i*1024), FALSE)
0
+ );
0
     http_parser_init(&parser);
0
     parser.on_element = donothing_element_cb;
0
     parser.http_field = 0;
0
- http_parser_execute(&parser, req, strlen(req), 0);
0
+ http_parser_execute(&parser, req->str, req->len, 0);
0
     assertTrue(http_parser_has_error(&parser));
0
   }
0
+ req->len = 0;
0
   
0
- /* then large headers are rejected too */
0
- // # then large headers are rejected too
0
- // req = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
0
- // req << "X-Test: test\r\n" * (80 * 1024)
0
- // assert drops_request?(req), "large headers are rejected"
0
+ /* then large headers are rejected too */
0
+ g_string_append_printf(req, "GET /%s HTTP/1.1\r\n", rand_data(10,120, TRUE));
0
+ for(i = 0; i < 80 * 1024; i++) {
0
+ g_string_append(req, "X-Test: test\r\n");
0
+ }
0
+ http_parser_init(&parser);
0
+ parser.on_element = donothing_element_cb;
0
+ parser.http_field = 0;
0
+ http_parser_execute(&parser, req->str, req->len, 0);
0
+ //assertTrue(http_parser_is_finished(&parser));
0
+ assertTrue(http_parser_has_error(&parser));
0
+ req->len = 0;
0
   
0
   
0
   /* finally just that random garbage gets blocked all the time */
0
   for(i = 0; i < 10; i++) {
0
- sprintf(req, "GET %s %s\r\n\r\n"
0
- , rand_data(1024, 1024+(i*1024), FALSE)
0
- , rand_data(1024, 1024+(i*1024), FALSE)
0
- );
0
+ g_string_append_printf(req, "GET %s %s\r\n\r\n"
0
+ , rand_data(1024, 1024+(i*1024), FALSE)
0
+ , rand_data(1024, 1024+(i*1024), FALSE)
0
+ );
0
     http_parser_init(&parser);
0
     parser.on_element = donothing_element_cb;
0
     parser.http_field = 0;
0
- http_parser_execute(&parser, req, strlen(req), 0);
0
+ http_parser_execute(&parser, req->str, req->len, 0);
0
     assertTrue(http_parser_has_error(&parser));
0
   }
0
+
0
+ g_string_free(req, TRUE);
0
 }
0
 
0
 int main(int argc, char *argv[])

Comments

    No one has commented yet.