ruby binding clean-up
[ebb.git] / src / parser.rl
blob68f85863d7a31e89ec0ead572c56c94b27b22271
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)
18 /** machine **/
19 %%{
20   machine http_parser;
22   action mark {MARK(mark, fpc); }
24   action start_field { MARK(field_start, fpc); }
25   action write_field { 
26     parser->field_len = LEN(field_start, fpc);
27     if(parser->field_len > 256) {
28       parser->overflow_error = TRUE;
29       fbreak;
30     }
31   }
33   action start_value { MARK(mark, fpc); }
34   action write_value {
35     if(LEN(mark, fpc) > 80 * 1024) {
36       parser->overflow_error = TRUE;
37       fbreak;
38     }
39     if(parser->http_field != NULL) {
40       parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
41     }
42   }
43   
44   action content_length {
45     if(parser->content_length != NULL) {
46       parser->content_length(parser->data, PTR_TO(mark), LEN(mark, fpc));
47     }
48   }
49   
50   action request_method { 
51     if(parser->request_method != NULL) 
52       parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
53   }
54   
55   action request_uri {
56     if(LEN(mark, fpc) > 12 * 1024) {
57       parser->overflow_error = TRUE;
58       fbreak;
59     }
60     if(parser->request_uri != NULL)
61       parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
62   }
63   
64   action start_query {MARK(query_start, fpc); }
65   action query_string { 
66     if(LEN(query_start, fpc) > 10 * 1024) {
67       parser->overflow_error = TRUE;
68       fbreak;
69     }
70     if(parser->query_string != NULL)
71       parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
72   }
73   
74   action http_version { 
75     if(parser->http_version != NULL)
76       parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
77   }
78   
79   action request_path {
80     if(LEN(mark, fpc) > 1024) {
81       parser->overflow_error = TRUE;
82       fbreak;
83     }
84     if(parser->request_path != NULL)
85       parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
86   }
87   
88   action fragment {
89     /* Don't know if this length is specified somewhere or not */
90     if(LEN(mark, fpc) > 1024) {
91       parser->overflow_error = TRUE;
92       fbreak;
93     }
94     if(parser->fragment != NULL)
95       parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
96   }
97   
98   action done {
99     if(parser->nread > 1024 * (80 + 32)) {
100       parser->overflow_error = TRUE;
101       fbreak;
102     }
103     parser->body_start = fpc - buffer + 1; 
104     if(parser->header_done != NULL)
105       parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
106     fbreak;
107   }
109 #### HTTP PROTOCOL GRAMMAR
110 # line endings
111   CRLF = "\r\n";
113 # character types
114   CTL = (cntrl | 127);
115   safe = ("$" | "-" | "_" | ".");
116   extra = ("!" | "*" | "'" | "(" | ")" | ",");
117   reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
118   unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
119   national = any -- (alpha | digit | reserved | extra | safe | unsafe);
120   unreserved = (alpha | digit | safe | extra | national);
121   escape = ("%" xdigit xdigit);
122   uchar = (unreserved | escape);
123   pchar = (uchar | ":" | "@" | "&" | "=" | "+");
124   tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
126 # elements
127   token = (ascii -- (CTL | tspecials));
129 # URI schemes and absolute paths
130   scheme = ( alpha | digit | "+" | "-" | "." )* ;
131   absolute_uri = (scheme ":" (uchar | reserved )*);
133   path = ( pchar+ ( "/" pchar* )* ) ;
134   query = ( uchar | reserved )* %query_string ;
135   param = ( pchar | "/" )* ;
136   params = ( param ( ";" param )* ) ;
137   rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?;
138   absolute_path = ( "/"+ rel_path );
140   Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri;
141   Fragment = ( uchar | reserved )* >mark %fragment;
142   Method = ( upper | digit | safe ){1,20} >mark %request_method;
144   http_number = ( digit+ "." digit+ ) ;
145   HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
146   Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;
148   field_name = ( token -- ":" )+ >start_field %write_field;
150   field_value = any* >start_value %write_value;
152   message_header = field_name ":" " "* field_value :> CRLF;
153   content_length = "Content-Length:"i " "* (digit{1,12} >mark %content_length) :> CRLF;
155   Request = Request_Line (content_length | message_header )* ( CRLF @done );
157 main := Request;
161 /** Data **/
162 %% write data;
164 void http_parser_init(http_parser *parser)  {
165   int cs = 0;
166   %% write init;
167   parser->cs = cs;
168   parser->overflow_error = FALSE;
169   parser->body_start = 0;
170   parser->content_len = 0;
171   parser->mark = 0;
172   parser->nread = 0;
173   parser->field_len = 0;
174   parser->field_start = 0;
175   parser->data = NULL;
176   parser->http_field = NULL;
177   parser->request_method = NULL;
178   parser->request_uri = NULL;
179   parser->fragment = NULL;
180   parser->request_path = NULL;
181   parser->query_string = NULL;
182   parser->http_version = NULL;
183   parser->content_length = NULL;
187 /** exec **/
188 size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off)  {
189   const char *p, *pe;
190   int cs = parser->cs;
191   
192   assert(off <= len && "offset past end of buffer");
193   
194   p = buffer+off;
195   pe = buffer+len;
196   
197   /* Ragel 6 does not require this */
198   // assert(*pe == '\0' && "pointer does not end on NUL");
199   assert(pe - p == len - off && "pointers aren't same distance");
200   
201   %% write exec;
202   
203   parser->cs = cs;
204   parser->nread += p - (buffer + off);
205   
206   assert(p <= pe && "buffer overflow after parsing execute");
207   assert(parser->nread <= len && "nread longer than length");
208   assert(parser->body_start <= len && "body starts after buffer end");
209   assert(parser->mark < len && "mark is after buffer end");
210   assert(parser->field_len <= len && "field has length longer than whole buffer");
211   assert(parser->field_start < len && "field starts after buffer end");
212   
213   /* Ragel 6 does not use write eof; no need for this
214   if(parser->body_start) {
215     // final \r\n combo encountered so stop right here 
216     parser->nread++;
217     %% write eof;
218   }
219   */
220   
221   return(parser->nread);
224 int http_parser_finish(http_parser *parser)
226   if (http_parser_has_error(parser))
227     return -1;
228   else if (http_parser_is_finished(parser))
229     return 1;
230   else
231     return 0;
234 int http_parser_has_error(http_parser *parser) {
235   return parser->cs == http_parser_error || parser->overflow_error;
238 int http_parser_is_finished(http_parser *parser) {
239   return parser->cs >= http_parser_first_final;