http: remove noise functions
[unicorn.git] / ext / unicorn_http / unicorn_http.rl
bloba9c2243bf77074900881e46be4bc7ac3674746da
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, size_t flen, VALUE val);
28 static void header_done(VALUE req, const char *at, size_t length);
30 #define LEN(AT, FPC) (FPC - buffer - hp->AT)
31 #define MARK(M,FPC) (hp->M = (FPC) - buffer)
32 #define PTR_TO(F) (buffer + hp->F)
33 #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
35 /** Machine **/
37 %%{
38   machine http_parser;
40   action mark {MARK(mark, fpc); }
42   action start_field { MARK(start.field, fpc); }
43   action snake_upcase_field { snake_upcase_char((char *)fpc); }
44   action downcase_char { downcase_char((char *)fpc); }
45   action write_field { hp->field_len = LEN(start.field, fpc); }
46   action start_value { MARK(mark, fpc); }
47   action write_value {
48     VALIDATE_MAX_LENGTH(LEN(mark, fpc), FIELD_VALUE);
49     http_field(req, PTR_TO(start.field), hp->field_len, STR_NEW(mark, fpc));
50   }
51   action request_method {
52     rb_hash_aset(req, g_request_method, STR_NEW(mark, fpc));
53   }
54   action scheme {
55     rb_hash_aset(req, g_rack_url_scheme, STR_NEW(mark, fpc));
56   }
57   action host {
58     rb_hash_aset(req, g_http_host, STR_NEW(mark, fpc));
59   }
60   action request_uri {
61     size_t len = LEN(mark, fpc);
62     VALIDATE_MAX_LENGTH(len, REQUEST_URI);
63     rb_hash_aset(req, g_request_uri, STR_NEW(mark, fpc));
64     /*
65      * "OPTIONS * HTTP/1.1\r\n" is a valid request, but we can't have '*'
66      * in REQUEST_PATH or PATH_INFO or else Rack::Lint will complain
67      */
68     if (len == 1 && *PTR_TO(mark) == '*') {
69       VALUE val = rb_str_new(NULL, 0);
70       rb_hash_aset(req, g_request_path, val);
71       rb_hash_aset(req, g_path_info, val);
72     }
73   }
74   action fragment {
75     VALIDATE_MAX_LENGTH(LEN(mark, fpc), FRAGMENT);
76     rb_hash_aset(req, g_fragment, STR_NEW(mark, fpc));
77   }
78   action start_query {MARK(start.query, fpc); }
79   action query_string {
80     VALIDATE_MAX_LENGTH(LEN(start.query, fpc), QUERY_STRING);
81     rb_hash_aset(req, g_query_string, STR_NEW(start.query, fpc));
82   }
83   action http_version {
84     rb_hash_aset(req, g_http_version, STR_NEW(mark, fpc));
85   }
86   action request_path {
87     VALUE val;
88     size_t len = LEN(mark, fpc);
90     VALIDATE_MAX_LENGTH(len, REQUEST_PATH);
91     val = STR_NEW(mark, fpc);
93     rb_hash_aset(req, g_request_path, val);
94     /* rack says PATH_INFO must start with "/" or be empty */
95     if (!(len == 1 && *PTR_TO(mark) == '*'))
96       rb_hash_aset(req, g_path_info, val);
97   }
98   action done {
99     hp->start.body = fpc - buffer + 1;
100     header_done(req, fpc + 1, pe - fpc - 1);
101     fbreak;
102   }
104   include unicorn_http_common "unicorn_http_common.rl";
107 /** Data **/
108 %% write data;
110 static void http_parser_init(struct http_parser *hp)
112   int cs = 0;
113   memset(hp, 0, sizeof(struct http_parser));
114   %% write init;
115   hp->cs = cs;
118 /** exec **/
119 static void http_parser_execute(struct http_parser *hp,
120   VALUE req, const char *buffer, size_t len)
122   const char *p, *pe;
123   int cs = hp->cs;
124   size_t off = hp->start.offset;
126   assert(off <= len && "offset past end of buffer");
128   p = buffer+off;
129   pe = buffer+len;
131   assert(pe - p == len - off && "pointers aren't same distance");
133   %% write exec;
135   if (hp->cs != http_parser_error)
136     hp->cs = cs;
137   hp->start.offset = p - buffer;
139   assert(p <= pe && "buffer overflow after parsing execute");
140   assert(hp->start.offset <= len && "start.offset longer than length");
141   assert(hp->mark < len && "mark is after buffer end");
142   assert(hp->field_len <= len && "field has length longer than whole buffer");
145 static struct http_parser *data_get(VALUE self)
147   struct http_parser *hp;
149   Data_Get_Struct(self, struct http_parser, hp);
150   assert(hp);
151   return hp;
154 static void http_field(VALUE req, const char *field, size_t flen, VALUE val)
156   VALUE f = find_common_field(field, flen);
158   if (f == Qnil) {
159     VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
160     f = uncommon_field(field, flen);
161   } else if (f == g_http_host && rb_hash_aref(req, f) != Qnil) {
162     return;
163   }
165   rb_hash_aset(req, f, val);
168 static int is_https(VALUE str)
170   return RSTRING_LEN(str) == 5 && !memcmp("https", RSTRING_PTR(str), 5);
173 static void set_server_params(VALUE req)
175   VALUE temp = rb_hash_aref(req, g_rack_url_scheme);
176   VALUE server_name = g_localhost;
177   VALUE server_port = g_port_80;
179   /* set rack.url_scheme to "https" or "http", no others are allowed by Rack */
180   if (temp == Qnil) {
181     temp = rb_hash_aref(req, g_http_x_forwarded_proto);
182     if (temp != Qnil && is_https(temp))
183       server_port = g_port_443;
184     else
185       temp = g_http;
186     rb_hash_aset(req, g_rack_url_scheme, temp);
187   } else if (is_https(temp)) {
188     server_port = g_port_443;
189   }
191   /* parse and set the SERVER_NAME and SERVER_PORT variables */
192   temp = rb_hash_aref(req, g_http_host);
193   if (temp != Qnil) {
194     char *colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
195     if (colon) {
196       long port_start = colon - RSTRING_PTR(temp) + 1;
198       server_name = rb_str_substr(temp, 0, colon - RSTRING_PTR(temp));
199       if ((RSTRING_LEN(temp) - port_start) > 0)
200         server_port = rb_str_substr(temp, port_start, RSTRING_LEN(temp));
201     } else {
202       server_name = temp;
203     }
204   }
205   rb_hash_aset(req, g_server_name, server_name);
206   rb_hash_aset(req, g_server_port, server_port);
209 /** Finalizes the request header to have a bunch of stuff that's needed. */
210 static void header_done(VALUE req, const char *at, size_t length)
212   VALUE temp;
214   /* rack requires QUERY_STRING */
215   if (rb_hash_aref(req, g_query_string) == Qnil)
216     rb_hash_aset(req, g_query_string, rb_str_new(NULL, 0));
218   set_server_params(req);
219   rb_hash_aset(req, g_server_protocol, g_server_protocol_value);
221   /* grab the initial body and stuff it into the hash */
222   temp = rb_hash_aref(req, g_request_method);
223   if (temp != Qnil) {
224     long len = RSTRING_LEN(temp);
225     char *ptr = RSTRING_PTR(temp);
227     if (memcmp(ptr, "HEAD", len) && memcmp(ptr, "GET", len))
228       rb_hash_aset(req, sym_http_body, rb_str_new(at, length));
229   }
232 static VALUE HttpParser_alloc(VALUE klass)
234   struct http_parser *hp;
235   return Data_Make_Struct(klass, struct http_parser, NULL, NULL, hp);
240  * call-seq:
241  *    parser.new -> parser
243  * Creates a new parser.
244  */
245 static VALUE HttpParser_init(VALUE self)
247   http_parser_init(data_get(self));
249   return self;
254  * call-seq:
255  *    parser.reset -> nil
257  * Resets the parser to it's initial state so that you can reuse it
258  * rather than making new ones.
259  */
260 static VALUE HttpParser_reset(VALUE self)
262   http_parser_init(data_get(self));
264   return Qnil;
269  * call-seq:
270  *    parser.execute(req, data) -> true/false
272  * Takes a Hash and a String of data, parses the String of data filling
273  * in the Hash returning a boolean to indicate whether or not parsing
274  * is finished.
276  * This function now throws an exception when there is a parsing error.
277  * This makes the logic for working with the parser much easier.  You
278  * will need to wrap the parser with an exception handling block.
279  */
281 static VALUE HttpParser_execute(VALUE self, VALUE req, VALUE data)
283   struct http_parser *hp = data_get(self);
284   char *dptr = RSTRING_PTR(data);
285   long dlen = RSTRING_LEN(data);
287   if (hp->start.offset < dlen) {
288     http_parser_execute(hp, req, dptr, dlen);
290     VALIDATE_MAX_LENGTH(hp->start.offset, HEADER);
292     if (hp->cs != http_parser_error)
293       return hp->cs == http_parser_first_final ? Qtrue : Qfalse;
295     rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
296   }
297   rb_raise(eHttpParserError, "Requested start is after data buffer end.");
300 #define SET_GLOBAL(var,str) do { \
301   var = find_common_field(str, sizeof(str) - 1); \
302   assert(var != Qnil); \
303 } while (0)
305 void Init_unicorn_http(void)
307   init_globals();
308   rb_define_alloc_func(cHttpParser, HttpParser_alloc);
309   rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
310   rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
311   rb_define_method(cHttpParser, "execute", HttpParser_execute,2);
312   init_common_fields();
313   SET_GLOBAL(g_http_host, "HOST");
315 #undef SET_GLOBAL