radio

radio.ircforever.org
git clone git://git.ircforever.org/radio
Log | Files | Refs | Submodules | README | LICENSE

http.h (27348B)


      1 /*
      2 ------------------------------------------------------------------------------
      3           Licensing information can be found at the end of the file.
      4 ------------------------------------------------------------------------------
      5 
      6 http.hpp - v1.0 - Basic HTTP protocol implementation over sockets (no https).
      7 
      8 Do this:
      9     #define HTTP_IMPLEMENTATION
     10 before you include this file in *one* C/C++ file to create the implementation.
     11 */
     12 
     13 #ifndef http_hpp
     14 #define http_hpp
     15 
     16 #define _CRT_NONSTDC_NO_DEPRECATE 
     17 #define _CRT_SECURE_NO_WARNINGS
     18 #include <stddef.h> // for size_t
     19 #include <stdint.h> // for uintptr_t
     20 #include <sys/select.h>
     21 
     22 typedef enum http_status_t
     23     {
     24     HTTP_STATUS_PENDING,
     25     HTTP_STATUS_COMPLETED,
     26     HTTP_STATUS_FAILED,
     27     } http_status_t;
     28 
     29 typedef struct http_t
     30     {
     31     http_status_t status;
     32     int status_code;
     33     char const* reason_phrase;
     34     char const* content_type;
     35     size_t response_size;
     36     void* response_data;
     37     } http_t;
     38 
     39 http_t* http_get( char const* url, void* memctx );
     40 http_t* http_post( char const* url, void const* data, size_t size, void* memctx );
     41 
     42 http_status_t http_process( http_t* http );
     43 
     44 void http_release( http_t* http );
     45 
     46 #endif /* http_hpp */
     47 
     48 /** 
     49 
     50 http.hpp
     51 ========
     52 
     53 Basic HTTP protocol implementation over sockets (no https).
     54 
     55 
     56 Example
     57 -------
     58 
     59     #define HTTP_IMPLEMENTATION
     60     #include "http.h"
     61 
     62     int main( int argc, char** argv ) {
     63         http_t* request = http_get( "http://www.mattiasgustavsson.com/http_test.txt", NULL );
     64         if( !request ) {
     65             printf( "Invalid request.\n" );
     66             return 1;
     67         }
     68 
     69         http_status_t status = HTTP_STATUS_PENDING;
     70         int prev_size = -1;
     71         while( status == HTTP_STATUS_PENDING ) {
     72             status = http_process( request );
     73             if( prev_size != (int) request->response_size ) {
     74                 printf( "%d byte(s) received.\n", (int) request->response_size );
     75                 prev_size = (int) request->response_size;
     76             }
     77         }
     78 
     79         if( status == HTTP_STATUS_FAILED ) {
     80             printf( "HTTP request failed (%d): %s.\n", request->status_code, request->reason_phrase );
     81             http_release( request );
     82             return 1;
     83         }
     84     
     85         printf( "\nContent type: %s\n\n%s\n", request->content_type, (char const*)request->response_data );
     86         http_release( request );
     87         return 0;
     88     }
     89 
     90 
     91 API Documentation
     92 -----------------
     93 
     94 http.h is a small library for making http requests from a web server. It only supports GET and POST http commands, and
     95 is designed for when you just need a very basic way of communicating over http. http.h does not support https 
     96 connections, just plain http.
     97 
     98 http.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use 
     99 it, you just include http.h to get the API declarations. To get the definitions, you must include http.h from 
    100 *one* single C or C++ file, and #define the symbol `HTTP_IMPLEMENTATION` before you do. 
    101 
    102 
    103 #### Custom memory allocators
    104 
    105 For working memory and to store the retrieved data, http.h needs to do dynamic allocation by calling `malloc`. Programs 
    106 might want to keep track of allocations done, or use custom defined pools to allocate memory from. http.h allows 
    107 for specifying custom memory allocation functions for `malloc` and `free`. This is done with the following code:
    108 
    109     #define HTTP_IMPLEMENTATION
    110     #define HTTP_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) )
    111     #define HTTP_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) )
    112     #include "http.h"
    113 
    114 where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter
    115 is an optional parameter of type `void*`. When `http_get` or `http_post` is called, , you can pass in a `memctx` 
    116 parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every 
    117 `HTTP_MALLOC`/`HTTP_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your 
    118 tracking data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the 
    119 right type, and access the tracking data.
    120 
    121 If no custom allocator is defined, http.h will default to `malloc` and `free` from the C runtime library.
    122 
    123 
    124 http_get
    125 --------
    126 
    127     http_t* http_get( char const* url, void* memctx )
    128 
    129 Initiates a http GET request with the specified url. `url` is a zero terminated string containing the request location,
    130 just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `memctx` is a 
    131 pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if 
    132 no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the
    133 request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling
    134 `http_release`. If the request was invalid, `http_get` returns NULL.
    135 
    136 
    137 http_post
    138 ---------
    139 
    140     http_t* http_post( char const* url, void const* data, size_t size, void* memctx )
    141 
    142 Initiates a http POST request with the specified url. `url` is a zero terminated string containing the request location,
    143 just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `data` is a
    144 pointer to the data to be sent along as part of the request, and `size` is the number of bytes to send. `memctx` is a 
    145 pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if 
    146 no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the
    147 request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling
    148 `http_release`. If the request was invalid, `http_post` returns NULL.
    149 
    150 
    151 http_process
    152 ------------
    153 
    154     http_status_t http_process( http_t* http )
    155 
    156 http.h uses non-blocking sockets, so after a request have been made by calling either `http_get` or `http_post`, you 
    157 have to keep calling `http_process` for as long as it returns `HTTP_STATUS_PENDING`. You can call it from a loop which 
    158 does other work too, for example from inside a game loop or from a loop which calls `http_process` on multiple requests.
    159 If the request fails, `http_process` returns `HTTP_STATUS_FAILED`, and the fields `status_code` and `reason_phrase` may
    160 contain more details (for example, status code can be 404 if the requested resource was not found on the server). If the 
    161 request completes successfully, it returns `HTTP_STATUS_COMPLETED`. In this case, the `http_t` instance will contain 
    162 details about the result. `status_code` and `reason_phrase` contains the details about the result, as specified in the
    163 HTTP protocol. `content_type` contains the MIME type for the returns resource, for example `text/html` for a normal web
    164 page. `response_data` is the pointer to the received data, and `resonse_size` is the number of bytes it contains. In the
    165 case when the response data is in text format, http.h ensures there is a zero terminator placed immediately after the
    166 response data block, so it is safe to interpret the resonse data as a `char*`. Note that the data size in this case will 
    167 be the length of the data without the additional zero terminator.
    168 
    169 
    170 http_release
    171 ------------
    172 
    173     void http_release( http_t* http )
    174 
    175 Releases the resources acquired by `http_get` or `http_post`. Should be call when you are finished with the request.
    176 
    177 */
    178 
    179 /*
    180 ----------------------
    181     IMPLEMENTATION
    182 ----------------------
    183 */
    184 
    185 #ifdef HTTP_IMPLEMENTATION
    186 
    187 #ifdef _WIN32
    188     #define _CRT_NONSTDC_NO_DEPRECATE 
    189     #define _CRT_SECURE_NO_WARNINGS
    190     #pragma warning( push )
    191     #pragma warning( disable: 4127 ) // conditional expression is constant
    192     #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)'
    193     #pragma warning( disable: 4365 ) // 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch
    194     #pragma warning( disable: 4574 ) // 'Identifier' is defined to be '0': did you mean to use '#if identifier'?
    195     #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directive'
    196     #pragma warning( disable: 4706 ) // assignment within conditional expression
    197     #include <winsock2.h>
    198     #include <ws2tcpip.h>
    199     #pragma warning( pop )
    200     #pragma comment (lib, "Ws2_32.lib") 
    201     #include <string.h>
    202     #include <stdio.h>
    203     #define HTTP_SOCKET SOCKET
    204     #define HTTP_INVALID_SOCKET INVALID_SOCKET
    205 #else
    206     #include <stdlib.h>
    207     #include <stdio.h>
    208     #include <string.h>
    209     #include <sys/types.h>
    210     #include <sys/socket.h>
    211     #include <unistd.h>
    212     #include <errno.h>
    213     #include <fcntl.h>
    214     #include <netdb.h>
    215     #define HTTP_SOCKET int
    216     #define HTTP_INVALID_SOCKET -1
    217 #endif
    218 
    219 #ifndef HTTP_MALLOC
    220     #define _CRT_NONSTDC_NO_DEPRECATE 
    221     #define _CRT_SECURE_NO_WARNINGS
    222     #include <stdlib.h>
    223     #define HTTP_MALLOC( ctx, size ) ( malloc( size ) )
    224     #define HTTP_FREE( ctx, ptr ) ( free( ptr ) )
    225 #endif
    226 
    227 typedef struct http_internal_t 
    228     {
    229     /* keep this at the top!*/ 
    230     http_t http;
    231     /* because http_internal_t* can be cast to http_t*. */
    232     
    233     void* memctx;
    234     HTTP_SOCKET socket;
    235     int connect_pending;
    236     int request_sent;
    237     char address[ 256 ];
    238     char request_header[ 256 ];
    239     char* request_header_large;
    240     void* request_data;
    241     size_t request_data_size;
    242     char reason_phrase[ 1024 ];
    243     char content_type[ 256 ];
    244     size_t data_size;
    245     size_t data_capacity;
    246     void* data;
    247     } http_internal_t;
    248 
    249 
    250 static int http_internal_parse_url( char const* url, char* address, size_t address_capacity, char* port, 
    251     size_t port_capacity, char const** resource )
    252     {
    253     // make sure url starts with http://
    254     if( strncmp( url, "http://", 7 ) != 0 ) return 0;
    255     url += 7; // skip http:// part of url
    256     
    257     size_t url_len = strlen( url );
    258 
    259     // find end of address part of url
    260     char const* address_end = strchr( url, ':' );
    261     if( !address_end ) address_end = strchr( url, '/' );
    262     if( !address_end ) address_end = url + url_len;
    263 
    264     // extract address
    265     size_t address_len = (size_t)( address_end - url );
    266     if( address_len >= address_capacity ) return 0;
    267     memcpy( address, url, address_len );
    268     address[ address_len ] = 0;
    269 
    270     // check if there's a port defined
    271     char const* port_end = address_end;
    272     if( *address_end == ':' )
    273         {
    274         ++address_end;
    275         port_end = strchr( address_end, '/' );
    276         if( !port_end ) port_end = address_end + strlen( address_end );
    277         size_t port_len = (size_t)( port_end - address_end );
    278         if( port_len >= port_capacity ) return 0;
    279         memcpy( port, address_end, port_len );
    280         port[ port_len ] = 0;
    281         }
    282     else
    283         {
    284         // use default port number 80
    285         if( port_capacity <= 2 ) return 0;
    286         strcpy( port, "80" );
    287         }
    288 
    289 
    290     *resource = port_end;
    291 
    292     return 1;
    293     }
    294 
    295 
    296 HTTP_SOCKET http_internal_connect( char const* address, char const* port )
    297     {   
    298     // set up hints for getaddrinfo
    299     struct addrinfo hints;
    300     memset( &hints, 0, sizeof( hints ) );
    301     hints.ai_family = AF_INET; // the Internet Protocol version 4 (IPv4) address family.
    302     hints.ai_flags = AI_PASSIVE;
    303     hints.ai_socktype = SOCK_STREAM;
    304     hints.ai_protocol = IPPROTO_TCP;    // Use Transmission Control Protocol (TCP).
    305 
    306     // resolve the server address and port
    307     struct addrinfo* addri = 0;
    308     int error = getaddrinfo( address, port, &hints, &addri) ;
    309     if( error != 0 ) return HTTP_INVALID_SOCKET;
    310 
    311     // create the socket
    312     HTTP_SOCKET sock = socket( addri->ai_family, addri->ai_socktype, addri->ai_protocol );
    313     if( sock == -1) 
    314         {
    315         freeaddrinfo( addri );
    316         return HTTP_INVALID_SOCKET;
    317         }
    318 
    319     // set socket to nonblocking mode
    320     u_long nonblocking = 1;
    321     #ifdef _WIN32
    322         int res = ioctlsocket( sock, FIONBIO, &nonblocking );
    323     #else
    324         int flags = fcntl( sock, F_GETFL, 0 );
    325         int res = fcntl( sock, F_SETFL, flags | O_NONBLOCK ); 
    326     #endif
    327     if( res == -1 )
    328         {
    329         freeaddrinfo( addri );
    330         #ifdef _WIN32
    331             closesocket( sock );
    332         #else
    333             close( sock );
    334         #endif
    335         return HTTP_INVALID_SOCKET;
    336         }
    337 
    338     // connect to server
    339     if( connect( sock, addri->ai_addr, (int)addri->ai_addrlen ) == -1 )
    340         {
    341         #ifdef _WIN32
    342             if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAEINPROGRESS )
    343                 {
    344                 freeaddrinfo( addri );
    345                 closesocket( sock );
    346                 return HTTP_INVALID_SOCKET;
    347                 }
    348         #else
    349             if( errno != EWOULDBLOCK && errno != EINPROGRESS && errno != EAGAIN )
    350                 {
    351                 freeaddrinfo( addri );
    352                 close( sock );
    353                 return HTTP_INVALID_SOCKET;
    354                 }
    355         #endif
    356         }
    357 
    358     freeaddrinfo( addri );
    359     return sock;
    360     }
    361 
    362     
    363 static http_internal_t* http_internal_create( size_t request_data_size, void* memctx )
    364     {
    365     http_internal_t* internal = (http_internal_t*) HTTP_MALLOC( memctx, sizeof( http_internal_t ) + request_data_size );
    366 
    367     internal->http.status = HTTP_STATUS_PENDING;
    368     internal->http.status_code = 0;
    369     internal->http.response_size = 0;
    370     internal->http.response_data = NULL;
    371 
    372     internal->memctx = memctx;
    373     internal->connect_pending = 1;
    374     internal->request_sent = 0;
    375     
    376     strcpy( internal->reason_phrase, "" );
    377     internal->http.reason_phrase = internal->reason_phrase;
    378 
    379     strcpy( internal->content_type, "" );
    380     internal->http.content_type = internal->content_type;
    381 
    382     internal->data_size = 0;
    383     internal->data_capacity = 64 * 1024;
    384     internal->data = HTTP_MALLOC( memctx, internal->data_capacity );
    385     
    386     internal->request_data = NULL;
    387     internal->request_data_size = 0;
    388     
    389     return internal;
    390     }
    391 
    392 
    393 http_t* http_get( char const* url, void* memctx )
    394     {       
    395     #ifdef _WIN32
    396         WSADATA wsa_data;
    397         if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return NULL;
    398     #endif
    399     
    400     char address[ 256 ];
    401     char port[ 16 ];
    402     char const* resource;
    403     
    404     if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 )
    405         return NULL; 
    406 
    407     HTTP_SOCKET socket = http_internal_connect( address, port );
    408     if( socket == HTTP_INVALID_SOCKET ) return NULL;
    409     
    410     http_internal_t* internal = http_internal_create( 0, memctx );
    411     internal->socket = socket;
    412 
    413     char* request_header;   
    414     size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port );
    415     if( request_header_len < sizeof( internal->request_header ) )
    416         {
    417         internal->request_header_large = NULL;
    418         request_header = internal->request_header;
    419         }
    420     else
    421         {
    422         internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 );
    423         request_header = internal->request_header_large;
    424         }       
    425     int default_http_port = (strcmp(port, "80") == 0);
    426     sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port );
    427     
    428     return &internal->http;
    429     }
    430 
    431 
    432 http_t* http_post( char const* url, void const* data, size_t size, void* memctx )
    433     {
    434     #ifdef _WIN32
    435         WSADATA wsa_data;
    436         if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return 0;
    437     #endif
    438     
    439     char address[ 256 ];
    440     char port[ 16 ];
    441     char const* resource;
    442     
    443     if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 )
    444         return NULL; 
    445 
    446     HTTP_SOCKET socket = http_internal_connect( address, port );
    447     if( socket == HTTP_INVALID_SOCKET ) return NULL;
    448     
    449     http_internal_t* internal = http_internal_create( size, memctx );
    450     internal->socket = socket;
    451 
    452     char* request_header;   
    453     size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port );
    454     if( request_header_len < sizeof( internal->request_header ) )
    455         {
    456         internal->request_header_large = NULL;
    457         request_header = internal->request_header;
    458         }
    459     else
    460         {
    461         internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 );
    462         request_header = internal->request_header_large;
    463         }       
    464     int default_http_port = (strcmp(port, "80") == 0);
    465     sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port, 
    466         (int) size );
    467     
    468     internal->request_data_size = size;
    469     internal->request_data = ( internal + 1 );
    470     memcpy( internal->request_data, data, size );
    471     
    472     return &internal->http;
    473     }
    474 
    475 
    476 http_status_t http_process( http_t* http )
    477     {
    478     http_internal_t* internal = (http_internal_t*) http;    
    479     
    480     if( http->status == HTTP_STATUS_FAILED ) return http->status;
    481     
    482     if( internal->connect_pending )
    483         {   
    484         fd_set sockets_to_check; 
    485         FD_ZERO( &sockets_to_check );
    486         #pragma warning( push )
    487         #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
    488         FD_SET( internal->socket, &sockets_to_check );
    489         #pragma warning( pop )
    490         struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
    491         // check if socket is ready for send
    492         if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 ) 
    493             {
    494             int opt = -1;
    495             socklen_t len = sizeof( opt ); 
    496             if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 ) 
    497                 internal->connect_pending = 0; // if it is, we're connected
    498             }
    499         }
    500 
    501     if( internal->connect_pending ) return http->status;
    502 
    503     if( !internal->request_sent )
    504         {
    505         char const* request_header = internal->request_header_large ? 
    506             internal->request_header_large : internal->request_header;
    507         if( send( internal->socket, request_header, (int) strlen( request_header ), 0 ) == -1 )
    508             {
    509             http->status = HTTP_STATUS_FAILED;
    510             return http->status;
    511             }
    512         if( internal->request_data_size )
    513             {
    514             int res = send( internal->socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 );
    515             if( res == -1 )
    516                 {
    517                 http->status = HTTP_STATUS_FAILED;
    518                 return http->status;
    519                 }
    520             }
    521         internal->request_sent = 1;
    522         return http->status;
    523         }
    524 
    525     // check if socket is ready for recv
    526     fd_set sockets_to_check; 
    527     FD_ZERO( &sockets_to_check );
    528     #pragma warning( push )
    529     #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
    530     FD_SET( internal->socket, &sockets_to_check );
    531     #pragma warning( pop )
    532     struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
    533     while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
    534         {
    535         char buffer[ 4096 ];
    536         int size = recv( internal->socket, buffer, sizeof( buffer ), 0 );
    537         if( size == -1 )
    538             {
    539             http->status = HTTP_STATUS_FAILED;
    540             return http->status;
    541             }
    542         else if( size > 0 )
    543             {
    544             size_t min_size = internal->data_size + size + 1;
    545             if( internal->data_capacity < min_size )
    546                 {
    547                 internal->data_capacity *= 2; 
    548                 if( internal->data_capacity < min_size ) internal->data_capacity = min_size;
    549                 void* new_data = HTTP_MALLOC( memctx, internal->data_capacity );
    550                 memcpy( new_data, internal->data, internal->data_size );
    551                 HTTP_FREE( memctx, internal->data );
    552                 internal->data = new_data;
    553                 }
    554             memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size );
    555             internal->data_size += size;
    556             }
    557         else if( size == 0 )
    558             {
    559             char const* status_line = (char const*) internal->data;
    560 
    561             int header_size = 0;
    562             char const* header_end = strstr( status_line, "\r\n\r\n" );
    563             if( header_end )
    564                 {
    565                 header_end += 4;
    566                 header_size = (int)( header_end - status_line );
    567                 }
    568             else
    569                 {
    570                 http->status = HTTP_STATUS_FAILED;
    571                 return http->status;
    572                 }
    573 
    574             // skip http version
    575             status_line = strchr( status_line, ' ' );
    576             if( !status_line )
    577                 {
    578                 http->status = HTTP_STATUS_FAILED;
    579                 return http->status;
    580                 }
    581             ++status_line;
    582             
    583             // extract status code
    584             char status_code[ 16 ];
    585             char const* status_code_end = strchr( status_line, ' ' );
    586             if( !status_code_end )
    587                 {
    588                 http->status = HTTP_STATUS_FAILED;
    589                 return http->status;
    590                 }
    591             memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) );
    592             status_code[ status_code_end - status_line ] = 0;
    593             status_line = status_code_end + 1;
    594             http->status_code = atoi( status_code );
    595             
    596             // extract reason phrase
    597             char const* reason_phrase_end = strstr( status_line, "\r\n" );
    598             if( !reason_phrase_end )
    599                 {
    600                 http->status = HTTP_STATUS_FAILED;
    601                 return http->status;
    602                 }
    603             size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line );
    604             if( reason_phrase_len >= sizeof( internal->reason_phrase ) ) 
    605                 reason_phrase_len = sizeof( internal->reason_phrase ) - 1;
    606             memcpy( internal->reason_phrase, status_line, reason_phrase_len );
    607             internal->reason_phrase[ reason_phrase_len ] = 0;
    608             status_line = reason_phrase_end + 1;
    609             
    610             // extract content type
    611             char const* content_type_start = strstr( status_line, "Content-Type: " );
    612             if( content_type_start )
    613                 {
    614                 content_type_start += strlen( "Content-Type: " );
    615                 char const* content_type_end = strstr( content_type_start, "\r\n" );
    616                 if( content_type_end )
    617                     {
    618                     size_t content_type_len = (size_t)( content_type_end - content_type_start );
    619                     if( content_type_len >= sizeof( internal->content_type ) ) 
    620                         content_type_len = sizeof( internal->content_type ) - 1;
    621                     memcpy( internal->content_type, content_type_start, content_type_len );
    622                     internal->content_type[ content_type_len ] = 0;
    623                     }
    624                 }
    625 
    626             http->status =  http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED;
    627             http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size );
    628             http->response_size = internal->data_size - header_size;
    629 
    630             // add an extra zero after the received data, but don't modify the size, so ascii results can be used as
    631             // a zero terminated string. the size returned will be the string without this extra zero terminator.
    632             ( (char*)http->response_data )[ http->response_size ] = 0;
    633             return http->status;
    634             }
    635         }
    636     
    637     return http->status;
    638     }
    639 
    640 
    641 void http_release( http_t* http )
    642     {
    643     http_internal_t* internal = (http_internal_t*) http;
    644     #ifdef _WIN32
    645         closesocket( internal->socket );
    646     #else
    647         close( internal->socket );
    648     #endif
    649 
    650     if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large );
    651     HTTP_FREE( memctx, internal->data );
    652     HTTP_FREE( memctx, internal );
    653     #ifdef _WIN32
    654         WSACleanup();
    655     #endif
    656     }
    657 
    658 
    659 #endif /* HTTP_IMPLEMENTATION */
    660 
    661 /*
    662 revision history:
    663     1.0     first released version  
    664 */
    665 
    666 /*
    667 ------------------------------------------------------------------------------
    668 
    669 This software is available under 2 licenses - you may choose the one you like.
    670 
    671 ------------------------------------------------------------------------------
    672 
    673 ALTERNATIVE A - MIT License
    674 
    675 Copyright (c) 2016 Mattias Gustavsson
    676 
    677 Permission is hereby granted, free of charge, to any person obtaining a copy of 
    678 this software and associated documentation files (the "Software"), to deal in 
    679 the Software without restriction, including without limitation the rights to 
    680 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
    681 of the Software, and to permit persons to whom the Software is furnished to do 
    682 so, subject to the following conditions:
    683 
    684 The above copyright notice and this permission notice shall be included in all 
    685 copies or substantial portions of the Software.
    686 
    687 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
    688 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
    689 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
    690 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
    691 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
    692 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
    693 SOFTWARE.
    694 
    695 ------------------------------------------------------------------------------
    696 
    697 ALTERNATIVE B - Public Domain (www.unlicense.org)
    698 
    699 This is free and unencumbered software released into the public domain.
    700 
    701 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 
    702 software, either in source code form or as a compiled binary, for any purpose, 
    703 commercial or non-commercial, and by any means.
    704 
    705 In jurisdictions that recognize copyright laws, the author or authors of this 
    706 software dedicate any and all copyright interest in the software to the public 
    707 domain. We make this dedication for the benefit of the public at large and to 
    708 the detriment of our heirs and successors. We intend this dedication to be an 
    709 overt act of relinquishment in perpetuity of all present and future rights to 
    710 this software under copyright law.
    711 
    712 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
    713 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
    714 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
    715 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
    716 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
    717 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    718 
    719 ------------------------------------------------------------------------------
    720 */