Skip to content

Commit

Permalink
merged net_error2client into master
Browse files Browse the repository at this point in the history
  • Loading branch information
tomatolog committed Mar 29, 2023
2 parents 6c5f3b9 + 30cc189 commit e9bc2f9
Show file tree
Hide file tree
Showing 21 changed files with 955 additions and 509 deletions.
677 changes: 405 additions & 272 deletions src/http/http_parser.c

Large diffs are not rendered by default.

152 changes: 130 additions & 22 deletions src/http/http_parser.h
Expand Up @@ -26,13 +26,13 @@ extern "C" {

/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 4
#define HTTP_PARSER_VERSION_PATCH 2
#define HTTP_PARSER_VERSION_MINOR 9
#define HTTP_PARSER_VERSION_PATCH 4

#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
#include <BaseTsd.h>
#include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
Expand Down Expand Up @@ -76,6 +76,11 @@ typedef struct http_parser_settings http_parser_settings;
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
Expand All @@ -84,6 +89,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);


/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \

enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};


/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
Expand All @@ -95,7 +170,7 @@ typedef int (*http_cb) (http_parser*);
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* webdav */ \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
Expand All @@ -104,21 +179,30 @@ typedef int (*http_cb) (http_parser*);
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(16, REPORT, REPORT) \
XX(17, MKACTIVITY, MKACTIVITY) \
XX(18, CHECKOUT, CHECKOUT) \
XX(19, MERGE, MERGE) \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(20, MSEARCH, M-SEARCH) \
XX(21, NOTIFY, NOTIFY) \
XX(22, SUBSCRIBE, SUBSCRIBE) \
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(24, PATCH, PATCH) \
XX(25, PURGE, PURGE) \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(26, MKCALENDAR, MKCALENDAR) \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \

enum http_method
{
Expand All @@ -140,11 +224,13 @@ enum flags
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
, F_TRANSFER_ENCODING = 1 << 8 /* Never set in http_parser.flags */
};


/* Map for errno-related constants
*
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
Expand All @@ -160,6 +246,8 @@ enum flags
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
Expand All @@ -180,13 +268,17 @@ enum flags
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
XX(UNKNOWN, "an unknown error occurred") \
XX(INVALID_TRANSFER_ENCODING, \
"request has invalid transfer-encoding") \


/* Define HPE_* values for each errno value above */
Expand All @@ -204,10 +296,12 @@ enum http_errno {
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 7; /* F_* values from 'flags' enum; semi-public */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 8; /* enum header_state from http_parser.c */
unsigned int index : 8; /* index into current matcher */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 5; /* index into current matcher */
unsigned int extra_flags : 2;
unsigned int lenient_http_headers : 1;

uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
Expand Down Expand Up @@ -240,6 +334,11 @@ struct http_parser_settings {
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};


Expand Down Expand Up @@ -312,12 +411,18 @@ int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);

/* Returns a string version of the HTTP status code. */
const char *http_status_str(enum http_status s);

/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);

/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);

/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);

/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
Expand All @@ -329,6 +434,9 @@ void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);

/* Change the maximum header size provided at compile time. */
void http_parser_set_max_header_size(uint32_t size);

#ifdef __cplusplus
}
#endif
Expand Down
7 changes: 7 additions & 0 deletions src/netreceive_api.cpp
Expand Up @@ -119,6 +119,7 @@ void ApiServe ( std::unique_ptr<AsyncNetBuffer_c> pBuf )
sphLogDebugv ( "conn %s(%d): bailing idle pconn on client_timeout", sClientIP, iCID );
else
{
pBuf->ResetError();
sphLogDebugv ( "conn %s(%d): timeout, not reached, continue", sClientIP, iCID );
continue;
}
Expand Down Expand Up @@ -195,6 +196,12 @@ void ApiServe ( std::unique_ptr<AsyncNetBuffer_c> pBuf )

if ( !tOut.Flush () )
break;

pBuf->SyncErrorState();
if ( tIn.GetError() )
sphWarning ( "%s", tIn.GetErrorMessage().cstr() );
pBuf->ResetError();

} while ( tSess.GetPersistent());

sphLogDebugv ( "conn %s(%d): exiting", sClientIP, iCID );
Expand Down
35 changes: 25 additions & 10 deletions src/netreceive_http.cpp
Expand Up @@ -37,7 +37,7 @@ void HttpServe ( std::unique_ptr<AsyncNetBuffer_c> pBuf )
if ( bHeNeedSSL && !bICanSSL )
{
if ( bINeedSSL )
sphWarning ( "Client tries to connect with https to secure port, but we can't serve" );
LogNetError ( "Client tries to connect with https to secure port, but we can't serve" );

// that will drop the connection (we can't say anything as can't encrypt our message)
return;
Expand All @@ -59,9 +59,6 @@ void HttpServe ( std::unique_ptr<AsyncNetBuffer_c> pBuf )
auto & tCrashQuery = GlobalCrashQueryGetRef();
tCrashQuery.m_eType = QUERY_JSON;

int iCID = tSess.GetConnID();
const char * sClientIP = tSess.szClientName();

// needed to check permission to turn maintenance mode on/off

if ( bHeNeedSSL )
Expand All @@ -70,16 +67,22 @@ void HttpServe ( std::unique_ptr<AsyncNetBuffer_c> pBuf )
auto& tOut = *(NetGenericOutputBuffer_c *) pBuf.get();
auto& tIn = *(AsyncNetInputBuffer_c *) pBuf.get();

CSphString sError;
bool bOk = true;
HttpRequestParser_c tParser;
CSphVector<BYTE> dResult;
TRACE_CONN ( "conn", "HttpServe" );

auto HttpReply = [&dResult, &tOut] ( ESphHttpStatus eCode, Str_t sMsg )
{
if ( IsEmpty ( sMsg ) )
{
HttpBuildReply ( dResult, eCode, sMsg, false );
else
} else
{
LogNetError ( sMsg.first );
sphHttpErrorReply ( dResult, eCode, sMsg.first );
}
tOut.SwapData ( dResult );
return tOut.Flush();
};
Expand All @@ -99,16 +102,23 @@ void HttpServe ( std::unique_ptr<AsyncNetBuffer_c> pBuf )
if ( iChunk > 0 )
continue;

if ( !iChunk && tIn.GetError() )
sphWarning ( "failed to receive HTTP request (client=%s(%d)) max packet size(%d) exceeded)", sClientIP, iCID, g_iMaxPacketSize );
if ( !iChunk || tIn.GetError() )
{
sError.SetSprintf ( "failed to receive HTTP request, %s", ( tIn.GetError() ? tIn.GetErrorMessage().cstr() : sphSockError() ) );
HttpReply ( SPH_HTTP_STATUS_400, FromStr ( sError ) );
}

return;
}

// malformed header
if ( tParser.Error() )
{
HttpReply ( SPH_HTTP_STATUS_400, FromSz ( tParser.Error() ) );
if ( tIn.GetError() )
sError.SetSprintf ( "%s, %s", tIn.GetErrorMessage().cstr(), tParser.Error() );
else
sError = tParser.Error();
HttpReply ( SPH_HTTP_STATUS_400, FromStr ( sError ) );
break;
}

Expand Down Expand Up @@ -141,11 +151,16 @@ void HttpServe ( std::unique_ptr<AsyncNetBuffer_c> pBuf )
}

// tracer.Instant ( [&tIn](StringBuilder_c& sOut) {sOut<< ",\"args\":{\"step\":"<<tIn.HasBytes()<<"}";} );
tParser.ProcessClientHttp ( tIn, dResult );
bOk = tParser.ProcessClientHttp ( tIn, dResult );

tOut.SwapData (dResult);
if ( !tOut.Flush () )
break;

pBuf->SyncErrorState();
} while ( tSess.GetPersistent() );
if ( tIn.GetError() )
LogNetError ( tIn.GetErrorMessage().cstr() );
pBuf->ResetError();

} while ( tSess.GetPersistent() && bOk );
}

0 comments on commit e9bc2f9

Please sign in to comment.