HTTP Parser and message builder / objects in plain C Snapshot
Classes | Typedefs | Enumerations | Functions
HTTP_PARSER

base class of both http request and http response parsers More...

Classes

struct  tagHTTP_PARSER

Typedefs

typedef enum tagHTTP_STATE_PARSING HTTP_STATE_PARSING
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_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 int(* HTTP_REQ_FINISHED )(HTTP_REQUEST *request, void *ctx)

Enumerations

enum  tagHTTP_STATE_PARSING {
  HTTP_STATE_PARSING_REQUEST_LINE, HTTP_STATE_PARSING_HEADERS, HTTP_STATE_PARSING_BODY_CONTENT_LENGTH, HTTP_STATE_PARSING_BODY_CHUNK_HEADER,
  HTTP_STATE_PARSING_BODY_CHUNK_DATA, HTTP_STATE_PARSING_BODY_CHUNK_EOF_AFTER_DATA, HTTP_STATE_PARSING_BODY_CHUNK_TRAILER
}
enum  PARSER_STATUS { PARSER_STATUS_ERROR = -1, PARSER_STATUS_COMPLETED = 0, PARSER_STATUS_NEED_MORE_DATA = 1 }
enum  HTTP_TK_TYPE { HTTP_TK_QUOTED_STRING, HTTP_TK_TEXT, HTTP_TK_SEPARATOR, HTTP_TK_EOF }

Functions

int HTTP_PARSER_init (HTTP_PARSER *parser)
int HTTP_PARSER_free (HTTP_PARSER *parser)
int HTTP_add_header_parser (HTTP_PARSER *parser, const char *header_name, HEADER_ACTION action)
 add a parser for a specific http header type
PARSER_STATUS HTTP_get_line (BF *bf, char **start_line)
 returns next raw line from http header, adjoins line continuations.
int HTTP_get_header_token (HTTP_PARSER *parser)
 return next token while parsing http header value.
PARSER_STATUS HTTP_parse_header_line (HTTP_PARSER *parser, HTTP_MESSAGE *request, BF *bf, int *eof_header)
 dispatch parsing of one http header
int HTTP_PARSER_content_length_init (HTTP_PARSER *parser, HTTP_MESSAGE *msg)
 start parsing content length message body
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.
int HTTP_PARSER_chunked_data_init (HTTP_PARSER *parser)
 start parsing chunked data
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.

Detailed Description

base class of both http request and http response parsers


Typedef Documentation

typedef struct tagHTTP_PARSER HTTP_PARSER
typedef int(* HTTP_PROCESS_MSG_DATA)(HTTP_MESSAGE *msg, void *data, size_t data_size, void *ctx)

Definition at line 251 of file http.h.

typedef int(* HTTP_REQ_FINISHED)(HTTP_REQUEST *request, void *ctx)

Definition at line 254 of file http.h.

typedef int(* HTTP_REQ_HEADER_PARSED)(HTTP_REQUEST *request, void *ctx)

Definition at line 252 of file http.h.

typedef int(* HTTP_REQ_MESSAGE_BODY_DATA)(HTTP_REQUEST *request, void *data, size_t data_size, void *ctx)

Definition at line 253 of file http.h.


Enumeration Type Documentation

Enumerator:
HTTP_TK_QUOTED_STRING 
HTTP_TK_TEXT 
HTTP_TK_SEPARATOR 
HTTP_TK_EOF 

Definition at line 244 of file http.h.

Enumerator:
PARSER_STATUS_ERROR 
PARSER_STATUS_COMPLETED 
PARSER_STATUS_NEED_MORE_DATA 

Definition at line 237 of file http.h.

Enumerator:
HTTP_STATE_PARSING_REQUEST_LINE 
HTTP_STATE_PARSING_HEADERS 
HTTP_STATE_PARSING_BODY_CONTENT_LENGTH 
HTTP_STATE_PARSING_BODY_CHUNK_HEADER 
HTTP_STATE_PARSING_BODY_CHUNK_DATA 
HTTP_STATE_PARSING_BODY_CHUNK_EOF_AFTER_DATA 
HTTP_STATE_PARSING_BODY_CHUNK_TRAILER 

Definition at line 213 of file http.h.


Function Documentation

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.

Returns:
-1 on error, otherwise type of token returns (value out of HTTP_TK_TYPE)

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

Returns:
-1 error, 0 - http header finished, 1 - need more data, 2 - header line parsed ok.

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_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)

Definition at line 181 of file http.c.

{
  if (parser->token) {
    free( parser->token );
    parser->token = 0;
  }
  return 0;
}
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;
}