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