test
Fork of mbed-libxively-6eca970 by
src/libxively/http_layer_parser.c@0:82702e998d3f, 2013-06-26 (annotated)
- Committer:
- xively
- Date:
- Wed Jun 26 10:40:43 2013 +0000
- Revision:
- 0:82702e998d3f
libxively v0.1.1-rc0 (34c8b32)
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
xively | 0:82702e998d3f | 1 | // Copyright (c) 2003-2013, LogMeIn, Inc. All rights reserved. |
xively | 0:82702e998d3f | 2 | // This is part of Xively C library, it is under the BSD 3-Clause license. |
xively | 0:82702e998d3f | 3 | |
xively | 0:82702e998d3f | 4 | /** |
xively | 0:82702e998d3f | 5 | * \file http_layer_parser.c |
xively | 0:82702e998d3f | 6 | * \author Olgierd Humenczuk |
xively | 0:82702e998d3f | 7 | * \brief Our simple HTTP parser [see http_layer_parser.h] |
xively | 0:82702e998d3f | 8 | */ |
xively | 0:82702e998d3f | 9 | |
xively | 0:82702e998d3f | 10 | #include <stdio.h> |
xively | 0:82702e998d3f | 11 | #include <string.h> |
xively | 0:82702e998d3f | 12 | #include <stdlib.h> |
xively | 0:82702e998d3f | 13 | |
xively | 0:82702e998d3f | 14 | #include "xively.h" |
xively | 0:82702e998d3f | 15 | #include "xi_macros.h" |
xively | 0:82702e998d3f | 16 | #include "http_consts.h" |
xively | 0:82702e998d3f | 17 | #include "http_layer_parser.h" |
xively | 0:82702e998d3f | 18 | #include "xi_debug.h" |
xively | 0:82702e998d3f | 19 | #include "xi_err.h" |
xively | 0:82702e998d3f | 20 | |
xively | 0:82702e998d3f | 21 | static const char XI_HTTP_STATUS_PATTERN[] = |
xively | 0:82702e998d3f | 22 | "HTTP/%d.%d %d %" XI_STR(XI_HTTP_STATUS_STRING_SIZE) "[^\r\n]\r\n"; //!< the match pattern |
xively | 0:82702e998d3f | 23 | |
xively | 0:82702e998d3f | 24 | #define SET_HTTP_STATUS_PATTERN(a,b,c,d) XI_HTTP_STATUS_PATTERN, &a, &b, &c, d |
xively | 0:82702e998d3f | 25 | |
xively | 0:82702e998d3f | 26 | static const char XI_HTTP_TOKEN_NAMES[ XI_HTTP_HEADERS_COUNT ][ XI_HTTP_HEADER_NAME_MAX_SIZE ] = |
xively | 0:82702e998d3f | 27 | { |
xively | 0:82702e998d3f | 28 | "date" // XI_HTTP_HEADER_DATE |
xively | 0:82702e998d3f | 29 | , "content-type" // XI_HTTP_HEADER_CONTENT_TYPE |
xively | 0:82702e998d3f | 30 | , "content-length" // XI_HTTP_HEADER_CONTENT_LENGTH |
xively | 0:82702e998d3f | 31 | , "connection" // XI_HTTP_HEADER_CONNECTION |
xively | 0:82702e998d3f | 32 | , "x-request-id" // XI_HTTP_HEADER_X_REQUEST_ID |
xively | 0:82702e998d3f | 33 | , "cache-control" // XI_HTTP_HEADER_CACHE_CONTROL |
xively | 0:82702e998d3f | 34 | , "age" // XI_HTTP_HEADER_AGE |
xively | 0:82702e998d3f | 35 | , "vary" // XI_HTTP_HEADER_VARY |
xively | 0:82702e998d3f | 36 | , "unknown" // XI_HTTP_HEADER_UNKNOWN, //!< !!!! this must be always on the last position |
xively | 0:82702e998d3f | 37 | }; |
xively | 0:82702e998d3f | 38 | |
xively | 0:82702e998d3f | 39 | static inline http_header_type_t classify_header( const char* header ) |
xively | 0:82702e998d3f | 40 | { |
xively | 0:82702e998d3f | 41 | for( unsigned short i = 0; i < XI_HTTP_HEADER_COUNT - 1; ++i ) |
xively | 0:82702e998d3f | 42 | { |
xively | 0:82702e998d3f | 43 | if( strcasecmp( header, XI_HTTP_TOKEN_NAMES[ i ] ) == 0 ) |
xively | 0:82702e998d3f | 44 | return ( http_header_type_t ) i; |
xively | 0:82702e998d3f | 45 | } |
xively | 0:82702e998d3f | 46 | |
xively | 0:82702e998d3f | 47 | return XI_HTTP_HEADER_UNKNOWN; |
xively | 0:82702e998d3f | 48 | } |
xively | 0:82702e998d3f | 49 | |
xively | 0:82702e998d3f | 50 | const char* parse_http_status( http_response_t* response, const char* content ) |
xively | 0:82702e998d3f | 51 | { |
xively | 0:82702e998d3f | 52 | // variables |
xively | 0:82702e998d3f | 53 | int c = 0; |
xively | 0:82702e998d3f | 54 | |
xively | 0:82702e998d3f | 55 | // find the first occurrence of CRLF |
xively | 0:82702e998d3f | 56 | const char* header_end_ptr = strstr( content, XI_HTTP_CRLF ); |
xively | 0:82702e998d3f | 57 | |
xively | 0:82702e998d3f | 58 | // check continuation condition |
xively | 0:82702e998d3f | 59 | XI_CHECK_ZERO( header_end_ptr, XI_HTTP_STATUS_PARSE_ERROR ); |
xively | 0:82702e998d3f | 60 | |
xively | 0:82702e998d3f | 61 | // update the pointer |
xively | 0:82702e998d3f | 62 | header_end_ptr += 2; |
xively | 0:82702e998d3f | 63 | |
xively | 0:82702e998d3f | 64 | // parse |
xively | 0:82702e998d3f | 65 | c = sscanf( content |
xively | 0:82702e998d3f | 66 | , SET_HTTP_STATUS_PATTERN( |
xively | 0:82702e998d3f | 67 | response->http_version1 |
xively | 0:82702e998d3f | 68 | , response->http_version2 |
xively | 0:82702e998d3f | 69 | , response->http_status |
xively | 0:82702e998d3f | 70 | , response->http_status_string ) ); |
xively | 0:82702e998d3f | 71 | |
xively | 0:82702e998d3f | 72 | // check if all arguments found |
xively | 0:82702e998d3f | 73 | XI_CHECK_CND( c != 4, XI_HTTP_STATUS_PARSE_ERROR ); |
xively | 0:82702e998d3f | 74 | |
xively | 0:82702e998d3f | 75 | // return updated ptr |
xively | 0:82702e998d3f | 76 | return header_end_ptr; |
xively | 0:82702e998d3f | 77 | |
xively | 0:82702e998d3f | 78 | err_handling: |
xively | 0:82702e998d3f | 79 | return 0; |
xively | 0:82702e998d3f | 80 | } |
xively | 0:82702e998d3f | 81 | |
xively | 0:82702e998d3f | 82 | const char* parse_http_header( http_response_t* response |
xively | 0:82702e998d3f | 83 | , const char* content ) |
xively | 0:82702e998d3f | 84 | { |
xively | 0:82702e998d3f | 85 | // find the first occurrence of CRLF |
xively | 0:82702e998d3f | 86 | const char* header_end_ptr = strstr( content, XI_HTTP_CRLF ); |
xively | 0:82702e998d3f | 87 | const char* header_name_end_ptr = strstr( content, ":" ); |
xively | 0:82702e998d3f | 88 | |
xively | 0:82702e998d3f | 89 | // check continuation condition |
xively | 0:82702e998d3f | 90 | XI_CHECK_ZERO( header_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); |
xively | 0:82702e998d3f | 91 | XI_CHECK_ZERO( header_name_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); |
xively | 0:82702e998d3f | 92 | XI_CHECK_CND( header_name_end_ptr > header_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); |
xively | 0:82702e998d3f | 93 | |
xively | 0:82702e998d3f | 94 | { |
xively | 0:82702e998d3f | 95 | int size = sizeof( response->http_headers[ response->http_headers_size ].name ); |
xively | 0:82702e998d3f | 96 | |
xively | 0:82702e998d3f | 97 | XI_CHECK_CND( header_name_end_ptr - content > size - 1, XI_HTTP_HEADER_PARSE_ERROR ); |
xively | 0:82702e998d3f | 98 | |
xively | 0:82702e998d3f | 99 | memcpy( response->http_headers[ response->http_headers_size ].name |
xively | 0:82702e998d3f | 100 | , content, XI_MIN( size - 1, header_name_end_ptr - content ) ); |
xively | 0:82702e998d3f | 101 | |
xively | 0:82702e998d3f | 102 | // set the guard |
xively | 0:82702e998d3f | 103 | XI_GUARD_EOS( response->http_headers[ response->http_headers_size ].name, size ); |
xively | 0:82702e998d3f | 104 | } |
xively | 0:82702e998d3f | 105 | |
xively | 0:82702e998d3f | 106 | // update the pointer |
xively | 0:82702e998d3f | 107 | header_name_end_ptr += 2; |
xively | 0:82702e998d3f | 108 | |
xively | 0:82702e998d3f | 109 | { |
xively | 0:82702e998d3f | 110 | int size = sizeof( response->http_headers[ response->http_headers_size ].value ); |
xively | 0:82702e998d3f | 111 | |
xively | 0:82702e998d3f | 112 | XI_CHECK_CND( header_end_ptr - header_name_end_ptr > size - 1, XI_HTTP_HEADER_PARSE_ERROR ); |
xively | 0:82702e998d3f | 113 | |
xively | 0:82702e998d3f | 114 | memcpy( response->http_headers[ response->http_headers_size ].value |
xively | 0:82702e998d3f | 115 | , header_name_end_ptr, XI_MIN( size - 1, header_end_ptr - header_name_end_ptr ) ); |
xively | 0:82702e998d3f | 116 | |
xively | 0:82702e998d3f | 117 | // set the guard |
xively | 0:82702e998d3f | 118 | XI_GUARD_EOS( response->http_headers[ response->http_headers_size ].value, size ); |
xively | 0:82702e998d3f | 119 | } |
xively | 0:82702e998d3f | 120 | |
xively | 0:82702e998d3f | 121 | // @TODO change the complexity of the classify header |
xively | 0:82702e998d3f | 122 | // implementation now it's O( n*m ) it can be done in O( n ) |
xively | 0:82702e998d3f | 123 | // using some sort of simple DFA |
xively | 0:82702e998d3f | 124 | // I'm working on a very simple Python DFA generator for C |
xively | 0:82702e998d3f | 125 | // but still this is a w.i.p. at the moment |
xively | 0:82702e998d3f | 126 | |
xively | 0:82702e998d3f | 127 | // parse the header name |
xively | 0:82702e998d3f | 128 | { |
xively | 0:82702e998d3f | 129 | http_header_type_t ht = classify_header( |
xively | 0:82702e998d3f | 130 | response->http_headers[ response->http_headers_size ].name ); |
xively | 0:82702e998d3f | 131 | |
xively | 0:82702e998d3f | 132 | // accept headers that differs from unknown |
xively | 0:82702e998d3f | 133 | if( ht != XI_HTTP_HEADER_UNKNOWN ) |
xively | 0:82702e998d3f | 134 | { |
xively | 0:82702e998d3f | 135 | response->http_headers_checklist[ ht ] = |
xively | 0:82702e998d3f | 136 | &response->http_headers[ response->http_headers_size ]; |
xively | 0:82702e998d3f | 137 | } |
xively | 0:82702e998d3f | 138 | |
xively | 0:82702e998d3f | 139 | // set header type |
xively | 0:82702e998d3f | 140 | response->http_headers[ response->http_headers_size ].header_type = ht; |
xively | 0:82702e998d3f | 141 | |
xively | 0:82702e998d3f | 142 | // increment headers size and check if it's okay |
xively | 0:82702e998d3f | 143 | response->http_headers_size += 1; |
xively | 0:82702e998d3f | 144 | |
xively | 0:82702e998d3f | 145 | XI_CHECK_CND( response->http_headers_size >= XI_HTTP_MAX_HEADERS |
xively | 0:82702e998d3f | 146 | , XI_HTTP_HEADER_PARSE_ERROR ); |
xively | 0:82702e998d3f | 147 | |
xively | 0:82702e998d3f | 148 | // update the pointer |
xively | 0:82702e998d3f | 149 | header_end_ptr += sizeof( XI_HTTP_CRLF ) - 1; |
xively | 0:82702e998d3f | 150 | } |
xively | 0:82702e998d3f | 151 | |
xively | 0:82702e998d3f | 152 | return header_end_ptr; |
xively | 0:82702e998d3f | 153 | |
xively | 0:82702e998d3f | 154 | err_handling: |
xively | 0:82702e998d3f | 155 | return 0; |
xively | 0:82702e998d3f | 156 | } |
xively | 0:82702e998d3f | 157 | |
xively | 0:82702e998d3f | 158 | http_response_t* parse_http( http_response_t* response, const char* content ) |
xively | 0:82702e998d3f | 159 | { |
xively | 0:82702e998d3f | 160 | memset( response, 0, sizeof( http_response_t ) ); |
xively | 0:82702e998d3f | 161 | |
xively | 0:82702e998d3f | 162 | // remember where is the end of header section |
xively | 0:82702e998d3f | 163 | const char* ptr_to_headers_end = strstr( content, XI_HTTP_CRLFX2 ); |
xively | 0:82702e998d3f | 164 | |
xively | 0:82702e998d3f | 165 | // check the continuation condition |
xively | 0:82702e998d3f | 166 | XI_CHECK_ZERO( ptr_to_headers_end, XI_HTTP_PARSE_ERROR ); |
xively | 0:82702e998d3f | 167 | |
xively | 0:82702e998d3f | 168 | // update the pointer |
xively | 0:82702e998d3f | 169 | ptr_to_headers_end += sizeof( XI_HTTP_CRLF ) - 1; |
xively | 0:82702e998d3f | 170 | |
xively | 0:82702e998d3f | 171 | const char* payload_begin = ptr_to_headers_end + sizeof( XI_HTTP_CRLF ) - 1; |
xively | 0:82702e998d3f | 172 | |
xively | 0:82702e998d3f | 173 | XI_CHECK_ZERO( payload_begin, XI_HTTP_PARSE_ERROR ); |
xively | 0:82702e998d3f | 174 | |
xively | 0:82702e998d3f | 175 | // parse status |
xively | 0:82702e998d3f | 176 | const char* ptr = parse_http_status( response, content ); |
xively | 0:82702e998d3f | 177 | |
xively | 0:82702e998d3f | 178 | // check the continuation condition |
xively | 0:82702e998d3f | 179 | XI_CHECK_ZERO( ptr, XI_HTTP_PARSE_ERROR ); |
xively | 0:82702e998d3f | 180 | |
xively | 0:82702e998d3f | 181 | // read the headers |
xively | 0:82702e998d3f | 182 | while( ( ptr = parse_http_header( response, ptr ) ) != '\0' |
xively | 0:82702e998d3f | 183 | && ptr != ptr_to_headers_end ); |
xively | 0:82702e998d3f | 184 | |
xively | 0:82702e998d3f | 185 | // if there was an error, forward it |
xively | 0:82702e998d3f | 186 | xi_err_t e = xi_get_last_error(); |
xively | 0:82702e998d3f | 187 | XI_CHECK_CND( e != XI_NO_ERR, e ); |
xively | 0:82702e998d3f | 188 | |
xively | 0:82702e998d3f | 189 | // just copy the content |
xively | 0:82702e998d3f | 190 | strncpy( response->http_content, payload_begin, sizeof( response->http_content ) ); |
xively | 0:82702e998d3f | 191 | |
xively | 0:82702e998d3f | 192 | return response; |
xively | 0:82702e998d3f | 193 | |
xively | 0:82702e998d3f | 194 | err_handling: |
xively | 0:82702e998d3f | 195 | return 0; |
xively | 0:82702e998d3f | 196 | } |