diff options
| author | mo khan <mo@mokhan.ca> | 2013-08-28 07:20:13 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2013-08-28 07:20:13 -0600 |
| commit | db1191ec0e7305d684383f8974e2fa437f77ad5a (patch) | |
| tree | 5e27b197dff849d22d8e1f50eb75aa499b16bd06 /code/spyglass/ext/spyglass_parser | |
Diffstat (limited to 'code/spyglass/ext/spyglass_parser')
| -rw-r--r-- | code/spyglass/ext/spyglass_parser/common.rl | 55 | ||||
| -rw-r--r-- | code/spyglass/ext/spyglass_parser/ext_help.h | 14 | ||||
| -rw-r--r-- | code/spyglass/ext/spyglass_parser/extconf.rb | 6 | ||||
| -rw-r--r-- | code/spyglass/ext/spyglass_parser/parser.c | 1249 | ||||
| -rw-r--r-- | code/spyglass/ext/spyglass_parser/parser.h | 49 | ||||
| -rw-r--r-- | code/spyglass/ext/spyglass_parser/parser.rl | 157 | ||||
| -rw-r--r-- | code/spyglass/ext/spyglass_parser/spyglass.c | 436 |
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); +} |
