int test_http_parser2() { http_parser_settings settings; settings.on_message_begin = on_message_begin; settings.on_url = request_url_cb; settings.on_status = on_status; settings.on_header_field = header_field_cb; settings.on_header_value = header_value_cb; settings.on_headers_complete = on_headers_complete; settings.on_body = on_body; settings.on_message_complete = on_message_complete; settings.on_chunk_header = NULL; settings.on_chunk_complete = NULL; http_parser parser; http_parser_init(&parser, HTTP_REQUEST); Request req; parser.data = &req; int nparsed = http_parser_execute(&parser, &settings, TEST_POST_REQ1.c_str(), TEST_POST_REQ1.size()); LOG_INFO("parsed size:%d, parser->upgrade:%d,total_size:%u", nparsed, parser.upgrade, TEST_POST_REQ1.size()); if (parser.http_errno) { LOG_INFO("ERROR:%s", http_errno_description(HTTP_PARSER_ERRNO(&parser))); } nparsed = http_parser_execute(&parser, &settings, TEST_POST_REQ2.c_str(), TEST_POST_REQ2.size()); LOG_INFO("parsed size:%d, parser->upgrade:%d,total_size:%u", nparsed, parser.upgrade, TEST_POST_REQ2.size()); if (parser.http_errno) { LOG_INFO("ERROR:%s", http_errno_description(HTTP_PARSER_ERRNO(&parser))); } return 0; }
int HTTPConnectionPeek(HTTPConnectionRef const conn, HTTPEvent *const type, uv_buf_t *const buf) { if(!conn) return UV_EINVAL; if(!type) return UV_EINVAL; if(!buf) return UV_EINVAL; size_t len; int rc; if(HTTPStreamEOF & conn->flags) return UV_EOF; // Repeat previous errors. rc = HTTP_PARSER_ERRNO(conn->parser); if(HPE_OK != rc && HPE_PAUSED != rc) return UV_UNKNOWN; while(HTTPNothing == conn->type) { if(!conn->raw->len) { // It might seem counterintuitive to free the buffer // just before we could reuse it, but the one time we // don't need it is while blocking. We could free it // after a timeout to give us a chance to reuse it, // but even the two second timeout Apache uses causes // a lot of problems... FREE(&conn->buf); *conn->raw = uv_buf_init(NULL, 0); *conn->out = uv_buf_init(NULL, 0); rc = async_read((uv_stream_t *)conn->stream, conn->raw); if(UV_EOF == rc) conn->flags |= HTTPStreamEOF; if(rc < 0) return rc; conn->buf = conn->raw->base; } http_parser_pause(conn->parser, 0); len = http_parser_execute(conn->parser, &settings, conn->raw->base, conn->raw->len); rc = HTTP_PARSER_ERRNO(conn->parser); // HACK: http_parser returns 1 when the input length is 0 (EOF). if(len > conn->raw->len) len = conn->raw->len; conn->raw->base += len; conn->raw->len -= len; if(HPE_OK != rc && HPE_PAUSED != rc) { // TODO: We should convert HPE_* and return them // instead of logging and returning UV_UNKNOWN. fprintf(stderr, "HTTP parse error %s (%d)\n", http_errno_name(rc), HTTP_PARSER_ERRNO_LINE(conn->parser)); // fprintf(stderr, "%s (%lu)\n", strndup(conn->raw->base, conn->raw->len), conn->raw->len); return UV_UNKNOWN; } } assertf(HTTPNothing != conn->type, "HTTPConnectionPeek must return an event"); *type = conn->type; *buf = *conn->out; return 0; }
void read_response(struct Client *c) { int nparsed; int status = recv(c->fd, c->buf, RECV_BUFFER, 0); if (status < 0 && errno != EAGAIN && errno != EWOULDBLOCK ) { // TODO: Leave this on until we work on the possible errors // from recv. In the future we should handle them. err(EXIT_FAILURE, "Message recv error (client: %d)\n", c->fd); } else { if (status == 0) { printf("Client %d closed the connection.\n", c->fd); c->cstate = DISCONNECTED; c->to_reply = 1; } nparsed = http_parser_execute(c->parser, c->parser_settings, c->buf, status); if (nparsed != status) { c->pstate = ERROR; c->to_reply = 1; printf("Parse error: %s\n", http_errno_description(HTTP_PARSER_ERRNO(c->parser))); } } }
TEST(RequestTest, test_http_parser_post) { http_parser_settings settings; settings.on_message_begin = NULL; settings.on_url = request_url_cb; settings.on_status = on_status; settings.on_header_field = ss_on_header_field; settings.on_header_value = ss_on_header_value; settings.on_headers_complete = ss_on_headers_complete; settings.on_body = ss_on_body; settings.on_message_complete = on_message_complete; settings.on_chunk_header = NULL; settings.on_chunk_complete = NULL; http_parser parser; http_parser_init(&parser, HTTP_REQUEST); Request req; parser.data = &req; int nparsed = http_parser_execute(&parser, &settings, TEST_POST_REQ_LOW_LEN.c_str(), TEST_POST_REQ_LOW_LEN.size()); LOG_INFO("parsed size:%d, parser->upgrade:%d,total_size:%u", nparsed, parser.upgrade, TEST_POST_REQ_LOW_LEN.size()); if (parser.http_errno) { LOG_INFO("ERROR:%s", http_errno_description(HTTP_PARSER_ERRNO(&parser))); } ASSERT_EQ(0, (int)parser.http_errno); }
int Request::parse_request(const char *read_buffer, int read_size) { _total_req_size += read_size; if (_total_req_size > MAX_REQ_SIZE) { LOG_INFO("TOO BIG REQUEST WE WILL REFUSE IT!"); return -1; } LOG_DEBUG("read from client: size:%d, content:%s", read_size, read_buffer); ssize_t nparsed = http_parser_execute(&_parser, &_settings, read_buffer, read_size); if (nparsed != read_size) { std::string err_msg = "unkonw"; if (_parser.http_errno) { err_msg = http_errno_description(HTTP_PARSER_ERRNO(&_parser)); } LOG_ERROR("parse request error! msg:%s", err_msg.c_str()); return -1; } if (_parse_err) { return _parse_err; } if (_parse_part != PARSE_REQ_OVER) { return NEED_MORE_STATUS; } return 0; }
static int lhttp_parser_finish (lua_State *L) { http_parser* parser = (http_parser *)luaL_checkudata(L, 1, "lhttp_parser"); int rv = http_parser_execute(parser, &lhttp_parser_settings, NULL, 0); if (rv != 0) { return luaL_error(L, http_errno_description(HTTP_PARSER_ERRNO(parser))); } return 0; }
void log_http_errors(http_parser *parser) { enum http_errno err = HTTP_PARSER_ERRNO(parser); if (err != HPE_OK) { log(log_error, "http_parser error code = %s", http_errno_name(err)); } else { /* no errors */ } }
int processhttp(char* data, int http_length) { if(!start) start = time(NULL); printf("t=%d\n",t++); _init_c_info(); http_parser_settings settings; size_t nparsed; memset(&settings, 0, sizeof(settings)); settings.on_url = on_url; settings.on_header_field = on_header_field; settings.on_header_value = on_header_value; settings.on_body = on_body; http_parser parser; http_parser_init(&parser, HTTP_REQUEST); nparsed = http_parser_execute(&parser, &settings, data, (size_t)http_length); http.method = parser.method; end = time(NULL); printf("%fms\n", difftime(end, start)/t); //test _print_c_info(); if (nparsed != (size_t)http_length) { printf( "Error: %s (%s)\n", http_errno_description(HTTP_PARSER_ERRNO(&parser)), http_errno_name(HTTP_PARSER_ERRNO(&parser))); } if(content_length != con_len && http.method == 3 && http_length < 4096) { memcpy(http.content, data, http_length); return FALSE; } return TRUE; }
void testParser(const char* buf, enum http_parser_type type){ parser = (struct http_parser*)malloc(sizeof(http_parser)); http_parser_init(parser, type); enum http_errno err; size_t nparsed = http_parser_execute(parser, &settings, buf, strlen(buf)); err = HTTP_PARSER_ERRNO(parser); printf("nparsed %d, total %d, err %d\n", nparsed, strlen(buf), err); printf("*** %s ***\n\n\n\n", http_errno_description(err)); free(parser); parser = NULL; }
void testParser1(const char* buf[], int len, enum http_parser_type type){ parser = (struct http_parser*)malloc(sizeof(http_parser)); http_parser_init(parser, type); enum http_errno err; for (int i = 0; i < len; i++){ size_t nparsed = http_parser_execute(parser, &settings, buf[i], strlen(buf[i])); err = HTTP_PARSER_ERRNO(parser); printf("i %d;total %d, nparsed %d, total %d, err %d\n", i,len, nparsed, strlen(buf[i]), err); printf("*** %s ***\n\n", http_errno_description(err)); } printf("\n\n"); free(parser); parser = NULL; }
TEST(RequestTest, test_http_parser_stream) { set_log_level("WARN"); http_parser_settings settings; settings.on_message_begin = on_message_begin; settings.on_url = request_url_cb; settings.on_status = on_status; settings.on_header_field = header_field_cb; settings.on_header_value = header_value_cb; settings.on_headers_complete = on_headers_complete; settings.on_body = on_body; settings.on_message_complete = on_message_complete; settings.on_chunk_header = NULL; settings.on_chunk_complete = NULL; http_parser parser; http_parser_init(&parser, HTTP_REQUEST); Request req; parser.data = &req; size_t nparsed = http_parser_execute(&parser, &settings, TEST_POST_REQ1.c_str(), TEST_POST_REQ1.size()); LOG_INFO("parsed size:%d, parser->upgrade:%d,total_size:%u", nparsed, parser.upgrade, TEST_POST_REQ1.size()); ASSERT_EQ(TEST_POST_REQ1.size(), nparsed); if (parser.http_errno) { LOG_INFO("ERROR:%s", http_errno_description(HTTP_PARSER_ERRNO(&parser))); } ASSERT_EQ((unsigned int)0, parser.http_errno); nparsed = http_parser_execute(&parser, &settings, TEST_POST_REQ2.c_str(), TEST_POST_REQ2.size()); LOG_INFO("parsed size:%d, parser->upgrade:%d,total_size:%u", nparsed, parser.upgrade, TEST_POST_REQ2.size()); ASSERT_EQ(TEST_POST_REQ2.size(), nparsed); if (parser.http_errno) { LOG_INFO("ERROR:%s", http_errno_description(HTTP_PARSER_ERRNO(&parser))); } ASSERT_EQ((unsigned int)0, parser.http_errno); }
static void passive_extract_parse_buffer(struct connection_context *conn) { size_t bytes_to_end_of_buffer; size_t nparsed; while (conn->buffer_count && (HTTP_PARSER_ERRNO(conn->parser) == HPE_OK)) { bytes_to_end_of_buffer = conn->buffer_size - conn->buffer_index; nparsed = http_parser_execute(conn->parser, conn->parser_settings, (char *)&conn->buffer[conn->buffer_index], imin(conn->buffer_count, bytes_to_end_of_buffer)); conn->buffer_count -= nparsed; conn->buffer_index += nparsed; if (conn->buffer_index == conn->buffer_size) conn->buffer_index = 0; } }
static int mooa_http_parser_execute(lua_State *L) { size_t nparsed; luaL_checkudata(L, 1, "mooa_http_parser"); luaL_checkstring(L, 2); http_parser *parser = lua_touserdata(L, 1); size_t length; const char *data = lua_tolstring(L, 2, &length); parser->data = L; nparsed = http_parser_execute(parser, &mooa_http_parser_settings, data, length); if (nparsed != length) { enum http_errno error = HTTP_PARSER_ERRNO(parser); return luaL_error(L, "HTTP parsing failed (error %d): %s", error, http_errno_name(error)); } return 0; }
VALUE rb_parser_parse(VALUE self, VALUE data) { http_parser *parser = rb_http_parser_handle(self); http_parser_settings settings = { .on_url = (http_data_cb)rb_parser_on_url, .on_status_complete = (http_cb)rb_parser_on_status_complete, .on_header_field = (http_data_cb)rb_parser_on_header_field, .on_header_value = (http_data_cb)rb_parser_on_header_value, .on_headers_complete = (http_cb)rb_parser_on_headers_complete, .on_body = (http_data_cb)rb_parser_on_body, .on_message_begin = (http_cb)rb_parser_on_message_begin, .on_message_complete = (http_cb)rb_parser_on_message_complete }; size_t parsed = http_parser_execute(parser, &settings, RSTRING_PTR(data), RSTRING_LEN(data)); if (parsed != (size_t)RSTRING_LEN(data)) rb_raise(eParserError, "Error Parsing data: %s", http_errno_description(HTTP_PARSER_ERRNO(parser))); return Qtrue; }
int http_handle(struct data_node *p_node) { http_parser *hp = &p_node->http_info.hp; struct http_status *p_stat = &p_node->http_info.hs; if ((p_stat->dosize == 0) || (p_stat->step == 0)){ http_parser_init(hp, HTTP_REQUEST); } int todo = (p_node->recv.get_size - p_stat->dosize); // printf("recv.get_size %d dosize %d\n", p_node->recv.get_size, p_stat->dosize); int done = http_parser_execute(hp, &settings, (p_node->recv.buf_addr + p_stat->dosize), todo); p_stat->step++; if (p_stat->over) { p_stat->dosize = 0; }else{ p_stat->dosize += done; } // x_printf("%d of %d\n", done, todo); if (hp->upgrade) { /* handle new protocol 处理新协议*/ //TODO // x_printf("upgrade!\n"); return FALSE; } else { if (done == todo) { return (p_stat->over); }else{ /*it's error request,change to over and shoud broken socket*/ /* Handle error. Usually just close the connection. 处理错误,通常是关闭这个连接*/ p_stat->err = HTTP_PARSER_ERRNO(hp); if (HPE_OK != p_stat->err) { fprintf(stderr, "\n*** server expected %s, but saw %s ***\n%s\n", http_errno_name(HPE_OK), http_errno_name(p_stat->err), (p_node->recv.buf_addr + p_stat->dosize)); } p_stat->dosize = 0; return TRUE; } } }
int HTTPConnectionDrainMessage(HTTPConnectionRef const conn) { if(!conn) return 0; int rc = HTTP_PARSER_ERRNO(conn->parser); if(HPE_OK != rc && HPE_PAUSED != rc) return UV_UNKNOWN; if(HTTPStreamEOF & conn->flags) return UV_EOF; if(!(HTTPMessageIncomplete & conn->flags)) return 0; uv_buf_t buf[1]; HTTPEvent type; for(;;) { rc = HTTPConnectionPeek(conn, &type, buf); if(rc < 0) return rc; if(HTTPMessageBegin == type) { assertf(0, "HTTPConnectionDrainMessage shouldn't start a new message"); return UV_UNKNOWN; } HTTPConnectionPop(conn, buf->len); if(HTTPMessageEnd == type) break; } return 0; }
std::size_t http_decoder::decode(const char *begin, std::size_t length, message::message& msg) { message_ = dynamic_cast<message::http::http_message *>(&msg); assert(message_); auto consumed = ::http_parser_execute(&parser_, &settings_, begin, length); if (consumed != length) { if (HTTP_PARSER_ERRNO(&parser_) != HPE_OK) { XERROR << "decode error, code: " << parser_.http_errno << ", message: " << error_message() << '\n' << "----- error message dump begin -----\n" << std::string(begin, length) << "\n----- error message dump end -----"; return 0; } else { #warning TODO will this happen? a message is parsed, but there is still data? XWARN << "Weird: parser does not consume all data, but there is no error."; return consumed; } } return consumed; }
VALUE rb_parser_is_paused(VALUE self) { http_parser *parser = rb_http_parser_handle(self); return HTTP_PARSER_ERRNO(parser) == HPE_PAUSED ? Qtrue : Qfalse; }
void *hpcd_server_handle_connection ( void *arg ) { char buffer[80 * 1024]; int n, newsockfd = * ( ( int * ) arg ); free ( arg ); /** Set time limit on execution of thread **/ clock_t begin, end; double time_spent = 0; begin = clock(); http_parser_settings settings; hpcd_server_http_request *request_container = (hpcd_server_http_request *) malloc ( sizeof ( hpcd_server_http_request ) ); request_container->complete = 0; memset ( &settings, 0, sizeof ( settings ) ); settings.on_url = hpcd_server_handle_on_url; settings.on_message_complete = hpcd_server_handle_on_message_complete; settings.on_headers_complete = hpcd_server_handle_on_headers_complete; settings.on_header_field = hpcd_server_handle_on_header_field; settings.on_header_value = hpcd_server_handle_on_header_value; /* Clear the buffer */ bzero ( buffer, 80 * 1024 ); http_parser *parser = malloc ( sizeof ( http_parser ) ); http_parser_init ( parser, HTTP_REQUEST ); request_container->sock_fd = &newsockfd; parser->data = request_container; while(!request_container->complete) { /* Reading from buffer */ //printf ( "Reading from buffer: %d\n ", request_container->complete ); n = recv ( newsockfd, buffer, 80 * 1024, 0 ); if ( n < 0 ) { printf ( "ERROR reading from socket %d", n ); exit ( 1 ); } //printf("captured n %d\n", n); size_t nparsed = http_parser_execute ( parser, &settings, buffer, n ); if ( nparsed != ( size_t ) n ) { fprintf ( stderr, "Error: %s (%s)\n", http_errno_description ( HTTP_PARSER_ERRNO ( parser ) ), http_errno_name ( HTTP_PARSER_ERRNO ( parser ) ) ); } bzero ( buffer, n ); /** Thread execution time **/ end = clock(); if (((double)(end - begin) / CLOCKS_PER_SEC) > 60) { printf("Request timed out\n"); close(*request_container->sock_fd); break; } } printf("Loop Closed\n"); return NULL; }
int main(int argc, char* argv[]) { enum http_parser_type file_type; if (argc != 3) { usage(argv[0]); } char* type = argv[1]; if (type[0] != '-') { usage(argv[0]); } switch (type[1]) { /* in the case of "-", type[1] will be NUL */ case 'r': file_type = HTTP_RESPONSE; break; case 'q': file_type = HTTP_REQUEST; break; case 'b': file_type = HTTP_BOTH; break; default: usage(argv[0]); } char* filename = argv[2]; FILE* file = fopen(filename, "r"); if (file == NULL) { perror("fopen"); goto fail; } fseek(file, 0, SEEK_END); long file_length = ftell(file); if (file_length == -1) { perror("ftell"); goto fail; } fseek(file, 0, SEEK_SET); char* data = malloc(file_length); if (fread(data, 1, file_length, file) != (size_t)file_length) { fprintf(stderr, "couldn't read entire file\n"); free(data); goto fail; } http_parser_settings settings; memset(&settings, 0, sizeof(settings)); settings.on_message_begin = on_message_begin; settings.on_url = on_url; 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; http_parser parser; http_parser_init(&parser, file_type); size_t nparsed = http_parser_execute(&parser, &settings, data, file_length); free(data); if (nparsed != (size_t)file_length) { fprintf(stderr, "Error: %s (%s)\n", http_errno_description(HTTP_PARSER_ERRNO(&parser)), http_errno_name(HTTP_PARSER_ERRNO(&parser))); goto fail; } return EXIT_SUCCESS; fail: fclose(file); return EXIT_FAILURE; }
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); }
INT32 restAdaptor::recvRequestHeader( pmdRestSession *pSession ) { INT32 rc = SDB_OK ; PD_TRACE_ENTRY( SDB__RESTADP_RECVREQHE ) ; SDB_ASSERT ( pSession, "pSession is NULL" ) ; httpConnection *pHttpCon = pSession->getRestConn() ; CHAR *pBuffer = pSession->getFixBuff() ; INT32 bufSize = pSession->getFixBuffSize() ; http_parser *pParser = &(pHttpCon->_httpParser) ; CHAR *pUrl = NULL ; INT32 curRecvSize = 0 ; INT32 receivedSize = 0 ; INT32 bodyOffset = 0 ; INT32 urlSize = 0 ; INT32 tempSize = 0 ; UINT32 recvSize = 0 ; _paraInit( pHttpCon ) ; _maxHttpHeaderSize = _maxHttpHeaderSize > bufSize ? bufSize : _maxHttpHeaderSize ; pHttpCon->_pHeaderBuf = pBuffer ; while( true ) { recvSize = _maxHttpHeaderSize - receivedSize - 1 ; rc = pSession->recvData( pBuffer + receivedSize, recvSize, _timeout, FALSE, &curRecvSize, 0 ) ; if ( rc ) { PD_LOG ( PDERROR, "Failed to recv, rc=%d", rc ) ; goto error ; } pBuffer[ receivedSize + curRecvSize + 1 ] = '\0' ; if ( _checkEndOfHeader( pHttpCon, pBuffer + receivedSize, curRecvSize, bodyOffset ) ) { if ( bodyOffset > 0 ) { pHttpCon->_partSize = curRecvSize - bodyOffset ; pHttpCon->_pPartBody = pBuffer + receivedSize + bodyOffset ; receivedSize += bodyOffset ; } else { receivedSize += curRecvSize ; } pHttpCon->_headerSize = receivedSize ; break ; } else { receivedSize += curRecvSize ; if ( receivedSize >= _maxHttpHeaderSize ) { rc = SDB_REST_RECV_SIZE ; PD_LOG ( PDERROR, "http header size %d greater than %d", receivedSize, _maxHttpHeaderSize ) ; goto error ; } } } http_parser_init( pParser, HTTP_BOTH ) ; if( http_parser_execute( pParser, (http_parser_settings *)_pSettings, pBuffer, (UINT32)receivedSize ) != (UINT32)receivedSize ) { if ( HTTP_PARSER_ERRNO( pParser ) != 28 ) { rc = SDB_REST_EHS ; PD_LOG ( PDERROR, "Failed to parse http, %s, rc=%d", http_errno_description( HTTP_PARSER_ERRNO( pParser ) ), rc ) ; goto error ; } } if( pHttpCon->_pQuery != NULL ) { urlSize = urlDecodeSize( pHttpCon->_pQuery, pHttpCon->_querySize ) ; rc = pSession->allocBuff( urlSize + 1, &pUrl, tempSize ) ; if ( rc ) { PD_LOG ( PDERROR, "Unable to allocate %d bytes memory, rc=%d", urlSize + 1, rc ) ; goto error ; } urlDecode( pHttpCon->_pQuery, pHttpCon->_querySize, &pUrl, urlSize ) ; pUrl[ urlSize ] = 0 ; _parse_http_query( pHttpCon, pUrl, urlSize ) ; } done: PD_TRACE_EXITRC( SDB__RESTADP_RECVREQHE, rc ) ; return rc ; error: goto done ; }
VALUE rb_parser_error_q(VALUE self) { http_parser *parser = rb_http_parser_handle(self); return HTTP_PARSER_ERRNO(parser) != HPE_OK ? Qtrue : Qfalse; }
const char * http_request_get_error_name(http_request_t *request) { assert(request); return http_errno_name(HTTP_PARSER_ERRNO(&request->parser)); }
static int lhttp_parser_getErrorString (lua_State *L) { http_parser* parser = (http_parser *)luaL_checkudata(L, 1, "lhttp_parser"); lua_pushstring(L, http_errno_description(HTTP_PARSER_ERRNO(parser))); return 1; }
QByteArray Pillow::HttpResponseParser::errorString() const { const char* desc = http_errno_description(HTTP_PARSER_ERRNO(&parser)); return QByteArray::fromRawData(desc, qstrlen(desc)); }
VALUE rb_parser_error(VALUE self) { http_parser *parser = rb_http_parser_handle(self); int errno = HTTP_PARSER_ERRNO(parser); return errno != HPE_OK ? rb_str_new2(http_errno_description(errno)) : Qnil; }
/* * The passive http extraction code works by alternately parsing the * passively reconstructed request and response streams. The same callback * (below) is used to drive the parsing of each stream. Parsing begins with * the request stream, and once a complete request has been parsed, the * parser and read watcher for the request stream are paused and the parser * and read watcher for the response stream are activated. Once an entire * response is parsed, the parser and read watcher for the response stream * are paused, and the parser and read watcher for the request stream are * activated. Along the way, response bodies that match the supplied list * of content types are extracted to files. * * This is example code whose purpose is to demonstrate upper layer protocol * processing using libuinet passive sockets functionality. Little to no * attempt is made to deal with a number of ugly realities involved in * robustly parsing http streams in the wild. */ static void passive_extract_cb(struct ev_loop *loop, ev_uinet *w, int revents) { struct connection_context *conn = (struct connection_context *)w->data; struct uinet_iovec iov; struct uinet_uio uio; int max_read; int read_size; int bytes_read; int error; int flags; size_t nparsed; max_read = uinet_soreadable(w->so, 0); if (max_read <= 0) { /* the watcher should never be invoked if there is no error and there no bytes to be read */ assert(max_read != 0); /* * There are no more complete requests/responses to be had, shut everything down. */ if (conn->verbose) printf("%s: can't read, closing\n", conn->label); goto err; } else { read_size = imin(max_read, conn->buffer_size - conn->buffer_index); uio.uio_iov = &iov; iov.iov_base = &conn->buffer[conn->buffer_index]; iov.iov_len = read_size; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = read_size; flags = UINET_MSG_HOLE_BREAK; error = uinet_soreceive(w->so, NULL, &uio, &flags); if (0 != error) { printf("%s: read error (%d), closing\n", conn->label, error); goto err; } if (flags & UINET_MSG_HOLE_BREAK) { printf("%s: hole in data, closing connections\n", conn->label); goto err; } bytes_read = read_size - uio.uio_resid; conn->buffer_count += bytes_read; conn->bytes_read += bytes_read; do { passive_extract_parse_buffer(conn); if (HTTP_PARSER_ERRNO(conn->parser) != HPE_OK) { if (HTTP_PARSER_ERRNO(conn->parser) == HPE_PAUSED) { if (conn->verbose > 1) printf("%s: completed parsing request or response\n", conn->label); http_parser_pause(conn->peer->parser, 0); passive_extract_parse_buffer(conn->peer); if (HTTP_PARSER_ERRNO(conn->peer->parser) == HPE_OK) { if (conn->verbose > 1) printf("%s: peer needs more data\n", conn->label); /* Peer parser needs more data */ ev_uinet_stop(conn->server->loop, &conn->watcher); ev_uinet_start(conn->server->loop, &conn->peer->watcher); break; } else if (HTTP_PARSER_ERRNO(conn->peer->parser) != HPE_PAUSED) { printf("Peer parse failure %s, closing connections\n", http_errno_name(HTTP_PARSER_ERRNO(conn->peer->parser))); goto err; } else { if (conn->verbose > 1) printf("%s: peer completed parsing request or response\n", conn->label); /* * The other parser has paused, so it's time for us to continue * parsing/receiving. */ http_parser_pause(conn->parser, 0); } } else { printf("Parse failure %s, closing connections\n", http_errno_name(HTTP_PARSER_ERRNO(conn->parser))); goto err; } } } while (conn->buffer_count); } return; err: /* * Deliver EOS to each parser. If a parser is paused or otherwise * in an error state, no work will be done. The main reason for * doing this is to correctly handle the case where response parsing * requires an EOS to complete. Under such circumstances, one of * the calls below will complete the work. */ http_parser_execute(conn->parser, conn->parser_settings, NULL, 0); http_parser_execute(conn->peer->parser, conn->peer->parser_settings, NULL, 0); destroy_conn(conn->peer); destroy_conn(conn); }
static void on_read(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { printf("on_read: <<%.*s\n>>", (int) nread, buf.base); if (nread < 0) { if (buf.base) free(buf.base); printf("uv_shutdown\n"); uv_shutdown_t* req = (uv_shutdown_t*) malloc(sizeof *req); uv_shutdown(req, stream, after_shutdown); return; } if (nread == 0) { /* Everything OK, but nothing read. */ free(buf.base); return; } if (stream->data == NULL) internal_error("stream->data is null in on_read"); Connection* connection = (Connection*) stream->data; if (connection->server != NULL && connection->server->serverType == WEBSOCK) { http_parser* parser = &connection->parser; printf("parsing as http\n"); int parsed = http_parser_execute(parser, &connection->server->parser_settings, buf.base, nread); if (parser->upgrade) { connection->state = WEBSOCK_DUPLEX_STATE; return; } if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { printf("http parse error: [%s] %s\n", http_errno_name(HTTP_PARSER_ERRNO(parser)), http_errno_description(HTTP_PARSER_ERRNO(parser)) ); // handle parse error return; } if (parsed < nread) { printf("TODO: Handle second message?\n"); } } else { circa_string_append_len(&connection->incomingStr, buf.base, nread); try_parse(&connection->incomingStr, &connection->incomingMsgs); } free(buf.base); }
const char * http_request_get_error_description(http_request_t *request) { assert(request); return http_errno_description(HTTP_PARSER_ERRNO(&request->parser)); }