2 * Copyright (c) 2005 Zed A. Shaw
3 * You can redistribute it and/or modify it under the same terms as Ruby.
9 #include "http11_parser.h"
12 static VALUE mMongrel
;
13 static VALUE cHttpParser
;
14 static VALUE eHttpParserError
;
16 #define id_handler_map rb_intern("@handler_map")
17 #define id_http_body rb_intern("@http_body")
19 static VALUE global_http_prefix
;
20 static VALUE global_request_method
;
21 static VALUE global_request_uri
;
22 static VALUE global_fragment
;
23 static VALUE global_query_string
;
24 static VALUE global_http_version
;
25 static VALUE global_content_length
;
26 static VALUE global_http_content_length
;
27 static VALUE global_request_path
;
28 static VALUE global_content_type
;
29 static VALUE global_http_content_type
;
30 static VALUE global_gateway_interface
;
31 static VALUE global_gateway_interface_value
;
32 static VALUE global_server_name
;
33 static VALUE global_server_port
;
34 static VALUE global_server_protocol
;
35 static VALUE global_server_protocol_value
;
36 static VALUE global_http_host
;
37 static VALUE global_mongrel_version
;
38 static VALUE global_server_software
;
39 static VALUE global_port_80
;
41 #define TRIE_INCREASE 30
43 /** Defines common length and error messages for input length validation. */
44 #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length."
46 /** Validates the max length of given input and throws an HttpParserError exception if over. */
47 #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
49 /** Defines global strings in the init method. */
50 #define DEF_GLOBAL(N, val) global_##N = ID2SYM(rb_intern(val));
53 /* Defines the maximum allowed lengths for various input elements.*/
54 DEF_MAX_LENGTH(FIELD_NAME
, 256);
55 DEF_MAX_LENGTH(FIELD_VALUE
, 80 * 1024);
56 DEF_MAX_LENGTH(REQUEST_URI
, 1024 * 12);
57 DEF_MAX_LENGTH(FRAGMENT
, 1024); /* Don't know if this length is specified somewhere or not */
58 DEF_MAX_LENGTH(REQUEST_PATH
, 1024);
59 DEF_MAX_LENGTH(QUERY_STRING
, (1024 * 10));
60 DEF_MAX_LENGTH(HEADER
, (1024 * (80 + 32)));
63 void http_field(void *data
, const char *field
, size_t flen
, const char *value
, size_t vlen
)
66 VALUE req
= (VALUE
)data
;
70 VALIDATE_MAX_LENGTH(flen
, FIELD_NAME
);
71 VALIDATE_MAX_LENGTH(vlen
, FIELD_VALUE
);
73 v
= rb_str_new(value
, vlen
);
74 f
= rb_str_dup(global_http_prefix
);
75 f
= rb_str_buf_cat(f
, field
, flen
);
77 for(ch
= rb_str_get_char_ptr(f
), end
= ch
+ rb_str_get_char_len(f
); ch
< end
; ch
++) {
85 rb_str_flush_char_ptr(f
, ch
);
87 rb_hash_aset(req
, f
, v
);
90 void request_method(void *data
, const char *at
, size_t length
)
92 VALUE req
= (VALUE
)data
;
95 val
= rb_str_new(at
, length
);
96 rb_hash_aset(req
, global_request_method
, val
);
99 void request_uri(void *data
, const char *at
, size_t length
)
101 VALUE req
= (VALUE
)data
;
104 VALIDATE_MAX_LENGTH(length
, REQUEST_URI
);
106 val
= rb_str_new(at
, length
);
107 rb_hash_aset(req
, global_request_uri
, val
);
110 void fragment(void *data
, const char *at
, size_t length
)
112 VALUE req
= (VALUE
)data
;
115 VALIDATE_MAX_LENGTH(length
, FRAGMENT
);
117 val
= rb_str_new(at
, length
);
118 rb_hash_aset(req
, global_fragment
, val
);
121 void request_path(void *data
, const char *at
, size_t length
)
123 VALUE req
= (VALUE
)data
;
126 VALIDATE_MAX_LENGTH(length
, REQUEST_PATH
);
128 val
= rb_str_new(at
, length
);
129 rb_hash_aset(req
, global_request_path
, val
);
132 void query_string(void *data
, const char *at
, size_t length
)
134 VALUE req
= (VALUE
)data
;
137 VALIDATE_MAX_LENGTH(length
, QUERY_STRING
);
139 val
= rb_str_new(at
, length
);
140 rb_hash_aset(req
, global_query_string
, val
);
143 void http_version(void *data
, const char *at
, size_t length
)
145 VALUE req
= (VALUE
)data
;
146 VALUE val
= rb_str_new(at
, length
);
147 rb_hash_aset(req
, global_http_version
, val
);
150 /** Finalizes the request header to have a bunch of stuff that's
153 void header_done(void *data
, const char *at
, size_t length
)
155 VALUE req
= (VALUE
)data
;
159 char *colon
= NULL
, *temp_ptr
= NULL
;
161 clen
= rb_hash_aref(req
, global_http_content_length
);
163 rb_hash_aset(req
, global_content_length
, clen
);
166 ctype
= rb_hash_aref(req
, global_http_content_type
);
168 rb_hash_aset(req
, global_content_type
, ctype
);
171 rb_hash_aset(req
, global_gateway_interface
, global_gateway_interface_value
);
172 if((temp
= rb_hash_aref(req
, global_http_host
)) != Qnil
) {
173 temp_ptr
= rb_str_get_char_ptr(temp
);
174 /* ruby better close strings off with a '\0' dammit */
175 colon
= strchr(temp_ptr
, ':');
177 rb_hash_aset(req
, global_server_name
, rb_str_substr(temp
, 0, colon
- temp_ptr
));
178 rb_hash_aset(req
, global_server_port
,
179 rb_str_substr(temp
, colon
- temp_ptr
+1,
180 rb_str_get_char_len(temp
)));
182 rb_hash_aset(req
, global_server_name
, temp
);
183 rb_hash_aset(req
, global_server_port
, global_port_80
);
188 /* grab the initial body and stuff it into an ivar */
189 rb_ivar_set(req
, id_http_body
, rb_str_new(at
, length
));
190 rb_hash_aset(req
, global_server_protocol
, global_server_protocol_value
);
191 rb_hash_aset(req
, global_server_software
, global_mongrel_version
);
195 void HttpParser_free(void *data
) {
204 VALUE
HttpParser_alloc(VALUE klass
)
207 http_parser
*hp
= malloc(sizeof(http_parser
)); // ALLOC_N(http_parser, 1);
209 hp
->http_field
= http_field
;
210 hp
->request_method
= request_method
;
211 hp
->request_uri
= request_uri
;
212 hp
->fragment
= fragment
;
213 hp
->request_path
= request_path
;
214 hp
->query_string
= query_string
;
215 hp
->http_version
= http_version
;
216 hp
->header_done
= header_done
;
217 http_parser_init(hp
);
219 obj
= Data_Wrap_Struct(klass
, NULL
, HttpParser_free
, hp
);
227 * parser.new -> parser
229 * Creates a new parser.
231 VALUE
HttpParser_init(VALUE self
)
233 http_parser
*http
= NULL
;
234 DATA_GET(self
, http_parser
, http
);
235 http_parser_init(http
);
243 * parser.reset -> nil
245 * Resets the parser to it's initial state so that you can reuse it
246 * rather than making new ones.
248 VALUE
HttpParser_reset(VALUE self
)
250 http_parser
*http
= NULL
;
251 DATA_GET(self
, http_parser
, http
);
252 http_parser_init(http
);
260 * parser.finish -> true/false
262 * Finishes a parser early which could put in a "good" or bad state.
263 * You should call reset after finish it or bad things will happen.
265 VALUE
HttpParser_finish(VALUE self
)
267 http_parser
*http
= NULL
;
268 DATA_GET(self
, http_parser
, http
);
269 http_parser_finish(http
);
271 return http_parser_is_finished(http
) ? Qtrue
: Qfalse
;
277 * parser.execute(req_hash, data, start) -> Integer
279 * Takes a Hash and a String of data, parses the String of data filling in the Hash
280 * returning an Integer to indicate how much of the data has been read. No matter
281 * what the return value, you should call HttpParser#finished? and HttpParser#error?
282 * to figure out if it's done parsing or there was an error.
284 * This function now throws an exception when there is a parsing error. This makes
285 * the logic for working with the parser much easier. You can still test for an
286 * error, but now you need to wrap the parser with an exception handling block.
288 * The third argument allows for parsing a partial request and then continuing
289 * the parsing from that position. It needs all of the original data as well
290 * so you have to append to the data buffer as you read.
292 VALUE
HttpParser_execute(VALUE self
, VALUE req_hash
, VALUE data
, VALUE start
)
294 http_parser
*http
= NULL
;
299 DATA_GET(self
, http_parser
, http
);
301 from
= FIX2INT(start
);
302 dptr
= rb_str_get_char_ptr(data
);
303 dlen
= rb_str_get_char_len(data
);
306 rb_raise(eHttpParserError
, "Requested start is after data buffer end.");
308 http
->data
= (void *)req_hash
;
309 http_parser_execute(http
, dptr
, dlen
, from
);
311 VALIDATE_MAX_LENGTH(http_parser_nread(http
), HEADER
);
313 if(http_parser_has_error(http
)) {
314 rb_raise(eHttpParserError
, "Invalid HTTP format, parsing fails.");
316 return INT2FIX(http_parser_nread(http
));
320 rb_str_flush_char_ptr(data
, dptr
);
327 * parser.error? -> true/false
329 * Tells you whether the parser is in an error state.
331 VALUE
HttpParser_has_error(VALUE self
)
333 http_parser
*http
= NULL
;
334 DATA_GET(self
, http_parser
, http
);
336 return http_parser_has_error(http
) ? Qtrue
: Qfalse
;
342 * parser.finished? -> true/false
344 * Tells you whether the parser is finished or not and in a good state.
346 VALUE
HttpParser_is_finished(VALUE self
)
348 http_parser
*http
= NULL
;
349 DATA_GET(self
, http_parser
, http
);
351 return http_parser_is_finished(http
) ? Qtrue
: Qfalse
;
357 * parser.nread -> Integer
359 * Returns the amount of data processed so far during this processing cycle. It is
360 * set to 0 on initialize or reset calls and is incremented each time execute is called.
362 VALUE
HttpParser_nread(VALUE self
)
364 http_parser
*http
= NULL
;
365 DATA_GET(self
, http_parser
, http
);
367 return INT2FIX(http
->nread
);
373 mMongrel
= rb_define_module("Mongrel");
375 DEF_GLOBAL(http_prefix
, "HTTP_");
376 DEF_GLOBAL(request_method
, "REQUEST_METHOD");
377 DEF_GLOBAL(request_uri
, "REQUEST_URI");
378 DEF_GLOBAL(fragment
, "FRAGMENT");
379 DEF_GLOBAL(query_string
, "QUERY_STRING");
380 DEF_GLOBAL(http_version
, "HTTP_VERSION");
381 DEF_GLOBAL(request_path
, "REQUEST_PATH");
382 DEF_GLOBAL(content_length
, "CONTENT_LENGTH");
383 DEF_GLOBAL(http_content_length
, "HTTP_CONTENT_LENGTH");
384 DEF_GLOBAL(content_type
, "CONTENT_TYPE");
385 DEF_GLOBAL(http_content_type
, "HTTP_CONTENT_TYPE");
386 DEF_GLOBAL(gateway_interface
, "GATEWAY_INTERFACE");
387 DEF_GLOBAL(gateway_interface_value
, "CGI/1.2");
388 DEF_GLOBAL(server_name
, "SERVER_NAME");
389 DEF_GLOBAL(server_port
, "SERVER_PORT");
390 DEF_GLOBAL(server_protocol
, "SERVER_PROTOCOL");
391 DEF_GLOBAL(server_protocol_value
, "HTTP/1.1");
392 DEF_GLOBAL(http_host
, "HTTP_HOST");
393 DEF_GLOBAL(mongrel_version
, "Mongrel 1.1.1"); /* XXX Why is this defined here? */
394 DEF_GLOBAL(server_software
, "SERVER_SOFTWARE");
395 DEF_GLOBAL(port_80
, "80");
397 eHttpParserError
= rb_define_class_under(mMongrel
, "HttpParserError", rb_eIOError
);
399 cHttpParser
= rb_define_class_under(mMongrel
, "HttpParser", rb_cObject
);
400 rb_define_alloc_func(cHttpParser
, HttpParser_alloc
);
401 rb_define_method(cHttpParser
, "initialize", HttpParser_init
,0);
402 rb_define_method(cHttpParser
, "reset", HttpParser_reset
,0);
403 rb_define_method(cHttpParser
, "finish", HttpParser_finish
,0);
404 rb_define_method(cHttpParser
, "execute", HttpParser_execute
,3);
405 rb_define_method(cHttpParser
, "error?", HttpParser_has_error
,0);
406 rb_define_method(cHttpParser
, "finished?", HttpParser_is_finished
,0);
407 rb_define_method(cHttpParser
, "nread", HttpParser_nread
,0);