From 3d4b551b7766b3cfa2c6475531dbf273e8272319 Mon Sep 17 00:00:00 2001 From: Anatoly Zaretsky Date: Sat, 4 May 2019 18:57:07 +0300 Subject: [PATCH] Add an ability to disable body parsing Currently the on_headers_complete callback can return 0 to proceed with the response body, 1 to skip the body, and 2 to skip the body and stop looking for further messages, and this decision on what to do with the body can only be made inside the on_headers_complete callback. This commit adds an ability to alter the parser behaviour later after on_headers_complete pauses the parser and returns 0. --- http_parser.c | 49 +++++++++++++++++++++++++++++++++++-------------- http_parser.h | 8 ++++++++ 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/http_parser.c b/http_parser.c index 48963853..c9fd6477 100644 --- a/http_parser.c +++ b/http_parser.c @@ -160,6 +160,25 @@ do { \ } \ } while (0) +#define APPLY_ON_HEADERS_COMPLETE_RESULT(V, DEFAULT) \ +do { \ + switch (V) { \ + case 0: \ + break; \ + \ + case 2: \ + parser->upgrade = 1; \ + \ + /* fall through */ \ + case 1: \ + parser->flags |= F_SKIPBODY; \ + break; \ + \ + default: \ + DEFAULT \ + } \ +} while (0) + #define PROXY_CONNECTION "proxy-connection" #define CONNECTION "connection" @@ -1798,22 +1817,10 @@ size_t http_parser_execute (http_parser *parser, * we have to simulate it by handling a change in errno below. */ if (settings->on_headers_complete) { - switch (settings->on_headers_complete(parser)) { - case 0: - break; - - case 2: - parser->upgrade = 1; - - /* fall through */ - case 1: - parser->flags |= F_SKIPBODY; - break; - - default: + APPLY_ON_HEADERS_COMPLETE_RESULT(settings->on_headers_complete(parser), SET_ERRNO(HPE_CB_headers_complete); RETURN(p - data); /* Error */ - } + ); } if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { @@ -2480,6 +2487,20 @@ http_parser_pause(http_parser *parser, int paused) { } } +void +http_parser_continue_after_on_headers_complete(http_parser *parser, int result) { + uint32_t nread; + assert(parser->state == s_headers_done && + (HTTP_PARSER_ERRNO(parser) == HPE_OK || + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED)); + APPLY_ON_HEADERS_COMPLETE_RESULT(result, + ; + ); + nread = parser->nread; /* used by the SET_ERRNO macro */ + /* unpause the parser - after all, it says "continue", right? */ + SET_ERRNO(HPE_OK); +} + int http_body_is_final(const struct http_parser *parser) { return parser->state == s_message_done; diff --git a/http_parser.h b/http_parser.h index 16b5281d..72b8f295 100644 --- a/http_parser.h +++ b/http_parser.h @@ -427,6 +427,14 @@ int http_parser_parse_url(const char *buf, size_t buflen, /* Pause or un-pause the parser; a nonzero value pauses */ void http_parser_pause(http_parser *parser, int paused); +/* Change the parser state as if the on_headers_complete callback + * returned `result` and un-pause the parser - only makes sense + * between two consecutive invocations of http_parser_execute + * when the first one returned due to the on_headers_complete pausing + * the parser and returning 0. + */ +void http_parser_continue_after_on_headers_complete(http_parser *parser, int result); + /* Checks if this is the final chunk of the body. */ int http_body_is_final(const http_parser *parser);