Xively Official / mbed-libxively-5d6fdd4

Dependents:   xively-jumpstart-demo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers http_layer_parser.c Source File

http_layer_parser.c

Go to the documentation of this file.
00001 // Copyright (c) 2003-2013, LogMeIn, Inc. All rights reserved.
00002 // This is part of Xively C library, it is under the BSD 3-Clause license.
00003 
00004 /**
00005  * \file    http_layer_parser.c
00006  * \author  Olgierd Humenczuk
00007  * \brief   Our simple HTTP parser [see http_layer_parser.h]
00008  */
00009 
00010 #include <stdio.h>
00011 #include <string.h>
00012 #include <stdlib.h>
00013 
00014 #include "xively.h"
00015 #include "xi_macros.h"
00016 #include "http_consts.h"
00017 #include "http_layer_parser.h"
00018 #include "xi_debug.h"
00019 #include "xi_err.h"
00020 
00021 static const char XI_HTTP_STATUS_PATTERN[] =
00022     "HTTP/%d.%d %d %" XI_STR(XI_HTTP_STATUS_STRING_SIZE) "[^\r\n]\r\n"; //!< the match pattern
00023 
00024 #define SET_HTTP_STATUS_PATTERN(a,b,c,d) XI_HTTP_STATUS_PATTERN, &a, &b, &c, d
00025 
00026 static const char XI_HTTP_TOKEN_NAMES[ XI_HTTP_HEADERS_COUNT ][ XI_HTTP_HEADER_NAME_MAX_SIZE ] =
00027     {
00028           "date"            // XI_HTTP_HEADER_DATE
00029         , "content-type"    // XI_HTTP_HEADER_CONTENT_TYPE
00030         , "content-length"  // XI_HTTP_HEADER_CONTENT_LENGTH
00031         , "connection"      // XI_HTTP_HEADER_CONNECTION
00032         , "x-request-id"    // XI_HTTP_HEADER_X_REQUEST_ID
00033         , "cache-control"   // XI_HTTP_HEADER_CACHE_CONTROL
00034         , "age"             // XI_HTTP_HEADER_AGE
00035         , "vary"            // XI_HTTP_HEADER_VARY
00036         , "unknown"         // XI_HTTP_HEADER_UNKNOWN, //!< !!!! this must be always on the last position
00037     };
00038 
00039 static inline http_header_type_t classify_header( const char* header )
00040 {
00041     for( unsigned short i = 0; i < XI_HTTP_HEADER_COUNT - 1; ++i )
00042     {
00043         if( strcasecmp( header, XI_HTTP_TOKEN_NAMES[ i ] ) == 0 )
00044             return ( http_header_type_t ) i;
00045     }
00046 
00047     return XI_HTTP_HEADER_UNKNOWN;
00048 }
00049 
00050 const char* parse_http_status( http_response_t* response, const char* content )
00051 {
00052     // variables
00053     int c = 0;
00054 
00055     // find the first occurrence of CRLF
00056     const char* header_end_ptr = strstr( content, XI_HTTP_CRLF );
00057 
00058     // check continuation condition
00059     XI_CHECK_ZERO( header_end_ptr, XI_HTTP_STATUS_PARSE_ERROR );
00060 
00061     // update the pointer
00062     header_end_ptr += 2;
00063 
00064     // parse
00065     c = sscanf( content
00066         , SET_HTTP_STATUS_PATTERN(
00067               response->http_version1
00068             , response->http_version2
00069             , response->http_status
00070             , response->http_status_string ) );
00071 
00072     // check if all arguments found
00073     XI_CHECK_CND( c != 4, XI_HTTP_STATUS_PARSE_ERROR );
00074 
00075     // return updated ptr
00076     return header_end_ptr;
00077 
00078 err_handling:
00079     return 0;
00080 }
00081 
00082 const char* parse_http_header( http_response_t* response
00083     , const char* content )
00084 {
00085     // find the first occurrence of CRLF
00086     const char* header_end_ptr      = strstr( content, XI_HTTP_CRLF );
00087     const char* header_name_end_ptr = strstr( content, ":" );
00088 
00089     // check continuation condition
00090     XI_CHECK_ZERO( header_end_ptr, XI_HTTP_HEADER_PARSE_ERROR );
00091     XI_CHECK_ZERO( header_name_end_ptr, XI_HTTP_HEADER_PARSE_ERROR );
00092     XI_CHECK_CND( header_name_end_ptr > header_end_ptr, XI_HTTP_HEADER_PARSE_ERROR );
00093 
00094     {
00095         int size = sizeof( response->http_headers[ response->http_headers_size ].name );
00096 
00097         XI_CHECK_CND( header_name_end_ptr - content > size - 1, XI_HTTP_HEADER_PARSE_ERROR );
00098 
00099         memcpy( response->http_headers[ response->http_headers_size ].name
00100             , content, XI_MIN( size - 1, header_name_end_ptr - content ) );
00101 
00102         // set the guard
00103         XI_GUARD_EOS( response->http_headers[ response->http_headers_size ].name, size );
00104     }
00105 
00106     // update the pointer
00107     header_name_end_ptr += 2;
00108 
00109     {
00110         int size = sizeof( response->http_headers[ response->http_headers_size ].value );
00111 
00112         XI_CHECK_CND( header_end_ptr - header_name_end_ptr > size - 1, XI_HTTP_HEADER_PARSE_ERROR );
00113 
00114         memcpy( response->http_headers[ response->http_headers_size ].value
00115             , header_name_end_ptr, XI_MIN( size - 1, header_end_ptr - header_name_end_ptr ) );
00116 
00117         // set the guard
00118         XI_GUARD_EOS( response->http_headers[ response->http_headers_size ].value, size );
00119     }
00120 
00121     // @TODO change the complexity of the classify header
00122     // implementation now it's O( n*m ) it can be done in O( n )
00123     // using some sort of simple DFA
00124     // I'm working on a very simple Python DFA generator for C
00125     // but still this is a w.i.p. at the moment
00126 
00127     // parse the header name
00128     {
00129         http_header_type_t ht = classify_header(
00130             response->http_headers[ response->http_headers_size ].name );
00131 
00132         // accept headers that differs from unknown
00133         if( ht != XI_HTTP_HEADER_UNKNOWN )
00134         {
00135             response->http_headers_checklist[ ht ] =
00136                 &response->http_headers[ response->http_headers_size ];
00137         }
00138 
00139         // set header type
00140         response->http_headers[ response->http_headers_size ].header_type = ht;
00141 
00142         // increment headers size and check if it's okay
00143         response->http_headers_size += 1;
00144 
00145         XI_CHECK_CND( response->http_headers_size >= XI_HTTP_MAX_HEADERS
00146             , XI_HTTP_HEADER_PARSE_ERROR );
00147 
00148         // update the pointer
00149         header_end_ptr += sizeof( XI_HTTP_CRLF ) - 1;
00150     }
00151 
00152     return header_end_ptr;
00153 
00154 err_handling:
00155     return 0;
00156 }
00157 
00158 http_response_t* parse_http( http_response_t* response, const char* content )
00159 {
00160     memset( response, 0, sizeof( http_response_t ) );
00161 
00162     // remember where is the end of header section
00163     const char* ptr_to_headers_end = strstr( content, XI_HTTP_CRLFX2 );
00164 
00165     // check the continuation condition
00166     XI_CHECK_ZERO( ptr_to_headers_end, XI_HTTP_PARSE_ERROR );
00167 
00168     // update the pointer
00169     ptr_to_headers_end += sizeof( XI_HTTP_CRLF ) - 1;
00170 
00171     const char* payload_begin = ptr_to_headers_end + sizeof( XI_HTTP_CRLF ) - 1;
00172 
00173     XI_CHECK_ZERO( payload_begin, XI_HTTP_PARSE_ERROR );
00174 
00175     // parse status
00176     const char* ptr = parse_http_status( response, content );
00177 
00178     // check the continuation condition
00179     XI_CHECK_ZERO( ptr, XI_HTTP_PARSE_ERROR );
00180 
00181     // read the headers
00182     while( ( ptr = parse_http_header( response, ptr ) ) != '\0'
00183         && ptr != ptr_to_headers_end );
00184 
00185     // if there was an error, forward it
00186     xi_err_t e = xi_get_last_error();
00187     XI_CHECK_CND( e != XI_NO_ERR, e );
00188 
00189     // just copy the content
00190     strncpy( response->http_content, payload_begin, sizeof( response->http_content ) );
00191 
00192     return response;
00193 
00194 err_handling:
00195     return 0;
00196 }