test
Fork of mbed-libxively-6eca970 by
Diff: src/libxively/http_layer_parser.c
- Revision:
- 0:82702e998d3f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_layer_parser.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,196 @@ +// Copyright (c) 2003-2013, LogMeIn, Inc. All rights reserved. +// This is part of Xively C library, it is under the BSD 3-Clause license. + +/** + * \file http_layer_parser.c + * \author Olgierd Humenczuk + * \brief Our simple HTTP parser [see http_layer_parser.h] + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "xively.h" +#include "xi_macros.h" +#include "http_consts.h" +#include "http_layer_parser.h" +#include "xi_debug.h" +#include "xi_err.h" + +static const char XI_HTTP_STATUS_PATTERN[] = + "HTTP/%d.%d %d %" XI_STR(XI_HTTP_STATUS_STRING_SIZE) "[^\r\n]\r\n"; //!< the match pattern + +#define SET_HTTP_STATUS_PATTERN(a,b,c,d) XI_HTTP_STATUS_PATTERN, &a, &b, &c, d + +static const char XI_HTTP_TOKEN_NAMES[ XI_HTTP_HEADERS_COUNT ][ XI_HTTP_HEADER_NAME_MAX_SIZE ] = + { + "date" // XI_HTTP_HEADER_DATE + , "content-type" // XI_HTTP_HEADER_CONTENT_TYPE + , "content-length" // XI_HTTP_HEADER_CONTENT_LENGTH + , "connection" // XI_HTTP_HEADER_CONNECTION + , "x-request-id" // XI_HTTP_HEADER_X_REQUEST_ID + , "cache-control" // XI_HTTP_HEADER_CACHE_CONTROL + , "age" // XI_HTTP_HEADER_AGE + , "vary" // XI_HTTP_HEADER_VARY + , "unknown" // XI_HTTP_HEADER_UNKNOWN, //!< !!!! this must be always on the last position + }; + +static inline http_header_type_t classify_header( const char* header ) +{ + for( unsigned short i = 0; i < XI_HTTP_HEADER_COUNT - 1; ++i ) + { + if( strcasecmp( header, XI_HTTP_TOKEN_NAMES[ i ] ) == 0 ) + return ( http_header_type_t ) i; + } + + return XI_HTTP_HEADER_UNKNOWN; +} + +const char* parse_http_status( http_response_t* response, const char* content ) +{ + // variables + int c = 0; + + // find the first occurrence of CRLF + const char* header_end_ptr = strstr( content, XI_HTTP_CRLF ); + + // check continuation condition + XI_CHECK_ZERO( header_end_ptr, XI_HTTP_STATUS_PARSE_ERROR ); + + // update the pointer + header_end_ptr += 2; + + // parse + c = sscanf( content + , SET_HTTP_STATUS_PATTERN( + response->http_version1 + , response->http_version2 + , response->http_status + , response->http_status_string ) ); + + // check if all arguments found + XI_CHECK_CND( c != 4, XI_HTTP_STATUS_PARSE_ERROR ); + + // return updated ptr + return header_end_ptr; + +err_handling: + return 0; +} + +const char* parse_http_header( http_response_t* response + , const char* content ) +{ + // find the first occurrence of CRLF + const char* header_end_ptr = strstr( content, XI_HTTP_CRLF ); + const char* header_name_end_ptr = strstr( content, ":" ); + + // check continuation condition + XI_CHECK_ZERO( header_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); + XI_CHECK_ZERO( header_name_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); + XI_CHECK_CND( header_name_end_ptr > header_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); + + { + int size = sizeof( response->http_headers[ response->http_headers_size ].name ); + + XI_CHECK_CND( header_name_end_ptr - content > size - 1, XI_HTTP_HEADER_PARSE_ERROR ); + + memcpy( response->http_headers[ response->http_headers_size ].name + , content, XI_MIN( size - 1, header_name_end_ptr - content ) ); + + // set the guard + XI_GUARD_EOS( response->http_headers[ response->http_headers_size ].name, size ); + } + + // update the pointer + header_name_end_ptr += 2; + + { + int size = sizeof( response->http_headers[ response->http_headers_size ].value ); + + XI_CHECK_CND( header_end_ptr - header_name_end_ptr > size - 1, XI_HTTP_HEADER_PARSE_ERROR ); + + memcpy( response->http_headers[ response->http_headers_size ].value + , header_name_end_ptr, XI_MIN( size - 1, header_end_ptr - header_name_end_ptr ) ); + + // set the guard + XI_GUARD_EOS( response->http_headers[ response->http_headers_size ].value, size ); + } + + // @TODO change the complexity of the classify header + // implementation now it's O( n*m ) it can be done in O( n ) + // using some sort of simple DFA + // I'm working on a very simple Python DFA generator for C + // but still this is a w.i.p. at the moment + + // parse the header name + { + http_header_type_t ht = classify_header( + response->http_headers[ response->http_headers_size ].name ); + + // accept headers that differs from unknown + if( ht != XI_HTTP_HEADER_UNKNOWN ) + { + response->http_headers_checklist[ ht ] = + &response->http_headers[ response->http_headers_size ]; + } + + // set header type + response->http_headers[ response->http_headers_size ].header_type = ht; + + // increment headers size and check if it's okay + response->http_headers_size += 1; + + XI_CHECK_CND( response->http_headers_size >= XI_HTTP_MAX_HEADERS + , XI_HTTP_HEADER_PARSE_ERROR ); + + // update the pointer + header_end_ptr += sizeof( XI_HTTP_CRLF ) - 1; + } + + return header_end_ptr; + +err_handling: + return 0; +} + +http_response_t* parse_http( http_response_t* response, const char* content ) +{ + memset( response, 0, sizeof( http_response_t ) ); + + // remember where is the end of header section + const char* ptr_to_headers_end = strstr( content, XI_HTTP_CRLFX2 ); + + // check the continuation condition + XI_CHECK_ZERO( ptr_to_headers_end, XI_HTTP_PARSE_ERROR ); + + // update the pointer + ptr_to_headers_end += sizeof( XI_HTTP_CRLF ) - 1; + + const char* payload_begin = ptr_to_headers_end + sizeof( XI_HTTP_CRLF ) - 1; + + XI_CHECK_ZERO( payload_begin, XI_HTTP_PARSE_ERROR ); + + // parse status + const char* ptr = parse_http_status( response, content ); + + // check the continuation condition + XI_CHECK_ZERO( ptr, XI_HTTP_PARSE_ERROR ); + + // read the headers + while( ( ptr = parse_http_header( response, ptr ) ) != '\0' + && ptr != ptr_to_headers_end ); + + // if there was an error, forward it + xi_err_t e = xi_get_last_error(); + XI_CHECK_CND( e != XI_NO_ERR, e ); + + // just copy the content + strncpy( response->http_content, payload_begin, sizeof( response->http_content ) ); + + return response; + +err_handling: + return 0; +}