http: find_common_field_value => find_common_field
[unicorn.git] / ext / unicorn_http / unicorn_http.rl
blob706c77cd1ba2be01509149f74377f4ddbae4243a
1 /**
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.
5  */
6 #include "ruby.h"
7 #include "ext_help.h"
8 #include <assert.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include "common_field_optimization.h"
12 #include "global_variables.h"
13 #include "c_util.h"
15 struct http_parser {
16   int cs;
17   union {
18     size_t body;
19     size_t field;
20     size_t query;
21     size_t offset;
22   } start;
23   size_t mark;
24   size_t field_len;
27 static void http_field(VALUE req, const char *field,
28                        size_t flen, const char *value, size_t vlen);
29 static void request_method(VALUE req, const char *at, size_t length);
30 static void scheme(VALUE req, const char *at, size_t length);
31 static void host(VALUE req, const char *at, size_t length);
32 static void request_uri(VALUE req, const char *at, size_t length);
33 static void fragment(VALUE req, const char *at, size_t length);
34 static void request_path(VALUE req, const char *at, size_t length);
35 static void query_string(VALUE req, const char *at, size_t length);
36 static void http_version(VALUE req, const char *at, size_t length);
37 static void header_done(VALUE req, const char *at, size_t length);
39 static int http_parser_has_error(struct http_parser *parser);
40 static int http_parser_is_finished(struct http_parser *parser);
43 #define LEN(AT, FPC) (FPC - buffer - parser->AT)
44 #define MARK(M,FPC) (parser->M = (FPC) - buffer)
45 #define PTR_TO(F) (buffer + parser->F)
47 /** Machine **/
49 %%{
50   machine http_parser;
52   action mark {MARK(mark, fpc); }
54   action start_field { MARK(start.field, fpc); }
55   action snake_upcase_field { snake_upcase_char((char *)fpc); }
56   action downcase_char { downcase_char((char *)fpc); }
57   action write_field { parser->field_len = LEN(start.field, fpc); }
58   action start_value { MARK(mark, fpc); }
59   action write_value {
60     http_field(req, PTR_TO(start.field), parser->field_len,
61                PTR_TO(mark), LEN(mark, fpc));
62   }
63   action request_method { request_method(req, PTR_TO(mark), LEN(mark, fpc)); }
64   action scheme { scheme(req, PTR_TO(mark), LEN(mark, fpc)); }
65   action host { host(req, PTR_TO(mark), LEN(mark, fpc)); }
66   action request_uri { request_uri(req, PTR_TO(mark), LEN(mark, fpc)); }
67   action fragment { fragment(req, PTR_TO(mark), LEN(mark, fpc)); }
69   action start_query {MARK(start.query, fpc); }
70   action query_string {
71     query_string(req, PTR_TO(start.query), LEN(start.query, fpc));
72   }
74   action http_version { http_version(req, PTR_TO(mark), LEN(mark, fpc)); }
75   action request_path { request_path(req, PTR_TO(mark), LEN(mark,fpc)); }
77   action done {
78     parser->start.body = fpc - buffer + 1;
79     header_done(req, fpc + 1, pe - fpc - 1);
80     fbreak;
81   }
83   include unicorn_http_common "unicorn_http_common.rl";
84 }%%
86 /** Data **/
87 %% write data;
89 static void http_parser_init(struct http_parser *parser)
91   int cs = 0;
92   memset(parser, 0, sizeof(*parser));
93   %% write init;
94   parser->cs = cs;
97 /** exec **/
98 static void http_parser_execute(struct http_parser *parser,
99   VALUE req, const char *buffer, size_t len)
101   const char *p, *pe;
102   int cs = parser->cs;
103   size_t off = parser->start.offset;
105   assert(off <= len && "offset past end of buffer");
107   p = buffer+off;
108   pe = buffer+len;
110   assert(pe - p == len - off && "pointers aren't same distance");
112   %% write exec;
114   if (!http_parser_has_error(parser))
115     parser->cs = cs;
116   parser->start.offset = p - buffer;
118   assert(p <= pe && "buffer overflow after parsing execute");
119   assert(parser->start.offset <= len && "start.offset longer than length");
120   assert(parser->mark < len && "mark is after buffer end");
121   assert(parser->field_len <= len && "field has length longer than whole buffer");
124 static int http_parser_has_error(struct http_parser *parser)
126   return parser->cs == http_parser_error;
129 static int http_parser_is_finished(struct http_parser *parser)
131   return parser->cs == http_parser_first_final;
134 static struct http_parser *data_get(VALUE self)
136   struct http_parser *http;
138   Data_Get_Struct(self, struct http_parser, http);
139   assert(http);
140   return http;
143 static void http_field(VALUE req, const char *field,
144                        size_t flen, const char *value, size_t vlen)
146   VALUE f = Qnil;
148   VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
149   VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
151   f = find_common_field(field, flen);
153   if (f == Qnil) {
154     /*
155      * We got a strange header that we don't have a memoized value for.
156      * Fallback to creating a new string to use as a hash key.
157      *
158      * using rb_str_new(NULL, len) here is faster than rb_str_buf_new(len)
159      * in my testing, because: there's no minimum allocation length (and
160      * no check for it, either), RSTRING_LEN(f) does not need to be
161      * written twice, and and RSTRING_PTR(f) will already be
162      * null-terminated for us.
163      */
164     f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
165     memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
166     memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
167     assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0'); /* paranoia */
168     /* fprintf(stderr, "UNKNOWN HEADER <%s>\n", RSTRING_PTR(f)); */
169   } else if (f == g_http_host && rb_hash_aref(req, f) != Qnil) {
170     return;
171   }
173   rb_hash_aset(req, f, rb_str_new(value, vlen));
176 static void request_method(VALUE req, const char *at, size_t length)
178   rb_hash_aset(req, g_request_method, rb_str_new(at, length));
181 static void scheme(VALUE req, const char *at, size_t length)
183   rb_hash_aset(req, g_rack_url_scheme, rb_str_new(at, length));
186 static void host(VALUE req, const char *at, size_t length)
188   rb_hash_aset(req, g_http_host, rb_str_new(at, length));
191 static void request_uri(VALUE req, const char *at, size_t length)
193   VALIDATE_MAX_LENGTH(length, REQUEST_URI);
195   rb_hash_aset(req, g_request_uri, rb_str_new(at, length));
197   /* "OPTIONS * HTTP/1.1\r\n" is a valid request */
198   if (length == 1 && *at == '*') {
199     VALUE val = rb_str_new(NULL, 0);
200     rb_hash_aset(req, g_request_path, val);
201     rb_hash_aset(req, g_path_info, val);
202   }
205 static void fragment(VALUE req, const char *at, size_t length)
207   VALIDATE_MAX_LENGTH(length, FRAGMENT);
209   rb_hash_aset(req, g_fragment, rb_str_new(at, length));
212 static void request_path(VALUE req, const char *at, size_t length)
214   VALUE val = Qnil;
216   VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
218   val = rb_str_new(at, length);
219   rb_hash_aset(req, g_request_path, val);
221   /* rack says PATH_INFO must start with "/" or be empty */
222   if (!(length == 1 && *at == '*'))
223     rb_hash_aset(req, g_path_info, val);
226 static void query_string(VALUE req, const char *at, size_t length)
228   VALIDATE_MAX_LENGTH(length, QUERY_STRING);
230   rb_hash_aset(req, g_query_string, rb_str_new(at, length));
233 static void http_version(VALUE req, const char *at, size_t length)
235   rb_hash_aset(req, g_http_version, rb_str_new(at, length));
238 /** Finalizes the request header to have a bunch of stuff that's needed. */
239 static void header_done(VALUE req, const char *at, size_t length)
241   VALUE server_name = g_localhost;
242   VALUE server_port = g_port_80;
243   VALUE temp;
245   /* rack requires QUERY_STRING */
246   if (rb_hash_aref(req, g_query_string) == Qnil)
247     rb_hash_aset(req, g_query_string, rb_str_new(NULL, 0));
249   /* set rack.url_scheme to "https" or "http", no others are allowed by Rack */
250   if ((temp = rb_hash_aref(req, g_rack_url_scheme)) == Qnil) {
251     if ((temp = rb_hash_aref(req, g_http_x_forwarded_proto)) != Qnil &&
252         RSTRING_LEN(temp) == 5 &&
253         !memcmp("https", RSTRING_PTR(temp), 5))
254       server_port = g_port_443;
255     else
256       temp = g_http;
257     rb_hash_aset(req, g_rack_url_scheme, temp);
258   } else if (RSTRING_LEN(temp) == 5 && !memcmp("https", RSTRING_PTR(temp), 5)) {
259     server_port = g_port_443;
260   }
262   /* parse and set the SERVER_NAME and SERVER_PORT variables */
263   if ((temp = rb_hash_aref(req, g_http_host)) != Qnil) {
264     char *colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
265     if (colon) {
266       long port_start = colon - RSTRING_PTR(temp) + 1;
268       server_name = rb_str_substr(temp, 0, colon - RSTRING_PTR(temp));
269       if ((RSTRING_LEN(temp) - port_start) > 0)
270         server_port = rb_str_substr(temp, port_start, RSTRING_LEN(temp));
271     } else {
272       server_name = temp;
273     }
274   }
275   rb_hash_aset(req, g_server_name, server_name);
276   rb_hash_aset(req, g_server_port, server_port);
277   rb_hash_aset(req, g_server_protocol, g_server_protocol_value);
279   /* grab the initial body and stuff it into the hash */
280   temp = rb_hash_aref(req, g_request_method);
281   if (temp != Qnil) {
282     long len = RSTRING_LEN(temp);
283     char *ptr = RSTRING_PTR(temp);
285     if (memcmp(ptr, "HEAD", len) && memcmp(ptr, "GET", len))
286       rb_hash_aset(req, sym_http_body, rb_str_new(at, length));
287   }
290 static VALUE HttpParser_alloc(VALUE klass)
292   struct http_parser *http;
293   return Data_Make_Struct(klass, struct http_parser, NULL, NULL, http);
298  * call-seq:
299  *    parser.new -> parser
301  * Creates a new parser.
302  */
303 static VALUE HttpParser_init(VALUE self)
305   http_parser_init(data_get(self));
307   return self;
312  * call-seq:
313  *    parser.reset -> nil
315  * Resets the parser to it's initial state so that you can reuse it
316  * rather than making new ones.
317  */
318 static VALUE HttpParser_reset(VALUE self)
320   http_parser_init(data_get(self));
322   return Qnil;
327  * call-seq:
328  *    parser.execute(req, data) -> true/false
330  * Takes a Hash and a String of data, parses the String of data filling
331  * in the Hash returning a boolean to indicate whether or not parsing
332  * is finished.
334  * This function now throws an exception when there is a parsing error.
335  * This makes the logic for working with the parser much easier.  You
336  * will need to wrap the parser with an exception handling block.
337  */
339 static VALUE HttpParser_execute(VALUE self, VALUE req, VALUE data)
341   struct http_parser *http = data_get(self);
342   char *dptr = RSTRING_PTR(data);
343   long dlen = RSTRING_LEN(data);
345   if (http->start.offset < dlen) {
346     http_parser_execute(http, req, dptr, dlen);
348     VALIDATE_MAX_LENGTH(http->start.offset, HEADER);
350     if (!http_parser_has_error(http))
351       return http_parser_is_finished(http) ? Qtrue : Qfalse;
353     rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
354   }
355   rb_raise(eHttpParserError, "Requested start is after data buffer end.");
358 void Init_unicorn_http(void)
360   mUnicorn = rb_define_module("Unicorn");
362   DEF_GLOBAL(rack_url_scheme, "rack.url_scheme");
363   DEF_GLOBAL(request_method, "REQUEST_METHOD");
364   DEF_GLOBAL(request_uri, "REQUEST_URI");
365   DEF_GLOBAL(fragment, "FRAGMENT");
366   DEF_GLOBAL(query_string, "QUERY_STRING");
367   DEF_GLOBAL(http_version, "HTTP_VERSION");
368   DEF_GLOBAL(request_path, "REQUEST_PATH");
369   DEF_GLOBAL(path_info, "PATH_INFO");
370   DEF_GLOBAL(server_name, "SERVER_NAME");
371   DEF_GLOBAL(server_port, "SERVER_PORT");
372   DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
373   DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
374   DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
375   DEF_GLOBAL(port_80, "80");
376   DEF_GLOBAL(port_443, "443");
377   DEF_GLOBAL(localhost, "localhost");
378   DEF_GLOBAL(http, "http");
380   eHttpParserError = rb_define_class_under(mUnicorn, "HttpParserError", rb_eIOError);
382   cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
383   rb_define_alloc_func(cHttpParser, HttpParser_alloc);
384   rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
385   rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
386   rb_define_method(cHttpParser, "execute", HttpParser_execute,2);
387   sym_http_body = ID2SYM(rb_intern("http_body"));
388   init_common_fields();
389   g_http_host = find_common_field("HOST", 4);
390   assert(g_http_host != Qnil);