2 * Copyright (c) 2009 Eric Wong (all bugs are Eric's fault)
3 * Copyright (c) 2005 Zed A. Shaw
4 * You can redistribute it and/or modify it under the same terms as Ruby.
10 #include <sys/types.h>
11 #include "common_field_optimization.h"
12 #include "global_variables.h"
27 static void http_field(VALUE req, const char *field,
28 size_t flen, const char *value, size_t vlen);
29 static void header_done(VALUE req, const char *at, size_t length);
31 static int http_parser_has_error(struct http_parser *hp);
32 static int http_parser_is_finished(struct http_parser *hp);
35 #define LEN(AT, FPC) (FPC - buffer - hp->AT)
36 #define MARK(M,FPC) (hp->M = (FPC) - buffer)
37 #define PTR_TO(F) (buffer + hp->F)
38 #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
45 action mark {MARK(mark, fpc); }
47 action start_field { MARK(start.field, fpc); }
48 action snake_upcase_field { snake_upcase_char((char *)fpc); }
49 action downcase_char { downcase_char((char *)fpc); }
50 action write_field { hp->field_len = LEN(start.field, fpc); }
51 action start_value { MARK(mark, fpc); }
53 http_field(req, PTR_TO(start.field), hp->field_len,
54 PTR_TO(mark), LEN(mark, fpc));
56 action request_method {
57 rb_hash_aset(req, g_request_method, STR_NEW(mark, fpc));
60 rb_hash_aset(req, g_rack_url_scheme, STR_NEW(mark, fpc));
63 rb_hash_aset(req, g_http_host, STR_NEW(mark, fpc));
66 size_t len = LEN(mark, fpc);
67 VALIDATE_MAX_LENGTH(len, REQUEST_URI);
68 rb_hash_aset(req, g_request_uri, STR_NEW(mark, fpc));
70 * "OPTIONS * HTTP/1.1\r\n" is a valid request, but we can't have '*'
71 * in REQUEST_PATH or PATH_INFO or else Rack::Lint will complain
73 if (len == 1 && *PTR_TO(mark) == '*') {
74 VALUE val = rb_str_new(NULL, 0);
75 rb_hash_aset(req, g_request_path, val);
76 rb_hash_aset(req, g_path_info, val);
80 VALIDATE_MAX_LENGTH(LEN(mark, fpc), FRAGMENT);
81 rb_hash_aset(req, g_fragment, STR_NEW(mark, fpc));
83 action start_query {MARK(start.query, fpc); }
85 VALIDATE_MAX_LENGTH(LEN(start.query, fpc), QUERY_STRING);
86 rb_hash_aset(req, g_query_string, STR_NEW(start.query, fpc));
89 rb_hash_aset(req, g_http_version, STR_NEW(mark, fpc));
93 size_t len = LEN(mark, fpc);
95 VALIDATE_MAX_LENGTH(len, REQUEST_PATH);
96 val = STR_NEW(mark, fpc);
98 rb_hash_aset(req, g_request_path, val);
99 /* rack says PATH_INFO must start with "/" or be empty */
100 if (!(len == 1 && *PTR_TO(mark) == '*'))
101 rb_hash_aset(req, g_path_info, val);
104 hp->start.body = fpc - buffer + 1;
105 header_done(req, fpc + 1, pe - fpc - 1);
109 include unicorn_http_common "unicorn_http_common.rl";
115 static void http_parser_init(struct http_parser *hp)
118 memset(hp, 0, sizeof(struct http_parser));
124 static void http_parser_execute(struct http_parser *hp,
125 VALUE req, const char *buffer, size_t len)
129 size_t off = hp->start.offset;
131 assert(off <= len && "offset past end of buffer");
136 assert(pe - p == len - off && "pointers aren't same distance");
140 if (!http_parser_has_error(hp))
142 hp->start.offset = p - buffer;
144 assert(p <= pe && "buffer overflow after parsing execute");
145 assert(hp->start.offset <= len && "start.offset longer than length");
146 assert(hp->mark < len && "mark is after buffer end");
147 assert(hp->field_len <= len && "field has length longer than whole buffer");
150 static int http_parser_has_error(struct http_parser *hp)
152 return hp->cs == http_parser_error;
155 static int http_parser_is_finished(struct http_parser *hp)
157 return hp->cs == http_parser_first_final;
160 static struct http_parser *data_get(VALUE self)
162 struct http_parser *hp;
164 Data_Get_Struct(self, struct http_parser, hp);
169 static void http_field(VALUE req, const char *field,
170 size_t flen, const char *value, size_t vlen)
172 VALUE f = find_common_field(field, flen);
174 VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
177 VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
178 f = uncommon_field(field, flen);
179 } else if (f == g_http_host && rb_hash_aref(req, f) != Qnil) {
183 rb_hash_aset(req, f, rb_str_new(value, vlen));
186 static int is_https(VALUE str)
188 return RSTRING_LEN(str) == 5 && !memcmp("https", RSTRING_PTR(str), 5);
191 /** Finalizes the request header to have a bunch of stuff that's needed. */
192 static void header_done(VALUE req, const char *at, size_t length)
194 VALUE server_name = g_localhost;
195 VALUE server_port = g_port_80;
198 /* rack requires QUERY_STRING */
199 if (rb_hash_aref(req, g_query_string) == Qnil)
200 rb_hash_aset(req, g_query_string, rb_str_new(NULL, 0));
202 /* set rack.url_scheme to "https" or "http", no others are allowed by Rack */
203 if ((temp = rb_hash_aref(req, g_rack_url_scheme)) == Qnil) {
204 if ((temp = rb_hash_aref(req, g_http_x_forwarded_proto)) != Qnil &&
206 server_port = g_port_443;
209 rb_hash_aset(req, g_rack_url_scheme, temp);
210 } else if (is_https(temp)) {
211 server_port = g_port_443;
214 /* parse and set the SERVER_NAME and SERVER_PORT variables */
215 if ((temp = rb_hash_aref(req, g_http_host)) != Qnil) {
216 char *colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
218 long port_start = colon - RSTRING_PTR(temp) + 1;
220 server_name = rb_str_substr(temp, 0, colon - RSTRING_PTR(temp));
221 if ((RSTRING_LEN(temp) - port_start) > 0)
222 server_port = rb_str_substr(temp, port_start, RSTRING_LEN(temp));
227 rb_hash_aset(req, g_server_name, server_name);
228 rb_hash_aset(req, g_server_port, server_port);
229 rb_hash_aset(req, g_server_protocol, g_server_protocol_value);
231 /* grab the initial body and stuff it into the hash */
232 temp = rb_hash_aref(req, g_request_method);
234 long len = RSTRING_LEN(temp);
235 char *ptr = RSTRING_PTR(temp);
237 if (memcmp(ptr, "HEAD", len) && memcmp(ptr, "GET", len))
238 rb_hash_aset(req, sym_http_body, rb_str_new(at, length));
242 static VALUE HttpParser_alloc(VALUE klass)
244 struct http_parser *hp;
245 return Data_Make_Struct(klass, struct http_parser, NULL, NULL, hp);
251 * parser.new -> parser
253 * Creates a new parser.
255 static VALUE HttpParser_init(VALUE self)
257 http_parser_init(data_get(self));
265 * parser.reset -> nil
267 * Resets the parser to it's initial state so that you can reuse it
268 * rather than making new ones.
270 static VALUE HttpParser_reset(VALUE self)
272 http_parser_init(data_get(self));
280 * parser.execute(req, data) -> true/false
282 * Takes a Hash and a String of data, parses the String of data filling
283 * in the Hash returning a boolean to indicate whether or not parsing
286 * This function now throws an exception when there is a parsing error.
287 * This makes the logic for working with the parser much easier. You
288 * will need to wrap the parser with an exception handling block.
291 static VALUE HttpParser_execute(VALUE self, VALUE req, VALUE data)
293 struct http_parser *hp = data_get(self);
294 char *dptr = RSTRING_PTR(data);
295 long dlen = RSTRING_LEN(data);
297 if (hp->start.offset < dlen) {
298 http_parser_execute(hp, req, dptr, dlen);
300 VALIDATE_MAX_LENGTH(hp->start.offset, HEADER);
302 if (!http_parser_has_error(hp))
303 return http_parser_is_finished(hp) ? Qtrue : Qfalse;
305 rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
307 rb_raise(eHttpParserError, "Requested start is after data buffer end.");
310 void Init_unicorn_http(void)
312 mUnicorn = rb_define_module("Unicorn");
314 DEF_GLOBAL(rack_url_scheme, "rack.url_scheme");
315 DEF_GLOBAL(request_method, "REQUEST_METHOD");
316 DEF_GLOBAL(request_uri, "REQUEST_URI");
317 DEF_GLOBAL(fragment, "FRAGMENT");
318 DEF_GLOBAL(query_string, "QUERY_STRING");
319 DEF_GLOBAL(http_version, "HTTP_VERSION");
320 DEF_GLOBAL(request_path, "REQUEST_PATH");
321 DEF_GLOBAL(path_info, "PATH_INFO");
322 DEF_GLOBAL(server_name, "SERVER_NAME");
323 DEF_GLOBAL(server_port, "SERVER_PORT");
324 DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
325 DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
326 DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
327 DEF_GLOBAL(port_80, "80");
328 DEF_GLOBAL(port_443, "443");
329 DEF_GLOBAL(localhost, "localhost");
330 DEF_GLOBAL(http, "http");
332 eHttpParserError = rb_define_class_under(mUnicorn, "HttpParserError", rb_eIOError);
334 cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
335 rb_define_alloc_func(cHttpParser, HttpParser_alloc);
336 rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
337 rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
338 rb_define_method(cHttpParser, "execute", HttpParser_execute,2);
339 sym_http_body = ID2SYM(rb_intern("http_body"));
340 init_common_fields();
341 g_http_host = find_common_field("HOST", 4);
342 assert(g_http_host != Qnil);