Example #1
0
 static int on_headers_complete(http_parser* p)
 {
   DataDecoder* decoder = (DataDecoder*) p->data;
   decoder->request->method = http_method_str((http_method) decoder->parser.method);
   decoder->request->keepAlive = http_should_keep_alive(&decoder->parser);
   return 0;
 }
Example #2
0
int guava_request_on_headers_complete(http_parser *parser) {
  guava_conn_t *conn = (guava_conn_t *)parser->data;
  Request *request = (Request *)conn->request;

  request->req->major = parser->http_major;
  request->req->minor = parser->http_minor;
  request->req->keep_alive = http_should_keep_alive(parser);
  request->req->method = parser->method;

  conn->keep_alive = request->req->keep_alive;

  conn->auxiliary_current_header = NULL;
  conn->auxiliary_last_was_header = 0;

  PyObject *host = PyDict_GetItemString(request->req->headers, "Host");
  if (host) {
    request->req->host = guava_string_new(PyString_AsString(host));
  }

  PyObject *cookie = PyDict_GetItemString(request->req->headers, "Cookie");
  if (cookie) {
    char *p = PyString_AsString(cookie);
    char **data = (char **)&p;
    Cookie *c = NULL;

    while ((c = (Cookie *)guava_cookie_parse(data))) {
      PyDict_SetItemString(request->req->COOKIES, c->data.name, (PyObject *)c);
    }
  }

  return 0;
}
Example #3
0
static int headers_complete_cb(http_parser *hp)
{
	// x_printf("on_headers_complete\n");
	struct data_node *p_node = hp->data;
	struct http_status *p_stat = &p_node->http_info.hs;

	p_stat->method = hp->method;
	p_stat->http_major = hp->http_major;
	p_stat->http_minor = hp->http_minor;
	p_stat->should_keep_alive = http_should_keep_alive(hp);
	// x_printf("%d,%d,%d,%d\n", p_stat->method, p_stat->http_major, p_stat->http_minor, p_stat->should_keep_alive);
	
	p_stat->path_offset = p_stat->url_offset;
	char *p_mark = x_strstr( (p_node->recv.buf_addr + p_stat->url_offset), "?", p_stat->url_len);
	if (p_mark){
		p_stat->path_len = p_mark - (p_node->recv.buf_addr + p_stat->path_offset);
		p_stat->uri_offset = p_mark + 1 - p_node->recv.buf_addr;
		p_stat->uri_len = p_stat->url_len - p_stat->path_len -1;
	}else{
		p_stat->path_len = p_stat->url_len;
		p_stat->uri_offset = 0;
		p_stat->uri_len = 0;
	}
	return 0;
}
Example #4
0
static
void write_response(http_connection *http)
{
  int bytes_written = write(http->watcher.fd, http->response, http->response_length);

  if (bytes_written >= 0)
  {
    http->response += bytes_written;
    http->response_length -= bytes_written;
    
    if (http->response_length == 0)
    {
      if (!http_should_keep_alive(&http->parser))
      {
        shutdown(http->watcher.fd, SHUT_RDWR);
        http_destroy(http);
      }
      else
      {
        http->message_complete = 0;
      }
    }
  }
  else if (errno != EWOULDBLOCK)
  {
    http_destroy(http);
  }
}
Example #5
0
static int http_connect(http_subtransport *t)
{
	int flags = 0;

	if (t->connected &&
		http_should_keep_alive(&t->parser) &&
		http_body_is_final(&t->parser))
		return 0;

	if (t->socket.socket)
		gitno_close(&t->socket);

	if (t->connection_data.use_ssl) {
		int tflags;

		if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
			return -1;

		flags |= GITNO_CONNECT_SSL;

		if (GIT_TRANSPORTFLAGS_NO_CHECK_CERT & tflags)
			flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
	}

	if (gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags) < 0)
		return -1;

	t->connected = 1;
	return 0;
}
Example #6
0
 int connection::on_message_complete(http_parser* parser)
 {
     connection* c = (connection*)parser->data;
     c->_keep_alive = (http_should_keep_alive(parser) != 0);
     c->_request_complete = true;
     return 0;
 }
Example #7
0
static int bb_session_request_complete(http_parser *parser) {
        if (parser->upgrade) return 0;
        if (http_should_keep_alive(parser)) {
                // prepare for a new request
                struct bb_session_request *bbsr = (struct bb_session_request *) parser->data;
                bbsr->bbs->new_request = 1;
        }
        return 0;
}
Example #8
0
static int response_headers_complete(http_parser *parser) {
        struct bb_session_request *bbsr = (struct bb_session_request *) parser->data;
        if (!http_should_keep_alive(parser)) {
                bbsr->close = 1;
        }
        if (parser->content_length != ULLONG_MAX) {
                bbsr->content_length = parser->content_length;
        }
        return 0;
}
Example #9
0
void
bolt_connection_recv_handler(int sock, short event, void *arg)
{
    bolt_connection_t *c = (bolt_connection_t *)arg;
    int nbytes, remain, retval;

    if (!c || c->sock != sock) {
        return;
    }

    remain = c->rend - c->rpos;
    if (remain <= 0) {
        bolt_free_connection(c);
        bolt_log(BOLT_LOG_ERROR,
                 "Connection header too big, socket(%d)", c->sock);
        return;
    }

    nbytes = read(c->sock, c->rpos, remain);
    if (nbytes < 0) {
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            bolt_free_connection(c);
            bolt_log(BOLT_LOG_ERROR,
                     "Connection read error, socket(%d)", c->sock);
        }
        return;

    } else if (nbytes == 0) {
        bolt_free_connection(c);
        return;
    }

    c->rpos += nbytes;

    if (bolt_connection_recv_completed(c) == 0) {
        retval = http_parser_execute(&c->hp, &http_parser_callbacks,
                                     c->rbuf, c->rlast - c->rbuf);

        if (c->hp.method != HTTP_GET) {
            bolt_free_connection(c);
            bolt_log(BOLT_LOG_ERROR,
                     "Connection request method not `GET', socket(%d)",
                     c->sock);
            return;
        }

        c->keepalive = http_should_keep_alive(&c->hp);

        /* Process connection request */
        if (bolt_connection_process_request(c) == -1) {
            bolt_free_connection(c);
        }
    }
}
Example #10
0
int OnHeadersComplete(http_parser* parser)
{
	ParserWrap* wrap = (ParserWrap*)parser->data;
	NPP npp = wrap->m_Instance;


	// this is a bit hacky
	// we have to create an empty javascript object
	// so we use a function we created and passed to
	// the plugin
	NPVariant info;
	wrap->apply(wrap->create_info,NULL,0,&info);
	
	NPVariant major;
	major.type = NPVariantType_Double;
	major.value.doubleValue = parser->http_major;
	NPN_SetProperty(npp,info.value.objectValue,NPN_GetStringIdentifier("versionMajor"),&major);

	NPVariant minor;
	minor.type = NPVariantType_Double;
	minor.value.doubleValue = parser->http_major;
	NPN_SetProperty(npp,info.value.objectValue,NPN_GetStringIdentifier("versionMinor"),&minor);

	/*NPVariant method;
	method.type = NPVariantType_String;
	NPString method_str = {(NPUTF8 *)parser->method,strlen((char*)parser->method)};
	method.value.stringValue = method;
	NPN_SetProperty(npp,info.value.objectValue,NPN_GetStringIdentifier("method"),&method);*/

	NPVariant status;
	status.type = NPVariantType_Double;
	status.value.doubleValue = parser->status_code;
	NPN_SetProperty(npp,info.value.objectValue,NPN_GetStringIdentifier("statusCode"),&status);

	NPVariant upgrade;
	upgrade.type = NPVariantType_Bool;
	upgrade.value.boolValue = parser->upgrade;
	NPN_SetProperty(npp,info.value.objectValue,NPN_GetStringIdentifier("upgrade"),&upgrade);

	NPVariant keepAlive;
	keepAlive.type = NPVariantType_Bool;
	keepAlive.value.boolValue = http_should_keep_alive(parser);
	NPN_SetProperty(npp,info.value.objectValue,NPN_GetStringIdentifier("shouldKeepAlive"),&keepAlive);

	NPVariant* args = new NPVariant;
	args->type = NPVariantType_Object;
	args->value.objectValue = info.value.objectValue;
	NPN_RetainObject(info.value.objectValue);

	NPVariant result;
	wrap->apply(wrap->onheaderscomplete_callback,args,1,&result);
	return 0;
}
Example #11
0
int headers_complete_cb (http_parser *p)
{
    http_msg *msg = p->data;

    msg->method = p->method;
    msg->status_code = p->status_code;
    msg->http_major = p->http_major;
    msg->http_minor = p->http_minor;
    msg->headers_complete_cb_called = 1;
    msg->should_keep_alive = http_should_keep_alive(p);
    return 0;
}
Example #12
0
int
headers_complete_cb (http_parser *p)
{
  assert(p == parser);
  messages[num_messages].method = parser->method;
  messages[num_messages].status_code = parser->status_code;
  messages[num_messages].http_major = parser->http_major;
  messages[num_messages].http_minor = parser->http_minor;
  messages[num_messages].headers_complete_cb_called = TRUE;
  messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
  return 0;
}
Example #13
0
static int do_connect(transport_http *t, const char *host, const char *port)
{
	if (t->parent.connected && http_should_keep_alive(&t->parser))
		return 0;

	if (gitno_connect((git_transport *) t, host, port) < 0)
		return -1;

	t->parent.connected = 1;

	return 0;
}
Example #14
0
  static int on_headers_complete(http_parser* p)
  {
    DataDecoder* decoder = (DataDecoder*) p->data;

    // Add final header.
    decoder->request->headers[decoder->field] = decoder->value;
    decoder->field.clear();
    decoder->value.clear();

    decoder->request->method = http_method_str((http_method) decoder->parser.method);
    decoder->request->keepAlive = http_should_keep_alive(&decoder->parser);
    return 0;
  }
Example #15
0
int headers_complete_cb (http_parser *p)
{
    RpcServerHttpConn *httpconn = (RpcServerHttpConn *)p->data;
    if(p->method == HTTP_GET) {
        httpconn->bodyFinal(true);
    }
    if(p->method == HTTP_POST && p->content_length == 0) {
        RPC_LOG(RPC_LOG_LEV::WARNING, "not support content_length 0");
        httpconn->setFail(true);
    }
    httpconn->setKeepAlive(http_should_keep_alive(p));
    return 0;
}
int HttpParser::headers_complete_cb (http_parser *p)
{
  m_message.m_method = (http_method)p->method;
  m_message.m_status_code = p->status_code;
  m_message.m_http_major = p->http_major;
  m_message.m_http_minor = p->http_minor;
  m_message.m_headers_complete_cb_called = 1;
  m_message.m_should_keep_alive = http_should_keep_alive(p);

  int rv = http_parser_parse_url(m_message.m_request_url.c_str(), m_message.m_request_url.size(),
    0, &m_message.m_http_parser_url);
    m_message.url_UF_SCHEMA.assign(
        m_message.m_request_url.c_str() + m_message.m_http_parser_url.field_data[UF_SCHEMA].off,
        m_message.m_http_parser_url.field_data[UF_SCHEMA].len);
    m_message.url_UF_HOST.assign(
        m_message.m_request_url.c_str() + m_message.m_http_parser_url.field_data[UF_HOST].off,
        m_message.m_http_parser_url.field_data[UF_HOST].len);
    m_message.url_UF_PORT.assign(
        m_message.m_request_url.c_str() + m_message.m_http_parser_url.field_data[UF_PORT].off,
        m_message.m_http_parser_url.field_data[UF_PORT].len);
    m_message.url_UF_PATH.assign(
        m_message.m_request_url.c_str() + m_message.m_http_parser_url.field_data[UF_PATH].off,
        m_message.m_http_parser_url.field_data[UF_PATH].len);
    m_message.url_UF_QUERY.assign(
        m_message.m_request_url.c_str() + m_message.m_http_parser_url.field_data[UF_QUERY].off,
        m_message.m_http_parser_url.field_data[UF_QUERY].len);
    m_message.url_UF_FRAGMENT.assign(
        m_message.m_request_url.c_str() + m_message.m_http_parser_url.field_data[UF_FRAGMENT].off,
        m_message.m_http_parser_url.field_data[UF_FRAGMENT].len);
    m_message.url_UF_USERINFO.assign(
        m_message.m_request_url.c_str() + m_message.m_http_parser_url.field_data[UF_USERINFO].off,
        m_message.m_http_parser_url.field_data[UF_USERINFO].len);
  printf("SCHEMA(%s)\n HOST(%s)\n PORT(%s)\n PATH(%s)\n QUERY(%s)\n FRAGMENT(%s)\n  USERINFO(%s)\n",
  m_message.url_UF_SCHEMA.c_str(),
  m_message.url_UF_HOST.c_str(),
  m_message.url_UF_PORT.c_str(),
  m_message.url_UF_PATH.c_str(),
  m_message.url_UF_QUERY.c_str(),
  m_message.url_UF_FRAGMENT.c_str(),
  m_message.url_UF_USERINFO.c_str()  );
  rv=rv; 
  printf("Method=%d;m_status_code=%d;http_major=%d;http_minor=%d;headers_complete_cb_called=%d;m_should_keep_alive=%d\n", 
  m_message.m_method,
  m_message.m_status_code,
  m_message.m_http_major,
  m_message.m_http_minor ,
  m_message.m_headers_complete_cb_called ,
  m_message.m_should_keep_alive 
  );
  return 0;
}
Example #17
0
static int
http_client_response(http_client *cp)
{
    size_t		bytes;
    char		buffer[BUFSIZ];
    int			sts;
    static int		setup;
    static http_parser_settings	settings;

    if (!setup) {
	memset(&settings, 0, sizeof(settings));
	settings.on_header_field = on_header_field;
	settings.on_header_value = on_header_value;
	settings.on_headers_complete = on_headers_complete;
	settings.on_body = on_body;
	settings.on_message_complete = on_message_complete;
	setup = 1;
    }

    if (pmDebug & DBG_TRACE_HTTP)
	fprintf(stderr, "http_client_response\n");

    http_parser_init(&cp->parser, HTTP_RESPONSE);
    cp->parser.data = (void *)cp;
    cp->error_code = 0;
    cp->offset = 0;

    do {
	if ((sts = __pmRecv(cp->fd, buffer, sizeof(buffer), 0)) <= 0) {
	    http_client_disconnect(cp);
	    return sts ? sts : -EAGAIN;
	}
	bytes = http_parser_execute(&cp->parser, &settings, buffer, sts);

    } while (bytes && !(cp->flags & F_MESSAGE_END));

    if (http_should_client_disconnect(cp))
	http_client_disconnect(cp);

    if (http_should_client_redirect(cp))
	return -EMLINK;

    if (http_should_keep_alive(&cp->parser) == 0)
	http_client_disconnect(cp);

    if (cp->error_code)
	return cp->error_code;

    return cp->offset;
}
Example #18
0
static
void setup_response(http_connection *http)
{
  if (http_should_keep_alive(&http->parser))
  {
    http->response = keepalive_response;
  }
  else
  {  
    http->response = normal_response;
  }
  
  http->response_length = strlen(http->response);
}
Example #19
0
static int lhttp_parser_on_headers_complete(http_parser *p) {
  lua_State *L = p->data;
  size_t nocontent;

  /* Put the environment of the userdata on the top of the stack */
  lua_getfenv(L, 1);
  /* Get the onHeadersComplete callback and put it on the stack */
  lua_getfield(L, -1, "onHeadersComplete");
  /* See if it's a function */
  if (lua_isfunction (L, -1) == 0) {
    /* no function defined */
    lua_pop(L, 2);
    return 0;
  };

  /* Push a new table as the argument */
  lua_newtable (L);

  /* METHOD */
  if (p->type == HTTP_REQUEST) {
    lua_pushstring(L, method_to_str(p->method));
    lua_setfield(L, -2, "method");
  }

  /* STATUS */
  if (p->type == HTTP_RESPONSE) {
    lua_pushinteger(L, p->status_code);
    lua_setfield(L, -2, "status_code");
  }

  /* VERSION */
  lua_pushinteger(L, p->http_major);
  lua_setfield(L, -2, "version_major");
  lua_pushinteger(L, p->http_minor);
  lua_setfield(L, -2, "version_minor");


  lua_pushboolean(L, http_should_keep_alive(p));
  lua_setfield(L, -2, "should_keep_alive");

  lua_pushboolean(L, p->upgrade);
  lua_setfield(L, -2, "upgrade");


  lua_call(L, 1, 1);
  nocontent = lua_tointeger(L, -1);

  lua_pop(L, 2); /* pop returned value and the userdata env */
  return nocontent;
}
Example #20
0
static int OnMessageComplete(http_parser* parser) {
  HttpProtocolData* protocol_data = (HttpProtocolData*) parser;

  protocol_data->current->http_major = parser->http_major;
  protocol_data->current->http_minor = parser->http_minor;
  protocol_data->current->status_code = parser->status_code;
  protocol_data->current->method = parser->method;
  protocol_data->current->keep_alive = http_should_keep_alive(parser);
  protocol_data->current->http_type = parser->type;
  
  protocol_data->messages.push_back(protocol_data->current);
  protocol_data->current = NULL;
  return 0;
}
Example #21
0
static int do_connect(transport_http *t, const char *host, const char *port)
{
	GIT_SOCKET s;

	if (t->parent.connected && http_should_keep_alive(&t->parser))
		return 0;

	if (gitno_connect(&s, host, port) < 0)
		return -1;

	t->socket = s;
	t->parent.connected = 1;

	return 0;
}
Example #22
0
static int bb_session_headers_complete(http_parser *parser) {
        //printf("headers parsed\n");
        struct bb_session_request *bbsr = (struct bb_session_request *) parser->data;
        off_t i;
        //printf("%s %.*s HTTP/%d.%d\n", http_method_str(parser->method), (int) bbsr->headers[0].keylen, bbsr->headers[0].key, parser->http_major, parser->http_minor);
        /*
        for(i=1;i<=bbsr->header_pos;i++) {
                printf("%.*s: %.*s\n", (int) bbsr->headers[i].keylen, bbsr->headers[i].key, (int)bbsr->headers[i].vallen, bbsr->headers[i].value);
        }
        */

        // ok get the Host header
        struct bb_http_header *bbhh = bb_http_req_header(bbsr, "Host", 4);
        if (!bbhh) {
                return -1;
        }

        if (!bbsr->bbs->dealer) {
                bbsr->bbs->dealer = bb_get_dealer(bbsr->bbs->acceptor, bbhh->value, bbhh->vallen);
        	if (!bbsr->bbs->dealer) {
                	return -1;
        	}
        }

        if (parser->upgrade) {
                struct bb_http_header *bbhh = bb_http_req_header(bbsr, "Upgrade", 7);
                if (bbhh) {
                        if (!bb_stricmp("websocket", 9, bbhh->value, bbhh->vallen)) {
                                bbsr->type = BLASTBEAT_TYPE_WEBSOCKET;
                                bb_send_websocket_handshake(bbsr);
				goto msg;
                        }
                }
        }

        if (!http_should_keep_alive(parser)) {
                //printf("NO KEEP ALIVE !!!\n");
                bbsr->close = 1;
        }

msg:
        // now encode headers in a uwsgi packet and send it as "headers" message
	if (bb_uwsgi(bbsr)) {
		return -1;
	}
        bb_zmq_send_msg(bbsr->bbs->dealer->identity, bbsr->bbs->dealer->len, (char *) &bbsr->bbs->uuid_part1, BB_UUID_LEN, "uwsgi", 5, bbsr->uwsgi_buf, bbsr->uwsgi_pos);
        return 0;
}
int HttpParser::message_complete_cb (http_parser *p)
{
  if (m_message.m_should_keep_alive != http_should_keep_alive(p))
  {
    fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
                    "value in both on_message_complete and on_headers_complete "
                    "but it doesn't! ***\n\n");
    assert(0);
    abort();
  }
  m_message.m_message_complete_cb_called = TRUE;
  m_message.m_message_complete_on_eof = m_currently_parsing_eof;
  m_num_messages++;
  printf("message_complete\n");
  return 0;
}
Example #24
0
    bool HttpParser::completeRequest()
    {
        Finally f([this]() { request_ = HttpRequest(); });

        switch(parser_.method)
        {
#define _(x) case HTTP_##x: request_.method_ = HttpRequest::x; break
            _(GET);
            _(HEAD);
            _(POST);
#undef _
        default:
            request_.method_ = HttpRequest::OTHER;
        }

        if(parser_.http_minor != 0) // treat as HTTP 1.1
        {
            request_.http11_ = true;
        }

        request_.upgrade_ = !!parser_.upgrade;
        request_.keepAlive_ = !!http_should_keep_alive(&parser_);

        if(!parseRequestTarget(request_.rawTarget_, request_.target_, request_.queries_))
        {
            return false;
        }

        try
        {
            // TODO: Content-Type parameters (charset, ...)
            if(parser_.method == HttpRequest::POST &&
                boost::iequals(request_.headers_.at("Content-Type"), "application/x-www-form-urlencoded"))
            {
                if(!parseQueryString(request_.rawBody_, request_.queries_))
                {
                    return false;
                }
            }
        }
        catch(const std::out_of_range &)
        {
        }

        return currentCallback_(request_);
    }
struct http_request *parse_request(char *request_data, int len) {
    http_parser *parser = malloc(sizeof(http_parser));
    http_parser_init(parser, HTTP_REQUEST);
    struct http_request *request = new_http_request();
    parser->data = request;
    int res = http_parser_execute(parser, &parser_settings, request_data, len);
    if (res == len) {
        if (http_should_keep_alive(parser)) {
            request->flags |= F_HREQ_KEEPALIVE;
        }
        free(parser);
        return request;
    }
    delete_http_request(request);
    free(parser);
    return NULL;
}
Example #26
0
int message_complete_cb (http_parser *p)
{
    http_msg *msg = p->data;

    if (msg->should_keep_alive != http_should_keep_alive(p))
    {
        fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
                "value in both on_message_complete and on_headers_complete "
                "but it doesn't! ***\n\n");
        return 1;
    }
    msg->message_complete_cb_called = 1;

    msg->message_complete_on_eof = currently_parsing_eof;

    return 0;
}
Example #27
0
int
message_complete_cb (http_parser *p)
{
  assert(p == parser);
  if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
  {
    fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
                    "value in both on_message_complete and on_headers_complete "
                    "but it doesn't! ***\n\n");
    assert(0);
    exit(1);
  }
  messages[num_messages].message_complete_cb_called = TRUE;

  messages[num_messages].message_complete_on_eof = currently_parsing_eof;

  num_messages++;
  return 0;
}
Example #28
0
static int headers_complete_cb (http_parser *parser)
{
	struct http_parser_url u;
	struct request *cur_req = get_cur_req();
	struct in_http_info *info = &I_INFO(cur_req);
	if (!parser)
		return -1;
	info->method = parser->method;
	info->status_code = parser->status_code;
	info->http_major = parser->http_major;
	info->http_minor = parser->http_minor;
	info->headers_complete_cb_called = 1;
	info->should_keep_alive = http_should_keep_alive(parser);

	memset(&u, 0, sizeof(u));
	if (-1 == http_parser_parse_url(info->request_url, info->request_url_len, info->method == HTTP_CONNECT ? 1 : 0, &u)) {
		seterrstatus(cur_req, BAD_REQUEST);
		info->message_complete_cb_called = 1;
		return -1;
	}

	if (u.field_data[UF_PATH].len >= MAX_ELEMENT_SIZE) {
		seterrstatus(cur_req, BAD_REQUEST);
		info->message_complete_cb_called = 1;
		return -1;
	}

	memcpy(info->request_path, info->request_url + u.field_data[UF_PATH].off, u.field_data[UF_PATH].len);

	if (check_headers(cur_req) == -1) {
		info->message_complete_cb_called = 1;
		return -1;
	}

	return 0;
}
Example #29
0
bool response_parser::is_keep_alive() const
{
    return http_should_keep_alive(&_parser);
}
Example #30
0
size_t http_parser_execute (http_parser *parser,
                            const http_parser_settings *settings,
                            const char *data,
                            size_t len)
{
  char c, ch;
  int8_t unhex_val;
  const char *p = data, *pe;
  int64_t to_read;
  enum state state;
  enum header_states header_state;
  uint64_t index = parser->index;
  uint64_t nread = parser->nread;

  /* We're in an error state. Don't bother doing anything. */
  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
    return 0;
  }

  state = (enum state) parser->state;
  header_state = (enum header_states) parser->header_state;

  if (len == 0) {
    switch (state) {
      case s_body_identity_eof:
        CALLBACK2(message_complete);
        return 0;

      case s_dead:
      case s_start_req_or_res:
      case s_start_res:
      case s_start_req:
        return 0;

      default:
        SET_ERRNO(HPE_INVALID_EOF_STATE);
        return 1;
    }
  }

  /* technically we could combine all of these (except for url_mark) into one
     variable, saving stack space, but it seems more clear to have them
     separated. */
  const char *header_field_mark = 0;
  const char *header_value_mark = 0;
  const char *url_mark = 0;

  if (state == s_header_field)
    header_field_mark = data;
  if (state == s_header_value)
    header_value_mark = data;
  if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
      || state == s_req_schema_slash_slash || state == s_req_port
      || state == s_req_query_string_start || state == s_req_query_string
      || state == s_req_host
      || state == s_req_fragment_start || state == s_req_fragment)
    url_mark = data;

  for (p=data, pe=data+len; p != pe; p++) {
    ch = *p;

    if (PARSING_HEADER(state)) {
      ++nread;
      /* Buffer overflow attack */
      if (nread > HTTP_MAX_HEADER_SIZE) {
        SET_ERRNO(HPE_HEADER_OVERFLOW);
        goto error;
      }
    }

    switch (state) {

      case s_dead:
        /* this state is used after a 'Connection: close' message
         * the parser will error out if it reads another message
         */
        SET_ERRNO(HPE_CLOSED_CONNECTION);
        goto error;

      case s_start_req_or_res:
      {
        if (ch == CR || ch == LF)
          break;
        parser->flags = 0;
        parser->content_length = -1;

        CALLBACK2(message_begin);

        if (ch == 'H')
          state = s_res_or_resp_H;
        else {
          parser->type = HTTP_REQUEST;
          goto start_req_method_assign;
        }
        break;
      }

      case s_res_or_resp_H:
        if (ch == 'T') {
          parser->type = HTTP_RESPONSE;
          state = s_res_HT;
        } else {
          if (ch != 'E') {
            SET_ERRNO(HPE_INVALID_CONSTANT);
            goto error;
          }

          parser->type = HTTP_REQUEST;
          parser->method = HTTP_HEAD;
          index = 2;
          state = s_req_method;
        }
        break;

      case s_start_res:
      {
        parser->flags = 0;
        parser->content_length = -1;

        CALLBACK2(message_begin);

        switch (ch) {
          case 'H':
            state = s_res_H;
            break;

          case CR:
          case LF:
            break;

          default:
            SET_ERRNO(HPE_INVALID_CONSTANT);
            goto error;
        }
        break;
      }

      case s_res_H:
        STRICT_CHECK(ch != 'T');
        state = s_res_HT;
        break;

      case s_res_HT:
        STRICT_CHECK(ch != 'T');
        state = s_res_HTT;
        break;

      case s_res_HTT:
        STRICT_CHECK(ch != 'P');
        state = s_res_HTTP;
        break;

      case s_res_HTTP:
        STRICT_CHECK(ch != '/');
        state = s_res_first_http_major;
        break;

      case s_res_first_http_major:
        if (ch < '1' || ch > '9') {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_major = ch - '0';
        state = s_res_http_major;
        break;

      /* major HTTP version or dot */
      case s_res_http_major:
      {
        if (ch == '.') {
          state = s_res_first_http_minor;
          break;
        }

        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_major *= 10;
        parser->http_major += ch - '0';

        if (parser->http_major > 999) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        break;
      }

      /* first digit of minor HTTP version */
      case s_res_first_http_minor:
        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_minor = ch - '0';
        state = s_res_http_minor;
        break;

      /* minor HTTP version or end of request line */
      case s_res_http_minor:
      {
        if (ch == ' ') {
          state = s_res_first_status_code;
          break;
        }

        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_minor *= 10;
        parser->http_minor += ch - '0';

        if (parser->http_minor > 999) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        break;
      }

      case s_res_first_status_code:
      {
        if (!IS_NUM(ch)) {
          if (ch == ' ') {
            break;
          }

          SET_ERRNO(HPE_INVALID_STATUS);
          goto error;
        }
        parser->status_code = ch - '0';
        state = s_res_status_code;
        break;
      }

      case s_res_status_code:
      {
        if (!IS_NUM(ch)) {
          switch (ch) {
            case ' ':
              state = s_res_status;
              break;
            case CR:
              state = s_res_line_almost_done;
              break;
            case LF:
              state = s_header_field_start;
              break;
            default:
              SET_ERRNO(HPE_INVALID_STATUS);
              goto error;
          }
          break;
        }

        parser->status_code *= 10;
        parser->status_code += ch - '0';

        if (parser->status_code > 999) {
          SET_ERRNO(HPE_INVALID_STATUS);
          goto error;
        }

        break;
      }

      case s_res_status:
        /* the human readable status. e.g. "NOT FOUND"
         * we are not humans so just ignore this */
        if (ch == CR) {
          state = s_res_line_almost_done;
          break;
        }

        if (ch == LF) {
          state = s_header_field_start;
          break;
        }
        break;

      case s_res_line_almost_done:
        STRICT_CHECK(ch != LF);
        state = s_header_field_start;
        break;

      case s_start_req:
      {
        if (ch == CR || ch == LF)
          break;
        parser->flags = 0;
        parser->content_length = -1;

        CALLBACK2(message_begin);

        if (!IS_ALPHA(ch)) {
          SET_ERRNO(HPE_INVALID_METHOD);
          goto error;
        }

      start_req_method_assign:
        parser->method = (enum http_method) 0;
        index = 1;
        switch (ch) {
          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
          case 'D': parser->method = HTTP_DELETE; break;
          case 'G': parser->method = HTTP_GET; break;
          case 'H': parser->method = HTTP_HEAD; break;
          case 'L': parser->method = HTTP_LOCK; break;
          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
          case 'N': parser->method = HTTP_NOTIFY; break;
          case 'O': parser->method = HTTP_OPTIONS; break;
          case 'P': parser->method = HTTP_POST;
            /* or PROPFIND or PROPPATCH or PUT or PATCH */
            break;
          case 'R': parser->method = HTTP_REPORT; break;
          case 'S': parser->method = HTTP_SUBSCRIBE; break;
          case 'T': parser->method = HTTP_TRACE; break;
          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
          default:
            SET_ERRNO(HPE_INVALID_METHOD);
            goto error;
        }
        state = s_req_method;
        break;
      }

      case s_req_method:
      {
        if (ch == '\0') {
          SET_ERRNO(HPE_INVALID_METHOD);
          goto error;
        }

        const char *matcher = method_strings[parser->method];
        if (ch == ' ' && matcher[index] == '\0') {
          state = s_req_spaces_before_url;
        } else if (ch == matcher[index]) {
          ; /* nada */
        } else if (parser->method == HTTP_CONNECT) {
          if (index == 1 && ch == 'H') {
            parser->method = HTTP_CHECKOUT;
          } else if (index == 2  && ch == 'P') {
            parser->method = HTTP_COPY;
          } else {
            goto error;
          }
        } else if (parser->method == HTTP_MKCOL) {
          if (index == 1 && ch == 'O') {
            parser->method = HTTP_MOVE;
          } else if (index == 1 && ch == 'E') {
            parser->method = HTTP_MERGE;
          } else if (index == 1 && ch == '-') {
            parser->method = HTTP_MSEARCH;
          } else if (index == 2 && ch == 'A') {
            parser->method = HTTP_MKACTIVITY;
          } else {
            goto error;
          }
        } else if (index == 1 && parser->method == HTTP_POST) {
          if (ch == 'R') {
            parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
          } else if (ch == 'U') {
            parser->method = HTTP_PUT;
          } else if (ch == 'A') {
            parser->method = HTTP_PATCH;
          } else {
            goto error;
          }
        } else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {
          parser->method = HTTP_UNSUBSCRIBE;
        } else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
          parser->method = HTTP_PROPPATCH;
        } else {
          SET_ERRNO(HPE_INVALID_METHOD);
          goto error;
        }

        ++index;
        break;
      }
      case s_req_spaces_before_url:
      {
        if (ch == ' ') break;

        if (ch == '/' || ch == '*') {
          MARK(url);
          state = s_req_path;
          break;
        }

        /* Proxied requests are followed by scheme of an absolute URI (alpha).
         * CONNECT is followed by a hostname, which begins with alphanum.
         * All other methods are followed by '/' or '*' (handled above).
         */
        if (IS_ALPHA(ch) || (parser->method == HTTP_CONNECT && IS_NUM(ch))) {
          MARK(url);
          state = (parser->method == HTTP_CONNECT) ? s_req_host : s_req_schema;
          break;
        }

        SET_ERRNO(HPE_INVALID_URL);
        goto error;
      }

      case s_req_schema:
      {
        if (IS_ALPHA(ch)) break;

        if (ch == ':') {
          state = s_req_schema_slash;
          break;
        }

        SET_ERRNO(HPE_INVALID_URL);
        goto error;
      }

      case s_req_schema_slash:
        STRICT_CHECK(ch != '/');
        state = s_req_schema_slash_slash;
        break;

      case s_req_schema_slash_slash:
        STRICT_CHECK(ch != '/');
        state = s_req_host;
        break;

      case s_req_host:
      {
        if (IS_HOST_CHAR(ch)) break;
        switch (ch) {
          case ':':
            state = s_req_port;
            break;
          case '/':
            state = s_req_path;
            break;
          case ' ':
            /* The request line looks like:
             *   "GET http://foo.bar.com HTTP/1.1"
             * That is, there is no path.
             */
            CALLBACK(url);
            state = s_req_http_start;
            break;
          case '?':
            state = s_req_query_string_start;
            break;
          default:
            SET_ERRNO(HPE_INVALID_HOST);
            goto error;
        }
        break;
      }

      case s_req_port:
      {
        if (IS_NUM(ch)) break;
        switch (ch) {
          case '/':
            state = s_req_path;
            break;
          case ' ':
            /* The request line looks like:
             *   "GET http://foo.bar.com:1234 HTTP/1.1"
             * That is, there is no path.
             */
            CALLBACK(url);
            state = s_req_http_start;
            break;
          case '?':
            state = s_req_query_string_start;
            break;
          default:
            SET_ERRNO(HPE_INVALID_PORT);
            goto error;
        }
        break;
      }

      case s_req_path:
      {
        if (IS_URL_CHAR(ch)) break;

        switch (ch) {
          case ' ':
            CALLBACK(url);
            state = s_req_http_start;
            break;
          case CR:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_req_line_almost_done;
            break;
          case LF:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_header_field_start;
            break;
          case '?':
            state = s_req_query_string_start;
            break;
          case '#':
            state = s_req_fragment_start;
            break;
          default:
            SET_ERRNO(HPE_INVALID_PATH);
            goto error;
        }
        break;
      }

      case s_req_query_string_start:
      {
        if (IS_URL_CHAR(ch)) {
          state = s_req_query_string;
          break;
        }

        switch (ch) {
          case '?':
            break; /* XXX ignore extra '?' ... is this right? */
          case ' ':
            CALLBACK(url);
            state = s_req_http_start;
            break;
          case CR:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_req_line_almost_done;
            break;
          case LF:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_header_field_start;
            break;
          case '#':
            state = s_req_fragment_start;
            break;
          default:
            SET_ERRNO(HPE_INVALID_QUERY_STRING);
            goto error;
        }
        break;
      }

      case s_req_query_string:
      {
        if (IS_URL_CHAR(ch)) break;

        switch (ch) {
          case '?':
            /* allow extra '?' in query string */
            break;
          case ' ':
            CALLBACK(url);
            state = s_req_http_start;
            break;
          case CR:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_req_line_almost_done;
            break;
          case LF:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_header_field_start;
            break;
          case '#':
            state = s_req_fragment_start;
            break;
          default:
            SET_ERRNO(HPE_INVALID_QUERY_STRING);
            goto error;
        }
        break;
      }

      case s_req_fragment_start:
      {
        if (IS_URL_CHAR(ch)) {
          state = s_req_fragment;
          break;
        }

        switch (ch) {
          case ' ':
            CALLBACK(url);
            state = s_req_http_start;
            break;
          case CR:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_req_line_almost_done;
            break;
          case LF:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_header_field_start;
            break;
          case '?':
            state = s_req_fragment;
            break;
          case '#':
            break;
          default:
            SET_ERRNO(HPE_INVALID_FRAGMENT);
            goto error;
        }
        break;
      }

      case s_req_fragment:
      {
        if (IS_URL_CHAR(ch)) break;

        switch (ch) {
          case ' ':
            CALLBACK(url);
            state = s_req_http_start;
            break;
          case CR:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_req_line_almost_done;
            break;
          case LF:
            CALLBACK(url);
            parser->http_major = 0;
            parser->http_minor = 9;
            state = s_header_field_start;
            break;
          case '?':
          case '#':
            break;
          default:
            SET_ERRNO(HPE_INVALID_FRAGMENT);
            goto error;
        }
        break;
      }

      case s_req_http_start:
        switch (ch) {
          case 'H':
            state = s_req_http_H;
            break;
          case ' ':
            break;
          default:
            SET_ERRNO(HPE_INVALID_CONSTANT);
            goto error;
        }
        break;

      case s_req_http_H:
        STRICT_CHECK(ch != 'T');
        state = s_req_http_HT;
        break;

      case s_req_http_HT:
        STRICT_CHECK(ch != 'T');
        state = s_req_http_HTT;
        break;

      case s_req_http_HTT:
        STRICT_CHECK(ch != 'P');
        state = s_req_http_HTTP;
        break;

      case s_req_http_HTTP:
        STRICT_CHECK(ch != '/');
        state = s_req_first_http_major;
        break;

      /* first digit of major HTTP version */
      case s_req_first_http_major:
        if (ch < '1' || ch > '9') {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_major = ch - '0';
        state = s_req_http_major;
        break;

      /* major HTTP version or dot */
      case s_req_http_major:
      {
        if (ch == '.') {
          state = s_req_first_http_minor;
          break;
        }

        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_major *= 10;
        parser->http_major += ch - '0';

        if (parser->http_major > 999) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        break;
      }

      /* first digit of minor HTTP version */
      case s_req_first_http_minor:
        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_minor = ch - '0';
        state = s_req_http_minor;
        break;

      /* minor HTTP version or end of request line */
      case s_req_http_minor:
      {
        if (ch == CR) {
          state = s_req_line_almost_done;
          break;
        }

        if (ch == LF) {
          state = s_header_field_start;
          break;
        }

        /* XXX allow spaces after digit? */

        if (!IS_NUM(ch)) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        parser->http_minor *= 10;
        parser->http_minor += ch - '0';

        if (parser->http_minor > 999) {
          SET_ERRNO(HPE_INVALID_VERSION);
          goto error;
        }

        break;
      }

      /* end of request line */
      case s_req_line_almost_done:
      {
        if (ch != LF) {
          SET_ERRNO(HPE_LF_EXPECTED);
          goto error;
        }

        state = s_header_field_start;
        break;
      }

      case s_header_field_start:
      header_field_start:
      {
        if (ch == CR) {
          state = s_headers_almost_done;
          break;
        }

        if (ch == LF) {
          /* they might be just sending \n instead of \r\n so this would be
           * the second \n to denote the end of headers*/
          state = s_headers_almost_done;
          goto headers_almost_done;
        }

        c = TOKEN(ch);

        if (!c) {
          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
          goto error;
        }

        MARK(header_field);

        index = 0;
        state = s_header_field;

        switch (c) {
          case 'c':
            header_state = h_C;
            break;

          case 'p':
            header_state = h_matching_proxy_connection;
            break;

          case 't':
            header_state = h_matching_transfer_encoding;
            break;

          case 'u':
            header_state = h_matching_upgrade;
            break;

          default:
            header_state = h_general;
            break;
        }
        break;
      }

      case s_header_field:
      {
        c = TOKEN(ch);

        if (c) {
          switch (header_state) {
            case h_general:
              break;

            case h_C:
              index++;
              header_state = (c == 'o' ? h_CO : h_general);
              break;

            case h_CO:
              index++;
              header_state = (c == 'n' ? h_CON : h_general);
              break;

            case h_CON:
              index++;
              switch (c) {
                case 'n':
                  header_state = h_matching_connection;
                  break;
                case 't':
                  header_state = h_matching_content_length;
                  break;
                default:
                  header_state = h_general;
                  break;
              }
              break;

            /* connection */

            case h_matching_connection:
              index++;
              if (index > sizeof(CONNECTION)-1
                  || c != CONNECTION[index]) {
                header_state = h_general;
              } else if (index == sizeof(CONNECTION)-2) {
                header_state = h_connection;
              }
              break;

            /* proxy-connection */

            case h_matching_proxy_connection:
              index++;
              if (index > sizeof(PROXY_CONNECTION)-1
                  || c != PROXY_CONNECTION[index]) {
                header_state = h_general;
              } else if (index == sizeof(PROXY_CONNECTION)-2) {
                header_state = h_connection;
              }
              break;

            /* content-length */

            case h_matching_content_length:
              index++;
              if (index > sizeof(CONTENT_LENGTH)-1
                  || c != CONTENT_LENGTH[index]) {
                header_state = h_general;
              } else if (index == sizeof(CONTENT_LENGTH)-2) {
                header_state = h_content_length;
              }
              break;

            /* transfer-encoding */

            case h_matching_transfer_encoding:
              index++;
              if (index > sizeof(TRANSFER_ENCODING)-1
                  || c != TRANSFER_ENCODING[index]) {
                header_state = h_general;
              } else if (index == sizeof(TRANSFER_ENCODING)-2) {
                header_state = h_transfer_encoding;
              }
              break;

            /* upgrade */

            case h_matching_upgrade:
              index++;
              if (index > sizeof(UPGRADE)-1
                  || c != UPGRADE[index]) {
                header_state = h_general;
              } else if (index == sizeof(UPGRADE)-2) {
                header_state = h_upgrade;
              }
              break;

            case h_connection:
            case h_content_length:
            case h_transfer_encoding:
            case h_upgrade:
              if (ch != ' ') header_state = h_general;
              break;

            default:
              assert(0 && "Unknown header_state");
              break;
          }
          break;
        }

        if (ch == ':') {
          CALLBACK(header_field);
          state = s_header_value_start;
          break;
        }

        if (ch == CR) {
          state = s_header_almost_done;
          CALLBACK(header_field);
          break;
        }

        if (ch == LF) {
          CALLBACK(header_field);
          state = s_header_field_start;
          break;
        }

        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
        goto error;
      }

      case s_header_value_start:
      {
        if (ch == ' ' || ch == '\t') break;

        MARK(header_value);

        state = s_header_value;
        index = 0;

        if (ch == CR) {
          CALLBACK(header_value);
          header_state = h_general;
          state = s_header_almost_done;
          break;
        }

        if (ch == LF) {
          CALLBACK(header_value);
          state = s_header_field_start;
          break;
        }

        c = LOWER(ch);

        switch (header_state) {
          case h_upgrade:
            parser->flags |= F_UPGRADE;
            header_state = h_general;
            break;

          case h_transfer_encoding:
            /* looking for 'Transfer-Encoding: chunked' */
            if ('c' == c) {
              header_state = h_matching_transfer_encoding_chunked;
            } else {
              header_state = h_general;
            }
            break;

          case h_content_length:
            if (!IS_NUM(ch)) {
              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
              goto error;
            }

            parser->content_length = ch - '0';
            break;

          case h_connection:
            /* looking for 'Connection: keep-alive' */
            if (c == 'k') {
              header_state = h_matching_connection_keep_alive;
            /* looking for 'Connection: close' */
            } else if (c == 'c') {
              header_state = h_matching_connection_close;
            } else {
              header_state = h_general;
            }
            break;

          default:
            header_state = h_general;
            break;
        }
        break;
      }

      case s_header_value:
      {

        if (ch == CR) {
          CALLBACK(header_value);
          state = s_header_almost_done;
          break;
        }

        if (ch == LF) {
          CALLBACK(header_value);
          goto header_almost_done;
        }

        c = LOWER(ch);

        switch (header_state) {
          case h_general:
            break;

          case h_connection:
          case h_transfer_encoding:
            assert(0 && "Shouldn't get here.");
            break;

          case h_content_length:
            if (ch == ' ') break;
            if (!IS_NUM(ch)) {
              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
              goto error;
            }

            parser->content_length *= 10;
            parser->content_length += ch - '0';
            break;

          /* Transfer-Encoding: chunked */
          case h_matching_transfer_encoding_chunked:
            index++;
            if (index > sizeof(CHUNKED)-1
                || c != CHUNKED[index]) {
              header_state = h_general;
            } else if (index == sizeof(CHUNKED)-2) {
              header_state = h_transfer_encoding_chunked;
            }
            break;

          /* looking for 'Connection: keep-alive' */
          case h_matching_connection_keep_alive:
            index++;
            if (index > sizeof(KEEP_ALIVE)-1
                || c != KEEP_ALIVE[index]) {
              header_state = h_general;
            } else if (index == sizeof(KEEP_ALIVE)-2) {
              header_state = h_connection_keep_alive;
            }
            break;

          /* looking for 'Connection: close' */
          case h_matching_connection_close:
            index++;
            if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
              header_state = h_general;
            } else if (index == sizeof(CLOSE)-2) {
              header_state = h_connection_close;
            }
            break;

          case h_transfer_encoding_chunked:
          case h_connection_keep_alive:
          case h_connection_close:
            if (ch != ' ') header_state = h_general;
            break;

          default:
            state = s_header_value;
            header_state = h_general;
            break;
        }
        break;
      }

      case s_header_almost_done:
      header_almost_done:
      {
        STRICT_CHECK(ch != LF);

        state = s_header_value_lws;

        switch (header_state) {
          case h_connection_keep_alive:
            parser->flags |= F_CONNECTION_KEEP_ALIVE;
            break;
          case h_connection_close:
            parser->flags |= F_CONNECTION_CLOSE;
            break;
          case h_transfer_encoding_chunked:
            parser->flags |= F_CHUNKED;
            break;
          default:
            break;
        }
        break;
      }

      case s_header_value_lws:
      {
        if (ch == ' ' || ch == '\t')
          state = s_header_value_start;
        else
        {
          state = s_header_field_start;
          goto header_field_start;
        }
        break;
      }

      case s_headers_almost_done:
      headers_almost_done:
      {
        STRICT_CHECK(ch != LF);

        if (parser->flags & F_TRAILING) {
          /* End of a chunked request */
          CALLBACK2(message_complete);
          state = NEW_MESSAGE();
          break;
        }

        nread = 0;

        if (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT) {
          parser->upgrade = 1;
        }

        /* Here we call the headers_complete callback. This is somewhat
         * different than other callbacks because if the user returns 1, we
         * will interpret that as saying that this message has no body. This
         * is needed for the annoying case of recieving a response to a HEAD
         * request.
         */
        if (settings->on_headers_complete) {
          switch (settings->on_headers_complete(parser)) {
            case 0:
              break;

            case 1:
              parser->flags |= F_SKIPBODY;
              break;

            default:
              parser->state = state;
              SET_ERRNO(HPE_CB_headers_complete);
              return p - data; /* Error */
          }
        }

        /* Exit, the rest of the connect is in a different protocol. */
        if (parser->upgrade) {
          CALLBACK2(message_complete);
          return (p - data) + 1;
        }

        if (parser->flags & F_SKIPBODY) {
          CALLBACK2(message_complete);
          state = NEW_MESSAGE();
        } else if (parser->flags & F_CHUNKED) {
          /* chunked encoding - ignore Content-Length header */
          state = s_chunk_size_start;
        } else {
          if (parser->content_length == 0) {
            /* Content-Length header given but zero: Content-Length: 0\r\n */
            CALLBACK2(message_complete);
            state = NEW_MESSAGE();
          } else if (parser->content_length > 0) {
            /* Content-Length header given and non-zero */
            state = s_body_identity;
          } else {
            if (parser->type == HTTP_REQUEST || http_should_keep_alive(parser)) {
              /* Assume content-length 0 - read the next */
              CALLBACK2(message_complete);
              state = NEW_MESSAGE();
            } else {
              /* Read body until EOF */
              state = s_body_identity_eof;
            }
          }
        }

        break;
      }

      case s_body_identity:
        to_read = MIN(pe - p, (int64_t)parser->content_length);
        if (to_read > 0) {
          if (settings->on_body) settings->on_body(parser, p, to_read);
          p += to_read - 1;
          parser->content_length -= to_read;
          if (parser->content_length == 0) {
            CALLBACK2(message_complete);
            state = NEW_MESSAGE();
          }
        }
        break;

      /* read until EOF */
      case s_body_identity_eof:
        to_read = pe - p;
        if (to_read > 0) {
          if (settings->on_body) settings->on_body(parser, p, to_read);
          p += to_read - 1;
        }
        break;

      case s_chunk_size_start:
      {
        assert(nread == 1);
        assert(parser->flags & F_CHUNKED);

        unhex_val = unhex[(unsigned char)ch];
        if (unhex_val == -1) {
          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
          goto error;
        }

        parser->content_length = unhex_val;
        state = s_chunk_size;
        break;
      }

      case s_chunk_size:
      {
        assert(parser->flags & F_CHUNKED);

        if (ch == CR) {
          state = s_chunk_size_almost_done;
          break;
        }

        unhex_val = unhex[(unsigned char)ch];

        if (unhex_val == -1) {
          if (ch == ';' || ch == ' ') {
            state = s_chunk_parameters;
            break;
          }

          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
          goto error;
        }

        parser->content_length *= 16;
        parser->content_length += unhex_val;
        break;
      }

      case s_chunk_parameters:
      {
        assert(parser->flags & F_CHUNKED);
        /* just ignore this shit. TODO check for overflow */
        if (ch == CR) {
          state = s_chunk_size_almost_done;
          break;
        }
        break;
      }

      case s_chunk_size_almost_done:
      {
        assert(parser->flags & F_CHUNKED);
        STRICT_CHECK(ch != LF);

        nread = 0;

        if (parser->content_length == 0) {
          parser->flags |= F_TRAILING;
          state = s_header_field_start;
        } else {
          state = s_chunk_data;
        }
        break;
      }

      case s_chunk_data:
      {
        assert(parser->flags & F_CHUNKED);

        to_read = MIN(pe - p, (int64_t)(parser->content_length));

        if (to_read > 0) {
          if (settings->on_body) settings->on_body(parser, p, to_read);
          p += to_read - 1;
        }

        if (to_read == parser->content_length) {
          state = s_chunk_data_almost_done;
        }

        parser->content_length -= to_read;
        break;
      }

      case s_chunk_data_almost_done:
        assert(parser->flags & F_CHUNKED);
        STRICT_CHECK(ch != CR);
        state = s_chunk_data_done;
        break;

      case s_chunk_data_done:
        assert(parser->flags & F_CHUNKED);
        STRICT_CHECK(ch != LF);
        state = s_chunk_size_start;
        break;

      default:
        assert(0 && "unhandled state");
        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
        goto error;
    }
  }

  CALLBACK(header_field);
  CALLBACK(header_value);
  CALLBACK(url);

  parser->state = state;
  parser->header_state = header_state;
  parser->index = index;
  parser->nread = nread;

  return len;

error:
  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
    SET_ERRNO(HPE_UNKNOWN);
  }

  return (p - data);
}