http: remove some redundant functions
[unicorn.git] / ext / unicorn_http / unicorn_http.rl
blobbba2fcfce5b984c9050d0e70fbd5304cd6e2a46b
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 header_done(VALUE req, const char *at, size_t length);
31 static int http_parser_has_error(struct http_parser *parser);
32 static int http_parser_is_finished(struct http_parser *parser);
35 #define LEN(AT, FPC) (FPC - buffer - parser->AT)
36 #define MARK(M,FPC) (parser->M = (FPC) - buffer)
37 #define PTR_TO(F) (buffer + parser->F)
38 #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
40 /** Machine **/
42 %%{
43   machine http_parser;
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 { parser->field_len = LEN(start.field, fpc); }
51   action start_value { MARK(mark, fpc); }
52   action write_value {
53     http_field(req, PTR_TO(start.field), parser->field_len,
54                PTR_TO(mark), LEN(mark, fpc));
55   }
56   action request_method {
57     rb_hash_aset(req, g_request_method, STR_NEW(mark, fpc));
58   }
59   action scheme {
60     rb_hash_aset(req, g_rack_url_scheme, STR_NEW(mark, fpc));
61   }
62   action host {
63     rb_hash_aset(req, g_http_host, STR_NEW(mark, fpc));
64   }
65   action request_uri {
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));
69     /*
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
72      */
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);
77     }
78   }
79   action fragment {
80     VALIDATE_MAX_LENGTH(LEN(mark, fpc), FRAGMENT);
81     rb_hash_aset(req, g_fragment, STR_NEW(mark, fpc));
82   }
83   action start_query {MARK(start.query, fpc); }
84   action query_string {
85     VALIDATE_MAX_LENGTH(LEN(start.query, fpc), QUERY_STRING);
86     rb_hash_aset(req, g_query_string, STR_NEW(start.query, fpc));
87   }
88   action http_version {
89     rb_hash_aset(req, g_http_version, STR_NEW(mark, fpc));
90   }
91   action request_path {
92     VALUE val;
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);
102   }
103   action done {
104     parser->start.body = fpc - buffer + 1;
105     header_done(req, fpc + 1, pe - fpc - 1);
106     fbreak;
107   }
109   include unicorn_http_common "unicorn_http_common.rl";
112 /** Data **/
113 %% write data;
115 static void http_parser_init(struct http_parser *parser)
117   int cs = 0;
118   memset(parser, 0, sizeof(*parser));
119   %% write init;
120   parser->cs = cs;
123 /** exec **/
124 static void http_parser_execute(struct http_parser *parser,
125   VALUE req, const char *buffer, size_t len)
127   const char *p, *pe;
128   int cs = parser->cs;
129   size_t off = parser->start.offset;
131   assert(off <= len && "offset past end of buffer");
133   p = buffer+off;
134   pe = buffer+len;
136   assert(pe - p == len - off && "pointers aren't same distance");
138   %% write exec;
140   if (!http_parser_has_error(parser))
141     parser->cs = cs;
142   parser->start.offset = p - buffer;
144   assert(p <= pe && "buffer overflow after parsing execute");
145   assert(parser->start.offset <= len && "start.offset longer than length");
146   assert(parser->mark < len && "mark is after buffer end");
147   assert(parser->field_len <= len && "field has length longer than whole buffer");
150 static int http_parser_has_error(struct http_parser *parser)
152   return parser->cs == http_parser_error;
155 static int http_parser_is_finished(struct http_parser *parser)
157   return parser->cs == http_parser_first_final;
160 static struct http_parser *data_get(VALUE self)
162   struct http_parser *http;
164   Data_Get_Struct(self, struct http_parser, http);
165   assert(http);
166   return http;
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);
176   if (f == Qnil) {
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) {
180     return;
181   }
183   rb_hash_aset(req, f, rb_str_new(value, vlen));
186 /** Finalizes the request header to have a bunch of stuff that's needed. */
187 static void header_done(VALUE req, const char *at, size_t length)
189   VALUE server_name = g_localhost;
190   VALUE server_port = g_port_80;
191   VALUE temp;
193   /* rack requires QUERY_STRING */
194   if (rb_hash_aref(req, g_query_string) == Qnil)
195     rb_hash_aset(req, g_query_string, rb_str_new(NULL, 0));
197   /* set rack.url_scheme to "https" or "http", no others are allowed by Rack */
198   if ((temp = rb_hash_aref(req, g_rack_url_scheme)) == Qnil) {
199     if ((temp = rb_hash_aref(req, g_http_x_forwarded_proto)) != Qnil &&
200         RSTRING_LEN(temp) == 5 &&
201         !memcmp("https", RSTRING_PTR(temp), 5))
202       server_port = g_port_443;
203     else
204       temp = g_http;
205     rb_hash_aset(req, g_rack_url_scheme, temp);
206   } else if (RSTRING_LEN(temp) == 5 && !memcmp("https", RSTRING_PTR(temp), 5)) {
207     server_port = g_port_443;
208   }
210   /* parse and set the SERVER_NAME and SERVER_PORT variables */
211   if ((temp = rb_hash_aref(req, g_http_host)) != Qnil) {
212     char *colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
213     if (colon) {
214       long port_start = colon - RSTRING_PTR(temp) + 1;
216       server_name = rb_str_substr(temp, 0, colon - RSTRING_PTR(temp));
217       if ((RSTRING_LEN(temp) - port_start) > 0)
218         server_port = rb_str_substr(temp, port_start, RSTRING_LEN(temp));
219     } else {
220       server_name = temp;
221     }
222   }
223   rb_hash_aset(req, g_server_name, server_name);
224   rb_hash_aset(req, g_server_port, server_port);
225   rb_hash_aset(req, g_server_protocol, g_server_protocol_value);
227   /* grab the initial body and stuff it into the hash */
228   temp = rb_hash_aref(req, g_request_method);
229   if (temp != Qnil) {
230     long len = RSTRING_LEN(temp);
231     char *ptr = RSTRING_PTR(temp);
233     if (memcmp(ptr, "HEAD", len) && memcmp(ptr, "GET", len))
234       rb_hash_aset(req, sym_http_body, rb_str_new(at, length));
235   }
238 static VALUE HttpParser_alloc(VALUE klass)
240   struct http_parser *http;
241   return Data_Make_Struct(klass, struct http_parser, NULL, NULL, http);
246  * call-seq:
247  *    parser.new -> parser
249  * Creates a new parser.
250  */
251 static VALUE HttpParser_init(VALUE self)
253   http_parser_init(data_get(self));
255   return self;
260  * call-seq:
261  *    parser.reset -> nil
263  * Resets the parser to it's initial state so that you can reuse it
264  * rather than making new ones.
265  */
266 static VALUE HttpParser_reset(VALUE self)
268   http_parser_init(data_get(self));
270   return Qnil;
275  * call-seq:
276  *    parser.execute(req, data) -> true/false
278  * Takes a Hash and a String of data, parses the String of data filling
279  * in the Hash returning a boolean to indicate whether or not parsing
280  * is finished.
282  * This function now throws an exception when there is a parsing error.
283  * This makes the logic for working with the parser much easier.  You
284  * will need to wrap the parser with an exception handling block.
285  */
287 static VALUE HttpParser_execute(VALUE self, VALUE req, VALUE data)
289   struct http_parser *http = data_get(self);
290   char *dptr = RSTRING_PTR(data);
291   long dlen = RSTRING_LEN(data);
293   if (http->start.offset < dlen) {
294     http_parser_execute(http, req, dptr, dlen);
296     VALIDATE_MAX_LENGTH(http->start.offset, HEADER);
298     if (!http_parser_has_error(http))
299       return http_parser_is_finished(http) ? Qtrue : Qfalse;
301     rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
302   }
303   rb_raise(eHttpParserError, "Requested start is after data buffer end.");
306 void Init_unicorn_http(void)
308   mUnicorn = rb_define_module("Unicorn");
310   DEF_GLOBAL(rack_url_scheme, "rack.url_scheme");
311   DEF_GLOBAL(request_method, "REQUEST_METHOD");
312   DEF_GLOBAL(request_uri, "REQUEST_URI");
313   DEF_GLOBAL(fragment, "FRAGMENT");
314   DEF_GLOBAL(query_string, "QUERY_STRING");
315   DEF_GLOBAL(http_version, "HTTP_VERSION");
316   DEF_GLOBAL(request_path, "REQUEST_PATH");
317   DEF_GLOBAL(path_info, "PATH_INFO");
318   DEF_GLOBAL(server_name, "SERVER_NAME");
319   DEF_GLOBAL(server_port, "SERVER_PORT");
320   DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
321   DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
322   DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
323   DEF_GLOBAL(port_80, "80");
324   DEF_GLOBAL(port_443, "443");
325   DEF_GLOBAL(localhost, "localhost");
326   DEF_GLOBAL(http, "http");
328   eHttpParserError = rb_define_class_under(mUnicorn, "HttpParserError", rb_eIOError);
330   cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
331   rb_define_alloc_func(cHttpParser, HttpParser_alloc);
332   rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
333   rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
334   rb_define_method(cHttpParser, "execute", HttpParser_execute,2);
335   sym_http_body = ID2SYM(rb_intern("http_body"));
336   init_common_fields();
337   g_http_host = find_common_field("HOST", 4);
338   assert(g_http_host != Qnil);