1
0
mirror of https://github.com/lammertb/libhttp.git synced 2025-08-09 03:22:45 +03:00

Improved error and memory handling

This commit is contained in:
Lammert Bies
2017-01-01 00:39:14 +01:00
parent 6b253f14d2
commit 52a8a20d2f
9 changed files with 65 additions and 56 deletions

View File

@@ -904,8 +904,8 @@ LIBHTTP_API int httplib_atomic_inc( volatile int *addr );
LIBHTTP_API int httplib_base64_encode( const unsigned char *src, int src_len, char *dst, int dst_len ); LIBHTTP_API int httplib_base64_encode( const unsigned char *src, int src_len, char *dst, int dst_len );
LIBHTTP_API unsigned httplib_check_feature( unsigned feature ); LIBHTTP_API unsigned httplib_check_feature( unsigned feature );
LIBHTTP_API int httplib_closedir( DIR *dir ); LIBHTTP_API int httplib_closedir( DIR *dir );
LIBHTTP_API struct httplib_connection * httplib_connect_client( struct httplib_context *ctx, const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size ); LIBHTTP_API struct httplib_connection * httplib_connect_client( struct httplib_context *ctx, const char *host, int port, int use_ssl );
LIBHTTP_API struct httplib_connection * httplib_connect_client_secure( struct httplib_context *ctx, const struct httplib_client_options *client_options, char *error_buffer, size_t error_buffer_size ); LIBHTTP_API struct httplib_connection * httplib_connect_client_secure( struct httplib_context *ctx, const struct httplib_client_options *client_options );
LIBHTTP_API struct httplib_connection * httplib_connect_websocket_client( struct httplib_context *ctx, const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, httplib_websocket_data_handler data_func, httplib_websocket_close_handler close_func, void *user_data ); LIBHTTP_API struct httplib_connection * httplib_connect_websocket_client( struct httplib_context *ctx, const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, httplib_websocket_data_handler data_func, httplib_websocket_close_handler close_func, void *user_data );
LIBHTTP_API struct httplib_context * httplib_create_client_context( const struct httplib_callbacks *callbacks, const struct httplib_option_t *options ); LIBHTTP_API struct httplib_context * httplib_create_client_context( const struct httplib_callbacks *callbacks, const struct httplib_option_t *options );
LIBHTTP_API void httplib_cry( enum debug_level_t debug_level, const struct httplib_context *ctx, const struct httplib_connection *conn, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(4, 5); LIBHTTP_API void httplib_cry( enum debug_level_t debug_level, const struct httplib_context *ctx, const struct httplib_connection *conn, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(4, 5);

View File

@@ -30,7 +30,7 @@
#include "httplib_ssl.h" #include "httplib_ssl.h"
#include "httplib_string.h" #include "httplib_string.h"
static struct httplib_connection * httplib_connect_client_impl( struct httplib_context *ctx, const struct httplib_client_options *client_options, int use_ssl, char *ebuf, size_t ebuf_len ); static struct httplib_connection * httplib_connect_client_impl( struct httplib_context *ctx, const struct httplib_client_options *client_options, int use_ssl );
/* /*
* struct httplib_connection *httplib_connect_client_secure( struct httplib_context_*ctx, const struct httplib_client_options *client options, char *error buffer, size_t error_buffer_size ); * struct httplib_connection *httplib_connect_client_secure( struct httplib_context_*ctx, const struct httplib_client_options *client options, char *error buffer, size_t error_buffer_size );
@@ -40,20 +40,20 @@ static struct httplib_connection * httplib_connect_client_impl( struct httplib_c
* information, or NULL if an error occured. * information, or NULL if an error occured.
*/ */
LIBHTTP_API struct httplib_connection *httplib_connect_client_secure( struct httplib_context *ctx, const struct httplib_client_options *client_options, char *error_buffer, size_t error_buffer_size ) { LIBHTTP_API struct httplib_connection *httplib_connect_client_secure( struct httplib_context *ctx, const struct httplib_client_options *client_options ) {
return httplib_connect_client_impl( ctx, client_options, 1, error_buffer, error_buffer_size ); return httplib_connect_client_impl( ctx, client_options, true );
} /* httplib_connect_client_secure */ } /* httplib_connect_client_secure */
/* /*
* struct httplib_connection *httplib_connect_client( struct httplib_context *ctx, const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size ); * struct httplib_connection *httplib_connect_client( struct httplib_context *ctx, const char *host, int port, int use_ssl );
* *
* The function httplib_connect_client() connects to a remote server as a client * The function httplib_connect_client() connects to a remote server as a client
* with the options of the connection provided as parameters. * with the options of the connection provided as parameters.
*/ */
struct httplib_connection *httplib_connect_client( struct httplib_context *ctx, const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size ) { struct httplib_connection *httplib_connect_client( struct httplib_context *ctx, const char *host, int port, int use_ssl ) {
struct httplib_client_options opts; struct httplib_client_options opts;
@@ -61,20 +61,20 @@ struct httplib_connection *httplib_connect_client( struct httplib_context *ctx,
opts.host = host; opts.host = host;
opts.port = port; opts.port = port;
return httplib_connect_client_impl( ctx, &opts, use_ssl, error_buffer, error_buffer_size ); return httplib_connect_client_impl( ctx, &opts, use_ssl );
} /* httplib_connect_client */ } /* httplib_connect_client */
/* /*
* static struct httplib_connection *httplib_connect_client_impl( struct httplib_context *ctx, const struct httplib_client_options *client_options, int use_ssl, char *ebuf, size_t ebuf_len ); * static struct httplib_connection *httplib_connect_client_impl( struct httplib_context *ctx, const struct httplib_client_options *client_options, int use_ssl );
* *
* The function httplib_connect_client_impl() is the background function doing the * The function httplib_connect_client_impl() is the background function doing the
* heavy lifting to make connections as a client to remote servers. * heavy lifting to make connections as a client to remote servers.
*/ */
static struct httplib_connection *httplib_connect_client_impl( struct httplib_context *ctx, const struct httplib_client_options *client_options, int use_ssl, char *ebuf, size_t ebuf_len ) { static struct httplib_connection *httplib_connect_client_impl( struct httplib_context *ctx, const struct httplib_client_options *client_options, int use_ssl ) {
struct httplib_connection *conn; struct httplib_connection *conn;
SOCKET sock; SOCKET sock;
@@ -85,18 +85,18 @@ static struct httplib_connection *httplib_connect_client_impl( struct httplib_co
if ( ctx == NULL ) return NULL; if ( ctx == NULL ) return NULL;
if ( ! XX_httplib_connect_socket( client_options->host, client_options->port, use_ssl, ebuf, ebuf_len, &sock, &sa ) ) return NULL; if ( ! XX_httplib_connect_socket( ctx, client_options->host, client_options->port, use_ssl, &sock, &sa ) ) return NULL;
if ( (conn = httplib_calloc( 1, sizeof(*conn) + MAX_REQUEST_SIZE )) == NULL ) { if ( (conn = httplib_calloc( 1, sizeof(*conn) + MAX_REQUEST_SIZE )) == NULL ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "calloc(): %s", httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, NULL, "%s (%u): calloc(): %s", __func__, __LINE__, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
closesocket( sock ); closesocket( sock );
} }
#ifndef NO_SSL #ifndef NO_SSL
else if ( use_ssl && (conn->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL ) { else if ( use_ssl && (conn->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "SSL_CTX_new error" ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%u): SSL_CTX_new error", __func__, __LINE__ );
closesocket( sock ); closesocket( sock );
conn = httplib_free( conn ); conn = httplib_free( conn );
} }
@@ -113,7 +113,7 @@ static struct httplib_connection *httplib_connect_client_impl( struct httplib_co
conn->client.sock = sock; conn->client.sock = sock;
conn->client.lsa = sa; conn->client.lsa = sa;
if ( getsockname( sock, psa, &len ) != 0 ) httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s: getsockname() failed: %s", __func__, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) ); if ( getsockname( sock, psa, &len ) != 0 ) httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%u): getsockname() failed: %s", __func__, __LINE__, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
conn->client.has_ssl = (use_ssl) ? true : false; conn->client.has_ssl = (use_ssl) ? true : false;
httplib_pthread_mutex_init( &conn->mutex, &XX_httplib_pthread_mutex_attr ); httplib_pthread_mutex_init( &conn->mutex, &XX_httplib_pthread_mutex_attr );
@@ -137,7 +137,7 @@ static struct httplib_connection *httplib_connect_client_impl( struct httplib_co
if ( ! XX_httplib_ssl_use_pem_file( ctx, client_options->client_cert ) ) { if ( ! XX_httplib_ssl_use_pem_file( ctx, client_options->client_cert ) ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "Can not use SSL client certificate" ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%u): can not use SSL client certificate", __func__, __LINE__ );
SSL_CTX_free( conn->client_ssl_ctx ); SSL_CTX_free( conn->client_ssl_ctx );
closesocket( sock ); closesocket( sock );
conn = httplib_free( conn ); conn = httplib_free( conn );
@@ -154,7 +154,7 @@ static struct httplib_connection *httplib_connect_client_impl( struct httplib_co
if ( ! XX_httplib_sslize( conn, conn->client_ssl_ctx, SSL_connect ) ) { if ( ! XX_httplib_sslize( conn, conn->client_ssl_ctx, SSL_connect ) ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "SSL connection error" ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%u): SSL connection error", __func__, __LINE__ );
SSL_CTX_free( conn->client_ssl_ctx ); SSL_CTX_free( conn->client_ssl_ctx );
closesocket( sock ); closesocket( sock );
conn = httplib_free( conn ); conn = httplib_free( conn );

View File

@@ -41,26 +41,26 @@
* has been established. * has been established.
*/ */
bool XX_httplib_connect_socket( const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len, SOCKET *sock, union usa *sa ) { bool XX_httplib_connect_socket( struct httplib_context *ctx, const char *host, int port, int use_ssl, SOCKET *sock, union usa *sa ) {
int ip_ver; int ip_ver;
char error_string[ERROR_STRING_LEN]; char error_string[ERROR_STRING_LEN];
if ( ctx == NULL ) return false;
ip_ver = 0; ip_ver = 0;
*sock = INVALID_SOCKET; *sock = INVALID_SOCKET;
memset( sa, 0, sizeof(*sa) ); memset( sa, 0, sizeof(*sa) );
if ( ebuf_len > 0 ) *ebuf = 0;
if ( host == NULL ) { if ( host == NULL ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "%s", "NULL host" ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, NULL, "%s (%u): NULL host", __func__, __LINE__ );
return false; return false;
} }
if ( port < 0 || ! XX_httplib_is_valid_port( (unsigned)port) ) { if ( port < 0 || ! XX_httplib_is_valid_port( (unsigned)port) ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "%s", "invalid port" ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, NULL, "%s (%u): invalid port", __func__, __LINE__ );
return false; return false;
} }
@@ -68,7 +68,7 @@ bool XX_httplib_connect_socket( const char *host, int port, int use_ssl, char *e
if ( use_ssl && SSLv23_client_method == NULL ) { if ( use_ssl && SSLv23_client_method == NULL ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "%s", "SSL is not initialized" ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, NULL, "%s (%u): SSL is not initialized", __func__, __LINE__ );
return false; return false;
} }
#else /* NO_SSL */ #else /* NO_SSL */
@@ -112,7 +112,7 @@ bool XX_httplib_connect_socket( const char *host, int port, int use_ssl, char *e
if ( ip_ver == 0 ) { if ( ip_ver == 0 ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "%s", "host not found" ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, NULL, "%s (%u): host not found", __func__, __LINE__ );
return false; return false;
} }
@@ -121,8 +121,8 @@ bool XX_httplib_connect_socket( const char *host, int port, int use_ssl, char *e
if ( *sock == INVALID_SOCKET ) { if ( *sock == INVALID_SOCKET ) {
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "socket(): %s", httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, NULL, "%s (%u): socket(): %s", __func__, __LINE__, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
return 0; return false;
} }
XX_httplib_set_close_on_exec( *sock ); XX_httplib_set_close_on_exec( *sock );
@@ -134,7 +134,7 @@ bool XX_httplib_connect_socket( const char *host, int port, int use_ssl, char *e
* Not connected * Not connected
*/ */
XX_httplib_snprintf( NULL, NULL, ebuf, ebuf_len, "connect(%s:%d): %s", host, port, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) ); httplib_cry( DEBUG_LEVEL_ERROR, ctx, NULL, "%s (%u): connect(%s:%d): %s", __func__, __LINE__, host, port, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
closesocket( *sock ); closesocket( *sock );
*sock = INVALID_SOCKET; *sock = INVALID_SOCKET;

View File

@@ -43,7 +43,13 @@ struct httplib_connection *httplib_connect_websocket_client( struct httplib_cont
static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw=="; static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
const char *handshake_req; const char *handshake_req;
conn = NULL; if ( ctx == NULL ) return NULL;
if ( ctx->status != CTX_STATUS_TERMINATED ) {
httplib_cry( DEBUG_LEVEL_CRASH, ctx, NULL, "%s (%u): client context not in terminated state", __func__, __LINE__ );
return NULL;
}
if ( origin != NULL ) handshake_req = "GET %s HTTP/1.1\r\n" if ( origin != NULL ) handshake_req = "GET %s HTTP/1.1\r\n"
"Host: %s\r\n" "Host: %s\r\n"
@@ -67,23 +73,16 @@ struct httplib_connection *httplib_connect_websocket_client( struct httplib_cont
*/ */
conn = httplib_download( ctx, host, port, use_ssl, error_buffer, error_buffer_size, handshake_req, path, host, magic, origin ); conn = httplib_download( ctx, host, port, use_ssl, error_buffer, error_buffer_size, handshake_req, path, host, magic, origin );
if ( conn == NULL ) {
/* httplib_cry( DEBUG_LEVEL_ERROR, ctx, NULL, "%s (%u): Init of download failed", __func__, __LINE__ );
* Connection object will be null if something goes wrong return NULL;
*/
if ( conn == NULL || strcmp( conn->request_info.request_uri, "101" ) ) {
if ( ! *error_buffer ) {
/*
* if there is a connection, but it did not return 101,
* error_buffer is not yet set
*/
XX_httplib_snprintf( conn, NULL, error_buffer, error_buffer_size, "Unexpected server reply" );
} }
if ( strcmp( conn->request_info.request_uri, "101" ) ) {
httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%u): unexpected server reply \"%s\"", __func__, __LINE__, conn->request_info.request_uri );
conn = httplib_free( conn ); conn = httplib_free( conn );
return NULL; return NULL;
} }
@@ -95,6 +94,8 @@ struct httplib_connection *httplib_connect_websocket_client( struct httplib_cont
if ( ctx->workerthreadids == NULL ) { if ( ctx->workerthreadids == NULL ) {
httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%u): out of memory allocating worker thread IDs", __func__, __LINE__ );
ctx->num_threads = 0; ctx->num_threads = 0;
ctx->user_data = NULL; ctx->user_data = NULL;
conn = httplib_free( conn ); conn = httplib_free( conn );
@@ -106,6 +107,8 @@ struct httplib_connection *httplib_connect_websocket_client( struct httplib_cont
if ( thread_data == NULL ) { if ( thread_data == NULL ) {
httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%u): out of memory allocating thread data", __func__, __LINE__ );
ctx->workerthreadids = httplib_free( ctx->workerthreadids ); ctx->workerthreadids = httplib_free( ctx->workerthreadids );
ctx->num_threads = 0; ctx->num_threads = 0;
ctx->user_data = NULL; ctx->user_data = NULL;
@@ -127,11 +130,13 @@ struct httplib_connection *httplib_connect_websocket_client( struct httplib_cont
if ( XX_httplib_start_thread_with_id( XX_httplib_websocket_client_thread, thread_data, ctx->workerthreadids) != 0 ) { if ( XX_httplib_start_thread_with_id( XX_httplib_websocket_client_thread, thread_data, ctx->workerthreadids) != 0 ) {
httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%u): thread failed to start", __func__, __LINE__ );
thread_data = httplib_free( thread_data ); thread_data = httplib_free( thread_data );
ctx->workerthreadids = httplib_free( ctx->workerthreadids ); ctx->workerthreadids = httplib_free( ctx->workerthreadids );
conn = httplib_free( conn );
ctx->num_threads = 0; ctx->num_threads = 0;
ctx->user_data = NULL; ctx->user_data = NULL;
conn = httplib_free( conn );
return NULL; return NULL;
} }

View File

@@ -54,6 +54,7 @@ struct httplib_context *httplib_create_client_context( const struct httplib_call
ctx->callbacks.exit_context = exit_callback; ctx->callbacks.exit_context = exit_callback;
ctx->ctx_type = CTX_TYPE_CLIENT; ctx->ctx_type = CTX_TYPE_CLIENT;
ctx->status = CTX_STATUS_TERMINATED;
return ctx; return ctx;

View File

@@ -38,6 +38,7 @@ void httplib_destroy_client_context( struct httplib_context *ctx ) {
XX_httplib_free_config_options( ctx ); XX_httplib_free_config_options( ctx );
ctx->workerthreadids = httplib_free( ctx->workerthreadids );
ctx = httplib_free( ctx ); ctx = httplib_free( ctx );
} /* httplib_destroy_client_context */ } /* httplib_destroy_client_context */

View File

@@ -47,13 +47,13 @@ struct httplib_connection * httplib_download( struct httplib_context *ctx, const
va_start( ap, fmt ); va_start( ap, fmt );
ebuf[0] = '\0'; ebuf[0] = '\0';
conn = httplib_connect_client( ctx, host, port, use_ssl, ebuf, ebuf_len ); conn = httplib_connect_client( ctx, host, port, use_ssl );
if ( conn != NULL ) { if ( conn != NULL ) {
i = XX_httplib_vprintf( conn, fmt, ap ); i = XX_httplib_vprintf( conn, fmt, ap );
if (i <= 0) XX_httplib_snprintf( conn, NULL, ebuf, ebuf_len, "%s", "Error sending request" ); if ( i <= 0 ) httplib_cry( DEBUG_LEVEL_ERROR, ctx, conn, "%s (%d): error sending request", __func__, __LINE__ );
else { else {
XX_httplib_getreq( conn, ebuf, ebuf_len, &reqerr ); XX_httplib_getreq( conn, ebuf, ebuf_len, &reqerr );
@@ -67,11 +67,13 @@ struct httplib_connection * httplib_download( struct httplib_context *ctx, const
} }
} }
else i = 0;
/* /*
* if an error occured, close the connection * if an error occured, close the connection
*/ */
if ( ebuf[0] != '\0' && conn != NULL ) { if ( i <= 0 && conn != NULL ) {
httplib_close_connection( conn ); httplib_close_connection( conn );
conn = NULL; conn = NULL;

View File

@@ -797,7 +797,7 @@ void XX_httplib_close_all_listening_sockets( struct httplib_context *ctx );
void XX_httplib_close_connection( struct httplib_connection *conn ); void XX_httplib_close_connection( struct httplib_connection *conn );
void XX_httplib_close_socket_gracefully( struct httplib_connection *conn ); void XX_httplib_close_socket_gracefully( struct httplib_connection *conn );
int WINCDECL XX_httplib_compare_dir_entries( const void *p1, const void *p2 ); int WINCDECL XX_httplib_compare_dir_entries( const void *p1, const void *p2 );
bool XX_httplib_connect_socket( const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len, SOCKET *sock, union usa *sa ); bool XX_httplib_connect_socket( struct httplib_context *ctx, const char *host, int port, int use_ssl, SOCKET *sock, union usa *sa );
void XX_httplib_construct_etag( char *buf, size_t buf_len, const struct file *filep ); void XX_httplib_construct_etag( char *buf, size_t buf_len, const struct file *filep );
int XX_httplib_consume_socket( struct httplib_context *ctx, struct socket *sp, int thread_index ); int XX_httplib_consume_socket( struct httplib_context *ctx, struct socket *sp, int thread_index );
void XX_httplib_delete_file( struct httplib_connection *conn, const char *path ); void XX_httplib_delete_file( struct httplib_connection *conn, const char *path );

View File

@@ -39,24 +39,24 @@ LIBHTTP_THREAD XX_httplib_websocket_client_thread( void *data ) {
struct websocket_client_thread_data *cdata; struct websocket_client_thread_data *cdata;
cdata = data; cdata = data;
if ( cdata == NULL || cdata->conn == NULL || cdata->conn->ctx == NULL ) return LIBHTTP_THREAD_RETNULL;
cdata->conn->ctx->status = CTX_STATUS_RUNNING;
XX_httplib_set_thread_name( "ws-client" ); XX_httplib_set_thread_name( "ws-client" );
if ( cdata->conn->ctx != NULL ) {
/*
* 3 indicates a websocket client thread
* TODO: check if conn->ctx can be set
*/
if ( cdata->conn->ctx->callbacks.init_thread != NULL ) cdata->conn->ctx->callbacks.init_thread( cdata->conn->ctx, 3 ); if ( cdata->conn->ctx->callbacks.init_thread != NULL ) cdata->conn->ctx->callbacks.init_thread( cdata->conn->ctx, 3 );
}
XX_httplib_read_websocket( cdata->conn, cdata->data_handler, cdata->callback_data ); XX_httplib_read_websocket( cdata->conn, cdata->data_handler, cdata->callback_data );
if ( cdata->close_handler != NULL ) cdata->close_handler( cdata->conn, cdata->callback_data ); if ( cdata->close_handler != NULL ) cdata->close_handler( cdata->conn, cdata->callback_data );
cdata->conn->ctx->workerthreadids = httplib_free( cdata->conn->ctx->workerthreadids );
cdata->conn = httplib_free( cdata->conn );
cdata = httplib_free( cdata ); cdata = httplib_free( cdata );
cdata->conn->ctx->user_data = NULL;
cdata->conn->ctx->num_threads = 0;
cdata->conn->ctx->status = CTX_STATUS_TERMINATED;
return LIBHTTP_THREAD_RETNULL; return LIBHTTP_THREAD_RETNULL;