Ensure request is sent even if ebb_client_body_write isn't called
[ebb.git] / src / parser.rl
blob5850e9f1aaf176f6e634bc79c456de0321b2ed91
1 /**
2  * Copyright (c) 2005 Zed A. Shaw
3  * You can redistribute it and/or modify it under the same terms as Ruby.
4  */
5 #include "parser.h"
6 #include <stdio.h>
7 #include <assert.h>
8 #include <stdlib.h>
9 #include <ctype.h>
10 #include <string.h>
12 #define TRUE 1
13 #define FALSE 0
14 #define LEN(AT, FPC) (FPC - buffer - parser->AT)
15 #define MARK(M,FPC) (parser->M = (FPC) - buffer)
16 #define PTR_TO(F) (buffer + parser->F)
17 /** machine **/
18 %%{
19   machine http_parser;
20   
21   action mark {MARK(mark, fpc); }
22   
23   action start_field { MARK(field_start, fpc); }
24   action write_field { 
25     parser->field_len = LEN(field_start, fpc);
26     if(parser->field_len > 256) {
27       parser->overflow_error = TRUE;
28       fbreak;
29     }
30   }
31   
32   action start_value { MARK(mark, fpc); }
33   action write_value {
34     if(LEN(mark, fpc) > 80 * 1024) { parser->overflow_error = TRUE; fbreak; }
35     if(parser->http_field != NULL) {
36       parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
37     }
38   }
39   
40   action content_length {
41     if(!apply_element(parser, MONGREL_CONTENT_LENGTH, PTR_TO(mark), fpc, 20))
42       fbreak;
43     set_content_length(parser, PTR_TO(mark), LEN(mark, fpc));
44   }
45   
46   action content_type {
47     if(!apply_element(parser, MONGREL_CONTENT_TYPE, PTR_TO(mark), fpc, 10*1024))
48       fbreak;
49   }
50   
51   action fragment {
52     if(!apply_element(parser, MONGREL_FRAGMENT, PTR_TO(mark), fpc, 10*1024))
53       fbreak;
54   }
55   
56   action http_version {
57     if(!apply_element(parser, MONGREL_HTTP_VERSION, PTR_TO(mark), fpc, 10))
58       fbreak;
59   }
60   
61   action request_path {
62     if(!apply_element(parser, MONGREL_REQUEST_PATH, PTR_TO(mark), fpc, 1024))
63       fbreak;
64   }
65   
66   action request_method {
67     if(!apply_element(parser, MONGREL_REQUEST_METHOD, PTR_TO(mark), fpc, 1024))
68       fbreak;
69   }
70   
71   action request_uri {
72     if(!apply_element(parser, MONGREL_REQUEST_URI, PTR_TO(mark), fpc, 12*1024))
73       fbreak;
74   }
75   
76   action start_query {MARK(query_start, fpc); }
77   action query_string {
78     if(!apply_element(parser, MONGREL_QUERY_STRING, PTR_TO(query_start), fpc, 10*1024))
79       fbreak;
80   }
81   
82   action done {
83     parser->body_start = fpc - buffer + 1;
84     fbreak;
85   }
87 #### HTTP PROTOCOL GRAMMAR
88 # line endings
89   CRLF = "\r\n";
91 # character types
92   CTL = (cntrl | 127);
93   safe = ("$" | "-" | "_" | ".");
94   extra = ("!" | "*" | "'" | "(" | ")" | ",");
95   reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
96   unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
97   national = any -- (alpha | digit | reserved | extra | safe | unsafe);
98   unreserved = (alpha | digit | safe | extra | national);
99   escape = ("%" xdigit xdigit);
100   uchar = (unreserved | escape);
101   pchar = (uchar | ":" | "@" | "&" | "=" | "+");
102   tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
104 # elements
105   token = (ascii -- (CTL | tspecials));
107 # URI schemes and absolute paths
108   scheme = ( alpha | digit | "+" | "-" | "." )* ;
109   absolute_uri = (scheme ":" (uchar | reserved )*);
111   path = ( pchar+ ( "/" pchar* )* ) ;
112   query = ( uchar | reserved )* %query_string ;
113   param = ( pchar | "/" )* ;
114   params = ( param ( ";" param )* ) ;
115   rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?;
116   absolute_path = ( "/"+ rel_path );
118   Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri;
119   Fragment = ( uchar | reserved )* >mark %fragment;
120   Method = ( upper | digit | safe ){1,20} >mark %request_method;
122   http_number = ( digit+ "." digit+ ) ;
123   HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
124   Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;
126   field_name = ( token -- ":" )+ >start_field %write_field;
128   field_value = any* >start_value %write_value;
129   
130   known_headers = ( ("Content-Length:"i " "* (digit+ >mark %content_length))
131                   | ("Content-Type:"i " "* (any* >mark %content_type))
132                   ) :> CRLF;
133   unknown_header = (field_name ":" " "* field_value :> CRLF) -- known_headers;
134   
135   Request = Request_Line (known_headers | unknown_header)* ( CRLF @done );
137 main := Request;
141 /** Data **/
142 %% write data;
144 /* returns TRUE if applied, FALSE if there was an error */
145 static int apply_element(http_parser *parser, int type, const char *begin, const char *end, int max_length)
147   int len = (int)(end-begin);
148   if(len > max_length) {
149     parser->overflow_error = TRUE;
150     return FALSE;
151   }
152   if(parser->on_element)
153     parser->on_element(parser->data, type, begin, len);
154   return TRUE;
157 static void set_content_length(http_parser *parser, const char *at, int length)
159   /* atoi_length - why isn't this in the statndard library? i hate c */
160   assert(parser->content_length == 0);
161   int i, mult;
162   for(mult=1, i=length-1; i>=0; i--, mult*=10)
163     parser->content_length += (at[i] - '0') * mult;
166 void http_parser_init(http_parser *parser)  {
167   int cs = 0;
168   %% write init;
169   parser->cs = cs;
170   parser->overflow_error = FALSE;
171   parser->body_start = 0;
172   parser->content_length = 0;
173   parser->mark = 0;
174   parser->nread = 0;
175   parser->field_len = 0;
176   parser->field_start = 0;
177   parser->data = NULL;
178   parser->http_field = NULL;
182 /** exec **/
183 size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off)  {
184   const char *p, *pe;
185   int cs = parser->cs;
186   
187   assert(off <= len && "offset past end of buffer");
188   
189   p = buffer+off;
190   pe = buffer+len;
191   
192   /* Ragel 6 does not require this */
193   // assert(*pe == '\0' && "pointer does not end on NUL");
194   assert(pe - p == len - off && "pointers aren't same distance");
195   
196   %% write exec;
197   
198   parser->cs = cs;
199   parser->nread += p - (buffer + off);
200   
201   assert(p <= pe && "buffer overflow after parsing execute");
202   assert(parser->nread <= len && "nread longer than length");
203   assert(parser->body_start <= len && "body starts after buffer end");
204   assert(parser->mark < len && "mark is after buffer end");
205   assert(parser->field_len <= len && "field has length longer than whole buffer");
206   assert(parser->field_start < len && "field starts after buffer end");
207   
208   if(parser->nread > 1024 * (80 + 32))
209     parser->overflow_error = TRUE;
210   
211   
212   /* Ragel 6 does not use write eof; no need for this
213   if(parser->body_start) {
214     // final \r\n combo encountered so stop right here 
215     parser->nread++;
216     %% write eof;
217   }
218   */
219   
220   return(parser->nread);
223 int http_parser_finish(http_parser *parser)
225   if (http_parser_has_error(parser))
226     return -1;
227   else if (http_parser_is_finished(parser))
228     return 1;
229   else
230     return 0;
233 int http_parser_has_error(http_parser *parser) {
234   return parser->cs == http_parser_error || parser->overflow_error;
237 int http_parser_is_finished(http_parser *parser) {
238   return parser->cs >= http_parser_first_final;