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; }
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; }
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; }
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); } }
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; }
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; }
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; }
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; }
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); } } }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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); }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
bool response_parser::is_keep_alive() const { return http_should_keep_alive(&_parser); }
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); }