* fix a bug

* fix a bug

(%
 % rowl - generation 1
 % Copyright (C) 2010 nineties
 %
 % $Id: eval.rl 2010-05-29 11:05:11 nineties $
 %);

include(stddef,code);
export(init_evaluator, assign, deref, check_arity, eval_sexp);

symbol_map: NULL; (% (symbol name, scope id) -> symbol object %);

scopeid: 0;
scopeid_stack: NULL;

(% p0: (symbol name, scope id) %);
symbol_hash: (p0) {
    return strhash(p0[0])*3 + p0[1];
};

symbol_equal: (p0, p1) {
    if (p0[1] != p1[1]) { return FALSE; };
    return streq(p0[0], p1[0]);
};

scope_push: () {
    scopeid = scopeid + 1;
    vec_pushback(scopeid_stack, scopeid);
};

scope_pop: () {
    vec_popback(scopeid_stack);
};

(% p0: symbol name, p1: value %);
assign: (p0, p1) {
    allocate(1);
    x0 = vec_at(scopeid_stack, vec_size(scopeid_stack)-1);
    map_add(symbol_map, mktup2(p0, x0), p1);
};

(% p0: symbol object %);
deref: (p0) {
    allocate(4);
    x0 = vec_size(scopeid_stack)-1;
    x3 = sym_name(p0);
    while (x0 >= 0) {
        x1 = vec_at(scopeid_stack, x0); (% scopeid-id %);
        x2 = map_find(symbol_map, mktup2(x3, x1));
        if (x2 != NULL) { return x2; };
        x0 = x0 - 1;
    };
    return p0;
};

check_arity: (p0, p1, p2) {
    if (length(p0) != p1) {
        fputs(stderr, "ERROR '");
        fputs(stderr, p2);
        fputs(stderr, "': required ");
        fputi(stderr, p1);
        fputs(stderr, " arguments");
        exit(1);
    }
};

eval_args: (p0) {
    if (p0 == nil_sym) { return nil_sym; };
    return mkcons(eval_sexp(car(p0)), eval_args(cdr(p0)));
};

eval_cons: (p0) {
    allocate(2);
    x0 = car(p0);
    if (sym_p(x0) == nil_sym) { goto &eval_cons_error; };
    x1 = eval_sexp(x0);
    if (x1 == var_sym)     { return eval_var(cdr(p0)); };
    if (x1 == set_sym)     { return eval_set(cdr(p0)); };
    if (x1 == if_sym)      { return eval_if(cdr(p0)); };
    if (x1 == cond_sym)    { return eval_cond(cdr(p0)); };
    if (x1 == while_sym)   { return eval_while(cdr(p0)); };
    if (x1 == do_sym)      { return eval_do(cdr(p0)); };
    if (x1 == lambda_sym)  { return eval_lambda(cdr(p0)); };
    if (x1 == macro_sym)   { return eval_macro(cdr(p0)); };
    if (x1 == import_sym)  { return eval_import(cdr(p0)); };
    if (prim_p(x1) != nil_sym)   { return (prim_funptr(x1))(eval_args(cdr(p0))); };
    if (lambda_p(x1) != nil_sym) { return eval_applambda(x1, cdr(p0)); };
    if (macro_p(x1) != nil_sym)  { return eval_appmacro(x1, cdr(p0)); };
label eval_cons_error;
    fputs(stderr, "ERROR: invalid application of '");
    pp_sexp(stderr, car(p0));
    fputs(stderr, "'\n");
    exit(1);
};

(% (var <id> <value>) %);
eval_var: (p0) {
    allocate(2);
    check_arity(p0, 2, "var");
    x0 = car(p0);
    x1 = eval_sexp(cadr(p0));
    sym_set(x0, x1);
    assign(sym_name(x0), x0);
    return x0;
};

eval_set: (p0) {
    allocate(2);
    check_arity(p0, 2, "set");
    x0 = deref(car(p0));
    x1 = eval_sexp(cadr(p0));
    if (sym_value(x0) == NULL) {
        fputs(stderr, "ERROR 'eval_set': undefined variable '");
        fputs(stderr, sym_name(x0));
        fputs(stderr, "'\n");
        exit(1);
    };
    sym_set(x0, x1);
    return x0;
};

eval_quote: (p0) {
    allocate(1);
    if (cons_p(p0) != nil_sym) {
        return mkcons(eval_quote(car(p0)), eval_quote(cdr(p0)));
    };
    if (unquote_p(p0) != nil_sym) {
        return eval_sexp(unquote_sexp(p0));
    };
    return p0;
};

(% p0 : (cond ifthen ifelse) %);
eval_if: (p0) {
    allocate(2);
    check_arity(p0, 3, "if");
    scope_push();
    x0 = eval_sexp(car(p0));
    p0 = cdr(p0);
    if (x0 != nil_sym)  {
        x1 = eval_sexp(car(p0));
        scope_pop();
        return x1;
    };
    x1 = eval_sexp(cadr(p0));
    scope_pop();
    return x1;
};

eval_cond: (p0) {
    allocate(1);
    scope_push();
    while (p0 != nil_sym) {
        x0 = car(p0);
        check_arity(x0, 2, "cond");
        if (eval_sexp(car(x0)) != nil_sym) {
            scope_pop();
            return eval_sexp(cadr(x0));
        };
        p0 = cdr(p0);
    };
    fputs(stderr, "ERROR 'eval_cond': any condition was not met\n");
    exit(1);
};

(% p0: (cond body) %);
eval_while: (p0) {
    allocate(3);
    check_arity(p0, 2, "while");
    scope_push();
    x0 = car(p0); (% condition %);
    x1 = cadr(p0); (% body %);
    x2 = eval_sexp(x0);
    while (x2 != nil_sym) {
        eval_sexp(x1);
        x2 = eval_sexp(x0);
    };
    scope_pop();
    if (x2 != nil_sym) {
        fputs(stderr, "ERROR 'while': conditional expression could not evaluated to true/false\n");
        exit(1);
    };
    return nil_sym;
};

eval_do: (p0) {
    allocate(1);
    scope_push();
    while (p0 != nil_sym) {
        x0 = eval_sexp(car(p0));
        p0 = cdr(p0);
    };
    scope_pop();
    return x0;
};

(% (lambda (params) body) %);
eval_lambda: (p0) {
    allocate(2);
    check_arity(p0, 2, "lambda");
    x0 = car(p0); (% params %);
    x1 = cadr(p0); (% body %);
    return mklambda(x0, x1);
};

(% (macro (params) body) %);
eval_macro: (p0) {
    allocate(2);
    check_arity(p0, 2, "macro");
    x0 = car(p0); (% params %);
    x1 = cadr(p0); (% body %);
    return mkmacro(x0, x1);
};

(% p0: argument pattern, p1:arguments %);
match_args: (p0, p1) {
    allocate(1);
    if (sym_p(p0) != nil_sym) {
        x0 = mksym(sym_name(p0));
        sym_set(x0, p1);
        assign(sym_name(x0), x0);
        return;
    };
    if (cons_p(p0) != nil_sym) {
        match_args(car(p0), car(p1));
        match_args(cdr(p0), cdr(p1));
        return;
    };
    fputs(stderr, "ERROR: invalid argument pattern\n");
    exit(1);
};

(% p0: lambda, p1: params %);
eval_applambda: (p0, p1) {
    allocate(1);
    scope_push();
    match_args(lambda_params(p0), eval_args(p1));
    x0 = eval_sexp(lambda_body(p0));
    scope_pop();
    return x0;
};

(% p0: macro, p1: params %);
eval_appmacro: (p0, p1) {
    allocate(3);
    scope_push();
    match_args(macro_params(p0), p1);
    x0 = eval_sexp(macro_body(p0));
    scope_pop();
    return eval_sexp(x0);
    (%
    x0 = macro_params(p0);
    if (length(x0) != length(p1)) {
        fputs(stderr, "ERROR: invalid number of argument (must be ");
        fputi(stderr, length(x0));
        fputs(stderr, ")\n");
        exit(1);
    };

    scope_push();
    while (x0 != nil_sym) {
        x1 = mksym(sym_name(car(x0)));
        sym_set(x1, car(p1));
        assign(sym_name(x1), x1);
        x0 = cdr(x0);
        p1 = cdr(p1);
    };
    x2 = eval_sexp(macro_body(p0));
    scope_pop();
    return eval_sexp(x2);
    %);
};

imported : NULL;

eval_import: (p0) {
    allocate(3);
    check_arity(p0, 1, "import");
    x0 = eval_sexp(car(p0));
    if (string_p(x0) == nil_sym) {
	fputs(stderr, "ERROR 'import': required string\n");
	exit(1);
    };
    x1 = string_value(x0);
    if (set_contains(imported, x1)) {
        return nil_sym;
    };
    set_add(imported, x1);
    x2 = parse_module(x1);
    while (x2 != nil_sym) {
	eval_sexp(car(x2));
	x2 = cdr(x2);
    };
    return nil_sym;
};

eval_sexp: (p0) {
    allocate(2);
    x0 = p0[0]; (% node code %);
    if (x0 == NODE_CONS)   { return eval_cons(p0); };
    if (x0 == NODE_SYMBOL) {
        x1 = deref(p0);
        if (sym_value(x1) != NULL) {
            return sym_value(x1);
        };
        return x1;
    };
    if (x0 == NODE_QUOTE) { return eval_quote(quote_sexp(p0)); };
    return p0;
};

init_evaluator: () {
    symbol_map = mkmap(&symbol_hash, &symbol_equal, 100);
    imported = mkset(&strhash, &streq, 10);
    scopeid_stack = mkvec(0);
    scope_push(); (% global scope %);
};

(%
 % rowl - generation 1
 % Copyright (C) 2010 nineties
 %
 % $Id: eval.rl 2010-05-29 10:58:35 nineties $
 %);

include(stddef,code);
export(init_evaluator, assign, deref, check_arity, eval_sexp);

symbol_map: NULL; (% (symbol name, scope id) -> symbol object %);

scopeid: 0;
scopeid_stack: NULL;

(% p0: (symbol name, scope id) %);
symbol_hash: (p0) {
    return strhash(p0[0])*3 + p0[1];
};

symbol_equal: (p0, p1) {
    if (p0[1] != p1[1]) { return FALSE; };
    return streq(p0[0], p1[0]);
};

scope_push: () {
    scopeid = scopeid + 1;
    vec_pushback(scopeid_stack, scopeid);
};

scope_pop: () {
    vec_popback(scopeid_stack);
};

(% p0: symbol name, p1: value %);
assign: (p0, p1) {
    allocate(1);
    x0 = vec_at(scopeid_stack, vec_size(scopeid_stack)-1);
    map_add(symbol_map, mktup2(p0, x0), p1);
};

(% p0: symbol object %);
deref: (p0) {
    allocate(4);
    x0 = vec_size(scopeid_stack)-1;
    x3 = sym_name(p0);
    while (x0 >= 0) {
        x1 = vec_at(scopeid_stack, x0); (% scopeid-id %);
        x2 = map_find(symbol_map, mktup2(x3, x1));
        if (x2 != NULL) { return x2; };
        x0 = x0 - 1;
    };
    return p0;
};

check_arity: (p0, p1, p2) {
    if (length(p0) != p1) {
        fputs(stderr, "ERROR '");
        fputs(stderr, p2);
        fputs(stderr, "': required ");
        fputi(stderr, p1);
        fputs(stderr, " arguments");
        exit(1);
    }
};

eval_args: (p0) {
    if (p0 == nil_sym) { return nil_sym; };
    return mkcons(eval_sexp(car(p0)), eval_args(cdr(p0)));
};

eval_cons: (p0) {
    allocate(2);
    x0 = car(p0);
    if (sym_p(x0) == nil_sym) { goto &eval_cons_error; };
    x1 = eval_sexp(x0);
    if (x1 == var_sym)     { return eval_var(cdr(p0)); };
    if (x1 == set_sym)     { return eval_set(cdr(p0)); };
    if (x1 == if_sym)      { return eval_if(cdr(p0)); };
    if (x1 == cond_sym)    { return eval_cond(cdr(p0)); };
    if (x1 == while_sym)   { return eval_while(cdr(p0)); };
    if (x1 == do_sym)      { return eval_do(cdr(p0)); };
    if (x1 == lambda_sym)  { return eval_lambda(cdr(p0)); };
    if (x1 == macro_sym)   { return eval_macro(cdr(p0)); };
    if (x1 == import_sym)  { return eval_import(cdr(p0)); };
    if (prim_p(x1) != nil_sym)   { return (prim_funptr(x1))(eval_args(cdr(p0))); };
    if (lambda_p(x1) != nil_sym) { return eval_applambda(x1, cdr(p0)); };
    if (macro_p(x1) != nil_sym)  { return eval_appmacro(x1, cdr(p0)); };
label eval_cons_error;
    fputs(stderr, "ERROR: invalid application of '");
    pp_sexp(stderr, car(p0));
    fputs(stderr, "'\n");
    exit(1);
};

(% (var <id> <value>) %);
eval_var: (p0) {
    allocate(2);
    check_arity(p0, 2, "var");
    x0 = car(p0);
    x1 = eval_sexp(cadr(p0));
    sym_set(x0, x1);
    assign(sym_name(x0), x0);
    return x0;
};

eval_set: (p0) {
    allocate(2);
    check_arity(p0, 2, "set");
    x0 = deref(car(p0));
    x1 = eval_sexp(cadr(p0));
    if (sym_value(x0) == NULL) {
        fputs(stderr, "ERROR 'eval_set': undefined variable '");
        fputs(stderr, sym_name(x0));
        fputs(stderr, "'\n");
        exit(1);
    };
    sym_set(x0, x1);
    return x0;
};

eval_quote: (p0) {
    allocate(1);
    if (cons_p(p0) != nil_sym) {
        return mkcons(eval_quote(car(p0)), eval_quote(cdr(p0)));
    };
    if (unquote_p(p0) != nil_sym) {
        return eval_sexp(unquote_sexp(p0));
    };
    return p0;
};

(% p0 : (cond ifthen ifelse) %);
eval_if: (p0) {
    allocate(2);
    check_arity(p0, 3, "if");
    scope_push();
    x0 = eval_sexp(car(p0));
    p0 = cdr(p0);
    if (x0 != nil_sym)  {
        x1 = eval_sexp(car(p0));
        scope_pop();
        return x1;
    };
    x1 = eval_sexp(cadr(p0));
    scope_pop();
    return x1;
};

eval_cond: (p0) {
    allocate(1);
    scope_push();
    while (p0 != nil_sym) {
        x0 = car(p0);
        check_arity(x0, 2, "cond");
        if (eval_sexp(car(x0)) != nil_sym) {
            scope_pop();
            return eval_sexp(cadr(x0));
        };
        p0 = cdr(p0);
    };
    fputs(stderr, "ERROR 'eval_cond': any condition was not met\n");
    exit(1);
};

(% p0: (cond body) %);
eval_while: (p0) {
    allocate(3);
    check_arity(p0, 2, "while");
    scope_push();
    x0 = car(p0); (% condition %);
    x1 = cadr(p0); (% body %);
    x2 = eval_sexp(x0);
    while (x2 != nil_sym) {
        eval_sexp(x1);
        x2 = eval_sexp(x0);
    };
    scope_pop();
    if (x2 != nil_sym) {
        fputs(stderr, "ERROR 'while': conditional expression could not evaluated to true/false\n");
        exit(1);
    };
    return nil_sym;
};

eval_do: (p0) {
    allocate(1);
    scope_push();
    while (p0 != nil_sym) {
        x0 = eval_sexp(car(p0));
        p0 = cdr(p0);
    };
    scope_pop();
    return x0;
};

(% (lambda (params) body) %);
eval_lambda: (p0) {
    allocate(2);
    check_arity(p0, 2, "lambda");
    x0 = car(p0); (% params %);
    x1 = cadr(p0); (% body %);
    return mklambda(x0, x1);
};

(% (macro (params) body) %);
eval_macro: (p0) {
    allocate(2);
    check_arity(p0, 2, "macro");
    x0 = car(p0); (% params %);
    x1 = cadr(p0); (% body %);
    return mkmacro(x0, x1);
};

(% p0: argument pattern, p1:arguments %);
match_args: (p0, p1) {
    allocate(1);
    if (sym_p(p0) != nil_sym) {
        x0 = mksym(sym_name(p0));
        sym_set(x0, p1);
        assign(sym_name(x0), x0);
        return;
    };
    if (cons_p(p0) != nil_sym) {
        match_args(car(p0), car(p1));
        match_args(cdr(p0), cdr(p1));
        return;
    };
    fputs(stderr, "ERROR: invalid argument pattern\n");
    exit(1);
};

(% p0: lambda, p1: params %);
eval_applambda: (p0, p1) {
    allocate(1);
    scope_push();
    match_args(lambda_params(p0), eval_args(p1));
    x0 = eval_sexp(lambda_body(p0));
    scope_pop();
    return x0;
};

(% p0: macro, p1: params %);
eval_appmacro: (p0, p1) {
    allocate(3);
    scope_push();
    match_args(macro_params(p0), p1);
    x0 = eval_sexp(macro_body(p0));
    scope_pop();
    return eval_sexp(x0);
    (%
    x0 = macro_params(p0);
    if (length(x0) != length(p1)) {
        fputs(stderr, "ERROR: invalid number of argument (must be ");
        fputi(stderr, length(x0));
        fputs(stderr, ")\n");
        exit(1);
    };

    scope_push();
    while (x0 != nil_sym) {
        x1 = mksym(sym_name(car(x0)));
        sym_set(x1, car(p1));
        assign(sym_name(x1), x1);
        x0 = cdr(x0);
        p1 = cdr(p1);
    };
    x2 = eval_sexp(macro_body(p0));
    scope_pop();
    return eval_sexp(x2);
    %);
};

imported : NULL;

eval_import: (p0) {
    allocate(2);
    check_arity(p0, 1, "import");
    x0 = eval_sexp(car(p0));
    if (string_p(x0) == nil_sym) {
	fputs(stderr, "ERROR 'import': required string\n");
	exit(1);
    };
    x1 = string_value(x0);
    if (set_contains(imported, x1)) {
        return nil_sym;
    };
    set_add(imported, x1);
    x2 = parse_module(x1);
    while (x2 != nil_sym) {
	eval_sexp(car(x2));
	x2 = cdr(x2);
    };
    return nil_sym;
};

eval_sexp: (p0) {
    allocate(2);
    x0 = p0[0]; (% node code %);
    if (x0 == NODE_CONS)   { return eval_cons(p0); };
    if (x0 == NODE_SYMBOL) {
        x1 = deref(p0);
        if (sym_value(x1) != NULL) {
            return sym_value(x1);
        };
        return x1;
    };
    if (x0 == NODE_QUOTE) { return eval_quote(quote_sexp(p0)); };
    return p0;
};

init_evaluator: () {
    symbol_map = mkmap(&symbol_hash, &symbol_equal, 100);
    imported = mkset(&strhash, &streq, 10);
    scopeid_stack = mkvec(0);
    scope_push(); (% global scope %);
};


Initialize the on_fragment callback pointer

http_parser_init() did not initialize the http_parser.on_fragment.

/* Copyright (c) 2008, 2009 Ryan Dahl (ry@tinyclouds.org)
 * Based on Zed Shaw's Mongrel, copyright (c) Zed A. Shaw
 *
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "http_parser.h"
#include <limits.h>
#include <assert.h>

static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
                     ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     };
#define TRUE 1
#define FALSE 0
#define MIN(a,b) (a < b ? a : b)
#define NULL (void*)(0)

#define MAX_FIELD_SIZE 80*1024

#define REMAINING (unsigned long)(pe - p)
#define CALLBACK(FOR)                                                \
do {                                                                 \
  if (parser->FOR##_mark) {                                          \
    parser->FOR##_size += p - parser->FOR##_mark;                    \
    if (parser->FOR##_size > MAX_FIELD_SIZE) {                       \
      parser->error = TRUE;                                          \
      return 0;                                                      \
    }                                                                \
    if (parser->on_##FOR) {                                          \
      callback_return_value = parser->on_##FOR(parser,               \
        parser->FOR##_mark,                                          \
        p - parser->FOR##_mark);                                     \
    }                                                                \
  }                                                                  \
} while(0)

#define RESET_PARSER(parser)                                         \
    parser->chunk_size = 0;                                          \
    parser->eating = 0;                                              \
    parser->header_field_mark = NULL;                                \
    parser->header_value_mark = NULL;                                \
    parser->query_string_mark = NULL;                                \
    parser->path_mark = NULL;                                        \
    parser->uri_mark = NULL;                                         \
    parser->fragment_mark = NULL;                                    \
    parser->status_code = 0;                                         \
    parser->method = 0;                                              \
    parser->transfer_encoding = HTTP_IDENTITY;                       \
    parser->version_major = 0;                                       \
    parser->version_minor = 0;                                       \
    parser->keep_alive = -1;                                         \
    parser->content_length = 0;                                      \
    parser->body_read = 0;

#define END_REQUEST                                                  \
do {                                                                 \
    if (parser->on_message_complete) {                               \
      callback_return_value =                                        \
        parser->on_message_complete(parser);                         \
    }                                                                \
    RESET_PARSER(parser);                                            \
} while (0)

#define SKIP_BODY(nskip)                                             \
do {                                                                 \
  tmp = (nskip);                                                     \
  if (parser->on_body && tmp > 0) {                                  \
    callback_return_value = parser->on_body(parser, p, tmp);         \
  }                                                                  \
  if (callback_return_value == 0) {                                  \
    p += tmp;                                                        \
    parser->body_read += tmp;                                        \
    parser->chunk_size -= tmp;                                       \
    if (0 == parser->chunk_size) {                                   \
      parser->eating = FALSE;                                        \
      if (parser->transfer_encoding == HTTP_IDENTITY) {              \
        END_REQUEST;                                                 \
      }                                                              \
    } else {                                                         \
      parser->eating = TRUE;                                         \
    }                                                                \
  }                                                                  \
} while (0)

%%{
  machine http_parser;

  action mark_header_field {
    parser->header_field_mark = p;
    parser->header_field_size = 0;
  }

  action mark_header_value {
    parser->header_value_mark = p;
    parser->header_value_size = 0;
  }

  action mark_fragment {
    parser->fragment_mark = p;
    parser->fragment_size = 0;
  }

  action mark_query_string {
    parser->query_string_mark = p;
    parser->query_string_size = 0;
  }

  action mark_request_path {
    parser->path_mark = p;
    parser->path_size = 0;
  }

  action mark_request_uri {
    parser->uri_mark = p;
    parser->uri_size = 0;
  }

  action header_field {
    CALLBACK(header_field);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->header_field_mark = NULL;
    parser->header_field_size = 0;
  }

  action header_value {
    CALLBACK(header_value);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->header_value_mark = NULL;
    parser->header_value_size = 0;
  }

  action request_uri {
    CALLBACK(uri);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->uri_mark = NULL;
    parser->uri_size = 0;
  }

  action fragment {
    CALLBACK(fragment);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->fragment_mark = NULL;
    parser->fragment_size = 0;
  }

  action query_string {
    CALLBACK(query_string);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->query_string_mark = NULL;
    parser->query_string_size = 0;
  }

  action request_path {
    CALLBACK(path);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->path_mark = NULL;
    parser->path_size = 0;
  }

  action headers_complete {
    if(parser->on_headers_complete) {
      callback_return_value = parser->on_headers_complete(parser);
      if (callback_return_value != 0) {
        parser->error = TRUE;
        return 0;
      }
    }
  }

  action begin_message {
    if(parser->on_message_begin) {
      callback_return_value = parser->on_message_begin(parser);
      if (callback_return_value != 0) {
        parser->error = TRUE;
        return 0;
      }
    }
  }

  action content_length {
    if (parser->content_length > INT_MAX) {
      parser->error = TRUE;
      return 0;
    }
    parser->content_length *= 10;
    parser->content_length += *p - '0';
  }

  action status_code {
    parser->status_code *= 10;
    parser->status_code += *p - '0';
  }

  action use_identity_encoding { parser->transfer_encoding = HTTP_IDENTITY; }
  action use_chunked_encoding  { parser->transfer_encoding = HTTP_CHUNKED;  }

  action set_keep_alive { parser->keep_alive = TRUE; }
  action set_not_keep_alive { parser->keep_alive = FALSE; }

  action version_major {
    parser->version_major *= 10;
    parser->version_major += *p - '0';
  }

  action version_minor {
    parser->version_minor *= 10;
    parser->version_minor += *p - '0';
  }

  action add_to_chunk_size {
    parser->chunk_size *= 16;
    parser->chunk_size += unhex[(int)*p];
  }

  action skip_chunk_data {
    SKIP_BODY(MIN(parser->chunk_size, REMAINING));
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }

    fhold;
    if (parser->chunk_size > REMAINING) {
      fbreak;
    } else {
      fgoto chunk_end;
    }
  }

  action end_chunked_body {
    END_REQUEST;
    if (parser->type == HTTP_REQUEST) {
      fnext Requests;
    } else {
      fnext Responses;
    }
  }

  action body_logic {
    if (parser->transfer_encoding == HTTP_CHUNKED) {
      fnext ChunkedBody;
    } else {
      /* this is pretty stupid. i'd prefer to combine this with skip_chunk_data */
      parser->chunk_size = parser->content_length;
      p += 1;

      SKIP_BODY(MIN(REMAINING, parser->content_length));

      if (callback_return_value != 0) {
        parser->error = TRUE;
        return 0;
      }

      fhold;
      if(parser->chunk_size > REMAINING) {
        fbreak;
      }
    }
  }

  CRLF = "\r\n";

# character types
  CTL = (cntrl | 127);
  safe = ("$" | "-" | "_" | ".");
  extra = ("!" | "*" | "'" | "(" | ")" | ",");
  reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
  unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
  national = any -- (alpha | digit | reserved | extra | safe | unsafe);
  unreserved = (alpha | digit | safe | extra | national);
  escape = ("%" xdigit xdigit);
  uchar = (unreserved | escape);
  pchar = (uchar | ":" | "@" | "&" | "=" | "+");
  tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\""
              | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");

# elements
  token = (ascii -- (CTL | tspecials));
  quote = "\"";
#  qdtext = token -- "\"";
#  quoted_pair = "\" ascii;
#  quoted_string = "\"" (qdtext | quoted_pair )* "\"";

#  headers

  Method = ( "COPY"      %{ parser->method = HTTP_COPY;      }
           | "DELETE"    %{ parser->method = HTTP_DELETE;    }
           | "GET"       %{ parser->method = HTTP_GET;       }
           | "HEAD"      %{ parser->method = HTTP_HEAD;      }
           | "LOCK"      %{ parser->method = HTTP_LOCK;      }
           | "MKCOL"     %{ parser->method = HTTP_MKCOL;     }
           | "MOVE"      %{ parser->method = HTTP_MOVE;      }
           | "OPTIONS"   %{ parser->method = HTTP_OPTIONS;   }
           | "POST"      %{ parser->method = HTTP_POST;      }
           | "PROPFIND"  %{ parser->method = HTTP_PROPFIND;  }
           | "PROPPATCH" %{ parser->method = HTTP_PROPPATCH; }
           | "PUT"       %{ parser->method = HTTP_PUT;       }
           | "TRACE"     %{ parser->method = HTTP_TRACE;     }
           | "UNLOCK"    %{ parser->method = HTTP_UNLOCK;    }
           ); # Not allowing extension methods

  HTTP_Version = "HTTP/" digit $version_major "." digit $version_minor;

  scheme = ( alpha | digit | "+" | "-" | "." )* ;
  absolute_uri = (scheme ":" (uchar | reserved )*);
  path = ( pchar+ ( "/" pchar* )* ) ;
  query = ( uchar | reserved )* >mark_query_string %query_string ;
  param = ( pchar | "/" )* ;
  params = ( param ( ";" param )* ) ;
  rel_path = ( path? (";" params)? ) ;
  absolute_path = ( "/"+ rel_path ) >mark_request_path %request_path ("?" query)?;
  Request_URI = ( "*" | absolute_uri | absolute_path ) >mark_request_uri %request_uri;
  Fragment = ( uchar | reserved )* >mark_fragment %fragment;

  field_name = ( token -- ":" )+;
  Field_Name = field_name >mark_header_field %header_field;

  field_value = ((any - " ") any*)?;
  Field_Value = field_value >mark_header_value %header_value;

  hsep = ":" " "*;
  header = (field_name hsep field_value) :> CRLF;
  Header = ( ("Content-Length"i hsep digit+ $content_length)
           | ("Connection"i hsep
               ( "Keep-Alive"i %set_keep_alive
               | "close"i %set_not_keep_alive
               )
             )
           | ("Transfer-Encoding"i %use_chunked_encoding hsep "identity" %use_identity_encoding)
           | (Field_Name hsep Field_Value)
           ) :> CRLF;

  Headers = (Header)* :> CRLF @headers_complete;

  Request_Line = ( Method " " Request_URI ("#" Fragment)? " " HTTP_Version CRLF ) ;

  StatusCode = (digit digit digit) $status_code;
  ReasonPhrase =  ascii* -- ("\r" | "\n");
  StatusLine = HTTP_Version  " " StatusCode (" " ReasonPhrase)? CRLF;

# chunked message
  trailing_headers = header*;
  #chunk_ext_val   = token | quoted_string;
  chunk_ext_val = token*;
  chunk_ext_name = token*;
  chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*;
  last_chunk = "0"+ chunk_extension CRLF;
  chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size;
  chunk_end  = CRLF;
  chunk_body = any >skip_chunk_data;
  chunk_begin = chunk_size chunk_extension CRLF;
  chunk = chunk_begin chunk_body chunk_end;
  ChunkedBody := chunk* last_chunk trailing_headers CRLF @end_chunked_body;

  Request = (Request_Line Headers) >begin_message @body_logic;
  Response = (StatusLine Headers) >begin_message @body_logic;

  Requests := Request*;
  Responses := Response*;

  main := any >{
    fhold;
    if (parser->type == HTTP_REQUEST) {
      fgoto Requests;
    } else {
      fgoto Responses;
    }
  };

}%%

%% write data;

void
http_parser_init (http_parser *parser, enum http_parser_type type)
{
  int cs = 0;
  %% write init;
  parser->cs = cs;
  parser->type = type;
  parser->error = 0;

  parser->on_message_begin = NULL;
  parser->on_path = NULL;
  parser->on_query_string = NULL;
  parser->on_uri = NULL;
  parser->on_fragment = NULL;
  parser->on_header_field = NULL;
  parser->on_header_value = NULL;
  parser->on_headers_complete = NULL;
  parser->on_body = NULL;
  parser->on_message_complete = NULL;

  RESET_PARSER(parser);
}

/** exec **/
size_t
http_parser_execute (http_parser *parser, const char *buffer, size_t len)
{
  size_t tmp; // REMOVE ME this is extremely hacky
  int callback_return_value = 0;
  const char *p, *pe;
  int cs = parser->cs;

  p = buffer;
  pe = buffer+len;

  if (0 < parser->chunk_size && parser->eating) {
    /* eat body */
    SKIP_BODY(MIN(len, parser->chunk_size));
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
  }

  if (parser->header_field_mark)   parser->header_field_mark   = buffer;
  if (parser->header_value_mark)   parser->header_value_mark   = buffer;
  if (parser->fragment_mark)       parser->fragment_mark       = buffer;
  if (parser->query_string_mark)   parser->query_string_mark   = buffer;
  if (parser->path_mark)           parser->path_mark           = buffer;
  if (parser->uri_mark)            parser->uri_mark            = buffer;

  %% write exec;

  parser->cs = cs;

  CALLBACK(header_field);
  CALLBACK(header_value);
  CALLBACK(fragment);
  CALLBACK(query_string);
  CALLBACK(path);
  CALLBACK(uri);

  assert(p <= pe && "buffer overflow after parsing execute");
  return(p - buffer);
}

int
http_parser_has_error (http_parser *parser)
{
  if (parser->error) return TRUE;
  return parser->cs == http_parser_error;
}

int
http_parser_should_keep_alive (http_parser *parser)
{
  if (parser->keep_alive == -1)
    if (parser->version_major == 1)
      return (parser->version_minor != 0);
    else if (parser->version_major == 0)
      return FALSE;
    else
      return TRUE;
  else
    return parser->keep_alive;
}

/* Copyright (c) 2008, 2009 Ryan Dahl (ry@tinyclouds.org)
 * Based on Zed Shaw's Mongrel, copyright (c) Zed A. Shaw
 *
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "http_parser.h"
#include <limits.h>
#include <assert.h>

static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
                     ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
                     };
#define TRUE 1
#define FALSE 0
#define MIN(a,b) (a < b ? a : b)
#define NULL (void*)(0)

#define MAX_FIELD_SIZE 80*1024

#define REMAINING (unsigned long)(pe - p)
#define CALLBACK(FOR)                                                \
do {                                                                 \
  if (parser->FOR##_mark) {                                          \
    parser->FOR##_size += p - parser->FOR##_mark;                    \
    if (parser->FOR##_size > MAX_FIELD_SIZE) {                       \
      parser->error = TRUE;                                          \
      return 0;                                                      \
    }                                                                \
    if (parser->on_##FOR) {                                          \
      callback_return_value = parser->on_##FOR(parser,               \
        parser->FOR##_mark,                                          \
        p - parser->FOR##_mark);                                     \
    }                                                                \
  }                                                                  \
} while(0)

#define RESET_PARSER(parser)                                         \
    parser->chunk_size = 0;                                          \
    parser->eating = 0;                                              \
    parser->header_field_mark = NULL;                                \
    parser->header_value_mark = NULL;                                \
    parser->query_string_mark = NULL;                                \
    parser->path_mark = NULL;                                        \
    parser->uri_mark = NULL;                                         \
    parser->fragment_mark = NULL;                                    \
    parser->status_code = 0;                                         \
    parser->method = 0;                                              \
    parser->transfer_encoding = HTTP_IDENTITY;                       \
    parser->version_major = 0;                                       \
    parser->version_minor = 0;                                       \
    parser->keep_alive = -1;                                         \
    parser->content_length = 0;                                      \
    parser->body_read = 0;

#define END_REQUEST                                                  \
do {                                                                 \
    if (parser->on_message_complete) {                               \
      callback_return_value =                                        \
        parser->on_message_complete(parser);                         \
    }                                                                \
    RESET_PARSER(parser);                                            \
} while (0)

#define SKIP_BODY(nskip)                                             \
do {                                                                 \
  tmp = (nskip);                                                     \
  if (parser->on_body && tmp > 0) {                                  \
    callback_return_value = parser->on_body(parser, p, tmp);         \
  }                                                                  \
  if (callback_return_value == 0) {                                  \
    p += tmp;                                                        \
    parser->body_read += tmp;                                        \
    parser->chunk_size -= tmp;                                       \
    if (0 == parser->chunk_size) {                                   \
      parser->eating = FALSE;                                        \
      if (parser->transfer_encoding == HTTP_IDENTITY) {              \
        END_REQUEST;                                                 \
      }                                                              \
    } else {                                                         \
      parser->eating = TRUE;                                         \
    }                                                                \
  }                                                                  \
} while (0)

%%{
  machine http_parser;

  action mark_header_field {
    parser->header_field_mark = p;
    parser->header_field_size = 0;
  }

  action mark_header_value {
    parser->header_value_mark = p;
    parser->header_value_size = 0;
  }

  action mark_fragment {
    parser->fragment_mark = p;
    parser->fragment_size = 0;
  }

  action mark_query_string {
    parser->query_string_mark = p;
    parser->query_string_size = 0;
  }

  action mark_request_path {
    parser->path_mark = p;
    parser->path_size = 0;
  }

  action mark_request_uri {
    parser->uri_mark = p;
    parser->uri_size = 0;
  }

  action header_field {
    CALLBACK(header_field);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->header_field_mark = NULL;
    parser->header_field_size = 0;
  }

  action header_value {
    CALLBACK(header_value);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->header_value_mark = NULL;
    parser->header_value_size = 0;
  }

  action request_uri {
    CALLBACK(uri);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->uri_mark = NULL;
    parser->uri_size = 0;
  }

  action fragment {
    CALLBACK(fragment);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->fragment_mark = NULL;
    parser->fragment_size = 0;
  }

  action query_string {
    CALLBACK(query_string);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->query_string_mark = NULL;
    parser->query_string_size = 0;
  }

  action request_path {
    CALLBACK(path);
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
    parser->path_mark = NULL;
    parser->path_size = 0;
  }

  action headers_complete {
    if(parser->on_headers_complete) {
      callback_return_value = parser->on_headers_complete(parser);
      if (callback_return_value != 0) {
        parser->error = TRUE;
        return 0;
      }
    }
  }

  action begin_message {
    if(parser->on_message_begin) {
      callback_return_value = parser->on_message_begin(parser);
      if (callback_return_value != 0) {
        parser->error = TRUE;
        return 0;
      }
    }
  }

  action content_length {
    if (parser->content_length > INT_MAX) {
      parser->error = TRUE;
      return 0;
    }
    parser->content_length *= 10;
    parser->content_length += *p - '0';
  }

  action status_code {
    parser->status_code *= 10;
    parser->status_code += *p - '0';
  }

  action use_identity_encoding { parser->transfer_encoding = HTTP_IDENTITY; }
  action use_chunked_encoding  { parser->transfer_encoding = HTTP_CHUNKED;  }

  action set_keep_alive { parser->keep_alive = TRUE; }
  action set_not_keep_alive { parser->keep_alive = FALSE; }

  action version_major {
    parser->version_major *= 10;
    parser->version_major += *p - '0';
  }

  action version_minor {
    parser->version_minor *= 10;
    parser->version_minor += *p - '0';
  }

  action add_to_chunk_size {
    parser->chunk_size *= 16;
    parser->chunk_size += unhex[(int)*p];
  }

  action skip_chunk_data {
    SKIP_BODY(MIN(parser->chunk_size, REMAINING));
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }

    fhold;
    if (parser->chunk_size > REMAINING) {
      fbreak;
    } else {
      fgoto chunk_end;
    }
  }

  action end_chunked_body {
    END_REQUEST;
    if (parser->type == HTTP_REQUEST) {
      fnext Requests;
    } else {
      fnext Responses;
    }
  }

  action body_logic {
    if (parser->transfer_encoding == HTTP_CHUNKED) {
      fnext ChunkedBody;
    } else {
      /* this is pretty stupid. i'd prefer to combine this with skip_chunk_data */
      parser->chunk_size = parser->content_length;
      p += 1;

      SKIP_BODY(MIN(REMAINING, parser->content_length));

      if (callback_return_value != 0) {
        parser->error = TRUE;
        return 0;
      }

      fhold;
      if(parser->chunk_size > REMAINING) {
        fbreak;
      }
    }
  }

  CRLF = "\r\n";

# character types
  CTL = (cntrl | 127);
  safe = ("$" | "-" | "_" | ".");
  extra = ("!" | "*" | "'" | "(" | ")" | ",");
  reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
  unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
  national = any -- (alpha | digit | reserved | extra | safe | unsafe);
  unreserved = (alpha | digit | safe | extra | national);
  escape = ("%" xdigit xdigit);
  uchar = (unreserved | escape);
  pchar = (uchar | ":" | "@" | "&" | "=" | "+");
  tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\""
              | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");

# elements
  token = (ascii -- (CTL | tspecials));
  quote = "\"";
#  qdtext = token -- "\"";
#  quoted_pair = "\" ascii;
#  quoted_string = "\"" (qdtext | quoted_pair )* "\"";

#  headers

  Method = ( "COPY"      %{ parser->method = HTTP_COPY;      }
           | "DELETE"    %{ parser->method = HTTP_DELETE;    }
           | "GET"       %{ parser->method = HTTP_GET;       }
           | "HEAD"      %{ parser->method = HTTP_HEAD;      }
           | "LOCK"      %{ parser->method = HTTP_LOCK;      }
           | "MKCOL"     %{ parser->method = HTTP_MKCOL;     }
           | "MOVE"      %{ parser->method = HTTP_MOVE;      }
           | "OPTIONS"   %{ parser->method = HTTP_OPTIONS;   }
           | "POST"      %{ parser->method = HTTP_POST;      }
           | "PROPFIND"  %{ parser->method = HTTP_PROPFIND;  }
           | "PROPPATCH" %{ parser->method = HTTP_PROPPATCH; }
           | "PUT"       %{ parser->method = HTTP_PUT;       }
           | "TRACE"     %{ parser->method = HTTP_TRACE;     }
           | "UNLOCK"    %{ parser->method = HTTP_UNLOCK;    }
           ); # Not allowing extension methods

  HTTP_Version = "HTTP/" digit $version_major "." digit $version_minor;

  scheme = ( alpha | digit | "+" | "-" | "." )* ;
  absolute_uri = (scheme ":" (uchar | reserved )*);
  path = ( pchar+ ( "/" pchar* )* ) ;
  query = ( uchar | reserved )* >mark_query_string %query_string ;
  param = ( pchar | "/" )* ;
  params = ( param ( ";" param )* ) ;
  rel_path = ( path? (";" params)? ) ;
  absolute_path = ( "/"+ rel_path ) >mark_request_path %request_path ("?" query)?;
  Request_URI = ( "*" | absolute_uri | absolute_path ) >mark_request_uri %request_uri;
  Fragment = ( uchar | reserved )* >mark_fragment %fragment;

  field_name = ( token -- ":" )+;
  Field_Name = field_name >mark_header_field %header_field;

  field_value = ((any - " ") any*)?;
  Field_Value = field_value >mark_header_value %header_value;

  hsep = ":" " "*;
  header = (field_name hsep field_value) :> CRLF;
  Header = ( ("Content-Length"i hsep digit+ $content_length)
           | ("Connection"i hsep
               ( "Keep-Alive"i %set_keep_alive
               | "close"i %set_not_keep_alive
               )
             )
           | ("Transfer-Encoding"i %use_chunked_encoding hsep "identity" %use_identity_encoding)
           | (Field_Name hsep Field_Value)
           ) :> CRLF;

  Headers = (Header)* :> CRLF @headers_complete;

  Request_Line = ( Method " " Request_URI ("#" Fragment)? " " HTTP_Version CRLF ) ;

  StatusCode = (digit digit digit) $status_code;
  ReasonPhrase =  ascii* -- ("\r" | "\n");
  StatusLine = HTTP_Version  " " StatusCode (" " ReasonPhrase)? CRLF;

# chunked message
  trailing_headers = header*;
  #chunk_ext_val   = token | quoted_string;
  chunk_ext_val = token*;
  chunk_ext_name = token*;
  chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*;
  last_chunk = "0"+ chunk_extension CRLF;
  chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size;
  chunk_end  = CRLF;
  chunk_body = any >skip_chunk_data;
  chunk_begin = chunk_size chunk_extension CRLF;
  chunk = chunk_begin chunk_body chunk_end;
  ChunkedBody := chunk* last_chunk trailing_headers CRLF @end_chunked_body;

  Request = (Request_Line Headers) >begin_message @body_logic;
  Response = (StatusLine Headers) >begin_message @body_logic;

  Requests := Request*;
  Responses := Response*;

  main := any >{
    fhold;
    if (parser->type == HTTP_REQUEST) {
      fgoto Requests;
    } else {
      fgoto Responses;
    }
  };

}%%

%% write data;

void
http_parser_init (http_parser *parser, enum http_parser_type type)
{
  int cs = 0;
  %% write init;
  parser->cs = cs;
  parser->type = type;
  parser->error = 0;

  parser->on_message_begin = NULL;
  parser->on_path = NULL;
  parser->on_query_string = NULL;
  parser->on_uri = NULL;
  parser->on_header_field = NULL;
  parser->on_header_value = NULL;
  parser->on_headers_complete = NULL;
  parser->on_body = NULL;
  parser->on_message_complete = NULL;

  RESET_PARSER(parser);
}

/** exec **/
size_t
http_parser_execute (http_parser *parser, const char *buffer, size_t len)
{
  size_t tmp; // REMOVE ME this is extremely hacky
  int callback_return_value = 0;
  const char *p, *pe;
  int cs = parser->cs;

  p = buffer;
  pe = buffer+len;

  if (0 < parser->chunk_size && parser->eating) {
    /* eat body */
    SKIP_BODY(MIN(len, parser->chunk_size));
    if (callback_return_value != 0) {
      parser->error = TRUE;
      return 0;
    }
  }

  if (parser->header_field_mark)   parser->header_field_mark   = buffer;
  if (parser->header_value_mark)   parser->header_value_mark   = buffer;
  if (parser->fragment_mark)       parser->fragment_mark       = buffer;
  if (parser->query_string_mark)   parser->query_string_mark   = buffer;
  if (parser->path_mark)           parser->path_mark           = buffer;
  if (parser->uri_mark)            parser->uri_mark            = buffer;

  %% write exec;

  parser->cs = cs;

  CALLBACK(header_field);
  CALLBACK(header_value);
  CALLBACK(fragment);
  CALLBACK(query_string);
  CALLBACK(path);
  CALLBACK(uri);

  assert(p <= pe && "buffer overflow after parsing execute");
  return(p - buffer);
}

int
http_parser_has_error (http_parser *parser)
{
  if (parser->error) return TRUE;
  return parser->cs == http_parser_error;
}

int
http_parser_should_keep_alive (http_parser *parser)
{
  if (parser->keep_alive == -1)
    if (parser->version_major == 1)
      return (parser->version_minor != 0);
    else if (parser->version_major == 0)
      return FALSE;
    else
      return TRUE;
  else
    return parser->keep_alive;
}