Skip to content

Commit

Permalink
Major update to parser.rl
Browse files Browse the repository at this point in the history
I'm changing Zed's original design of having a callback for each element in
the parser. Rather there will be two callbacks for headers: one for unknown
headers and one for known headers - there is an enum which defines the known
headers.

The idea here is to easily add preallocated global variables for often seen
header values in the ruby binding. This has only been done so far with
Content-Type, but I will add many more soon.

This change simplifies ebb.c greatly.
  • Loading branch information
Ryan Dahl committed Mar 22, 2008
1 parent eb87a28 commit 9d81073
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 157 deletions.
74 changes: 10 additions & 64 deletions src/ebb.c
Expand Up @@ -37,7 +37,7 @@ void env_add(ebb_client *client, const char *field, int flen, const char *value,
client->parser.overflow_error = TRUE;
return;
}
client->env[client->env_size].type = EBB_FIELD_VALUE_PAIR;
client->env[client->env_size].type = -1;
client->env[client->env_size].field = field;
client->env[client->env_size].field_length = flen;
client->env[client->env_size].value = value;
Expand Down Expand Up @@ -70,57 +70,10 @@ void http_field_cb(void *data, const char *field, size_t flen, const char *value
}


void request_method_cb(void *data, const char *at, size_t length)
void on_element(void *data, int type, const char *at, size_t length)
{
ebb_client *client = (ebb_client*)(data);
env_add_const(client, EBB_REQUEST_METHOD, at, length);
}


void request_uri_cb(void *data, const char *at, size_t length)
{
ebb_client *client = (ebb_client*)(data);
env_add_const(client, EBB_REQUEST_URI, at, length);
}


void fragment_cb(void *data, const char *at, size_t length)
{
ebb_client *client = (ebb_client*)(data);
env_add_const(client, EBB_FRAGMENT, at, length);
}


void request_path_cb(void *data, const char *at, size_t length)
{
ebb_client *client = (ebb_client*)(data);
env_add_const(client, EBB_REQUEST_PATH, at, length);
}


void query_string_cb(void *data, const char *at, size_t length)
{
ebb_client *client = (ebb_client*)(data);
env_add_const(client, EBB_QUERY_STRING, at, length);
}


void http_version_cb(void *data, const char *at, size_t length)
{
ebb_client *client = (ebb_client*)(data);
env_add_const(client, EBB_HTTP_VERSION, at, length);
}


void content_length_cb(void *data, const char *at, size_t length)
{
ebb_client *client = (ebb_client*)(data);
env_add_const(client, EBB_CONTENT_LENGTH, at, length);
/* atoi_length - why isn't this in the statndard library? i hate c */
assert(client->content_length == 0);
int i, mult;
for(mult=1, i=length-1; i>=0; i--, mult*=10)
client->content_length += (at[i] - '0') * mult;
env_add_const(client, type, at, length);
}


Expand All @@ -133,7 +86,7 @@ static void dispatch(ebb_client *client)

/* Set the env variables */
if(server->port) {
env_add_const(client, EBB_SERVER_PORT
env_add_const(client, MONGREL_SERVER_PORT
, server->port
, strlen(server->port)
);
Expand All @@ -157,7 +110,7 @@ static void on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents)
}

#define client_finished_parsing http_parser_is_finished(&client->parser)
#define total_request_size (client->content_length + client->parser.nread)
#define total_request_size (client->parser.content_length + client->parser.nread)

static void* read_body_into_file(void *_client)
{
Expand All @@ -167,7 +120,7 @@ static void* read_body_into_file(void *_client)

assert(client->open);
assert(client->server->open);
assert(client->content_length > 0);
assert(client->parser.content_length > 0);
assert(client_finished_parsing);

/* set blocking socket */
Expand Down Expand Up @@ -197,10 +150,10 @@ static void* read_body_into_file(void *_client)
int bufsize = 5*1024;
char buffer[bufsize];
size_t received;
while(written < client->content_length) {
while(written < client->parser.content_length) {
received = recv(client->fd
, buffer
, min(client->content_length - written, bufsize)
, min(client->parser.content_length - written, bufsize)
, 0
);
if(received < 0) goto error;
Expand Down Expand Up @@ -362,19 +315,12 @@ static client_init(ebb_server *server, ebb_client *client)
http_parser_init(&(client->parser));
client->parser.data = client;
client->parser.http_field = http_field_cb;
client->parser.request_method = request_method_cb;
client->parser.request_uri = request_uri_cb;
client->parser.fragment = fragment_cb;
client->parser.request_path = request_path_cb;
client->parser.query_string = query_string_cb;
client->parser.http_version = http_version_cb;
client->parser.content_length = content_length_cb;
client->parser.on_element = on_element;

/* OTHER */
client->env_size = 0;
client->read = client->nread_from_body = 0;
client->response_buffer->len = 0; /* see note in ebb_client_close */
client->content_length = 0;
if(client->request_buffer == NULL) {
client->request_buffer = (char*)malloc(EBB_BUFFERSIZE);
}
Expand Down Expand Up @@ -680,7 +626,7 @@ int ebb_client_read(ebb_client *client, char *buffer, int length)
} else {
char* request_body = client->request_buffer + client->parser.nread;

read = ramp(min(length, client->content_length - client->nread_from_body));
read = ramp(min(length, client->parser.content_length - client->nread_from_body));
memcpy( buffer
, request_body + client->nread_from_body
, read
Expand Down
13 changes: 1 addition & 12 deletions src/ebb.h
Expand Up @@ -35,16 +35,7 @@ void ebb_client_write_body(ebb_client*, const char *data, int length);
void ebb_client_begin_transmission( ebb_client *client);

struct ebb_env_item {
enum { EBB_FIELD_VALUE_PAIR
, EBB_REQUEST_METHOD
, EBB_REQUEST_URI
, EBB_FRAGMENT
, EBB_REQUEST_PATH
, EBB_QUERY_STRING
, EBB_HTTP_VERSION
, EBB_SERVER_PORT
, EBB_CONTENT_LENGTH
} type;
int type;
const char *field;
int field_length;
const char *value;
Expand All @@ -66,8 +57,6 @@ struct ebb_client {
char upload_file_filename[200];
FILE *upload_file;

int content_length;

ev_io write_watcher;
GString *response_buffer;
size_t written;
Expand Down
48 changes: 26 additions & 22 deletions src/ebb_ruby.c
Expand Up @@ -22,6 +22,7 @@ static VALUE global_request_body;
static VALUE global_server_port;
static VALUE global_path_info;
static VALUE global_content_length;
static VALUE global_content_type;
static VALUE global_http_host;

/* You don't want to run more than one server per Ruby VM. Really
Expand Down Expand Up @@ -118,29 +119,31 @@ VALUE server_open(VALUE _)

VALUE env_field(struct ebb_env_item *item)
{
VALUE f;
if(item->field) {
VALUE f = rb_str_new(NULL, RSTRING_LEN(global_http_prefix) + item->field_length);
memcpy( RSTRING_PTR(f)
, RSTRING_PTR(global_http_prefix)
, RSTRING_LEN(global_http_prefix)
);
int i;
for(i = 0; i < item->field_length; i++) {
char *ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix) + i;
*ch = item->field[i] == '-' ? '_' : ASCII_UPPER(item->field[i]);
}
return f;
}
switch(item->type) {
case EBB_FIELD_VALUE_PAIR:
f = rb_str_new(NULL, RSTRING_LEN(global_http_prefix) + item->field_length);
memcpy( RSTRING_PTR(f)
, RSTRING_PTR(global_http_prefix)
, RSTRING_LEN(global_http_prefix)
);
int i;
for(i = 0; i < item->field_length; i++) {
char *ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix) + i;
*ch = item->field[i] == '-' ? '_' : ASCII_UPPER(item->field[i]);
}
return f;
case EBB_REQUEST_METHOD: return global_request_method;
case EBB_REQUEST_URI: return global_request_uri;
case EBB_FRAGMENT: return global_fragment;
case EBB_REQUEST_PATH: return global_request_path;
case EBB_QUERY_STRING: return global_query_string;
case EBB_HTTP_VERSION: return global_http_version;
case EBB_SERVER_PORT: return global_server_port;
case EBB_CONTENT_LENGTH: return global_content_length;
case MONGREL_REQUEST_METHOD: return global_request_method;
case MONGREL_REQUEST_URI: return global_request_uri;
case MONGREL_FRAGMENT: return global_fragment;
case MONGREL_REQUEST_PATH: return global_request_path;
case MONGREL_QUERY_STRING: return global_query_string;
case MONGREL_HTTP_VERSION: return global_http_version;
case MONGREL_SERVER_PORT: return global_server_port;
case MONGREL_CONTENT_LENGTH: return global_content_length;
case MONGREL_CONTENT_TYPE: return global_content_type;
}
fprintf(stderr, "Unknown environ type: %d", item->type);
assert(FALSE);
return Qnil;
}
Expand Down Expand Up @@ -250,7 +253,8 @@ void Init_ebb_ext()
DEF_GLOBAL(request_body, "REQUEST_BODY");
DEF_GLOBAL(server_port, "SERVER_PORT");
DEF_GLOBAL(path_info, "PATH_INFO");
DEF_GLOBAL(content_length, "CONTENT_LENGTH");
DEF_GLOBAL(content_length, "HTTP_CONTENT_LENGTH");
DEF_GLOBAL(content_type, "HTTP_CONTENT_TYPE");
DEF_GLOBAL(http_host, "HTTP_HOST");

rb_define_singleton_method(mFFI, "server_process_connections", server_process_connections, 0);
Expand Down
27 changes: 19 additions & 8 deletions src/parser.h
Expand Up @@ -12,14 +12,30 @@
#include <stddef.h>
#endif


enum { MONGREL_REQUEST_METHOD
, MONGREL_REQUEST_URI
, MONGREL_FRAGMENT
, MONGREL_REQUEST_PATH
, MONGREL_QUERY_STRING
, MONGREL_HTTP_VERSION
, MONGREL_CONTENT_LENGTH
, MONGREL_CONTENT_TYPE
/* below - not used in the parser but often used by users of parser */
, MONGREL_SERVER_PORT
};

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 void (*new_element_cb)(void *data, int type, const char *at, size_t length);



typedef struct http_parser {
int cs;
int overflow_error;
size_t body_start;
int content_len;
size_t content_length;
size_t nread;
size_t mark;
size_t field_start;
Expand All @@ -29,14 +45,9 @@ typedef struct http_parser {
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;
element_cb content_length;
new_element_cb on_element;
} http_parser;

void http_parser_init(http_parser *parser);
Expand Down

0 comments on commit 9d81073

Please sign in to comment.