summaryrefslogtreecommitdiff
path: root/code/spyglass/ext/spyglass_parser
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2013-08-28 07:20:13 -0600
committermo khan <mo@mokhan.ca>2013-08-28 07:20:13 -0600
commitdb1191ec0e7305d684383f8974e2fa437f77ad5a (patch)
tree5e27b197dff849d22d8e1f50eb75aa499b16bd06 /code/spyglass/ext/spyglass_parser
initial commitHEADmaster
Diffstat (limited to 'code/spyglass/ext/spyglass_parser')
-rw-r--r--code/spyglass/ext/spyglass_parser/common.rl55
-rw-r--r--code/spyglass/ext/spyglass_parser/ext_help.h14
-rw-r--r--code/spyglass/ext/spyglass_parser/extconf.rb6
-rw-r--r--code/spyglass/ext/spyglass_parser/parser.c1249
-rw-r--r--code/spyglass/ext/spyglass_parser/parser.h49
-rw-r--r--code/spyglass/ext/spyglass_parser/parser.rl157
-rw-r--r--code/spyglass/ext/spyglass_parser/spyglass.c436
7 files changed, 1966 insertions, 0 deletions
diff --git a/code/spyglass/ext/spyglass_parser/common.rl b/code/spyglass/ext/spyglass_parser/common.rl
new file mode 100644
index 0000000..df7afc0
--- /dev/null
+++ b/code/spyglass/ext/spyglass_parser/common.rl
@@ -0,0 +1,55 @@
+%%{
+
+ machine http_parser_common;
+
+#### HTTP PROTOCOL GRAMMAR
+# line endings
+ CRLF = "\r\n";
+
+# character types
+ CTL = (cntrl | 127);
+ safe = ("$" | "-" | "_" | ".");
+ extra = ("!" | "*" | "'" | "(" | ")" | ",");
+ reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
+ sorta_safe = ("\"" | "<" | ">");
+ unsafe = (CTL | " " | "#" | "%" | sorta_safe);
+ national = any -- (alpha | digit | reserved | extra | safe | unsafe);
+ unreserved = (alpha | digit | safe | extra | national);
+ escape = ("%" "u"? xdigit xdigit);
+ uchar = (unreserved | escape | sorta_safe);
+ pchar = (uchar | ":" | "@" | "&" | "=" | "+");
+ tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
+
+# elements
+ token = (ascii -- (CTL | tspecials));
+
+# URI schemes and absolute paths
+ scheme = ( alpha | digit | "+" | "-" | "." )* ;
+ absolute_uri = (scheme ":" (uchar | reserved )*);
+
+ path = ( pchar+ ( "/" pchar* )* ) ;
+ query = ( uchar | reserved )* %query_string ;
+ param = ( pchar | "/" )* ;
+ params = ( param ( ";" param )* ) ;
+ rel_path = ( path? (";" params)? %request_path) ("?" %start_query query)?;
+ absolute_path = ( "/"+ rel_path );
+
+ Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri;
+ Fragment = ( uchar | reserved )* >mark %fragment;
+ Method = ( upper | digit | safe ){1,20} >mark %request_method;
+
+ http_number = ( digit+ "." digit+ ) ;
+ HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
+ Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;
+
+ field_name = ( token -- ":" )+ >start_field %write_field;
+
+ field_value = any* >start_value %write_value;
+
+ message_header = field_name ":" " "* field_value :> CRLF;
+
+ Request = Request_Line ( message_header )* ( CRLF @done );
+
+main := Request;
+
+}%%
diff --git a/code/spyglass/ext/spyglass_parser/ext_help.h b/code/spyglass/ext/spyglass_parser/ext_help.h
new file mode 100644
index 0000000..8b4d754
--- /dev/null
+++ b/code/spyglass/ext/spyglass_parser/ext_help.h
@@ -0,0 +1,14 @@
+#ifndef ext_help_h
+#define ext_help_h
+
+#define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
+#define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
+#define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);
+
+#ifdef DEBUG
+#define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
+#else
+#define TRACE()
+#endif
+
+#endif
diff --git a/code/spyglass/ext/spyglass_parser/extconf.rb b/code/spyglass/ext/spyglass_parser/extconf.rb
new file mode 100644
index 0000000..44bae83
--- /dev/null
+++ b/code/spyglass/ext/spyglass_parser/extconf.rb
@@ -0,0 +1,6 @@
+require 'mkmf'
+
+dir_config("spyglass_parser")
+have_library("c", "main")
+
+create_makefile("spyglass_parser")
diff --git a/code/spyglass/ext/spyglass_parser/parser.c b/code/spyglass/ext/spyglass_parser/parser.c
new file mode 100644
index 0000000..53329b0
--- /dev/null
+++ b/code/spyglass/ext/spyglass_parser/parser.c
@@ -0,0 +1,1249 @@
+
+#line 1 "parser.rl"
+/**
+ * Copyright (c) 2005 Zed A. Shaw
+ * You can redistribute it and/or modify it under the same terms as Ruby.
+ */
+#include "parser.h"
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#define LEN(AT, FPC) (FPC - buffer - parser->AT)
+#define MARK(M,FPC) (parser->M = (FPC) - buffer)
+#define PTR_TO(F) (buffer + parser->F)
+
+/** Machine **/
+
+
+#line 81 "parser.rl"
+
+
+/** Data **/
+
+#line 27 "parser.c"
+static const int http_parser_start = 1;
+static const int http_parser_first_final = 58;
+static const int http_parser_error = 0;
+
+static const int http_parser_en_main = 1;
+
+
+#line 85 "parser.rl"
+
+int spyglass_http_parser_init(http_parser *parser) {
+ int cs = 0;
+
+#line 40 "parser.c"
+ {
+ cs = http_parser_start;
+ }
+
+#line 89 "parser.rl"
+ parser->cs = cs;
+ parser->body_start = 0;
+ parser->content_len = 0;
+ parser->mark = 0;
+ parser->nread = 0;
+ parser->field_len = 0;
+ parser->field_start = 0;
+
+ return(1);
+}
+
+
+/** exec **/
+size_t spyglass_http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
+ const char *p, *pe;
+ int cs = parser->cs;
+
+ assert(off <= len && "offset past end of buffer");
+
+ p = buffer+off;
+ pe = buffer+len;
+
+ assert(*pe == '\0' && "pointer does not end on NUL");
+ assert(pe - p == len - off && "pointers aren't same distance");
+
+
+
+#line 73 "parser.c"
+ {
+ if ( p == pe )
+ goto _test_eof;
+ switch ( cs )
+ {
+case 1:
+ switch( (*p) ) {
+ case 36: goto tr0;
+ case 95: goto tr0;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto tr0;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto tr0;
+ } else
+ goto tr0;
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+tr0:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+ goto st2;
+st2:
+ if ( ++p == pe )
+ goto _test_eof2;
+case 2:
+#line 104 "parser.c"
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st39;
+ case 95: goto st39;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st39;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st39;
+ } else
+ goto st39;
+ goto st0;
+tr2:
+#line 36 "parser.rl"
+ {
+ if (parser->request_method != NULL) {
+ parser->request_method(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st3;
+st3:
+ if ( ++p == pe )
+ goto _test_eof3;
+case 3:
+#line 131 "parser.c"
+ switch( (*p) ) {
+ case 42: goto tr4;
+ case 43: goto tr5;
+ case 47: goto tr6;
+ case 58: goto tr7;
+ }
+ if ( (*p) < 65 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr5;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr5;
+ } else
+ goto tr5;
+ goto st0;
+tr4:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+ goto st4;
+st4:
+ if ( ++p == pe )
+ goto _test_eof4;
+case 4:
+#line 155 "parser.c"
+ switch( (*p) ) {
+ case 32: goto tr8;
+ case 35: goto tr9;
+ }
+ goto st0;
+tr8:
+#line 41 "parser.rl"
+ {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st5;
+tr31:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+#line 46 "parser.rl"
+ {
+ if (parser->fragment != NULL) {
+ parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st5;
+tr34:
+#line 46 "parser.rl"
+ {
+ if (parser->fragment != NULL) {
+ parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st5;
+tr44:
+#line 65 "parser.rl"
+ {
+ if (parser->request_path != NULL) {
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+ }
+ }
+#line 41 "parser.rl"
+ {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st5;
+tr51:
+#line 52 "parser.rl"
+ {MARK(query_start, p); }
+#line 53 "parser.rl"
+ {
+ if (parser->query_string != NULL) {
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+ }
+ }
+#line 41 "parser.rl"
+ {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st5;
+tr55:
+#line 53 "parser.rl"
+ {
+ if (parser->query_string != NULL) {
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+ }
+ }
+#line 41 "parser.rl"
+ {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st5;
+st5:
+ if ( ++p == pe )
+ goto _test_eof5;
+case 5:
+#line 235 "parser.c"
+ if ( (*p) == 72 )
+ goto tr10;
+ goto st0;
+tr10:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+ goto st6;
+st6:
+ if ( ++p == pe )
+ goto _test_eof6;
+case 6:
+#line 247 "parser.c"
+ if ( (*p) == 84 )
+ goto st7;
+ goto st0;
+st7:
+ if ( ++p == pe )
+ goto _test_eof7;
+case 7:
+ if ( (*p) == 84 )
+ goto st8;
+ goto st0;
+st8:
+ if ( ++p == pe )
+ goto _test_eof8;
+case 8:
+ if ( (*p) == 80 )
+ goto st9;
+ goto st0;
+st9:
+ if ( ++p == pe )
+ goto _test_eof9;
+case 9:
+ if ( (*p) == 47 )
+ goto st10;
+ goto st0;
+st10:
+ if ( ++p == pe )
+ goto _test_eof10;
+case 10:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st11;
+ goto st0;
+st11:
+ if ( ++p == pe )
+ goto _test_eof11;
+case 11:
+ if ( (*p) == 46 )
+ goto st12;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st11;
+ goto st0;
+st12:
+ if ( ++p == pe )
+ goto _test_eof12;
+case 12:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st13;
+ goto st0;
+st13:
+ if ( ++p == pe )
+ goto _test_eof13;
+case 13:
+ if ( (*p) == 13 )
+ goto tr18;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st13;
+ goto st0;
+tr18:
+#line 59 "parser.rl"
+ {
+ if (parser->http_version != NULL) {
+ parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st14;
+tr26:
+#line 30 "parser.rl"
+ { MARK(mark, p); }
+#line 31 "parser.rl"
+ {
+ if (parser->http_field != NULL) {
+ parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st14;
+tr29:
+#line 31 "parser.rl"
+ {
+ if (parser->http_field != NULL) {
+ parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st14;
+st14:
+ if ( ++p == pe )
+ goto _test_eof14;
+case 14:
+#line 334 "parser.c"
+ if ( (*p) == 10 )
+ goto st15;
+ goto st0;
+st15:
+ if ( ++p == pe )
+ goto _test_eof15;
+case 15:
+ switch( (*p) ) {
+ case 13: goto st16;
+ case 33: goto tr21;
+ case 124: goto tr21;
+ case 126: goto tr21;
+ }
+ if ( (*p) < 45 ) {
+ if ( (*p) > 39 ) {
+ if ( 42 <= (*p) && (*p) <= 43 )
+ goto tr21;
+ } else if ( (*p) >= 35 )
+ goto tr21;
+ } else if ( (*p) > 46 ) {
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr21;
+ } else if ( (*p) > 90 ) {
+ if ( 94 <= (*p) && (*p) <= 122 )
+ goto tr21;
+ } else
+ goto tr21;
+ } else
+ goto tr21;
+ goto st0;
+st16:
+ if ( ++p == pe )
+ goto _test_eof16;
+case 16:
+ if ( (*p) == 10 )
+ goto tr22;
+ goto st0;
+tr22:
+#line 71 "parser.rl"
+ {
+ parser->body_start = p - buffer + 1;
+ if (parser->header_done != NULL) {
+ parser->header_done(parser->data, p + 1, pe - p - 1);
+ }
+ {p++; cs = 58; goto _out;}
+ }
+ goto st58;
+st58:
+ if ( ++p == pe )
+ goto _test_eof58;
+case 58:
+#line 387 "parser.c"
+ goto st0;
+tr21:
+#line 25 "parser.rl"
+ { MARK(field_start, p); }
+ goto st17;
+st17:
+ if ( ++p == pe )
+ goto _test_eof17;
+case 17:
+#line 397 "parser.c"
+ switch( (*p) ) {
+ case 33: goto st17;
+ case 58: goto tr24;
+ case 124: goto st17;
+ case 126: goto st17;
+ }
+ if ( (*p) < 45 ) {
+ if ( (*p) > 39 ) {
+ if ( 42 <= (*p) && (*p) <= 43 )
+ goto st17;
+ } else if ( (*p) >= 35 )
+ goto st17;
+ } else if ( (*p) > 46 ) {
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st17;
+ } else if ( (*p) > 90 ) {
+ if ( 94 <= (*p) && (*p) <= 122 )
+ goto st17;
+ } else
+ goto st17;
+ } else
+ goto st17;
+ goto st0;
+tr24:
+#line 26 "parser.rl"
+ {
+ parser->field_len = LEN(field_start, p);
+ }
+ goto st18;
+tr27:
+#line 30 "parser.rl"
+ { MARK(mark, p); }
+ goto st18;
+st18:
+ if ( ++p == pe )
+ goto _test_eof18;
+case 18:
+#line 436 "parser.c"
+ switch( (*p) ) {
+ case 13: goto tr26;
+ case 32: goto tr27;
+ }
+ goto tr25;
+tr25:
+#line 30 "parser.rl"
+ { MARK(mark, p); }
+ goto st19;
+st19:
+ if ( ++p == pe )
+ goto _test_eof19;
+case 19:
+#line 450 "parser.c"
+ if ( (*p) == 13 )
+ goto tr29;
+ goto st19;
+tr9:
+#line 41 "parser.rl"
+ {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st20;
+tr45:
+#line 65 "parser.rl"
+ {
+ if (parser->request_path != NULL) {
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+ }
+ }
+#line 41 "parser.rl"
+ {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st20;
+tr52:
+#line 52 "parser.rl"
+ {MARK(query_start, p); }
+#line 53 "parser.rl"
+ {
+ if (parser->query_string != NULL) {
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+ }
+ }
+#line 41 "parser.rl"
+ {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st20;
+tr56:
+#line 53 "parser.rl"
+ {
+ if (parser->query_string != NULL) {
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
+ }
+ }
+#line 41 "parser.rl"
+ {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+ }
+ }
+ goto st20;
+st20:
+ if ( ++p == pe )
+ goto _test_eof20;
+case 20:
+#line 510 "parser.c"
+ switch( (*p) ) {
+ case 32: goto tr31;
+ case 35: goto st0;
+ case 37: goto tr32;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto tr30;
+tr30:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+ goto st21;
+st21:
+ if ( ++p == pe )
+ goto _test_eof21;
+case 21:
+#line 528 "parser.c"
+ switch( (*p) ) {
+ case 32: goto tr34;
+ case 35: goto st0;
+ case 37: goto st22;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st21;
+tr32:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+ goto st22;
+st22:
+ if ( ++p == pe )
+ goto _test_eof22;
+case 22:
+#line 546 "parser.c"
+ if ( (*p) == 117 )
+ goto st24;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st23;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st23;
+ } else
+ goto st23;
+ goto st0;
+st23:
+ if ( ++p == pe )
+ goto _test_eof23;
+case 23:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st21;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st21;
+ } else
+ goto st21;
+ goto st0;
+st24:
+ if ( ++p == pe )
+ goto _test_eof24;
+case 24:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st23;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st23;
+ } else
+ goto st23;
+ goto st0;
+tr5:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+ goto st25;
+st25:
+ if ( ++p == pe )
+ goto _test_eof25;
+case 25:
+#line 592 "parser.c"
+ switch( (*p) ) {
+ case 43: goto st25;
+ case 58: goto st26;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st25;
+ } else if ( (*p) > 57 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto st25;
+ } else if ( (*p) >= 65 )
+ goto st25;
+ } else
+ goto st25;
+ goto st0;
+tr7:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+ goto st26;
+st26:
+ if ( ++p == pe )
+ goto _test_eof26;
+case 26:
+#line 617 "parser.c"
+ switch( (*p) ) {
+ case 32: goto tr8;
+ case 35: goto tr9;
+ case 37: goto st27;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st26;
+st27:
+ if ( ++p == pe )
+ goto _test_eof27;
+case 27:
+ if ( (*p) == 117 )
+ goto st29;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st28;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st28;
+ } else
+ goto st28;
+ goto st0;
+st28:
+ if ( ++p == pe )
+ goto _test_eof28;
+case 28:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st26;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st26;
+ } else
+ goto st26;
+ goto st0;
+st29:
+ if ( ++p == pe )
+ goto _test_eof29;
+case 29:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st28;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st28;
+ } else
+ goto st28;
+ goto st0;
+tr6:
+#line 22 "parser.rl"
+ {MARK(mark, p); }
+ goto st30;
+st30:
+ if ( ++p == pe )
+ goto _test_eof30;
+case 30:
+#line 676 "parser.c"
+ switch( (*p) ) {
+ case 32: goto tr44;
+ case 35: goto tr45;
+ case 37: goto st31;
+ case 63: goto tr47;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st30;
+st31:
+ if ( ++p == pe )
+ goto _test_eof31;
+case 31:
+ if ( (*p) == 117 )
+ goto st33;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st32;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st32;
+ } else
+ goto st32;
+ goto st0;
+st32:
+ if ( ++p == pe )
+ goto _test_eof32;
+case 32:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st30;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st30;
+ } else
+ goto st30;
+ goto st0;
+st33:
+ if ( ++p == pe )
+ goto _test_eof33;
+case 33:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st32;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st32;
+ } else
+ goto st32;
+ goto st0;
+tr47:
+#line 65 "parser.rl"
+ {
+ if (parser->request_path != NULL) {
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
+ }
+ }
+ goto st34;
+st34:
+ if ( ++p == pe )
+ goto _test_eof34;
+case 34:
+#line 740 "parser.c"
+ switch( (*p) ) {
+ case 32: goto tr51;
+ case 35: goto tr52;
+ case 37: goto tr53;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto tr50;
+tr50:
+#line 52 "parser.rl"
+ {MARK(query_start, p); }
+ goto st35;
+st35:
+ if ( ++p == pe )
+ goto _test_eof35;
+case 35:
+#line 758 "parser.c"
+ switch( (*p) ) {
+ case 32: goto tr55;
+ case 35: goto tr56;
+ case 37: goto st36;
+ case 127: goto st0;
+ }
+ if ( 0 <= (*p) && (*p) <= 31 )
+ goto st0;
+ goto st35;
+tr53:
+#line 52 "parser.rl"
+ {MARK(query_start, p); }
+ goto st36;
+st36:
+ if ( ++p == pe )
+ goto _test_eof36;
+case 36:
+#line 776 "parser.c"
+ if ( (*p) == 117 )
+ goto st38;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st37;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st37;
+ } else
+ goto st37;
+ goto st0;
+st37:
+ if ( ++p == pe )
+ goto _test_eof37;
+case 37:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st35;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st35;
+ } else
+ goto st35;
+ goto st0;
+st38:
+ if ( ++p == pe )
+ goto _test_eof38;
+case 38:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st37;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st37;
+ } else
+ goto st37;
+ goto st0;
+st39:
+ if ( ++p == pe )
+ goto _test_eof39;
+case 39:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st40;
+ case 95: goto st40;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st40;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st40;
+ } else
+ goto st40;
+ goto st0;
+st40:
+ if ( ++p == pe )
+ goto _test_eof40;
+case 40:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st41;
+ case 95: goto st41;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st41;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st41;
+ } else
+ goto st41;
+ goto st0;
+st41:
+ if ( ++p == pe )
+ goto _test_eof41;
+case 41:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st42;
+ case 95: goto st42;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st42;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st42;
+ } else
+ goto st42;
+ goto st0;
+st42:
+ if ( ++p == pe )
+ goto _test_eof42;
+case 42:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st43;
+ case 95: goto st43;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st43;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st43;
+ } else
+ goto st43;
+ goto st0;
+st43:
+ if ( ++p == pe )
+ goto _test_eof43;
+case 43:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st44;
+ case 95: goto st44;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st44;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st44;
+ } else
+ goto st44;
+ goto st0;
+st44:
+ if ( ++p == pe )
+ goto _test_eof44;
+case 44:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st45;
+ case 95: goto st45;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st45;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st45;
+ } else
+ goto st45;
+ goto st0;
+st45:
+ if ( ++p == pe )
+ goto _test_eof45;
+case 45:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st46;
+ case 95: goto st46;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st46;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st46;
+ } else
+ goto st46;
+ goto st0;
+st46:
+ if ( ++p == pe )
+ goto _test_eof46;
+case 46:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st47;
+ case 95: goto st47;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st47;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st47;
+ } else
+ goto st47;
+ goto st0;
+st47:
+ if ( ++p == pe )
+ goto _test_eof47;
+case 47:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st48;
+ case 95: goto st48;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st48;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st48;
+ } else
+ goto st48;
+ goto st0;
+st48:
+ if ( ++p == pe )
+ goto _test_eof48;
+case 48:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st49;
+ case 95: goto st49;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st49;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st49;
+ } else
+ goto st49;
+ goto st0;
+st49:
+ if ( ++p == pe )
+ goto _test_eof49;
+case 49:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st50;
+ case 95: goto st50;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st50;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st50;
+ } else
+ goto st50;
+ goto st0;
+st50:
+ if ( ++p == pe )
+ goto _test_eof50;
+case 50:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st51;
+ case 95: goto st51;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st51;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st51;
+ } else
+ goto st51;
+ goto st0;
+st51:
+ if ( ++p == pe )
+ goto _test_eof51;
+case 51:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st52;
+ case 95: goto st52;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st52;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st52;
+ } else
+ goto st52;
+ goto st0;
+st52:
+ if ( ++p == pe )
+ goto _test_eof52;
+case 52:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st53;
+ case 95: goto st53;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st53;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st53;
+ } else
+ goto st53;
+ goto st0;
+st53:
+ if ( ++p == pe )
+ goto _test_eof53;
+case 53:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st54;
+ case 95: goto st54;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st54;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st54;
+ } else
+ goto st54;
+ goto st0;
+st54:
+ if ( ++p == pe )
+ goto _test_eof54;
+case 54:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st55;
+ case 95: goto st55;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st55;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st55;
+ } else
+ goto st55;
+ goto st0;
+st55:
+ if ( ++p == pe )
+ goto _test_eof55;
+case 55:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st56;
+ case 95: goto st56;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st56;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st56;
+ } else
+ goto st56;
+ goto st0;
+st56:
+ if ( ++p == pe )
+ goto _test_eof56;
+case 56:
+ switch( (*p) ) {
+ case 32: goto tr2;
+ case 36: goto st57;
+ case 95: goto st57;
+ }
+ if ( (*p) < 48 ) {
+ if ( 45 <= (*p) && (*p) <= 46 )
+ goto st57;
+ } else if ( (*p) > 57 ) {
+ if ( 65 <= (*p) && (*p) <= 90 )
+ goto st57;
+ } else
+ goto st57;
+ goto st0;
+st57:
+ if ( ++p == pe )
+ goto _test_eof57;
+case 57:
+ if ( (*p) == 32 )
+ goto tr2;
+ goto st0;
+ }
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof11: cs = 11; goto _test_eof;
+ _test_eof12: cs = 12; goto _test_eof;
+ _test_eof13: cs = 13; goto _test_eof;
+ _test_eof14: cs = 14; goto _test_eof;
+ _test_eof15: cs = 15; goto _test_eof;
+ _test_eof16: cs = 16; goto _test_eof;
+ _test_eof58: cs = 58; goto _test_eof;
+ _test_eof17: cs = 17; goto _test_eof;
+ _test_eof18: cs = 18; goto _test_eof;
+ _test_eof19: cs = 19; goto _test_eof;
+ _test_eof20: cs = 20; goto _test_eof;
+ _test_eof21: cs = 21; goto _test_eof;
+ _test_eof22: cs = 22; goto _test_eof;
+ _test_eof23: cs = 23; goto _test_eof;
+ _test_eof24: cs = 24; goto _test_eof;
+ _test_eof25: cs = 25; goto _test_eof;
+ _test_eof26: cs = 26; goto _test_eof;
+ _test_eof27: cs = 27; goto _test_eof;
+ _test_eof28: cs = 28; goto _test_eof;
+ _test_eof29: cs = 29; goto _test_eof;
+ _test_eof30: cs = 30; goto _test_eof;
+ _test_eof31: cs = 31; goto _test_eof;
+ _test_eof32: cs = 32; goto _test_eof;
+ _test_eof33: cs = 33; goto _test_eof;
+ _test_eof34: cs = 34; goto _test_eof;
+ _test_eof35: cs = 35; goto _test_eof;
+ _test_eof36: cs = 36; goto _test_eof;
+ _test_eof37: cs = 37; goto _test_eof;
+ _test_eof38: cs = 38; goto _test_eof;
+ _test_eof39: cs = 39; goto _test_eof;
+ _test_eof40: cs = 40; goto _test_eof;
+ _test_eof41: cs = 41; goto _test_eof;
+ _test_eof42: cs = 42; goto _test_eof;
+ _test_eof43: cs = 43; goto _test_eof;
+ _test_eof44: cs = 44; goto _test_eof;
+ _test_eof45: cs = 45; goto _test_eof;
+ _test_eof46: cs = 46; goto _test_eof;
+ _test_eof47: cs = 47; goto _test_eof;
+ _test_eof48: cs = 48; goto _test_eof;
+ _test_eof49: cs = 49; goto _test_eof;
+ _test_eof50: cs = 50; goto _test_eof;
+ _test_eof51: cs = 51; goto _test_eof;
+ _test_eof52: cs = 52; goto _test_eof;
+ _test_eof53: cs = 53; goto _test_eof;
+ _test_eof54: cs = 54; goto _test_eof;
+ _test_eof55: cs = 55; goto _test_eof;
+ _test_eof56: cs = 56; goto _test_eof;
+ _test_eof57: cs = 57; goto _test_eof;
+
+ _test_eof: {}
+ _out: {}
+ }
+
+#line 116 "parser.rl"
+
+ parser->cs = cs;
+ parser->nread += p - (buffer + off);
+
+ assert(p <= pe && "buffer overflow after parsing execute");
+ assert(parser->nread <= len && "nread longer than length");
+ assert(parser->body_start <= len && "body starts after buffer end");
+ assert(parser->mark < len && "mark is after buffer end");
+ assert(parser->field_len <= len && "field has length longer than whole buffer");
+ assert(parser->field_start < len && "field starts after buffer end");
+
+ if(parser->body_start) {
+ /* final \r\n combo encountered so stop right here */
+ parser->nread++;
+ }
+
+ return(parser->nread);
+}
+
+int spyglass_http_parser_finish(http_parser *parser)
+{
+ int cs = parser->cs;
+
+
+ parser->cs = cs;
+
+ if (spyglass_http_parser_has_error(parser) ) {
+ return -1;
+ } else if (spyglass_http_parser_is_finished(parser) ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int spyglass_http_parser_has_error(http_parser *parser) {
+ return parser->cs == http_parser_error;
+}
+
+int spyglass_http_parser_is_finished(http_parser *parser) {
+ return parser->cs == http_parser_first_final;
+}
diff --git a/code/spyglass/ext/spyglass_parser/parser.h b/code/spyglass/ext/spyglass_parser/parser.h
new file mode 100644
index 0000000..8d074ba
--- /dev/null
+++ b/code/spyglass/ext/spyglass_parser/parser.h
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2005 Zed A. Shaw
+ * You can redistribute it and/or modify it under the same terms as Ruby.
+ */
+
+#ifndef http11_parser_h
+#define http11_parser_h
+
+#include <sys/types.h>
+
+#if defined(_WIN32)
+#include <stddef.h>
+#endif
+
+typedef void (*element_cb)(void *data, const char *at, size_t length);
+typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
+
+typedef struct http_parser {
+ int cs;
+ size_t body_start;
+ int content_len;
+ size_t nread;
+ size_t mark;
+ size_t field_start;
+ size_t field_len;
+ size_t query_start;
+
+ void *data;
+
+ field_cb http_field;
+ element_cb request_method;
+ element_cb request_uri;
+ element_cb fragment;
+ element_cb request_path;
+ element_cb query_string;
+ element_cb http_version;
+ element_cb header_done;
+
+} http_parser;
+
+int http_parser_init(http_parser *parser);
+int http_parser_finish(http_parser *parser);
+size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
+int http_parser_has_error(http_parser *parser);
+int http_parser_is_finished(http_parser *parser);
+
+#define http_parser_nread(parser) (parser)->nread
+
+#endif
diff --git a/code/spyglass/ext/spyglass_parser/parser.rl b/code/spyglass/ext/spyglass_parser/parser.rl
new file mode 100644
index 0000000..684596a
--- /dev/null
+++ b/code/spyglass/ext/spyglass_parser/parser.rl
@@ -0,0 +1,157 @@
+/**
+ * Copyright (c) 2005 Zed A. Shaw
+ * You can redistribute it and/or modify it under the same terms as Ruby.
+ */
+#include "parser.h"
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#define LEN(AT, FPC) (FPC - buffer - parser->AT)
+#define MARK(M,FPC) (parser->M = (FPC) - buffer)
+#define PTR_TO(F) (buffer + parser->F)
+
+/** Machine **/
+
+%%{
+
+ machine http_parser;
+
+ action mark {MARK(mark, fpc); }
+
+
+ action start_field { MARK(field_start, fpc); }
+ action write_field {
+ parser->field_len = LEN(field_start, fpc);
+ }
+
+ action start_value { MARK(mark, fpc); }
+ action write_value {
+ if (parser->http_field != NULL) {
+ parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
+ }
+ }
+ action request_method {
+ if (parser->request_method != NULL) {
+ parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
+ }
+ }
+ action request_uri {
+ if (parser->request_uri != NULL) {
+ parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
+ }
+ }
+ action fragment {
+ if (parser->fragment != NULL) {
+ parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
+ }
+ }
+
+ action start_query {MARK(query_start, fpc); }
+ action query_string {
+ if (parser->query_string != NULL) {
+ parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
+ }
+ }
+
+ action http_version {
+ if (parser->http_version != NULL) {
+ parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
+ }
+ }
+
+ action request_path {
+ if (parser->request_path != NULL) {
+ parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
+ }
+ }
+
+ action done {
+ parser->body_start = fpc - buffer + 1;
+ if (parser->header_done != NULL) {
+ parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
+ }
+ fbreak;
+ }
+
+ include http_parser_common "common.rl";
+
+}%%
+
+/** Data **/
+%% write data;
+
+int spyglass_http_parser_init(http_parser *parser) {
+ int cs = 0;
+ %% write init;
+ parser->cs = cs;
+ parser->body_start = 0;
+ parser->content_len = 0;
+ parser->mark = 0;
+ parser->nread = 0;
+ parser->field_len = 0;
+ parser->field_start = 0;
+
+ return(1);
+}
+
+
+/** exec **/
+size_t spyglass_http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
+ const char *p, *pe;
+ int cs = parser->cs;
+
+ assert(off <= len && "offset past end of buffer");
+
+ p = buffer+off;
+ pe = buffer+len;
+
+ assert(*pe == '\0' && "pointer does not end on NUL");
+ assert(pe - p == len - off && "pointers aren't same distance");
+
+
+ %% write exec;
+
+ parser->cs = cs;
+ parser->nread += p - (buffer + off);
+
+ assert(p <= pe && "buffer overflow after parsing execute");
+ assert(parser->nread <= len && "nread longer than length");
+ assert(parser->body_start <= len && "body starts after buffer end");
+ assert(parser->mark < len && "mark is after buffer end");
+ assert(parser->field_len <= len && "field has length longer than whole buffer");
+ assert(parser->field_start < len && "field starts after buffer end");
+
+ if(parser->body_start) {
+ /* final \r\n combo encountered so stop right here */
+ parser->nread++;
+ }
+
+ return(parser->nread);
+}
+
+int spyglass_http_parser_finish(http_parser *parser)
+{
+ int cs = parser->cs;
+
+
+ parser->cs = cs;
+
+ if (spyglass_http_parser_has_error(parser) ) {
+ return -1;
+ } else if (spyglass_http_parser_is_finished(parser) ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int spyglass_http_parser_has_error(http_parser *parser) {
+ return parser->cs == http_parser_error;
+}
+
+int spyglass_http_parser_is_finished(http_parser *parser) {
+ return parser->cs == http_parser_first_final;
+}
diff --git a/code/spyglass/ext/spyglass_parser/spyglass.c b/code/spyglass/ext/spyglass_parser/spyglass.c
new file mode 100644
index 0000000..aa91520
--- /dev/null
+++ b/code/spyglass/ext/spyglass_parser/spyglass.c
@@ -0,0 +1,436 @@
+/**
+ * Thin Parser adpated to Spyglass.
+ *
+ * Orignal version Copyright (c) 2005 Zed A. Shaw
+ * You can redistribute it and/or modify it under the same terms as Ruby.
+ */
+#include "ruby.h"
+#include "ext_help.h"
+#include <assert.h>
+#include <string.h>
+#include "parser.h"
+#include <ctype.h>
+
+static VALUE mSpyglass;
+static VALUE cHttpParser;
+static VALUE eHttpParserError;
+
+static VALUE global_empty;
+static VALUE global_http_prefix;
+static VALUE global_request_method;
+static VALUE global_request_uri;
+static VALUE global_fragment;
+static VALUE global_query_string;
+static VALUE global_http_version;
+static VALUE global_content_length;
+static VALUE global_http_content_length;
+static VALUE global_request_path;
+static VALUE global_content_type;
+static VALUE global_http_content_type;
+static VALUE global_gateway_interface;
+static VALUE global_gateway_interface_value;
+static VALUE global_server_name;
+static VALUE global_server_port;
+static VALUE global_server_protocol;
+static VALUE global_server_protocol_value;
+static VALUE global_http_host;
+static VALUE global_port_80;
+static VALUE global_http_body;
+static VALUE global_url_scheme;
+static VALUE global_url_scheme_value;
+static VALUE global_script_name;
+static VALUE global_path_info;
+
+#define TRIE_INCREASE 30
+
+/** Defines common length and error messages for input length validation. */
+#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length."
+
+/** Validates the max length of given input and throws an HttpParserError exception if over. */
+#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
+
+/** Defines global strings in the init method. */
+#define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
+
+/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_PTR */
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(s) (RSTRING(s)->ptr)
+#endif
+
+/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_LEN */
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(s) (RSTRING(s)->len)
+#endif
+
+/* Defines the maximum allowed lengths for various input elements.*/
+DEF_MAX_LENGTH(FIELD_NAME, 256);
+DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
+DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
+DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
+DEF_MAX_LENGTH(REQUEST_PATH, 1024);
+DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
+DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
+
+
+static void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
+{
+ char *ch, *end;
+ VALUE req = (VALUE)data;
+ VALUE v = Qnil;
+ VALUE f = Qnil;
+
+ VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
+ VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
+
+ v = rb_str_new(value, vlen);
+ f = rb_str_dup(global_http_prefix);
+ f = rb_str_buf_cat(f, field, flen);
+
+ for(ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix), end = RSTRING_PTR(f) + RSTRING_LEN(f); ch < end; ch++) {
+ if(*ch == '-') {
+ *ch = '_';
+ } else {
+ *ch = toupper(*ch);
+ }
+ }
+
+ rb_hash_aset(req, f, v);
+}
+
+static void request_method(void *data, const char *at, size_t length)
+{
+ VALUE req = (VALUE)data;
+ VALUE val = Qnil;
+
+ val = rb_str_new(at, length);
+ rb_hash_aset(req, global_request_method, val);
+}
+
+static void request_uri(void *data, const char *at, size_t length)
+{
+ VALUE req = (VALUE)data;
+ VALUE val = Qnil;
+
+ VALIDATE_MAX_LENGTH(length, REQUEST_URI);
+
+ val = rb_str_new(at, length);
+ rb_hash_aset(req, global_request_uri, val);
+}
+
+static void fragment(void *data, const char *at, size_t length)
+{
+ VALUE req = (VALUE)data;
+ VALUE val = Qnil;
+
+ VALIDATE_MAX_LENGTH(length, FRAGMENT);
+
+ val = rb_str_new(at, length);
+ rb_hash_aset(req, global_fragment, val);
+}
+
+static void request_path(void *data, const char *at, size_t length)
+{
+ VALUE req = (VALUE)data;
+ VALUE val = Qnil;
+
+ VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
+
+ val = rb_str_new(at, length);
+ rb_hash_aset(req, global_request_path, val);
+ rb_hash_aset(req, global_path_info, val);
+}
+
+static void query_string(void *data, const char *at, size_t length)
+{
+ VALUE req = (VALUE)data;
+ VALUE val = Qnil;
+
+ VALIDATE_MAX_LENGTH(length, QUERY_STRING);
+
+ val = rb_str_new(at, length);
+ rb_hash_aset(req, global_query_string, val);
+}
+
+static void http_version(void *data, const char *at, size_t length)
+{
+ VALUE req = (VALUE)data;
+ VALUE val = rb_str_new(at, length);
+ rb_hash_aset(req, global_http_version, val);
+}
+
+/** Finalizes the request header to have a bunch of stuff that's
+ needed. */
+
+static void header_done(void *data, const char *at, size_t length)
+{
+ VALUE req = (VALUE)data;
+ VALUE temp = Qnil;
+ VALUE ctype = Qnil;
+ VALUE clen = Qnil;
+ VALUE body = Qnil;
+ char *colon = NULL;
+
+ clen = rb_hash_aref(req, global_http_content_length);
+ if(clen != Qnil) {
+ rb_hash_aset(req, global_content_length, clen);
+ rb_hash_delete(req, global_http_content_length);
+ }
+
+ ctype = rb_hash_aref(req, global_http_content_type);
+ if(ctype != Qnil) {
+ rb_hash_aset(req, global_content_type, ctype);
+ rb_hash_delete(req, global_http_content_type);
+ }
+
+ rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
+ if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
+ /* ruby better close strings off with a '\0' dammit */
+ colon = strchr(RSTRING_PTR(temp), ':');
+ if(colon != NULL) {
+ rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
+ rb_hash_aset(req, global_server_port,
+ rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
+ RSTRING_LEN(temp)));
+ } else {
+ rb_hash_aset(req, global_server_name, temp);
+ rb_hash_aset(req, global_server_port, global_port_80);
+ }
+ }
+
+ /* grab the initial body and stuff it into the hash */
+ if(length > 0) {
+ body = rb_hash_aref(req, global_http_body);
+ rb_io_write(body, rb_str_new(at, length));
+ }
+
+ /* according to Rack specs, query string must be empty string if none */
+ if (rb_hash_aref(req, global_query_string) == Qnil) {
+ rb_hash_aset(req, global_query_string, global_empty);
+ }
+ if (rb_hash_aref(req, global_path_info) == Qnil) {
+ rb_hash_aset(req, global_path_info, global_empty);
+ }
+
+ /* set some constants */
+ rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
+ rb_hash_aset(req, global_url_scheme, global_url_scheme_value);
+ rb_hash_aset(req, global_script_name, global_empty);
+}
+
+
+void Spyglass_HttpParser_free(void *data) {
+ TRACE();
+
+ if(data) {
+ free(data);
+ }
+}
+
+
+VALUE Spyglass_HttpParser_alloc(VALUE klass)
+{
+ VALUE obj;
+ http_parser *hp = ALLOC_N(http_parser, 1);
+ TRACE();
+ hp->http_field = http_field;
+ hp->request_method = request_method;
+ hp->request_uri = request_uri;
+ hp->fragment = fragment;
+ hp->request_path = request_path;
+ hp->query_string = query_string;
+ hp->http_version = http_version;
+ hp->header_done = header_done;
+ spyglass_http_parser_init(hp);
+
+ obj = Data_Wrap_Struct(klass, NULL, Spyglass_HttpParser_free, hp);
+
+ return obj;
+}
+
+
+/**
+ * call-seq:
+ * parser.new -> parser
+ *
+ * Creates a new parser.
+ */
+VALUE Spyglass_HttpParser_init(VALUE self)
+{
+ http_parser *http = NULL;
+ DATA_GET(self, http_parser, http);
+ spyglass_http_parser_init(http);
+
+ return self;
+}
+
+
+/**
+ * call-seq:
+ * parser.reset -> nil
+ *
+ * Resets the parser to it's initial state so that you can reuse it
+ * rather than making new ones.
+ */
+VALUE Spyglass_HttpParser_reset(VALUE self)
+{
+ http_parser *http = NULL;
+ DATA_GET(self, http_parser, http);
+ spyglass_http_parser_init(http);
+
+ return Qnil;
+}
+
+
+/**
+ * call-seq:
+ * parser.finish -> true/false
+ *
+ * Finishes a parser early which could put in a "good" or bad state.
+ * You should call reset after finish it or bad things will happen.
+ */
+VALUE Spyglass_HttpParser_finish(VALUE self)
+{
+ http_parser *http = NULL;
+ DATA_GET(self, http_parser, http);
+ spyglass_http_parser_finish(http);
+
+ return spyglass_http_parser_is_finished(http) ? Qtrue : Qfalse;
+}
+
+
+/**
+ * call-seq:
+ * parser.execute(req_hash, data, start) -> Integer
+ *
+ * Takes a Hash and a String of data, parses the String of data filling in the Hash
+ * returning an Integer to indicate how much of the data has been read. No matter
+ * what the return value, you should call HttpParser#finished? and HttpParser#error?
+ * to figure out if it's done parsing or there was an error.
+ *
+ * This function now throws an exception when there is a parsing error. This makes
+ * the logic for working with the parser much easier. You can still test for an
+ * error, but now you need to wrap the parser with an exception handling block.
+ *
+ * The third argument allows for parsing a partial request and then continuing
+ * the parsing from that position. It needs all of the original data as well
+ * so you have to append to the data buffer as you read.
+ */
+VALUE Spyglass_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
+{
+ http_parser *http = NULL;
+ int from = 0;
+ char *dptr = NULL;
+ long dlen = 0;
+
+ DATA_GET(self, http_parser, http);
+
+ from = FIX2INT(start);
+ dptr = RSTRING_PTR(data);
+ dlen = RSTRING_LEN(data);
+
+ if(from >= dlen) {
+ rb_raise(eHttpParserError, "Requested start is after data buffer end.");
+ } else {
+ http->data = (void *)req_hash;
+ spyglass_http_parser_execute(http, dptr, dlen, from);
+
+ VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
+
+ if(spyglass_http_parser_has_error(http)) {
+ rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
+ } else {
+ return INT2FIX(http_parser_nread(http));
+ }
+ }
+}
+
+
+
+/**
+ * call-seq:
+ * parser.error? -> true/false
+ *
+ * Tells you whether the parser is in an error state.
+ */
+VALUE Spyglass_HttpParser_has_error(VALUE self)
+{
+ http_parser *http = NULL;
+ DATA_GET(self, http_parser, http);
+
+ return spyglass_http_parser_has_error(http) ? Qtrue : Qfalse;
+}
+
+
+/**
+ * call-seq:
+ * parser.finished? -> true/false
+ *
+ * Tells you whether the parser is finished or not and in a good state.
+ */
+VALUE Spyglass_HttpParser_is_finished(VALUE self)
+{
+ http_parser *http = NULL;
+ DATA_GET(self, http_parser, http);
+
+ return spyglass_http_parser_is_finished(http) ? Qtrue : Qfalse;
+}
+
+
+/**
+ * call-seq:
+ * parser.nread -> Integer
+ *
+ * Returns the amount of data processed so far during this processing cycle. It is
+ * set to 0 on initialize or reset calls and is incremented each time execute is called.
+ */
+VALUE Spyglass_HttpParser_nread(VALUE self)
+{
+ http_parser *http = NULL;
+ DATA_GET(self, http_parser, http);
+
+ return INT2FIX(http->nread);
+}
+
+void Init_spyglass_parser()
+{
+
+ mSpyglass = rb_define_module("Spyglass");
+
+ DEF_GLOBAL(empty, "");
+ DEF_GLOBAL(http_prefix, "HTTP_");
+ DEF_GLOBAL(request_method, "REQUEST_METHOD");
+ DEF_GLOBAL(request_uri, "REQUEST_URI");
+ DEF_GLOBAL(fragment, "FRAGMENT");
+ DEF_GLOBAL(query_string, "QUERY_STRING");
+ DEF_GLOBAL(http_version, "HTTP_VERSION");
+ DEF_GLOBAL(request_path, "REQUEST_PATH");
+ DEF_GLOBAL(content_length, "CONTENT_LENGTH");
+ DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
+ DEF_GLOBAL(content_type, "CONTENT_TYPE");
+ DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
+ DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
+ DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
+ DEF_GLOBAL(server_name, "SERVER_NAME");
+ DEF_GLOBAL(server_port, "SERVER_PORT");
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
+ DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
+ DEF_GLOBAL(http_host, "HTTP_HOST");
+ DEF_GLOBAL(port_80, "80");
+ DEF_GLOBAL(http_body, "rack.input");
+ DEF_GLOBAL(url_scheme, "rack.url_scheme");
+ DEF_GLOBAL(url_scheme_value, "http");
+ DEF_GLOBAL(script_name, "SCRIPT_NAME");
+ DEF_GLOBAL(path_info, "PATH_INFO");
+
+ eHttpParserError = rb_define_class_under(mSpyglass, "InvalidRequest", rb_eIOError);
+
+ cHttpParser = rb_define_class_under(mSpyglass, "HttpParser", rb_cObject);
+ rb_define_alloc_func(cHttpParser, Spyglass_HttpParser_alloc);
+ rb_define_method(cHttpParser, "initialize", Spyglass_HttpParser_init,0);
+ rb_define_method(cHttpParser, "reset", Spyglass_HttpParser_reset,0);
+ rb_define_method(cHttpParser, "finish", Spyglass_HttpParser_finish,0);
+ rb_define_method(cHttpParser, "execute", Spyglass_HttpParser_execute,3);
+ rb_define_method(cHttpParser, "error?", Spyglass_HttpParser_has_error,0);
+ rb_define_method(cHttpParser, "finished?", Spyglass_HttpParser_is_finished,0);
+ rb_define_method(cHttpParser, "nread", Spyglass_HttpParser_nread,0);
+}