HTTP Parser and message builder / objects in plain C Snapshot
|
base class of both http request and http response parsers More...
base class of both http request and http response parsers
typedef struct tagHTTP_PARSER HTTP_PARSER |
typedef int(* HTTP_PROCESS_MSG_DATA)(HTTP_MESSAGE *msg, void *data, size_t data_size, void *ctx) |
typedef int(* HTTP_REQ_FINISHED)(HTTP_REQUEST *request, void *ctx) |
typedef int(* HTTP_REQ_HEADER_PARSED)(HTTP_REQUEST *request, void *ctx) |
typedef int(* HTTP_REQ_MESSAGE_BODY_DATA)(HTTP_REQUEST *request, void *data, size_t data_size, void *ctx) |
typedef enum tagHTTP_STATE_PARSING HTTP_STATE_PARSING |
enum HTTP_TK_TYPE |
enum PARSER_STATUS |
Definition at line 237 of file http.h.
{ PARSER_STATUS_ERROR = -1, PARSER_STATUS_COMPLETED = 0, PARSER_STATUS_NEED_MORE_DATA = 1, }
int HTTP_add_header_parser | ( | HTTP_PARSER * | parser, |
const char * | header_name, | ||
HEADER_ACTION | action | ||
) |
add a parser for a specific http header type
Definition at line 191 of file http.c.
{ HEADER_HASH_ACTION * action; action = ( HEADER_HASH_ACTION *) malloc( sizeof( HEADER_HASH_ACTION ) ) ; if (!action) { return -1; } action->key = strdup( header_name ); action->key_size = strlen( header_name ); action->action = action_func; return HASH_insert( &parser->header_action, &action->entry, (void *) header_name, strlen(header_name) ); }
int HTTP_get_header_token | ( | HTTP_PARSER * | parser | ) |
return next token while parsing http header value.
Definition at line 275 of file http.c.
{ char ch; size_t token_pos; if (*parser->tokpos == '\0') { return HTTP_TK_EOF; } // skip leading spaces for( ;http_is_space( *parser->tokpos ); ++parser->tokpos ); ch = *parser->tokpos; if (http_is_separator(ch)) { parser->token[ 0 ] = ch; parser->token[ 1 ] = '\0'; ++ parser->tokpos; return HTTP_TK_SEPARATOR; } if (ch == '"') { token_pos = 0; do { ch = *parser->tokpos ++ ; chk: if (http_is_ctl( ch )) { return -1; } if (ch == '"') { if (token_pos > parser->token_length) { return -1; } parser->token[ token_pos++ ] = '\0'; return HTTP_TK_QUOTED_STRING; } else { if (ch == '\\') { goto chk; } } // add character. if (token_pos > parser->token_length) { return -1; } parser->token[ token_pos++ ] = ch; } while (ch != '\0'); } if ( !http_is_separator( ch ) ) { token_pos = 0; do { if (token_pos >= parser->token_length) { return -1; } parser->token[ token_pos++ ] = ch; ch = *( ++ parser->tokpos ); } while( ch != 0 && ! http_is_ctl( ch ) && ! http_is_separator( ch ) ); parser->token[ token_pos ] = '\0'; return HTTP_TK_TEXT; } return -1; }
PARSER_STATUS HTTP_get_line | ( | BF * | bf, |
char ** | start_line | ||
) |
returns next raw line from http header, adjoins line continuations.
Definition at line 208 of file http.c.
{ uint8_t *pos; #if 0 uint8_t *prev; #endif char *line; pos = bf->get_pos; line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN ); if (!line) { if (BF_is_full( bf ) ) { // whole buffer does not even contain a line. untennable. return PARSER_STATUS_ERROR; } BF_compact(bf); return PARSER_STATUS_NEED_MORE_DATA; } #if 0 // check if this line is continued. while ( 1 ) { prev = bf->get_pos; line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN ); if (!line) { // restore the line marker. *(bf->get_pos - 2) = '\r'; if (BF_is_full( bf ) ) { // a continuation line spans the whole input buffer, // That's very bad, unfortunately we can't support this. return PARSER_STATUS_ERROR; } bf->get_pos = pos; return PARSER_STATUS_NEED_MORE_DATA; } if (!is_space( *line )) { *(bf->get_pos - 2) = '\r'; bf->get_pos = prev; break; } // adjoin the previous line to this line. *(bf->get_pos - 2) = ' '; *(bf->get_pos - 1) = ' '; } #endif *start_line = (char *) pos; return PARSER_STATUS_COMPLETED; }
PARSER_STATUS HTTP_parse_header_line | ( | HTTP_PARSER * | parser, |
HTTP_MESSAGE * | request, | ||
BF * | bf, | ||
int * | eof_header | ||
) |
dispatch parsing of one http header
Definition at line 347 of file http.c.
{ PARSER_STATUS rt; HEADER_HASH_ACTION *action; char *line, *pos; *eof_header = 0; rt = HTTP_get_line( bf, &line ); if (rt != PARSER_STATUS_COMPLETED) { return rt; } if (! *line ) { *eof_header = 1; return PARSER_STATUS_COMPLETED; } if (is_space(*line)) { return -1; } // good enough, do not test for all separators though. for( pos = line ;!http_is_ctl_or_space( *pos ) && *pos != ':'; ++pos ) { to_lower( pos ); } if (*pos != ':') { return -1; } *pos = '\0'; if (HTTP_MESSAGE_add_header( request, line, pos+1 )) { return -1; } // got the header line. find header processor. action = (HEADER_HASH_ACTION *) HASH_find( &parser->header_action, line, pos - line ); if (action) { parser->tokpos = pos + 1; action->action( request, parser ); } // recored header value in http request (optional) return 0; }
int HTTP_PARSER_chunked_data_init | ( | HTTP_PARSER * | parser | ) |
start parsing chunked data
Definition at line 437 of file http.c.
{ parser->state = HTTP_STATE_PARSING_BODY_CHUNK_HEADER; return 0; }
PARSER_STATUS HTTP_PARSER_chunked_data_process | ( | HTTP_PARSER * | parser, |
BF * | bf, | ||
HTTP_PROCESS_MSG_DATA | cb, | ||
HTTP_MESSAGE * | msg, | ||
void * | ctx | ||
) |
finish parsind chunked content data.
Definition at line 459 of file http.c.
{ char *line,*pos,*cur, *endptr; int rt, eof_header; long val; switch( parser->state ) { case HTTP_STATE_PARSING_BODY_CHUNK_HEADER: chunk_header: line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN ); if (!line) { BF_compact(bf); return PARSER_STATUS_NEED_MORE_DATA; } for(pos = line; is_space(*pos); ++pos); for(cur = pos; is_hex(*cur); ++cur); *cur = '\0'; val = strtol( pos, &endptr, 16 ); if (val != 0) { parser->content_left = (size_t) val; parser->state = HTTP_STATE_PARSING_BODY_CHUNK_DATA; goto chunk_data; } else { parser->state = HTTP_STATE_PARSING_BODY_CHUNK_TRAILER; goto chunk_trailer; } case HTTP_STATE_PARSING_BODY_CHUNK_DATA: chunk_data: rt = HTTP_PARSER_content_length_process( parser, bf, cb, msg, ctx ); if (rt != 0) { return rt; } parser->state = HTTP_STATE_PARSING_BODY_CHUNK_EOF_AFTER_DATA; goto chunk_eof_data; case HTTP_STATE_PARSING_BODY_CHUNK_EOF_AFTER_DATA: chunk_eof_data: line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN ); if (!line) { BF_compact(bf); return PARSER_STATUS_NEED_MORE_DATA; } parser->state = HTTP_STATE_PARSING_BODY_CHUNK_HEADER; goto chunk_header; case HTTP_STATE_PARSING_BODY_CHUNK_TRAILER: chunk_trailer: rt = HTTP_parse_header_line( parser, msg , bf, &eof_header ); if (rt != PARSER_STATUS_COMPLETED) { return rt; } if (eof_header) { BF_compact(bf); return PARSER_STATUS_COMPLETED; } return PARSER_STATUS_NEED_MORE_DATA; default: return -1; } return 0; }
int HTTP_PARSER_content_length_init | ( | HTTP_PARSER * | parser, |
HTTP_MESSAGE * | msg | ||
) |
start parsing content length message body
Definition at line 395 of file http.c.
{ parser->state = HTTP_STATE_PARSING_BODY_CONTENT_LENGTH; parser->content_left = msg-> content_length; return 0; }
PARSER_STATUS HTTP_PARSER_content_length_process | ( | HTTP_PARSER * | parser, |
BF * | bf, | ||
HTTP_PROCESS_MSG_DATA | cb, | ||
HTTP_MESSAGE * | msg, | ||
void * | ctx | ||
) |
consume content length message body.
Definition at line 402 of file http.c.
{ size_t bytes_to_take; size_t bytes_available; bytes_available = BF_get_size( bf ); if (bytes_available == 0) { return PARSER_STATUS_NEED_MORE_DATA; } bytes_to_take = parser->content_left; if (bytes_to_take == 0) { return PARSER_STATUS_ERROR; } if (bytes_available < bytes_to_take) { bytes_to_take = bytes_available; } if (cb( msg, bf->get_pos, bytes_to_take, ctx )) { return PARSER_STATUS_ERROR; } parser->content_left -= bytes_to_take; bf->get_pos += bytes_to_take; if (parser->content_left == 0) { parser->state = HTTP_STATE_PARSING_REQUEST_LINE; return PARSER_STATUS_COMPLETED; } BF_compact(bf); return PARSER_STATUS_NEED_MORE_DATA; }
int HTTP_PARSER_free | ( | HTTP_PARSER * | parser | ) |
int HTTP_PARSER_init | ( | HTTP_PARSER * | parser | ) |
Definition at line 165 of file http.c.
{ parser->state = HTTP_STATE_PARSING_REQUEST_LINE; parser->token = malloc( MAX_HTTP_TOKEN_SIZE ); if (! parser->token ) { return -1; } parser->token_length = MAX_HTTP_TOKEN_SIZE; if (HASH_init( &parser->header_action, 32, 0, hash_compare, 0 ) ) { return -1; } return 0; }