46#include <httpparser.hpp>
76 {{
"DELETE", HTTPMETHOD_DELETE},
77 {
"GET", HTTPMETHOD_GET},
78 {
"HEAD", HTTPMETHOD_HEAD},
79 {
"M-POST", HTTPMETHOD_MPOST},
80 {
"M-SEARCH", HTTPMETHOD_MSEARCH},
81 {
"NOTIFY", HTTPMETHOD_NOTIFY},
82 {
"POST", HTTPMETHOD_POST},
83 {
"SUBSCRIBE", HTTPMETHOD_SUBSCRIBE},
84 {
"UNSUBSCRIBE", HTTPMETHOD_UNSUBSCRIBE},
85 {
"POST", SOAPMETHOD_POST},
86 {
"PUT", HTTPMETHOD_PUT}}};
93constexpr char TOKCHAR_CR{0xD};
94constexpr char TOKCHAR_LF{0xA};
104 scanner->
cursor = (size_t)0;
105 scanner->
msg = bufptr;
115 return strchr(
" \t()<>@,;:\\\"/[]?={}", c) != 0;
133 return (c >= 0 && c <= 31) || c == 127;
145 return (c >= 32 && c != 127) || c < 0 || c == TOKCHAR_CR ||
146 c == TOKCHAR_LF || c ==
'\t';
166 char* null_terminator;
179 if (cursor == null_terminator)
184 token->buf = cursor++;
185 token_type = TT_IDENTIFIER;
192 token->length = (size_t)cursor - (
size_t)
token->buf;
193 }
else if (c ==
' ' || c ==
'\t') {
194 token->buf = cursor++;
195 token_type = TT_WHITESPACE;
196 while (cursor < null_terminator && (*cursor ==
' ' || *cursor ==
'\t'))
201 token->length = (size_t)cursor - (
size_t)
token->buf;
202 }
else if (c == TOKCHAR_CR) {
204 token->buf = cursor++;
205 if (cursor == null_terminator)
208 if (*cursor != TOKCHAR_LF) {
210 token_type = TT_CTRL;
211 token->length = (size_t)1;
214 token->length = (size_t)2;
215 token_type = TT_CRLF;
218 }
else if (c == TOKCHAR_LF) {
219 token->buf = cursor++;
220 token->length = (size_t)1;
221 token_type = TT_CRLF;
222 }
else if (c ==
'"') {
224 token->buf = cursor++;
225 token_type = TT_QUOTEDSTRING;
227 while (cursor < null_terminator) {
232 }
else if (c ==
'\\') {
233 if (cursor < null_terminator) {
247 token->length = (size_t)cursor - (
size_t)
token->buf;
250 assert(cursor == null_terminator);
255 token->buf = cursor++;
256 token_type = TT_SEPARATOR;
257 token->length = (size_t)1;
260 token->buf = cursor++;
261 token_type = TT_CTRL;
262 token->length = (size_t)1;
267 *tok_type = token_type;
294 assert(param1 != NULL);
295 assert(param2 != NULL);
362 save_pos = scanner->
cursor;
378 scanner->
cursor = save_pos;
412 save_cursor = scanner->
cursor;
437 if (str->
length == (
size_t)0) {
445 scanner->
cursor = save_cursor;
471 size_t pos_at_crlf = (size_t)0;
475 save_pos = scanner->
cursor;
479 raw_value->
length = (size_t)0;
508 scanner->
cursor = pos_at_crlf;
513 scanner->
cursor = save_pos;
520 while (raw_value->
length > (
size_t)0) {
522 c = raw_value->
buf[raw_value->
length - (size_t)1];
524 if (c !=
' ' && c !=
'\t' && c != TOKCHAR_CR && c != TOKCHAR_LF) {
561 save_pos = scanner->
cursor;
566 num = strtol(
token.buf, &end_ptr, base);
569 if (num < 0 || end_ptr !=
token.buf +
token.length ||
570 ((num == LONG_MIN || num == LONG_MAX) && (errno == ERANGE))) {
582 scanner->
cursor = save_pos;
609 start_pos = scanner->
cursor;
654 if (case_sensitive) {
716 const char* fmt_ptr = fmt;
724 int case_sensitive = 1;
729 assert(scanner != NULL);
733 save_pos = scanner->
cursor;
741 str_ptr = va_arg(argp,
memptr*);
742 assert(str_ptr != NULL);
746 str_ptr = va_arg(argp,
memptr*);
747 assert(str_ptr != NULL);
765 int_ptr = va_arg(argp,
int*);
766 assert(int_ptr != NULL);
767 base = c ==
'd' ? 10 : 16;
768 status =
match_int(scanner, base, int_ptr);
773 str_ptr = va_arg(argp,
memptr*);
777 assert(str_ptr != NULL);
781 assert(uri_ptr != NULL);
789 str_ptr = va_arg(argp,
memptr*);
790 assert(str_ptr != NULL);
795 status =
match_char(scanner, c, case_sensitive);
823 int_ptr = va_arg(argp,
int*);
824 assert(int_ptr != NULL);
825 *int_ptr = (int)scanner->
cursor;
857 status =
match_char(scanner, c, case_sensitive);
864 scanner->
cursor = save_pos;
891 ret_code =
vfmatch(scanner, fmt, args);
903 TRACE(
"Executing httpmsg_init()")
919 if (parser ==
nullptr)
962 status =
match(&parser->
scanner,
"%s\t%S%w%c", &method_str, &url_str);
966 index = http_method_table.
index_of(method_str.
buf,
true);
969 map_str_to_int(method_str.
buf, method_str.
length,
972 if (index == http_method_table.
npos) {
983 hmsg->
method = HTTPMETHOD_SIMPLEGET;
986 while (url_str.
length >= 2 && url_str.
buf[0] ==
'/' &&
987 url_str.
buf[1] ==
'/') {
993 if (hmsg->
urlbuf == NULL) {
1008 status =
match(&parser->
scanner,
"%s\t%S\t%ihttp%w/%w%L%c", &method_str,
1009 &url_str, &version_str);
1014 while (url_str.
length >= 2 && url_str.
buf[0] ==
'/' &&
1015 url_str.
buf[1] ==
'/') {
1021 if (hmsg->
urlbuf == NULL) {
1035 const std::string tmpbuf = std::string(method_str.
buf, method_str.
length);
1036 index = http_method_table.
index_of(tmpbuf.c_str(),
true);
1038 if (index == http_method_table.
npos) {
1045 save_char = version_str.
buf[version_str.
length];
1046 version_str.
buf[version_str.
length] =
'\0';
1049 sscanf_s(version_str.
buf,
1051 num_scanned = sscanf(version_str.
buf,
1054 version_str.
buf[version_str.
length] = save_char;
1055 if (num_scanned != 2 ||
1212 save_pos = scanner->
cursor;
1217 scanner->
cursor = save_pos;
1218 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1219 "CHUNK COULD NOT BE PARSED\n");
1224 scanner->
cursor = save_pos;
1274 assert(msg != NULL);
1287 const char* header_name) {
1293 while (node != NULL) {
1313 header.
name_id = header_name_id;
1319 if (value != NULL) {
1336 save_char = str[slen];
1349 va_start(arg_list, fmt);
1350 ret_code =
vfmatch(&scanner, fmt, arg_list);
1354 str[slen] = save_char;
1381 status =
match(&parser->
scanner,
"%ihttp%w/%w%L%c", &line);
1388 num_scanned = sscanf_s(line.
buf,
1390 num_scanned = sscanf(line.
buf,
1402 for (i = 0; i < 3; i++) {
1404 while (!isdigit(*p))
1411 if (*p !=
' ' && *p !=
'\t')
1414 while (*p ==
' ' || *p ==
'\t')
1417 n = line.
length - ((size_t)p - (
size_t)line.
buf);
1443 static char zero = 0;
1449 save_pos = scanner->
cursor;
1454 scanner->
cursor = save_pos;
1474 status =
match(scanner,
" : %R%c", &hdr_value);
1477 scanner->
cursor = save_pos;
1484 index = http_header_names_table.
index_of(
1485 std::string(
token.buf,
token.length).c_str());
1486 if (index != http_header_names_table.
npos) {
1500 if (orig_header == NULL) {
1503 if (header == NULL) {
1510 if (hdr_value.
length == (
size_t)0) {
1511 hdr_value.
buf = &zero;
1512 hdr_value.
length = (size_t)1;
1535 }
else if (hdr_value.
length > (
size_t)0) {
1572 case HTTPMETHOD_HEAD:
1573 case HTTPMETHOD_GET:
1575 case HTTPMETHOD_SUBSCRIBE:
1576 case HTTPMETHOD_UNSUBSCRIBE:
1577 case HTTPMETHOD_MSEARCH:
1589 if (response_code == 204 || response_code == 304 ||
1590 (response_code >= 100 && response_code <= 199) ||
1606 UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
1607 "Found Chunked Encoding ....\n");
1700 assert(parser !=
nullptr);
1737 size_t buf_length) {
1738 assert(parser !=
nullptr);
1739 assert(buf !=
nullptr);
1743 if (ret_code != 0) {
1757 if (raw_value->
length == (
size_t)0)
1760 num = strtol(raw_value->
buf, &end_ptr, base);
1763 || (end_ptr != raw_value->
buf + raw_value->
length) ||
1764 ((num == LONG_MIN || num == LONG_MAX) && (errno == ERANGE))) {
1780 for (i = 0; raw_value->
buf[i]; ++i) {
1781 raw_value->
buf[i] = (char)tolower(raw_value->
buf[i]);
1788 ptr = strstr(raw_value->
buf, str);
1798 return (
int)(ptr - raw_value->
buf);
1807 size_t index = http_method_table.
index_of(method);
1809 assert(index != http_method_table.
npos);
1811 return index == http_method_table.
npos ? NULL
1818 UPnPsdk_LOGINFO(log_msg)
"...\n";
1826 std::cerr <<
" method=" << hmsg->
method
1843 while (node != NULL) {
1846 std::cerr <<
" name_id=" << header->
name_id <<
", hdr_name=\""
http_message_t msg
entire raw message
#define ENTREAD_CHUNKY_BODY
Defines read method.
uri_type uri
Type of a uri, e.g. absolute, relative, etc.
constexpr std::array< const UPnPsdk::str_int_entry, 33 > Http_Header_Names
Assigns header-name id to its text representation.
http_method_t method
Http method of an outgoing request.
int is_request
If 1, msg is a request, else response.
http_method_t
Method in a HTTP request.
memptr name
Header name as a string.
int major_version
Http major version.
int entire_msg_loaded
Completeness of 'msg'.
#define HDR_TRANSFER_ENCODING
Type of a HTTP header.
memptr entity
message body(entity).
char * urlbuf
storage for url string.
#define ENTREAD_DETERMINE_READ_METHOD
Defines read method.
membuffer value
Raw-value; could be multi-lined; min-length = 0.
size_t amount_discarded
The amount of data that's been read by the user, that's no longer in the raw message buffer.
membuffer msg
entire raw message.
int http_error_code
read-only; in case of parse error, this contains the HTTP error code (4XX or 5XX).
membuffer name_buf
(Private use – don't touch.)
http_method_t request_method
Http method of an incoming response.
size_t chunk_size
Private data – don't touch.
parser_pos_t position
Private data – don't touch.
membuffer * msg
Raw http message.
parser_pos_t
Type of a parser position.
@ POS_COMPLETE
Position complete.
@ POS_RESPONSE_LINE
Position response line.
@ POS_ENTITY
Position entity.
@ POS_REQUEST_LINE
Position request line.
@ POS_HEADERS
Position headers.
parse_status_t
Status of parsing.
@ PARSE_INCOMPLETE_ENTITY
int initialized
Indicates if the object is initialized.
unsigned int content_length
Private data – don't touch.
int valid_ssdp_notify_hack
read-only; this is set to 1 if a NOTIFY request has no content-length. used to read valid sspd notify...
size_t cursor
Current position in buffer.
#define HDR_SOAPACTION
Type of a HTTP header.
#define ENTREAD_USING_CHUNKED
Defines read method.
#define ENTREAD_USING_CLEN
Defines read method.
int name_id
Header name id (for a selective group of headers only).
int ent_position
Private data – don't touch.
LinkedList headers
List of headers.
int minor_version
Http minor version.
#define HDR_UNKNOWN
Type of a HTTP header.
#define ENTREAD_UNTIL_CLOSE
Defines read method.
size_t entity_start_position
Offset in the raw message buffer, which contains the message body. preceding this are the headers of ...
#define ENTREAD_CHUNKY_HEADERS
Defines read method.
#define HDR_CONTENT_LENGTH
Type of a HTTP header.
Structure of an HTTP parser object.
Structure of a scanner object.
Structure of an HTTP message.
size_t size
Size of the buffer.
token pathquery
Member variable.
constexpr int HTTP_SUCCESS
Yet another success code.
Represents a URI used in parse_uri and elsewhere.
Buffer used in parsinghttp messages, urls, etc. Generally this simply holds a pointer into a larger a...
int ListDestroy(LinkedList *list, int freeItem)
Removes all memory associated with list nodes. Does not free LinkedList *list.
ListNode * ListNext(LinkedList *list, ListNode *node)
Returns the next item in the list.
ListNode * ListHead(LinkedList *list)
Returns the head of the list.
ListNode * ListAddTail(LinkedList *list, void *item)
Adds a node to the tail of the list. Node gets added immediately before list.tail.
ListNode * ListFind(LinkedList *list, ListNode *start, void *item)
Finds the specified item in the list.
int ListInit(LinkedList *list, cmp_routine cmp_func, free_function free_func)
Initializes LinkedList. Must be called first and only once for List.
Linked list node. Stores generic item and pointers to next and prev.
Table with C-strings mapped to an integer id.
static constexpr size_t npos
Value returned on error or when an index on a container is not found.
size_t index_of(const char *a_name, bool a_case_sensitive=false)
Match the given name with names from the entries in the table.
parse_status_t parser_parse_headers(http_parser_t *parser)
Get HTTP Method, URL location and version information.
http_header_t * httpmsg_find_hdr_str(http_message_t *msg, const char *header_name)
Compares the header name with the header names stored in the linked list of messages.
parse_status_t parser_parse(http_parser_t *parser)
The parser function.
void parser_request_init(http_parser_t *parser)
Initializes parser object for a request.
int raw_find_str(memptr *raw_value, const char *str)
Find a substring from raw character string buffer.
http_header_t * httpmsg_find_hdr(http_message_t *msg, int header_name_id, memptr *value)
Finds header from a list, with the given 'name_id'.
parse_status_t parser_parse_entity(http_parser_t *parser)
Determines method to read entity.
void parser_response_init(http_parser_t *parser, http_method_t request_method)
Initializes parser object for a response.
parse_status_t parser_parse_responseline(http_parser_t *parser)
Get HTTP Method, URL location and version information.
void httpmsg_destroy(http_message_t *msg)
Free memory allocated for the http message.
parse_status_t parser_get_entity_read_method(http_parser_t *parser)
Determines method to read entity.
int raw_to_int(memptr *raw_value, int base)
Converts raw character data to integer value.
parse_status_t parser_append(http_parser_t *parser, const char *buf, size_t buf_length)
Append date to HTTP parser, and do the parsing.
parse_status_t matchstr(char *str, size_t slen, const char *fmt,...)
Matches a variable parameter list with a string and takes actions based on the data type specified.
const char * method_to_str(http_method_t method)
A wrapper function that maps a method id to a method.
void print_http_headers(std::string_view log_msg, http_message_t *hmsg)
Print the HTTP headers.
void membuffer_destroy(membuffer *m)
Free's memory allocated for membuffer* m.
char * str_alloc(const char *str, size_t str_len)
Allocate memory and copy information from the input string to the newly allocated memory.
void membuffer_attach(membuffer *m, char *new_buf, size_t buf_len)
Free existing memory in membuffer and assign the new buffer in its place.
void membuffer_init(membuffer *m)
Wrapper to membuffer_initialize().
int membuffer_append(membuffer *m, const void *buf, size_t buf_len)
Invokes function to appends data from a constant buffer to the buffer.
int membuffer_append_str(membuffer *m, const char *c_str)
Invokes function to appends data from a constant string to the buffer.
int memptr_cmp_nocase(memptr *m, const char *s)
Compares characters of 2 strings irrespective of the case for a specific count of bytes.
int membuffer_assign(membuffer *m, const void *buf, size_t buf_len)
Allocate memory to membuffer *m and copy the contents of the in parameter const void *buf.
void membuffer_delete(membuffer *m, size_t index, size_t num_bytes)
Shrink the size of the buffer depending on the current size of the buffer and the input parameters....
size_t length
length of buffer without terminating null byte (read-only).
char * buf
mem buffer; must not write beyond buf[length-1] (read/write).
size_t length
length of memory (read-only).
char * buf
start of memory (read/write).
pointer to a chunk of memory.
Maintains a block of dynamically allocated memory.
#define UPNP_E_OUTOF_MEMORY
Not enough resources are currently available to complete the operation.
UPnPsdk_EXTERN bool g_dbug
Switch to enable verbose (debug) output.
parse_status_t parser_parse_requestline(http_parser_t *parser)
Get HTTP Method, URL location and version information.
parse_status_t parser_parse_entity_using_clen(http_parser_t *parser)
Reads entity using content-length.
parse_status_t parser_parse_chunky_headers(http_parser_t *parser)
Read headers at the end of the chunked entity.
constexpr std::array< const UPnPsdk::str_int_entry, 11 > Http_Method_Table
Defines the HTTP methods.
char * scanner_get_str(scanner_t *scanner)
Get pointer to next character in string.
int is_separator_char(int c)
Determines if the passed value is a separator.
parse_status_t parser_parse_entity_until_close(http_parser_t *parser)
Keep reading entity until the connection is closed.
parse_status_t parser_parse_chunky_entity(http_parser_t *parser)
Read entity using chunked transfer encoding.
int is_identifier_char(int c)
Determines if the passed value is permissible in token.
parse_status_t read_until_crlf(scanner_t *scanner, memptr *str)
Reads data until end of line.
void httpheader_free(void *msg)
Free memory allocated for the http header.
parse_status_t match(scanner_t *scanner, const char *fmt,...)
Matches a variable parameter list and takes necessary actions based on the data type specified.
token_type_t
Used to represent different types of tokens in input.
int httpmsg_compare(void *param1, void *param2)
Compares name id in the http headers.
void httpmsg_init(http_message_t *msg)
Initialize and allocate memory for http message.
parse_status_t skip_blank_lines(scanner_t *scanner)
Skips blank lines at the start of a msg.
int is_control_char(int c)
Determines if the passed value is a control character.
parse_status_t match_int(scanner_t *scanner, int base, int *value)
Matches an unsigned integer value in the input.
parse_status_t match_raw_value(scanner_t *scanner, memptr *raw_value)
Matches a raw value in a the input.
void scanner_init(scanner_t *scanner, membuffer *bufptr)
Initialize scanner.
void parser_init(http_parser_t *parser)
Initializes the parser object.
int is_qdtext_char(int c)
Determines if the passed value is permissible in qdtext.
parse_status_t scanner_get_token(scanner_t *scanner, memptr *token, token_type_t *tok_type)
Reads next token from the input stream.
parse_status_t parser_parse_chunky_body(http_parser_t *parser)
Read data in the chunks.
parse_status_t skip_lws(scanner_t *scanner)
Skip linear whitespace.
parse_status_t match_non_ws_string(scanner_t *scanner, memptr *str)
Match a string without whitespace or CRLF (S)
parse_status_t match_char(scanner_t *scanner, char c, int case_sensitive)
Compares a character to the next char in the scanner.
parse_status_t vfmatch(scanner_t *scanner, const char *fmt, va_list argp)
Extracts variable parameters depending on the passed in format parameter.
Define macro for synced logging to the console for detailed info and debug.
Manage Debug messages with levels "critical" to "all".
UPnPsdk_VIS void UpnpPrintf(Upnp_LogLevel DLevel, Dbg_Module Module, const char *DbgFileName, int DbgLineNo, const char *FmtStr,...)
Prints the debug statement.
int parse_uri(const char *in, size_t max, uri_type *out)
Parses a uri as defined in RFC 2396 (explaining URIs).