HTTP Parser and message builder / objects in plain C Snapshot
http.c
Go to the documentation of this file.
00001 #include "http.h"
00002 #include <sutils.h> 
00003 #include <charclass.h>
00004 #include <stdio.h>
00005 
00006 int init_parsers_request_header( HTTP_PARSER *parser );
00007 int init_parsers_general_header( HTTP_PARSER *parser );
00008  
00009 #define S_METHOD_GET            "GET"
00010 #define S_METHOD_GET_LEN        3
00011 #define S_METHOD_POST           "POST" 
00012 #define S_METHOD_POST_LEN       4
00013 #define S_METHOD_PUT            "PUT"
00014 #define S_METHOD_PUT_LEN        3
00015 #define S_METHOD_DELETE         "DELETE"
00016 #define S_METHOD_DELETE_LEN     6
00017 #define S_METHOD_HEAD           "HEAD"
00018 #define S_METHOD_HEAD_LEN       4
00019 #define S_METHOD_OPTIONS        "OPTIONS"
00020 #define S_METHOD_OPTIONS_LEN    7
00021 #define S_METHOD_TRACE          "TRACE"
00022 #define S_METHOD_TRACE_LEN      5
00023 #define S_METHOD_CONNECT        "CONNECT"
00024 #define S_METHOD_CONNECT_LEN    7
00025 
00026 #define HTTP_START_S "HTTP/1."
00027 #define HTTP_START_S_LEN 7
00028 
00029 
00030 
00031 #define HTTP_EOF_LINE  "\r\n"
00032 #define HTTP_EOF_LINE_LEN 2
00033 
00034 
00035 
00036 //==========================================================================
00037 
00038 #define to_lower(pos) \
00039   if (is_upper_case( *pos )) { \
00040     *pos = *pos - 'A' + 'a'; \
00041   }
00042 
00043 
00044 //CTL   = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
00045 M_INLINE int http_is_ctl( char ch )
00046 {
00047     return (ch >=0 && ch <= 31) || ch == 127;
00048 }
00049 
00050 M_INLINE int http_is_ctl_or_space( char ch )
00051 {
00052     return (ch >=0 && ch <= 32) || ch == 127;
00053 }
00054 
00055 M_INLINE int http_is_space( char ch )
00056 {
00057   return ch == ' ' || ch == '\t';
00058 }
00059 
00060 // separators     = "(" | ")" | "<" | ">" | "@"
00061 //                      | "," | ";" | ":" | "\" | <">
00062 //                      | "/" | "[" | "]" | "?" | "="
00063 //                      | "{" | "}" | SP | HT
00064 M_INLINE int http_is_separator( char ch )
00065 {
00066   return ch == '('  || ch ==  ')' || ch ==  '<' || ch ==  '>'  || ch ==  '@'
00067       || ch ==  ',' || ch ==  ';' || ch ==  ':' || ch ==  '\\' || ch ==  '"'
00068       || ch ==  '/' || ch ==  '[' || ch ==  ']' || ch ==  '?'  || ch ==  '='
00069       || ch ==  '{' || ch ==  '}' || ch ==  ' ' || ch ==  '\t';
00070 }
00071 
00072 
00073 static void free_stringpair( DLISTUNR *list, void *entry, void *context)
00074 {
00075   STRINGPAIR *pair = (STRINGPAIR *) entry;
00076  
00077   M_UNUSED(list);
00078   M_UNUSED(context);
00079  
00080   free( pair->key );
00081   free( pair->value );
00082 }
00083 
00084 
00085 //==========================================================================
00086 int HTTP_MESSAGE_init( HTTP_MESSAGE *message )
00087 {  
00088   message->flags = 0;
00089   message->content_length = -1;
00090 
00091   if (DLISTUNR_init( &message->header_values, sizeof(STRINGPAIR), 10) ) {
00092     return -1;
00093   }
00094   return 0;
00095 }
00096 
00097 void HTTP_MESSAGE_free( HTTP_MESSAGE *message )
00098 {  
00099   message->flags = 0;
00100   message->content_length = -1;
00101   DLISTUNR_free( &message->header_values,  free_stringpair, 0); 
00102 
00103 }
00104 
00105 int HTTP_MESSAGE_add_header( HTTP_MESSAGE *message, const char *name , const char *value )
00106 {
00107   STRINGPAIR entry;
00108 
00109   entry.key   = strdup(name);
00110   entry.value = strdup(value);
00111 
00112   return DLISTUNR_push_back( &message->header_values, &entry, sizeof(entry));
00113 }
00114 
00115 const char * HTTP_MESSAGE_find_header( HTTP_MESSAGE *message, const char *name ) {
00116   DLISTUNR_position idx;
00117   STRINGPAIR *cur;
00118 
00119   DLISTUNR_FOREACH( idx, &message->header_values ) {
00120      cur = (STRINGPAIR *) DLISTUNR_at( &message->header_values, idx );
00121      if (strcmp(cur->key, name) == 0)  {
00122         return cur->value;
00123      }
00124   }
00125   return 0;
00126 }
00127 
00128 STRINGPAIR * HTTP_MESSAGE_first_header( HTTP_MESSAGE *message, DLISTUNR_position *pos )
00129 {
00130   *pos =  DLISTUNR_get_first( &message->header_values );
00131   return (STRINGPAIR *)  DLISTUNR_at(  &message->header_values, *pos );
00132 }
00133 
00134 STRINGPAIR * HTTP_MESSAGE_next_header(HTTP_MESSAGE *message, DLISTUNR_position *pos )
00135 {
00136   if (pos->entry == (DLISTUNR_entry *) &message->header_values.root) {
00137     return 0;
00138   }
00139   *pos =  DLISTUNR_next( *pos );
00140   return (STRINGPAIR *)  DLISTUNR_at(  &message->header_values, *pos );
00141 }
00142 
00143 
00144 
00145 //==========================================================================
00146 
00147 typedef struct tagHEADER_HASH_ACTION {
00148   HASH_Entry entry;  
00149   HEADER_ACTION action;
00150   char *key;
00151   size_t key_size;
00152 } HEADER_HASH_ACTION;
00153 
00154 #define MAX_HTTP_TOKEN_SIZE 120
00155 
00156 static int hash_compare(HASH_Entry *entry, void * key, ssize_t key_length)
00157 {
00158   HEADER_HASH_ACTION *lhs;
00159 
00160   lhs = (HEADER_HASH_ACTION *) entry;
00161 
00162   return strncmp(lhs->key, key, key_length); 
00163 }
00164 
00165 int  HTTP_PARSER_init(  HTTP_PARSER *parser )
00166 {
00167   parser->state = HTTP_STATE_PARSING_REQUEST_LINE;
00168   parser->token = malloc( MAX_HTTP_TOKEN_SIZE );
00169   if (! parser->token ) {
00170     return -1;
00171   }
00172   parser->token_length = MAX_HTTP_TOKEN_SIZE;
00173   
00174   if (HASH_init( &parser->header_action, 32, 0, hash_compare, 0 ) ) {
00175     return -1;
00176   }
00177  
00178   return 0;
00179 }
00180 
00181 int  HTTP_PARSER_free(  HTTP_PARSER *parser )
00182 {
00183   if (parser->token) {
00184     free( parser->token );
00185     parser->token = 0;
00186   }
00187   return 0;
00188 }
00189 
00190 
00191 int HTTP_add_header_parser( HTTP_PARSER *parser, const char *header_name, HEADER_ACTION action_func )
00192 {
00193   HEADER_HASH_ACTION * action;
00194   
00195   action = ( HEADER_HASH_ACTION *) malloc( sizeof( HEADER_HASH_ACTION ) ) ;
00196   if (!action) {
00197     return -1;
00198   }
00199   action->key = strdup( header_name );
00200   action->key_size = strlen( header_name );
00201   action->action = action_func;
00202 
00203 
00204   return HASH_insert( &parser->header_action, &action->entry, (void *) header_name, strlen(header_name) );
00205 }
00206 
00207 
00208 PARSER_STATUS HTTP_get_line( BF *bf, char **start_line )
00209 {
00210    uint8_t *pos;
00211 #if 0   
00212    uint8_t *prev;
00213 #endif   
00214    char *line;
00215 
00216    pos = bf->get_pos;
00217    line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN );
00218    if (!line) {
00219       if (BF_is_full( bf ) ) {
00220         // whole buffer does not even contain a line. untennable.
00221         return PARSER_STATUS_ERROR;
00222       }
00223       BF_compact(bf);
00224       return PARSER_STATUS_NEED_MORE_DATA;
00225    }
00226 
00227 
00228 #if 0
00229    // check if this line is continued.
00230    while ( 1 ) {
00231      prev = bf->get_pos;
00232      line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN );
00233      if (!line) {
00234        // restore the line marker.
00235        *(bf->get_pos - 2)  = '\r';
00236 
00237         if (BF_is_full( bf ) ) {
00238           // a continuation line spans the whole input buffer,
00239           // That's very bad, unfortunately we can't support this.
00240           return  PARSER_STATUS_ERROR;
00241         }
00242         bf->get_pos = pos;
00243         return PARSER_STATUS_NEED_MORE_DATA;
00244      }
00245 
00246      if (!is_space( *line )) {
00247        *(bf->get_pos - 2)  = '\r';
00248        bf->get_pos = prev;
00249        break;
00250      }
00251      // adjoin the previous line to this line.
00252      *(bf->get_pos - 2)  = ' ';
00253      *(bf->get_pos - 1)  = ' ';
00254    }
00255 #endif
00256    
00257    *start_line = (char *) pos;
00258    return PARSER_STATUS_COMPLETED;
00259 }
00260  
00261 /*
00262 token          = 1*<any CHAR except CTLs or separators>
00263 separators     = "(" | ")" | "<" | ">" | "@"
00264                       | "," | ";" | ":" | "\" | <">
00265                       | "/" | "[" | "]" | "?" | "="
00266                       | "{" | "}" | SP | HT
00267 
00268 
00269 quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
00270 qdtext         = <any TEXT except <">>
00271 quoted-pair    = "\" CHAR
00272 
00273 */
00274 
00275 int HTTP_get_header_token( HTTP_PARSER *parser) 
00276 {
00277    char ch;
00278    size_t token_pos;
00279 
00280    if (*parser->tokpos == '\0') {
00281      return  HTTP_TK_EOF;
00282    }
00283     
00284 
00285      // skip leading spaces
00286    for( ;http_is_space( *parser->tokpos ); ++parser->tokpos );
00287 
00288    ch = *parser->tokpos;
00289 
00290    if (http_is_separator(ch)) {
00291      parser->token[ 0 ] = ch;
00292      parser->token[ 1 ] = '\0';
00293      ++ parser->tokpos;
00294      return HTTP_TK_SEPARATOR;
00295    }
00296 
00297    
00298    if (ch == '"') {
00299       token_pos = 0; 
00300       do {
00301         ch = *parser->tokpos ++ ;
00302 chk:
00303         if (http_is_ctl( ch )) {
00304            return -1;
00305         }
00306         if (ch == '"') {
00307           if (token_pos > parser->token_length) {
00308             return -1;
00309           }
00310           parser->token[ token_pos++ ] = '\0';
00311           return  HTTP_TK_QUOTED_STRING; 
00312         } else {
00313           if (ch == '\\') {
00314             goto chk;
00315           }
00316         }
00317 
00318         // add character.
00319         if (token_pos > parser->token_length) {
00320            return -1;
00321         }
00322         parser->token[ token_pos++ ] = ch;
00323 
00324       } while (ch != '\0');
00325       
00326    }
00327 
00328    if ( !http_is_separator( ch ) ) {
00329      token_pos = 0; 
00330      do {
00331 
00332         if (token_pos >= parser->token_length) {
00333            return -1;
00334         }
00335         parser->token[ token_pos++ ] = ch;
00336 
00337        ch = *( ++ parser->tokpos );
00338      } while( ch != 0 && ! http_is_ctl( ch ) &&  ! http_is_separator( ch ) );
00339 
00340      parser->token[ token_pos ] = '\0';
00341      return HTTP_TK_TEXT; 
00342    }
00343 
00344    return -1;
00345 }
00346 
00347 PARSER_STATUS HTTP_parse_header_line( HTTP_PARSER *parser, HTTP_MESSAGE *request, BF *bf, int *eof_header)
00348 {
00349   PARSER_STATUS rt;
00350   HEADER_HASH_ACTION *action;
00351   char *line, *pos;
00352   
00353   *eof_header = 0;
00354 
00355   rt = HTTP_get_line( bf, &line );
00356   if (rt != PARSER_STATUS_COMPLETED) {
00357     return rt;
00358   }
00359 
00360   if (! *line ) {
00361     *eof_header = 1;
00362      return PARSER_STATUS_COMPLETED;
00363   }
00364 
00365   if (is_space(*line)) {
00366     return -1;
00367   }
00368   // good enough, do not test for all separators though.
00369   for( pos = line  ;!http_is_ctl_or_space( *pos ) && *pos != ':'; ++pos ) {
00370      to_lower( pos );
00371   } 
00372   if (*pos != ':') {
00373     return -1;
00374   }
00375   *pos = '\0';
00376 
00377   if (HTTP_MESSAGE_add_header( request, line, pos+1 )) {
00378      return -1;
00379   }
00380 
00381 
00382   // got the header line. find header processor.
00383   action = (HEADER_HASH_ACTION *) HASH_find( &parser->header_action, line, pos - line );
00384   if (action) {
00385 
00386     parser->tokpos = pos + 1;
00387     action->action( request, parser );
00388   }
00389 
00390   // recored header value in http request (optional)
00391 
00392   return 0;
00393 }
00394 
00395 int HTTP_PARSER_content_length_init( HTTP_PARSER *parser, HTTP_MESSAGE *msg )
00396 {
00397     parser->state = HTTP_STATE_PARSING_BODY_CONTENT_LENGTH;
00398     parser->content_left = msg-> content_length;
00399     return 0;
00400 }
00401 
00402 int HTTP_PARSER_content_length_process( HTTP_PARSER *parser, BF *bf, HTTP_PROCESS_MSG_DATA cb, HTTP_MESSAGE *msg,   void *ctx)
00403 {
00404    size_t bytes_to_take;
00405    size_t bytes_available; 
00406 
00407    bytes_available = BF_get_size( bf );
00408    if (bytes_available == 0) {
00409        return PARSER_STATUS_NEED_MORE_DATA;
00410    }
00411 
00412    bytes_to_take =  parser->content_left; 
00413    if (bytes_to_take == 0) {
00414      return  PARSER_STATUS_ERROR;
00415    }
00416 
00417    if (bytes_available < bytes_to_take) {
00418      bytes_to_take = bytes_available;
00419    } 
00420 
00421    if (cb( msg, bf->get_pos, bytes_to_take, ctx )) {
00422      return PARSER_STATUS_ERROR; 
00423    }
00424 
00425    parser->content_left -= bytes_to_take;
00426    bf->get_pos += bytes_to_take;
00427 
00428    if (parser->content_left == 0) {
00429      parser->state =  HTTP_STATE_PARSING_REQUEST_LINE;
00430      return PARSER_STATUS_COMPLETED;
00431    }
00432 
00433    BF_compact(bf);
00434    return PARSER_STATUS_NEED_MORE_DATA;
00435 }
00436 
00437 int HTTP_PARSER_chunked_data_init( HTTP_PARSER *parser )
00438 {
00439     parser->state = HTTP_STATE_PARSING_BODY_CHUNK_HEADER;
00440     return 0;
00441 }
00442 
00443 
00444 
00445 /*
00446  Chunked-Body  = *chunk
00447                   last-chunk
00448                   trailer
00449                   CRLF
00450  chunk          = chunk-size [ chunk-extension ] CRLF
00451                   chunk-data CRLF
00452  chunk-size     = 1*HEX 
00453  last-chunk     = 1*("0") [ chunk-extension ] CRLF
00454  chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
00455  chunk-ext-val  = token | quoted-string
00456  chunk-data     = chunk-size(OCTET)
00457  trailer        = *(entity-header CRLF)
00458 */
00459 PARSER_STATUS HTTP_PARSER_chunked_data_process( HTTP_PARSER *parser, BF *bf, HTTP_PROCESS_MSG_DATA cb, HTTP_MESSAGE *msg, void *ctx)
00460 {
00461     char *line,*pos,*cur, *endptr;
00462     int rt, eof_header;
00463     long val;
00464 
00465     switch( parser->state ) {
00466       case HTTP_STATE_PARSING_BODY_CHUNK_HEADER:
00467 chunk_header:      
00468          line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN );
00469          if (!line) {
00470            BF_compact(bf);
00471            return PARSER_STATUS_NEED_MORE_DATA;
00472          }
00473          for(pos = line; is_space(*pos); ++pos);
00474          for(cur = pos; is_hex(*cur); ++cur);
00475          *cur = '\0';
00476 
00477          val = strtol( pos, &endptr, 16 );
00478          if (val != 0) {
00479            parser->content_left = (size_t) val;
00480            parser->state = HTTP_STATE_PARSING_BODY_CHUNK_DATA;
00481            goto chunk_data; 
00482          } else {
00483            parser->state = HTTP_STATE_PARSING_BODY_CHUNK_TRAILER;
00484            goto chunk_trailer;
00485          }      
00486          
00487 
00488       case HTTP_STATE_PARSING_BODY_CHUNK_DATA:
00489 chunk_data:      
00490         rt = HTTP_PARSER_content_length_process( parser, bf, cb, msg, ctx );
00491         if (rt != 0) {
00492           return rt;
00493         }
00494 
00495         parser->state = HTTP_STATE_PARSING_BODY_CHUNK_EOF_AFTER_DATA;
00496         goto chunk_eof_data;
00497 
00498      case HTTP_STATE_PARSING_BODY_CHUNK_EOF_AFTER_DATA:
00499 chunk_eof_data:
00500         line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN );
00501         if (!line) {
00502           BF_compact(bf);
00503           return PARSER_STATUS_NEED_MORE_DATA;
00504         }
00505         parser->state = HTTP_STATE_PARSING_BODY_CHUNK_HEADER;
00506         goto chunk_header;
00507 
00508       case HTTP_STATE_PARSING_BODY_CHUNK_TRAILER:
00509 chunk_trailer:      
00510         rt = HTTP_parse_header_line( parser, msg , bf, &eof_header );
00511         if (rt != PARSER_STATUS_COMPLETED) {
00512           return rt;
00513         }
00514         if (eof_header) {
00515           BF_compact(bf);
00516           return PARSER_STATUS_COMPLETED;
00517         }   
00518         return PARSER_STATUS_NEED_MORE_DATA;
00519       
00520       default:
00521         return -1;
00522     }
00523     return 0;
00524 }
00525 
00526 
00527 //==========================================================================
00528 
00529 // Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
00530 // Method         = "OPTIONS"                ; Section 9.2
00531 //             | "GET"                    ; Section 9.3
00532 //             | "HEAD"                   ; Section 9.4
00533 //             | "POST"                   ; Section 9.5
00534 //             | "PUT"                    ; Section 9.6
00535 //             | "DELETE"                 ; Section 9.7
00536 //             | "TRACE"                  ; Section 9.8
00537 //             | "CONNECT"                ; Section 9.9
00538 //             | extension-method
00539 //
00540 //     extension-method = token
00541 //
00542 //  Request-URI    = "*" | absoluteURI | abs_path | authority
00543 //
00544 static PARSER_STATUS parse_request_line( HTTP_REQUEST *request, BF *bf )
00545 {
00546   Http_method_type method;
00547   char *eof,*tok, *line;
00548   PARSER_STATUS rt;  
00549 
00550   rt = HTTP_get_line( bf, &line );
00551   if (rt != PARSER_STATUS_COMPLETED) {
00552     return rt;
00553   }
00554 
00555   switch( *line ) {
00556     case 'G':
00557       if (strncmp( line, S_METHOD_GET, S_METHOD_GET_LEN ) != 0) {
00558         return PARSER_STATUS_ERROR;
00559       }
00560       method = HTTP_METHOD_GET; 
00561       line += S_METHOD_GET_LEN;
00562       break;
00563     case 'P':
00564       if (strncmp( line, S_METHOD_POST, S_METHOD_POST_LEN ) == 0) {
00565         method = HTTP_METHOD_POST;
00566         line += S_METHOD_POST_LEN;
00567       } else if (strncmp( line, S_METHOD_PUT, S_METHOD_PUT_LEN ) == 0) {
00568         method = HTTP_METHOD_PUT; 
00569         line += S_METHOD_PUT_LEN;
00570       } else {
00571         return PARSER_STATUS_ERROR;
00572       }
00573       break;
00574     case 'H':
00575       if (strncmp( line, S_METHOD_HEAD, S_METHOD_HEAD_LEN ) != 0) {
00576         return  PARSER_STATUS_ERROR;
00577       }
00578       method = HTTP_METHOD_HEAD;
00579       line += S_METHOD_HEAD_LEN ;
00580       break;
00581     case 'D':
00582       if (strncmp( line, S_METHOD_DELETE, S_METHOD_DELETE_LEN ) != 0) {
00583         return  PARSER_STATUS_ERROR;
00584       }
00585       method = HTTP_METHOD_DELETE;
00586       line += S_METHOD_DELETE_LEN ;
00587       break;
00588     case 'T':
00589       if (strncmp( line, S_METHOD_TRACE, S_METHOD_TRACE_LEN ) != 0) {
00590         return PARSER_STATUS_ERROR;
00591       }
00592       method = HTTP_METHOD_TRACE;
00593       line += S_METHOD_TRACE_LEN ;
00594       break;
00595     case 'C':
00596       if (strncmp( line, S_METHOD_CONNECT, S_METHOD_CONNECT_LEN ) != 0) {
00597         return PARSER_STATUS_ERROR;
00598       }
00599       method = HTTP_METHOD_CONNECT; 
00600       line += S_METHOD_CONNECT_LEN;
00601       break;
00602     default:
00603       return -1;
00604   }
00605   request->method = method;
00606 
00607   tok =  get_token( line, &eof );
00608   if (!tok) {
00609    return PARSER_STATUS_ERROR;
00610   }
00611 
00612   *eof = 0;
00613   request->raw_url = strdup( tok );
00614 
00615   if (method != HTTP_METHOD_CONNECT) {
00616     if (URI_parse( &request->url, tok )) {
00617       return PARSER_STATUS_ERROR;
00618     }
00619   } else {
00620     if (strcmp(tok, "*") != 0) {
00621       return PARSER_STATUS_ERROR;
00622     }
00623     URI_init( &request->url );
00624   }
00625 
00626   tok =  get_token( eof+1, &eof );
00627   if (strncmp( tok, HTTP_START_S, HTTP_START_S_LEN ) != 0) {
00628     return PARSER_STATUS_ERROR;
00629   }
00630   tok += HTTP_START_S_LEN;
00631 
00632   switch(tok[0]) {
00633      case '0':
00634         request->version = HTTP_VERSION_1_0;
00635         break;
00636      case '1':
00637         request->version =  HTTP_VERSION_1_1; 
00638         break;
00639      default:
00640         return PARSER_STATUS_ERROR;
00641   }
00642   return PARSER_STATUS_COMPLETED;
00643 }
00644 
00645 
00646 int HTTP_REQUEST_PARSER_init( HTTP_REQUEST_PARSER *parser, 
00647                               HTTP_REQ_HEADER_PARSED header_parsed,
00648                               HTTP_REQ_MESSAGE_BODY_DATA on_message_body_data,
00649                               HTTP_REQ_FINISHED on_request_finished,
00650                               void *ctx)
00651 {
00652   parser->ctx = ctx;
00653   parser->ev_header  = header_parsed;
00654   parser->ev_body = on_message_body_data;
00655   parser->ev_finish = on_request_finished;
00656      
00657   if (HTTP_PARSER_init( &parser->base )) {
00658     return -1;
00659   }
00660  
00661   if (init_parsers_request_header( &parser->base ) ||
00662       init_parsers_general_header( &parser->base ) ) {
00663     return -1;
00664   }
00665  
00666   return 0;
00667 }
00668 
00669 
00670 
00671 PARSER_STATUS HTTP_REQUEST_PARSER_process( HTTP_REQUEST_PARSER *parser, HTTP_REQUEST *request, BF *bf )
00672 {
00673   int rt, eof_header;
00674 
00675 
00676   while(1) {
00677    switch( parser->base.state ) {
00678     case HTTP_STATE_PARSING_REQUEST_LINE:
00679       rt = parse_request_line( request, bf );
00680       if (rt != 0) {
00681        goto req_do_return;
00682       }
00683       parser->base.state = HTTP_STATE_PARSING_HEADERS; // 
00684    //break; - fall through
00685 
00686 
00687     case HTTP_STATE_PARSING_HEADERS:
00688       rt = HTTP_parse_header_line( (HTTP_PARSER *) parser, (HTTP_MESSAGE *) request, bf, &eof_header );
00689       if (rt != 0) {
00690         goto req_do_return;
00691       }
00692 
00693       if (eof_header) {
00694 #if 0
00695           // this check will we done by web server; if no host header then send back HTTP status 400.
00696 
00697           // if http 1.1 then host header is a must. no host header is an error.
00698           if (request->version ==  HTTP_VERSION_1_1 &&
00699               !request->has_host_header) {
00700             return PARSER_STATUS_ERROR;  
00701           }
00702 #endif    
00703 
00704           // eof parsing http header.
00705           if (parser->ev_header( request, parser->ctx )) {
00706             return PARSER_STATUS_ERROR; 
00707           }
00708           BF_compact(bf);
00709 
00710           if (request->base.flags & HTTP_MESSAGE_FLAG_TRANSFER_CHUNKED) {
00711              HTTP_PARSER_chunked_data_init( &parser->base );
00712              continue;  
00713           }
00714 
00715  
00716           if (request->base.flags & HTTP_MESSAGE_FLAG_HAS_CONTENT_LENGTH) {
00717              HTTP_PARSER_content_length_init( &parser->base, &request->base );
00718              continue;  
00719           }
00720 
00721 next_request:
00722           if (parser->ev_finish( request, parser->ctx )) {
00723             return PARSER_STATUS_ERROR;
00724           }
00725           parser->base.state = HTTP_STATE_PARSING_REQUEST_LINE; 
00726           HTTP_REQUEST_free(request);  
00727           return PARSER_STATUS_COMPLETED;
00728       }
00729       break;
00730 
00731     case HTTP_STATE_PARSING_BODY_CONTENT_LENGTH:
00732       rt = HTTP_PARSER_content_length_process( 
00733                 &parser->base, bf, 
00734                 (HTTP_PROCESS_MSG_DATA) parser->ev_body, 
00735                 &request->base, parser->ctx);
00736       if (rt != 0) {
00737         goto req_do_return;
00738       }
00739       goto next_request;
00740 
00741     default: // remaining states deal with chunks
00742       rt =  HTTP_PARSER_chunked_data_process( 
00743                 &parser->base, bf, 
00744                 (HTTP_PROCESS_MSG_DATA) parser->ev_body, 
00745                 &request->base, parser->ctx);
00746 
00747       if (rt != 0) {
00748         goto req_do_return;
00749       }
00750       goto next_request;
00751     }
00752   }
00753 
00754 req_do_return:
00755   if (rt ==  PARSER_STATUS_NEED_MORE_DATA) {
00756     BF_compact( bf );
00757   }
00758   return rt;
00759 }
00760 
00761 int HTTP_REQUEST_is_persistent( HTTP_REQUEST *message )
00762 {
00763   if (message->version == HTTP_VERSION_1_1) {
00764      if (message->base.flags & HTTP_MESSAGE_FLAG_CONNECTION_CLOSE) {
00765        return 0;
00766      }
00767      return 1;
00768   }
00769 
00770   if (message->base.flags & HTTP_MESSAGE_FLAG_KEEPALIVE) {
00771     return 0;
00772   }
00773   return 0;
00774 }
00775 
00776 
00777 // =============================================================================
00778 //Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
00779 static PARSER_STATUS parse_response_line( HTTP_RESPONSE *response, BF *bf )
00780 {
00781   PARSER_STATUS rt;  
00782   char *line, *tok, *eof;
00783   int st;
00784 
00785   rt = HTTP_get_line( bf, &line );
00786   if (rt != PARSER_STATUS_COMPLETED) {
00787     return rt;
00788   }
00789 
00790   tok = line;
00791   if (strncmp( tok, HTTP_START_S, HTTP_START_S_LEN ) != 0) {
00792     return PARSER_STATUS_ERROR;
00793   }
00794   tok += HTTP_START_S_LEN;
00795 
00796   switch(tok[0]) {
00797      case '0':
00798         response->version = HTTP_VERSION_1_0;
00799         break;
00800      case '1':
00801         response->version =  HTTP_VERSION_1_1; 
00802         break;
00803      default:
00804         return PARSER_STATUS_ERROR;
00805   }
00806  
00807   tok =  get_token( line, &eof );
00808   if (!tok) {
00809    return PARSER_STATUS_ERROR;
00810   }
00811 
00812   st = atoi( tok );
00813   if (st < 100 || st >= 600) {
00814     return PARSER_STATUS_ERROR;
00815   }
00816  
00817   response->status_code = st;
00818 
00819   return PARSER_STATUS_COMPLETED;
00820 }
00821 
00822 int HTTP_RESPONSE_PARSER_init( HTTP_RESPONSE_PARSER *parser,
00823                                HTTP_RESP_HEADER_PARSED header_parsed,
00824                                HTTP_RESP_MESSAGE_BODY_DATA on_message_body_data,
00825                                    HTTP_RESP_FINISHED on_request_finished,
00826                                void *ctx)
00827 {
00828   parser->ctx = ctx;
00829   parser->ev_header = header_parsed; 
00830   parser->ev_body   = on_message_body_data;
00831   parser->ev_finish = on_request_finished;
00832  
00833   if (! HTTP_PARSER_init( &parser->base )) {
00834     return -1;
00835   }
00836 
00837   if (init_parsers_general_header( &parser->base ) ) {
00838     return -1;
00839   }
00840   return 0;
00841 }
00842 
00843 
00844 
00845 PARSER_STATUS HTTP_RESPONSE_PARSER_process( HTTP_RESPONSE_PARSER *parser, HTTP_RESPONSE *response, BF *bf )
00846 {
00847   int rt, eof_header;
00848 
00849   while(1) {
00850    switch( parser->base.state ) {
00851     case HTTP_STATE_PARSING_REQUEST_LINE:
00852       rt = parse_response_line( response, bf );
00853       if (rt != 0) {
00854         goto resp_do_return; 
00855       }
00856       parser->base.state = HTTP_STATE_PARSING_HEADERS; // 
00857    //break; - fall through
00858 
00859 
00860     case HTTP_STATE_PARSING_HEADERS:
00861       rt = HTTP_parse_header_line( (HTTP_PARSER *) parser, (HTTP_MESSAGE *) response, bf, &eof_header );
00862       if (rt != 0) {
00863        goto resp_do_return; 
00864       }
00865 
00866       if (eof_header) {
00867           // eof parsing http header.
00868           parser->ev_header( response, parser->ctx ); 
00869         
00870           if (response->base.flags & HTTP_MESSAGE_FLAG_TRANSFER_CHUNKED) {
00871              parser->base.state = HTTP_STATE_PARSING_BODY_CHUNK_HEADER;
00872              continue;  
00873           }
00874 
00875  
00876           if (response->base.flags & HTTP_MESSAGE_FLAG_HAS_CONTENT_LENGTH) {
00877              HTTP_PARSER_content_length_init( &parser->base, &response->base );
00878              continue;  
00879           }
00880 
00881 next_response:
00882           HTTP_RESPONSE_free( response );
00883           parser->ev_finish( response, parser->ctx ); 
00884           parser->base.state = HTTP_STATE_PARSING_REQUEST_LINE; 
00885           BF_compact(bf);
00886           return PARSER_STATUS_COMPLETED;
00887       }
00888       break;
00889 
00890     case HTTP_STATE_PARSING_BODY_CONTENT_LENGTH:
00891       rt = HTTP_PARSER_content_length_process( 
00892                 &parser->base, bf, 
00893                 (HTTP_PROCESS_MSG_DATA) parser->ev_body, 
00894                 &response->base, parser->ctx);
00895       if (rt != 0) {
00896         goto resp_do_return; 
00897       }
00898       goto next_response;
00899 
00900     default: // remaining states deal with chunks
00901       rt =  HTTP_PARSER_chunked_data_process( 
00902                 &parser->base, bf, 
00903                 (HTTP_PROCESS_MSG_DATA) parser->ev_body, 
00904                 &response->base, parser->ctx);
00905 
00906       if (rt != 0) {
00907         goto resp_do_return; 
00908       }
00909       goto next_response;
00910     }
00911   }
00912 resp_do_return:
00913   if (rt ==  PARSER_STATUS_NEED_MORE_DATA) {
00914     BF_compact( bf );
00915   }
00916   return rt;
00917 }
00918 
00919 // =============================================================================
00920 
00921 static const char *http_version_to_str( Http_version_type ver )
00922 {
00923   switch(ver) {
00924     case HTTP_VERSION_1_0:
00925       return "1.0";
00926     case HTTP_VERSION_1_1:
00927       return "1.1";
00928   }
00929   return "";
00930 }
00931 
00932 #define S_CONNECTION_CLOSE "Connection: close\r\n"
00933 #define S_CONNECTION_CLOSE_LEN 19
00934 
00935 #define S_TRANSFER_CHUNKED "Transfer-encoding: chunked\r\n"
00936 #define S_TRANSFER_CHUNKED_LEN 28
00937 
00938 
00939 #define RETURN_WRITER_MORE_DATA \
00940   if (bf->start == bf->put_pos) { \
00941     return PARSER_STATUS_ERROR;  /* the buffer can't hold a single value */ \
00942   } \
00943   return PARSER_STATUS_NEED_MORE_DATA; 
00944 
00945 static const char *get_reason_phrase( int code )
00946 {
00947     switch( code ) {
00948           case 100:         return "Continue";
00949           case 101:         return "Switching Protocols";
00950           case 200:         return "OK";
00951           case 201:         return "Created";
00952           case 202:         return "Accepted";
00953           case 203:         return "Non-Authoritative Information";
00954           case 204:         return "No Content";
00955           case 205:         return "Reset Content";
00956           case 206:         return "Partial Content";
00957           case 300:         return "Multiple Choices";
00958           case 301:         return "Moved Permanently";
00959           case 302:         return "Found";
00960           case 303:         return "See Other";
00961           case 304:         return "Not Modified";
00962           case 305:         return "Use Proxy";
00963           case 307:         return "Temporary Redirect";
00964           case 400:         return "Bad Request";
00965           case 401:         return "Unauthorized";
00966           case 402:         return "Payment Required";
00967           case 403:         return "Forbidden";
00968           case 404:         return "Not Found";
00969           case 405:         return "Method Not Allowed";
00970           case 406:         return "Not Acceptable";
00971           case 407:         return "Proxy Authentication Required";
00972           case 408:         return "Request Time-out";
00973           case 409:         return "Conflict";
00974           case 410:         return "Gone";
00975           case 411:         return "Length Required";
00976           case 412:         return "Precondition Failed";
00977           case 413:         return "Request Entity Too Large";
00978           case 414:         return "Request-URI Too Large";
00979           case 415:         return "Unsupported Media Type";
00980           case 416:         return "Requested range not satisfiable";
00981           case 417:         return "Expectation Failed";
00982           case 500:         return "Internal Server Error";
00983           case 501:         return "Not Implemented";
00984           case 502:         return "Bad Gateway";
00985           case 503:         return "Service Unavailable";
00986           case 504:         return "Gateway Time-out";
00987           case 505:         return "HTTP Version not supported";
00988    }
00989    return "";
00990 }
00991 
00992 PARSER_STATUS HTTP_RESPONSE_WRITER_write( HTTP_RESPONSE_WRITER *writer, BF *bf )
00993 { 
00994   HTTP_RESPONSE *resp = writer->response;
00995   char line_buf[40];
00996   int nlen;
00997 
00998   while(1) {
00999   switch( writer->state ) {
01000     case HTTP_RESPONSE_WR_STATUS_LINE:
01001       nlen = sprintf(line_buf,"HTTP/%s %d %s\r\n", http_version_to_str( resp->version ), resp->status_code, get_reason_phrase( resp->status_code ) );
01002       if (BF_putn( bf, line_buf, nlen) ) {
01003          RETURN_WRITER_MORE_DATA;
01004       }
01005       writer->state = HTTP_RESPONSE_WR_CONNECTION_CLOSE; 
01006     //break;
01007     case HTTP_RESPONSE_WR_CONNECTION_CLOSE:
01008       if (resp->base.flags & HTTP_MESSAGE_FLAG_CONNECTION_CLOSE ) {
01009         if (BF_putn( bf, S_CONNECTION_CLOSE, S_CONNECTION_CLOSE_LEN ) ) {
01010           RETURN_WRITER_MORE_DATA;
01011         } 
01012       }
01013       writer->state = HTTP_RESPONSE_WR_CHUNKED; 
01014     //break;
01015     case HTTP_RESPONSE_WR_CHUNKED:
01016       if (resp->base.flags & HTTP_MESSAGE_FLAG_TRANSFER_CHUNKED ) {
01017         if (BF_putn( bf, S_TRANSFER_CHUNKED, S_TRANSFER_CHUNKED_LEN ) ) {
01018           RETURN_WRITER_MORE_DATA;
01019         }
01020       }
01021       writer->state = HTTP_RESPONSE_WR_CONTENT_LENGTH; 
01022      //break;
01023     case HTTP_RESPONSE_WR_CONTENT_LENGTH:
01024       if (resp->base.flags & HTTP_MESSAGE_FLAG_HAS_CONTENT_LENGTH ) {
01025         nlen = sprintf(line_buf,"Content-Length: %d\r\n", resp->base.content_length );
01026         if (BF_putn( bf, line_buf, nlen) ) {
01027           RETURN_WRITER_MORE_DATA;
01028         }
01029       }
01030       writer->state = HTTP_RESPONSE_WR_HEADERS; 
01031       writer->header_position = DLISTUNR_get_first( &resp->base.header_values );
01032       writer->state_header = 0;
01033      //break;
01034     case HTTP_RESPONSE_WR_HEADERS:
01035       if ( ! DLISTUNR_is_eof( &resp->base.header_values, writer->header_position)) {
01036         STRINGPAIR *header = (STRINGPAIR *) DLISTUNR_at(&resp->base.header_values, writer->header_position);
01037         switch(writer->state_header) {
01038             case 0:
01039                 if (BF_putn( bf, header->key, strlen(header->key))) {
01040                   RETURN_WRITER_MORE_DATA;
01041                 }
01042                 writer->state_header = 1;
01043               //break;
01044                 
01045             case 1:
01046                 if (BF_putn( bf, ": ", 2)) {
01047                   RETURN_WRITER_MORE_DATA;
01048                 }
01049                 writer->state_header = 2;
01050               //break;
01051 
01052             case 2:
01053                 if (BF_putn( bf, header->value, strlen(header->value))) {
01054                   RETURN_WRITER_MORE_DATA;
01055                 }
01056                 writer->state_header = 3;
01057               //break;
01058 
01059             case 3:
01060                 if (BF_putn( bf, "\r\n", 2)) {
01061                   RETURN_WRITER_MORE_DATA;
01062                 }
01063                 writer->header_position = DLISTUNR_next(writer->header_position);
01064                 writer->state_header = 0;
01065                 break;
01066            }
01067       } else {
01068          writer->state = HTTP_RESPONSE_WR_EOF;
01069          goto wr_eof;
01070         
01071       }
01072       break;
01073     case HTTP_RESPONSE_WR_EOF:
01074 wr_eof:    
01075       if (BF_putn( bf, "\r\n", 2)) {
01076           RETURN_WRITER_MORE_DATA;
01077       }
01078       return PARSER_STATUS_COMPLETED; 
01079     }
01080   }
01081 
01082 }
01083 
01084