mirror of
https://github.com/retroplasma/earth-reverse-engineering.git
synced 2025-12-08 18:02:00 +00:00
720 lines
27 KiB
C
720 lines
27 KiB
C
/*
|
|
------------------------------------------------------------------------------
|
|
Licensing information can be found at the end of the file.
|
|
------------------------------------------------------------------------------
|
|
|
|
http.hpp - v1.0 - Basic HTTP protocol implementation over sockets (no https).
|
|
|
|
Do this:
|
|
#define HTTP_IMPLEMENTATION
|
|
before you include this file in *one* C/C++ file to create the implementation.
|
|
*/
|
|
|
|
#ifndef http_hpp
|
|
#define http_hpp
|
|
|
|
#define _CRT_NONSTDC_NO_DEPRECATE
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#include <stddef.h> // for size_t
|
|
|
|
typedef enum http_status_t
|
|
{
|
|
HTTP_STATUS_PENDING,
|
|
HTTP_STATUS_COMPLETED,
|
|
HTTP_STATUS_FAILED,
|
|
} http_status_t;
|
|
|
|
typedef struct http_t
|
|
{
|
|
http_status_t status;
|
|
int status_code;
|
|
char const* reason_phrase;
|
|
char const* content_type;
|
|
size_t response_size;
|
|
void* response_data;
|
|
} http_t;
|
|
|
|
http_t* http_get( char const* url, void* memctx );
|
|
http_t* http_post( char const* url, void const* data, size_t size, void* memctx );
|
|
|
|
http_status_t http_process( http_t* http );
|
|
|
|
void http_release( http_t* http );
|
|
|
|
#endif /* http_hpp */
|
|
|
|
/**
|
|
|
|
Example
|
|
=======
|
|
|
|
#define HTTP_IMPLEMENTATION
|
|
#include "http.h"
|
|
|
|
int main( int argc, char** argv )
|
|
{
|
|
(void) argc, argv;
|
|
|
|
http_t* request = http_get( "http://www.mattiasgustavsson.com/http_test.txt", NULL );
|
|
if( !request )
|
|
{
|
|
printf( "Invalid request.\n" );
|
|
return 1;
|
|
}
|
|
|
|
http_status_t status = HTTP_STATUS_PENDING;
|
|
int prev_size = -1;
|
|
while( status == HTTP_STATUS_PENDING )
|
|
{
|
|
status = http_process( request );
|
|
if( prev_size != (int) request->response_size )
|
|
{
|
|
printf( "%d byte(s) received.\n", (int) request->response_size );
|
|
prev_size = (int) request->response_size;
|
|
}
|
|
}
|
|
|
|
if( status == HTTP_STATUS_FAILED )
|
|
{
|
|
printf( "HTTP request failed (%d): %s.\n", request->status_code, request->reason_phrase );
|
|
http_release( request );
|
|
return 1;
|
|
}
|
|
|
|
printf( "\nContent type: %s\n\n%s\n", request->content_type, (char const*)request->response_data );
|
|
http_release( request );
|
|
return 0;
|
|
}
|
|
|
|
|
|
API Documentation
|
|
=================
|
|
|
|
http.h is a small library for making http requests from a web server. It only supports GET and POST http commands, and
|
|
is designed for when you just need a very basic way of communicating over http. http.h does not support https
|
|
connections, just plain http.
|
|
|
|
http.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use
|
|
it, you just include http.h to get the API declarations. To get the definitions, you must include http.h from
|
|
*one* single C or C++ file, and #define the symbol `HTTP_IMPLEMENTATION` before you do.
|
|
|
|
|
|
Customization
|
|
-------------
|
|
|
|
### Custom memory allocators
|
|
|
|
For working memory and to store the retrieved data, http.h needs to do dynamic allocation by calling `malloc`. Programs
|
|
might want to keep track of allocations done, or use custom defined pools to allocate memory from. http.h allows
|
|
for specifying custom memory allocation functions for `malloc` and `free`. This is done with the following code:
|
|
|
|
#define HTTP_IMPLEMENTATION
|
|
#define HTTP_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) )
|
|
#define HTTP_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) )
|
|
#include "http.h"
|
|
|
|
where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter
|
|
is an optional parameter of type `void*`. When `http_get` or `http_post` is called, , you can pass in a `memctx`
|
|
parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every
|
|
`HTTP_MALLOC`/`HTTP_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your
|
|
tracking data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the
|
|
right type, and access the tracking data.
|
|
|
|
If no custom allocator is defined, http.h will default to `malloc` and `free` from the C runtime library.
|
|
|
|
|
|
http_get
|
|
--------
|
|
|
|
http_t* http_get( char const* url, void* memctx )
|
|
|
|
Initiates a http GET request with the specified url. `url` is a zero terminated string containing the request location,
|
|
just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `memctx` is a
|
|
pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if
|
|
no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the
|
|
request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling
|
|
`http_release`. If the request was invalid, `http_get` returns NULL.
|
|
|
|
|
|
http_post
|
|
---------
|
|
|
|
http_t* http_post( char const* url, void const* data, size_t size, void* memctx )
|
|
|
|
Initiates a http POST request with the specified url. `url` is a zero terminated string containing the request location,
|
|
just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `data` is a
|
|
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
|
|
pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if
|
|
no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the
|
|
request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling
|
|
`http_release`. If the request was invalid, `http_post` returns NULL.
|
|
|
|
|
|
http_process
|
|
------------
|
|
|
|
http_status_t http_process( http_t* http )
|
|
|
|
http.h uses non-blocking sockets, so after a request have been made by calling either `http_get` or `http_post`, you
|
|
have to keep calling `http_process` for as long as it returns `HTTP_STATUS_PENDING`. You can call it from a loop which
|
|
does other work too, for example from inside a game loop or from a loop which calls `http_process` on multiple requests.
|
|
If the request fails, `http_process` returns `HTTP_STATUS_FAILED`, and the fields `status_code` and `reason_phrase` may
|
|
contain more details (for example, status code can be 404 if the requested resource was not found on the server). If the
|
|
request completes successfully, it returns `HTTP_STATUS_COMPLETED`. In this case, the `http_t` instance will contain
|
|
details about the result. `status_code` and `reason_phrase` contains the details about the result, as specified in the
|
|
HTTP protocol. `content_type` contains the MIME type for the returns resource, for example `text/html` for a normal web
|
|
page. `response_data` is the pointer to the received data, and `resonse_size` is the number of bytes it contains. In the
|
|
case when the response data is in text format, http.h ensures there is a zero terminator placed immediately after the
|
|
response data block, so it is safe to interpret the resonse data as a `char*`. Note that the data size in this case will
|
|
be the length of the data without the additional zero terminator.
|
|
|
|
|
|
http_release
|
|
------------
|
|
|
|
void http_release( http_t* http )
|
|
|
|
Releases the resources acquired by `http_get` or `http_post`. Should be call when you are finished with the request.
|
|
|
|
*/
|
|
|
|
/*
|
|
----------------------
|
|
IMPLEMENTATION
|
|
----------------------
|
|
*/
|
|
|
|
#ifdef HTTP_IMPLEMENTATION
|
|
|
|
#ifdef _WIN32
|
|
#define _CRT_NONSTDC_NO_DEPRECATE
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#pragma warning( push )
|
|
#pragma warning( disable: 4127 ) // conditional expression is constant
|
|
#pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)'
|
|
#pragma warning( disable: 4365 ) // 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch
|
|
#pragma warning( disable: 4574 ) // 'Identifier' is defined to be '0': did you mean to use '#if identifier'?
|
|
#pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directive'
|
|
#pragma warning( disable: 4706 ) // assignment within conditional expression
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#pragma warning( pop )
|
|
#pragma comment (lib, "Ws2_32.lib")
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#define HTTP_SOCKET SOCKET
|
|
#define HTTP_INVALID_SOCKET INVALID_SOCKET
|
|
#else
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <netdb.h>
|
|
#define HTTP_SOCKET int
|
|
#define HTTP_INVALID_SOCKET -1
|
|
#endif
|
|
|
|
#ifndef HTTP_MALLOC
|
|
#define _CRT_NONSTDC_NO_DEPRECATE
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#include <stdlib.h>
|
|
#define HTTP_MALLOC( ctx, size ) ( malloc( size ) )
|
|
#define HTTP_FREE( ctx, ptr ) ( free( ptr ) )
|
|
#endif
|
|
|
|
typedef struct http_internal_t
|
|
{
|
|
/* keep this at the top!*/
|
|
http_t http;
|
|
/* because http_internal_t* can be cast to http_t*. */
|
|
|
|
void* memctx;
|
|
HTTP_SOCKET socket;
|
|
int connect_pending;
|
|
int request_sent;
|
|
char address[ 256 ];
|
|
char request_header[ 256 ];
|
|
char* request_header_large;
|
|
void* request_data;
|
|
size_t request_data_size;
|
|
char reason_phrase[ 1024 ];
|
|
char content_type[ 256 ];
|
|
size_t data_size;
|
|
size_t data_capacity;
|
|
void* data;
|
|
} http_internal_t;
|
|
|
|
|
|
static int http_internal_parse_url( char const* url, char* address, size_t address_capacity, char* port,
|
|
size_t port_capacity, char const** resource )
|
|
{
|
|
// make sure url starts with http://
|
|
if( strncmp( url, "http://", 7 ) != 0 ) return 0;
|
|
url += 7; // skip http:// part of url
|
|
|
|
size_t url_len = strlen( url );
|
|
|
|
// find end of address part of url
|
|
char const* address_end = strchr( url, ':' );
|
|
if( !address_end ) address_end = strchr( url, '/' );
|
|
if( !address_end ) address_end = url + url_len;
|
|
|
|
// extract address
|
|
size_t address_len = (size_t)( address_end - url );
|
|
if( address_len >= address_capacity ) return 0;
|
|
memcpy( address, url, address_len );
|
|
address[ address_len ] = 0;
|
|
|
|
// check if there's a port defined
|
|
char const* port_end = address_end;
|
|
if( *address_end == ':' )
|
|
{
|
|
++address_end;
|
|
port_end = strchr( address_end, '/' );
|
|
if( !port_end ) port_end = address_end + strlen( address_end );
|
|
size_t port_len = (size_t)( port_end - address_end );
|
|
if( port_len >= port_capacity ) return 0;
|
|
memcpy( port, address_end, port_len );
|
|
port[ port_len ] = 0;
|
|
}
|
|
else
|
|
{
|
|
// use default port number 80
|
|
if( port_capacity <= 2 ) return 0;
|
|
strcpy( port, "80" );
|
|
}
|
|
|
|
|
|
*resource = port_end;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
HTTP_SOCKET http_internal_connect( char const* address, char const* port )
|
|
{
|
|
// set up hints for getaddrinfo
|
|
struct addrinfo hints;
|
|
memset( &hints, 0, sizeof( hints ) );
|
|
hints.ai_family = AF_UNSPEC; // the Internet Protocol version 4 (IPv4) address family.
|
|
hints.ai_flags = AI_PASSIVE;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP; // Use Transmission Control Protocol (TCP).
|
|
|
|
// resolve the server address and port
|
|
struct addrinfo* addri = 0;
|
|
int error = getaddrinfo( address, port, &hints, &addri) ;
|
|
if( error != 0 ) return HTTP_INVALID_SOCKET;
|
|
|
|
// create the socket
|
|
HTTP_SOCKET sock = socket( addri->ai_family, addri->ai_socktype, addri->ai_protocol );
|
|
if( sock == -1)
|
|
{
|
|
freeaddrinfo( addri );
|
|
return HTTP_INVALID_SOCKET;
|
|
}
|
|
|
|
// set socket to nonblocking mode
|
|
u_long nonblocking = 1;
|
|
#ifdef _WIN32
|
|
int res = ioctlsocket( sock, FIONBIO, &nonblocking );
|
|
#else
|
|
int flags = fcntl( sock, F_GETFL, 0 );
|
|
int res = fcntl( sock, F_SETFL, flags | O_NONBLOCK );
|
|
#endif
|
|
if( res == -1 )
|
|
{
|
|
freeaddrinfo( addri );
|
|
#ifdef _WIN32
|
|
closesocket( sock );
|
|
#else
|
|
close( sock );
|
|
#endif
|
|
return HTTP_INVALID_SOCKET;
|
|
}
|
|
|
|
// connect to server
|
|
if( connect( sock, addri->ai_addr, (int)addri->ai_addrlen ) == -1 )
|
|
{
|
|
#ifdef _WIN32
|
|
if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAEINPROGRESS )
|
|
{
|
|
freeaddrinfo( addri );
|
|
closesocket( sock );
|
|
return HTTP_INVALID_SOCKET;
|
|
}
|
|
#else
|
|
if( errno != EWOULDBLOCK && errno != EINPROGRESS && errno != EAGAIN )
|
|
{
|
|
freeaddrinfo( addri );
|
|
close( sock );
|
|
return HTTP_INVALID_SOCKET;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
freeaddrinfo( addri );
|
|
return sock;
|
|
}
|
|
|
|
|
|
static http_internal_t* http_internal_create( size_t request_data_size, void* memctx )
|
|
{
|
|
http_internal_t* internal = (http_internal_t*) HTTP_MALLOC( memctx, sizeof( http_internal_t ) + request_data_size );
|
|
|
|
internal->http.status = HTTP_STATUS_PENDING;
|
|
internal->http.status_code = 0;
|
|
internal->http.response_size = 0;
|
|
internal->http.response_data = NULL;
|
|
|
|
internal->memctx = memctx;
|
|
internal->connect_pending = 1;
|
|
internal->request_sent = 0;
|
|
|
|
strcpy( internal->reason_phrase, "" );
|
|
internal->http.reason_phrase = internal->reason_phrase;
|
|
|
|
strcpy( internal->content_type, "" );
|
|
internal->http.content_type = internal->content_type;
|
|
|
|
internal->data_size = 0;
|
|
internal->data_capacity = 64 * 1024;
|
|
internal->data = HTTP_MALLOC( memctx, internal->data_capacity );
|
|
|
|
internal->request_data = NULL;
|
|
internal->request_data_size = 0;
|
|
|
|
return internal;
|
|
}
|
|
|
|
|
|
http_t* http_get( char const* url, void* memctx )
|
|
{
|
|
#ifdef _WIN32
|
|
WSADATA wsa_data;
|
|
if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return NULL;
|
|
#endif
|
|
|
|
char address[ 256 ];
|
|
char port[ 16 ];
|
|
char const* resource;
|
|
|
|
if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 )
|
|
return NULL;
|
|
|
|
HTTP_SOCKET socket = http_internal_connect( address, port );
|
|
if( socket == HTTP_INVALID_SOCKET ) return NULL;
|
|
|
|
http_internal_t* internal = http_internal_create( 0, memctx );
|
|
internal->socket = socket;
|
|
|
|
char* request_header;
|
|
size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port );
|
|
if( request_header_len < sizeof( internal->request_header ) )
|
|
{
|
|
internal->request_header_large = NULL;
|
|
request_header = internal->request_header;
|
|
}
|
|
else
|
|
{
|
|
internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 );
|
|
request_header = internal->request_header_large;
|
|
}
|
|
sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s:%s\r\n\r\n", resource, address, port );
|
|
|
|
return &internal->http;
|
|
}
|
|
|
|
|
|
http_t* http_post( char const* url, void const* data, size_t size, void* memctx )
|
|
{
|
|
#ifdef _WIN32
|
|
WSADATA wsa_data;
|
|
if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return 0;
|
|
#endif
|
|
|
|
char address[ 256 ];
|
|
char port[ 16 ];
|
|
char const* resource;
|
|
|
|
if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 )
|
|
return NULL;
|
|
|
|
HTTP_SOCKET socket = http_internal_connect( address, port );
|
|
if( socket == HTTP_INVALID_SOCKET ) return NULL;
|
|
|
|
http_internal_t* internal = http_internal_create( size, memctx );
|
|
internal->socket = socket;
|
|
|
|
char* request_header;
|
|
size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port );
|
|
if( request_header_len < sizeof( internal->request_header ) )
|
|
{
|
|
internal->request_header_large = NULL;
|
|
request_header = internal->request_header;
|
|
}
|
|
else
|
|
{
|
|
internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 );
|
|
request_header = internal->request_header_large;
|
|
}
|
|
sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s:%s\r\nContent-Length: %d\r\n\r\n", resource, address, port,
|
|
(int) size );
|
|
|
|
internal->request_data_size = size;
|
|
internal->request_data = ( internal + 1 );
|
|
memcpy( internal->request_data, data, size );
|
|
|
|
return &internal->http;
|
|
}
|
|
|
|
|
|
http_status_t http_process( http_t* http )
|
|
{
|
|
http_internal_t* internal = (http_internal_t*) http;
|
|
|
|
if( http->status == HTTP_STATUS_FAILED ) return http->status;
|
|
|
|
if( internal->connect_pending )
|
|
{
|
|
fd_set sockets_to_check;
|
|
FD_ZERO( &sockets_to_check );
|
|
#pragma warning( push )
|
|
#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
|
|
FD_SET( internal->socket, &sockets_to_check );
|
|
#pragma warning( pop )
|
|
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
|
|
// check if socket is ready for send
|
|
if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 )
|
|
{
|
|
int opt = -1;
|
|
socklen_t len = sizeof( opt );
|
|
if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 )
|
|
internal->connect_pending = 0; // if it is, we're connected
|
|
}
|
|
}
|
|
|
|
if( internal->connect_pending ) return http->status;
|
|
|
|
if( !internal->request_sent )
|
|
{
|
|
char const* request_header = internal->request_header_large ?
|
|
internal->request_header_large : internal->request_header;
|
|
if( send( internal->socket, request_header, (int) strlen( request_header ), 0 ) == -1 )
|
|
{
|
|
http->status = HTTP_STATUS_FAILED;
|
|
return http->status;
|
|
}
|
|
if( internal->request_data_size )
|
|
{
|
|
int res = send( internal->socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 );
|
|
if( res == -1 )
|
|
{
|
|
http->status = HTTP_STATUS_FAILED;
|
|
return http->status;
|
|
}
|
|
}
|
|
internal->request_sent = 1;
|
|
return http->status;
|
|
}
|
|
|
|
// check if socket is ready for recv
|
|
fd_set sockets_to_check;
|
|
FD_ZERO( &sockets_to_check );
|
|
#pragma warning( push )
|
|
#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
|
|
FD_SET( internal->socket, &sockets_to_check );
|
|
#pragma warning( pop )
|
|
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
|
|
while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
|
|
{
|
|
char buffer[ 4096 ];
|
|
int size = recv( internal->socket, buffer, sizeof( buffer ), 0 );
|
|
if( size == -1 )
|
|
{
|
|
http->status = HTTP_STATUS_FAILED;
|
|
return http->status;
|
|
}
|
|
else if( size > 0 )
|
|
{
|
|
size_t min_size = internal->data_size + size + 1;
|
|
if( internal->data_capacity < min_size )
|
|
{
|
|
internal->data_capacity *= 2;
|
|
if( internal->data_capacity < min_size ) internal->data_capacity = min_size;
|
|
void* new_data = HTTP_MALLOC( memctx, internal->data_capacity );
|
|
memcpy( new_data, internal->data, internal->data_size );
|
|
HTTP_FREE( memctx, internal->data );
|
|
internal->data = new_data;
|
|
}
|
|
memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size );
|
|
internal->data_size += size;
|
|
}
|
|
else if( size == 0 )
|
|
{
|
|
char const* status_line = (char const*) internal->data;
|
|
|
|
int header_size = 0;
|
|
char const* header_end = strstr( status_line, "\r\n\r\n" );
|
|
if( header_end )
|
|
{
|
|
header_end += 4;
|
|
header_size = (int)( header_end - status_line );
|
|
}
|
|
else
|
|
{
|
|
http->status = HTTP_STATUS_FAILED;
|
|
return http->status;
|
|
}
|
|
|
|
// skip http version
|
|
status_line = strchr( status_line, ' ' );
|
|
if( !status_line )
|
|
{
|
|
http->status = HTTP_STATUS_FAILED;
|
|
return http->status;
|
|
}
|
|
++status_line;
|
|
|
|
// extract status code
|
|
char status_code[ 16 ];
|
|
char const* status_code_end = strchr( status_line, ' ' );
|
|
if( !status_code_end )
|
|
{
|
|
http->status = HTTP_STATUS_FAILED;
|
|
return http->status;
|
|
}
|
|
memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) );
|
|
status_code[ status_code_end - status_line ] = 0;
|
|
status_line = status_code_end + 1;
|
|
http->status_code = atoi( status_code );
|
|
|
|
// extract reason phrase
|
|
char const* reason_phrase_end = strstr( status_line, "\r\n" );
|
|
if( !reason_phrase_end )
|
|
{
|
|
http->status = HTTP_STATUS_FAILED;
|
|
return http->status;
|
|
}
|
|
size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line );
|
|
if( reason_phrase_len >= sizeof( internal->reason_phrase ) )
|
|
reason_phrase_len = sizeof( internal->reason_phrase ) - 1;
|
|
memcpy( internal->reason_phrase, status_line, reason_phrase_len );
|
|
internal->reason_phrase[ reason_phrase_len ] = 0;
|
|
status_line = reason_phrase_end + 1;
|
|
|
|
// extract content type
|
|
char const* content_type_start = strstr( status_line, "Content-Type: " );
|
|
if( content_type_start )
|
|
{
|
|
content_type_start += strlen( "Content-Type: " );
|
|
char const* content_type_end = strstr( content_type_start, "\r\n" );
|
|
if( content_type_end )
|
|
{
|
|
size_t content_type_len = (size_t)( content_type_end - content_type_start );
|
|
if( content_type_len >= sizeof( internal->content_type ) )
|
|
content_type_len = sizeof( internal->content_type ) - 1;
|
|
memcpy( internal->content_type, content_type_start, content_type_len );
|
|
internal->content_type[ content_type_len ] = 0;
|
|
}
|
|
}
|
|
|
|
http->status = http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED;
|
|
http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size );
|
|
http->response_size = internal->data_size - header_size;
|
|
|
|
// add an extra zero after the received data, but don't modify the size, so ascii results can be used as
|
|
// a zero terminated string. the size returned will be the string without this extra zero terminator.
|
|
( (char*)http->response_data )[ http->response_size ] = 0;
|
|
return http->status;
|
|
}
|
|
}
|
|
|
|
return http->status;
|
|
}
|
|
|
|
|
|
void http_release( http_t* http )
|
|
{
|
|
http_internal_t* internal = (http_internal_t*) http;
|
|
#ifdef _WIN32
|
|
closesocket( internal->socket );
|
|
#else
|
|
close( internal->socket );
|
|
#endif
|
|
|
|
if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large );
|
|
HTTP_FREE( memctx, internal->data );
|
|
HTTP_FREE( memctx, internal );
|
|
#ifdef _WIN32
|
|
WSACleanup();
|
|
#endif
|
|
}
|
|
|
|
|
|
#endif /* HTTP_IMPLEMENTATION */
|
|
|
|
/*
|
|
revision history:
|
|
1.0 first released version
|
|
*/
|
|
|
|
/*
|
|
------------------------------------------------------------------------------
|
|
|
|
This software is available under 2 licenses - you may choose the one you like.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
ALTERNATIVE A - MIT License
|
|
|
|
Copyright (c) 2016 Mattias Gustavsson
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do
|
|
so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
|
|
|
This is free and unencumbered software released into the public domain.
|
|
|
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
|
software, either in source code form or as a compiled binary, for any purpose,
|
|
commercial or non-commercial, and by any means.
|
|
|
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
|
software dedicate any and all copyright interest in the software to the public
|
|
domain. We make this dedication for the benefit of the public at large and to
|
|
the detriment of our heirs and successors. We intend this dedication to be an
|
|
overt act of relinquishment in perpetuity of all present and future rights to
|
|
this software under copyright law.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
------------------------------------------------------------------------------
|
|
*/ |