From 06ab21048b39023f66c93b09094dec16e7eacdea Mon Sep 17 00:00:00 2001 From: Lammert Bies Date: Tue, 20 Dec 2016 15:00:53 +0100 Subject: [PATCH] Layout fixed and one send_file function --- RELEASE_NOTES.md | 1 + doc/APIReference.md | 4 +- doc/api/httplib_get_builtin_mime_type.md | 3 +- doc/api/httplib_send_file.md | 6 +- include/libhttp.h | 37 +++---- src/httplib_main.h | 3 +- src/httplib_refresh_trust.c | 52 +++++----- src/httplib_remove_directory.c | 108 +++++++++++++-------- src/httplib_remove_double_dots.c | 42 +++++--- src/httplib_reset_per_request_attributes.c | 2 +- src/httplib_send_file.c | 52 +++++----- src/httplib_set_non_blocking_mode.c | 30 +++--- src/httplib_skip.c | 12 ++- src/httplib_snprintf.c | 16 ++- src/httplib_ssl_locking_callback.c | 6 +- src/httplib_start_thread.c | 53 +++++----- src/httplib_strcasestr.c | 2 +- src/httplib_websocket_client_thread.c | 45 +++++---- src/httplib_websocket_client_write.c | 53 ++++++---- src/httplib_websocket_write.c | 2 +- src/httplib_websocket_write_exec.c | 103 +++++++++++++------- src/httplib_write.c | 77 +++++++++------ 22 files changed, 412 insertions(+), 297 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f847aa48..4fef2415 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,7 @@ Release Notes v2.0 (work in progress) Changes ------- +- Combined three send file functions into `httplib_send_file()`. - Memory allocation debugging can be switched on and off dynamically - Memory allocation functions are available for the main application - Full API documentation now available at [`www.libhttp.org/api-reference/`](http://www.libhttp.org/api-reference/) diff --git a/doc/APIReference.md b/doc/APIReference.md index 91785483..93ebfaea 100644 --- a/doc/APIReference.md +++ b/doc/APIReference.md @@ -52,9 +52,7 @@ LibHTTP is often used as HTTP and HTTPS library inside a larger application. A * [`httplib_handle_form_request( conn, fdh );`](api/httplib_handle_form_request.md) * [`httplib_printf( conn, fmt, ... );`](api/httplib_printf.md) * [`httplib_read( conn, buf, len );`](api/httplib_read.md) -* [`httplib_send_file( conn, path );`](api/httplib_send_file.md) -* [`httplib_send_mime_file( conn, path, mime_type );`](api/httplib_send_mime_file.md) -* [`httplib_send_mime_file2( conn, path, mime_type, additional_headers );`](api/httplib_send_mime_file2.md) +* [`httplib_send_file( conn, path, mime_type, additional_headers );`](api/httplib_send_mime_file2.md) * [`httplib_set_request_handler( ctx, uri, handler, cbdata );`](api/httplib_set_request_handler.md) * [`httplib_set_user_connection_data( conn, data );`](api/httplib_set_user_connection_data.md) * [`httplib_store_body( conn, path );`](api/httplib_store_body.md) diff --git a/doc/api/httplib_get_builtin_mime_type.md b/doc/api/httplib_get_builtin_mime_type.md index 05e126bb..12124993 100644 --- a/doc/api/httplib_get_builtin_mime_type.md +++ b/doc/api/httplib_get_builtin_mime_type.md @@ -24,5 +24,4 @@ The function uses an efficient binary search algorithm, but this has implication ### See Also -* [`httplib_send_mime_file();`](httplib_send_mime_file.md) -* [`httplib_send_mime_file2();`](httplib_send_mime_file2.md) +* [`httplib_send_file();`](httplib_send_file.md) diff --git a/doc/api/httplib_send_file.md b/doc/api/httplib_send_file.md index 870d3605..7a54fe6a 100644 --- a/doc/api/httplib_send_file.md +++ b/doc/api/httplib_send_file.md @@ -1,6 +1,6 @@ # LibHTTP API Reference -### `httplib_send_mime_file2( conn, path, mime_type, additional_headers );` +### `httplib_send_file( conn, path, mime_type, additional_headers );` ### Parameters @@ -17,7 +17,7 @@ ### Description -The function `httplib_send_mime_file2()` can be used to send a file over a connection. The function is similar to [`httplib_send_mime_file()`](httplib_send_mime_file.md) with the additional functionality that user specified headers can be sent. The MIME type of the file can be specified in the function call, or will be automatically determined based on the extension of the filename if the `mime_type` parameter has the value NULL. +The function `httplib_send_file()` can be used to send a file over a connection. The MIME type of the file can be specified in the function call, or will be automatically determined based on the extension of the filename if the `mime_type` parameter has the value NULL. Additional custom header fields can be added as a parameter. Please make sure that these header names begin with `X-` to prevent name clashes with other headers. If the `additional_headers` parameter is NULL, no custom headers will be added. @@ -25,6 +25,4 @@ Additional custom header fields can be added as a parameter. Please make sure th * [`httplib_get_builtin_mime_type();`](httplib_get_builtin_mime_type.md) * [`httplib_printf();`](httplib_printf.md) -* [`httplib_send_file();`](httplib_send_file.md) -* [`httplib_send_mime_file();`](httplib_send_mime_file.md) * [`httplib_write();`](httplib_write.md) diff --git a/include/libhttp.h b/include/libhttp.h index c4142eec..9a750ba7 100644 --- a/include/libhttp.h +++ b/include/libhttp.h @@ -589,30 +589,6 @@ enum { LIBHTTP_API int httplib_printf(struct httplib_connection *, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); -/* Send contents of the entire file together with HTTP headers. */ -LIBHTTP_API void httplib_send_file(struct httplib_connection *conn, const char *path); - -/* Send contents of the entire file together with HTTP headers. - Parameters: - conn: Current connection information. - path: Full path to the file to send. - mime_type: Content-Type for file. NULL will cause the type to be - looked up by the file extension. -*/ -LIBHTTP_API void httplib_send_mime_file(struct httplib_connection *conn, const char *path, const char *mime_type); - -/* Send contents of the entire file together with HTTP headers. - Parameters: - conn: Current connection information. - path: Full path to the file to send. - mime_type: Content-Type for file. NULL will cause the type to be - looked up by the file extension. - additional_headers: Additional custom header fields appended to the header. - Each header must start with an X- to ensure it is not - included twice. - NULL does not append anything. -*/ -LIBHTTP_API void httplib_send_mime_file2(struct httplib_connection *conn, const char *path, const char *mime_type, const char *additional_headers); /* Store body data into a file. */ LIBHTTP_API int64_t httplib_store_body(struct httplib_connection *conn, const char *path); @@ -964,6 +940,18 @@ LIBHTTP_API int httplib_get_response(struct httplib_connection *conn, char *ebuf */ LIBHTTP_API unsigned httplib_check_feature(unsigned feature); +#ifndef LIBHTTP_THREAD + +#if defined(_WIN32) +#define LIBHTTP_THREAD unsigned __stcall +#define LIBHTTP_THREAD_RETNULL 0 +#else /* _WIN32 */ +#define LIBHTTP_THREAD void * +#define LIBHTTP_THREAD_RETNULL NULL +#endif /* _WIN32 */ + +#endif /* LIBHTTP_THREAD */ + typedef void (*httplib_alloc_callback_func)( const char *file, unsigned line, const char *action, int64_t current_bytes, int64_t total_blocks, int64_t total_bytes ); #define httplib_calloc(a, b) XX_httplib_calloc_ex(a, b, __FILE__, __LINE__) @@ -1001,6 +989,7 @@ LIBHTTP_API pthread_t httplib_pthread_self( void ); LIBHTTP_API int httplib_pthread_setspecific( pthread_key_t key, const void *value ); LIBHTTP_API struct dirent * httplib_readdir( DIR *dir ); LIBHTTP_API int httplib_remove( const char *path ); +LIBHTTP_API void httplib_send_file( struct httplib_connection *conn, const char *path, const char *mime_type, const char *additional_headers ); LIBHTTP_API void httplib_set_alloc_callback_func( httplib_alloc_callback_func log_func ); LIBHTTP_API int httplib_strcasecmp( const char *s1, const char *s2 ); LIBHTTP_API const char * httplib_strcasestr( const char *big_str, const char *small_str ); diff --git a/src/httplib_main.h b/src/httplib_main.h index e8d044a4..072e0a3f 100644 --- a/src/httplib_main.h +++ b/src/httplib_main.h @@ -904,6 +904,7 @@ pid_t XX_httplib_spawn_process( struct httplib_connection *conn, const char *p int XX_httplib_stat( struct httplib_connection *conn, const char *path, struct file *filep ); int XX_httplib_substitute_index_file( struct httplib_connection *conn, char *path, size_t path_len, struct file *filep ); const char * XX_httplib_suggest_connection_header( const struct httplib_connection *conn ); +LIBHTTP_THREAD XX_httplib_websocket_client_thread( void *data ); int XX_httplib_websocket_write_exec( struct httplib_connection *conn, int opcode, const char *data, size_t dataLen, uint32_t masking_key ); @@ -926,14 +927,12 @@ void md5_finish( md5_state_t *pms, md5_byte_t digest[16] ); #ifdef _WIN32 unsigned __stdcall XX_httplib_master_thread( void *thread_func_param ); int XX_httplib_start_thread_with_id( unsigned(__stdcall *f)(void *), void *p, pthread_t *threadidptr ); -unsigned __stdcall XX_httplib_websocket_client_thread( void *data ); unsigned __stdcall XX_httplib_worker_thread( void *thread_func_param ); extern struct pthread_mutex_undefined_struct * XX_httplib_pthread_mutex_attr; #else /* _WIN32 */ void * XX_httplib_master_thread( void *thread_func_param ); int XX_httplib_start_thread_with_id( httplib_thread_func_t func, void *param, pthread_t *threadidptr ); -void * XX_httplib_websocket_client_thread( void *data ); void * XX_httplib_worker_thread( void *thread_func_param ); extern pthread_mutexattr_t XX_httplib_pthread_mutex_attr; diff --git a/src/httplib_refresh_trust.c b/src/httplib_refresh_trust.c index 053b7c17..03e3650f 100644 --- a/src/httplib_refresh_trust.c +++ b/src/httplib_refresh_trust.c @@ -22,13 +22,16 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 1.9 */ #include "httplib_main.h" #include "httplib_ssl.h" #include "httplib_utils.h" +static volatile int reload_lock = 0; +static long int data_check = 0; + /* * int XX_httplib_refresh_trust( struct httplib_connection *conn ); * @@ -40,35 +43,32 @@ int XX_httplib_refresh_trust( struct httplib_connection *conn ) { - static int reload_lock = 0; - static long int data_check = 0; - volatile int *p_reload_lock = (volatile int *)&reload_lock; - + volatile int *p_reload_lock; struct stat cert_buf; long int t; char *pem; int should_verify_peer; - if ((pem = conn->ctx->config[SSL_CERTIFICATE]) == NULL - && conn->ctx->callbacks.init_ssl == NULL) { - return 0; - } + p_reload_lock = & reload_lock; - t = data_check; - if (stat(pem, &cert_buf) != -1) t = (long int)cert_buf.st_mtime; + pem = conn->ctx->config[SSL_CERTIFICATE]; + if ( pem == NULL && conn->ctx->callbacks.init_ssl == NULL ) return 0; + + if ( stat( pem, &cert_buf ) != -1 ) t = (long int)cert_buf.st_mtime; + else t = data_check; + + if ( data_check != t ) { - if (data_check != t) { data_check = t; - should_verify_peer = - (conn->ctx->config[SSL_DO_VERIFY_PEER] != NULL) - && (httplib_strcasecmp(conn->ctx->config[SSL_DO_VERIFY_PEER], "yes") - == 0); + should_verify_peer = conn->ctx->config[SSL_DO_VERIFY_PEER] != NULL && ! httplib_strcasecmp( conn->ctx->config[SSL_DO_VERIFY_PEER], "yes" ); + + if ( should_verify_peer ) { - if (should_verify_peer) { char *ca_path = conn->ctx->config[SSL_CA_PATH]; char *ca_file = conn->ctx->config[SSL_CA_FILE]; - if (SSL_CTX_load_verify_locations(conn->ctx->ssl_ctx, ca_file, ca_path) != 1) { + + if ( SSL_CTX_load_verify_locations( conn->ctx->ssl_ctx, ca_file, ca_path ) != 1 ) { httplib_cry( XX_httplib_fc(conn->ctx), "SSL_CTX_load_verify_locations error: %s " @@ -76,18 +76,24 @@ int XX_httplib_refresh_trust( struct httplib_connection *conn ) { "either ssl_ca_path or ssl_ca_file. Is any of them " "present in " "the .conf file?", - XX_httplib_ssl_error()); + XX_httplib_ssl_error() ); + return 0; } } - if (1 == httplib_atomic_inc(p_reload_lock)) { - if (XX_httplib_ssl_use_pem_file(conn->ctx, pem) == 0) return 0; + if ( httplib_atomic_inc( p_reload_lock ) == 1 ) { + + if ( XX_httplib_ssl_use_pem_file( conn->ctx, pem ) == 0 ) return 0; *p_reload_lock = 0; } } - /* lock while cert is reloading */ - while (*p_reload_lock) sleep(1); + + /* + * lock while cert is reloading + */ + + while ( *p_reload_lock ) sleep( 1 ); return 1; diff --git a/src/httplib_remove_directory.c b/src/httplib_remove_directory.c index a1a95534..6559161b 100644 --- a/src/httplib_remove_directory.c +++ b/src/httplib_remove_directory.c @@ -22,12 +22,19 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #include "httplib_string.h" +/* + * int XX_httplib_remove_directory( struct httplib_connection *conn, const char *dir ); + * + * The function XX_httplib_remove_directory() removes recirsively a directory + * tree. + */ + int XX_httplib_remove_directory( struct httplib_connection *conn, const char *dir ) { char path[PATH_MAX]; @@ -35,53 +42,76 @@ int XX_httplib_remove_directory( struct httplib_connection *conn, const char *di DIR *dirp; struct de de; int truncated; - int ok = 1; + int ok; - if ((dirp = httplib_opendir( dir )) == NULL) { - return 0; - } else { - de.conn = conn; + ok = 1; - while ((dp = httplib_readdir(dirp)) != NULL) { - /* Do not show current dir (but show hidden files as they will - * also be removed) */ - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; + dirp = httplib_opendir( dir ); + if ( dirp == NULL ) return 0; + + de.conn = conn; - XX_httplib_snprintf( conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name); + while ( (dp = httplib_readdir( dirp )) != NULL ) { - /* If we don't memset stat structure to zero, mtime will have - * garbage and strftime() will segfault later on in - * XX_httplib_print_dir_entry(). memset is required only if XX_httplib_stat() - * fails. For more details, see - * http://code.google.com/p/mongoose/issues/detail?id=79 */ - memset(&de.file, 0, sizeof(de.file)); + /* + * Do not show current dir (but show hidden files as they will + * also be removed) + */ - if (truncated) { - /* Do not delete anything shorter */ - ok = 0; - continue; - } + if ( ! strcmp( dp->d_name, "." ) || ! strcmp( dp->d_name, ".." ) ) continue; - if (!XX_httplib_stat(conn, path, &de.file)) { - httplib_cry(conn, "%s: XX_httplib_stat(%s) failed: %s", __func__, path, strerror(ERRNO)); - ok = 0; - } - if (de.file.membuf == NULL) { - /* file is not in memory */ - if (de.file.is_directory) { - if (XX_httplib_remove_directory(conn, path) == 0) ok = 0; - } else { - if (httplib_remove( path ) == 0) ok = 0; - } - } else { - /* file is in memory. It can not be deleted. */ - ok = 0; - } + XX_httplib_snprintf( conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name ); + + /* + * If we don't memset stat structure to zero, mtime will have + * garbage and strftime() will segfault later on in + * XX_httplib_print_dir_entry(). memset is required only if XX_httplib_stat() + * fails. For more details, see + * http://code.google.com/p/mongoose/issues/detail?id=79 + */ + + memset( & de.file, 0, sizeof(de.file) ); + + if ( truncated ) { + + /* + * Do not delete anything shorter + */ + + ok = 0; + continue; } - httplib_closedir(dirp); - IGNORE_UNUSED_RESULT(rmdir(dir)); + if ( ! XX_httplib_stat( conn, path, & de.file ) ) { + + httplib_cry( conn, "%s: XX_httplib_stat(%s) failed: %s", __func__, path, strerror(ERRNO) ); + ok = 0; + } + if ( de.file.membuf == NULL ) { + + /* + * file is not in memory + */ + + if ( de.file.is_directory ) { + + if ( XX_httplib_remove_directory( conn, path ) == 0 ) ok = 0; + } + + else if ( httplib_remove( path ) == 0 ) ok = 0; + } + + else { + /* + * file is in memory. It can not be deleted. + */ + + ok = 0; + } } + httplib_closedir( dirp ); + + rmdir( dir ); return ok; diff --git a/src/httplib_remove_double_dots.c b/src/httplib_remove_double_dots.c index 23e53c79..78729cb8 100644 --- a/src/httplib_remove_double_dots.c +++ b/src/httplib_remove_double_dots.c @@ -22,32 +22,46 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -/* Protect against directory disclosure attack by removing '..', - * excessive '/' and '\' characters */ +/* + * void XX_httplib_remove_double_dots_and_double_slashes( char *s ); + * + * The function XX_httplib_remove_double_dots_and_double_slashes() removes + * '..' instances and excessive '/' and `\` characters to protext against + * directory disclosure attacks. + */ + void XX_httplib_remove_double_dots_and_double_slashes( char *s ) { - char *p = s; + char *p; - while ((s[0] == '.') && (s[1] == '.')) s++; + p = s; + + while ( s[0] == '.' && s[1] == '.' ) s++; + + while ( *s != '\0' ) { - while (*s != '\0') { *p++ = *s++; - if (s[-1] == '/' || s[-1] == '\\') { - /* Skip all following slashes, backslashes and double-dots */ - while (s[0] != '\0') { - if (s[0] == '/' || s[0] == '\\') { - s++; - } else if (s[0] == '.' && s[1] == '.') { - s += 2; - } else break; + + if ( s[-1] == '/' || s[-1] == '\\' ) { + + /* + * Skip all following slashes, backslashes and double-dots + */ + + while ( s[0] != '\0' ) { + + if ( s[0] == '/' || s[0] == '\\' ) s++; + else if ( s[0] == '.' && s[1] == '.' ) s += 2; + else break; } } } + *p = '\0'; } /* XX_httplib_remove_double_dots_and_double_slashes */ diff --git a/src/httplib_reset_per_request_attributes.c b/src/httplib_reset_per_request_attributes.c index f90dfd0f..df06e755 100644 --- a/src/httplib_reset_per_request_attributes.c +++ b/src/httplib_reset_per_request_attributes.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" diff --git a/src/httplib_send_file.c b/src/httplib_send_file.c index 51f43082..1a604e8a 100644 --- a/src/httplib_send_file.c +++ b/src/httplib_send_file.c @@ -22,40 +22,36 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -void httplib_send_file( struct httplib_connection *conn, const char *path ) { +/* + * void httplib_send_file( struct httplib_connection *conn, const char *path, const char *mime_type, const char *additional_headers ); + * + * The function httplib_send_file() sends a file to the other peer. Optionally + * the MIME type and additional headers can be specified. + */ - httplib_send_mime_file( conn, path, NULL ); - -} /* httplib_send_file */ - - -void httplib_send_mime_file( struct httplib_connection *conn, const char *path, const char *mime_type ) { - - httplib_send_mime_file2( conn, path, mime_type, NULL ); - -} /* httplib_send_mime_file */ - - -void httplib_send_mime_file2( struct httplib_connection *conn, const char *path, const char *mime_type, const char *additional_headers ) { +void httplib_send_file( struct httplib_connection *conn, const char *path, const char *mime_type, const char *additional_headers ) { struct file file = STRUCT_FILE_INITIALIZER; - if (XX_httplib_stat(conn, path, &file)) { - if (file.is_directory) { - if ( conn == NULL ) return; - if (!httplib_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) { - XX_httplib_handle_directory_request(conn, path); - } else { - XX_httplib_send_http_error(conn, 403, "%s", "Error: Directory listing denied"); - } - } else { - XX_httplib_handle_static_file_request( conn, path, &file, mime_type, additional_headers); - } - } else XX_httplib_send_http_error(conn, 404, "%s", "Error: File not found"); + if ( XX_httplib_stat( conn, path, &file ) ) { -} /* httplib_send_mime_file2 */ + if ( file.is_directory ) { + + if ( conn == NULL ) return; + + if ( ! httplib_strcasecmp( conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes" ) ) XX_httplib_handle_directory_request( conn, path ); + + else XX_httplib_send_http_error( conn, 403, "%s", "Error: Directory listing denied" ); + } + + else XX_httplib_handle_static_file_request( conn, path, &file, mime_type, additional_headers ); + } + + else XX_httplib_send_http_error( conn, 404, "%s", "Error: File not found" ); + +} /* httplib_send_file */ diff --git a/src/httplib_set_non_blocking_mode.c b/src/httplib_set_non_blocking_mode.c index de2521a9..957ce5f1 100644 --- a/src/httplib_set_non_blocking_mode.c +++ b/src/httplib_set_non_blocking_mode.c @@ -22,31 +22,37 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" +/* + * int XX_httplib_set_non_block_mode( SOCKET sock ); + * + * The function XX_httplib_set_non_blocking_mode() is an internal function to + * set a socket in non blocking mode, independent of the platform where the + * program is running on. + */ + +int XX_httplib_set_non_blocking_mode( SOCKET sock ) { + #if defined(_WIN32) -int XX_httplib_set_non_blocking_mode( SOCKET sock ) { + unsigned long on; - unsigned long on = 1; - return ioctlsocket( sock, (long)FIONBIO, &on ); + on = 1; + return ioctlsocket( sock, (long)FIONBIO, & on ); -} /* XX_httplib_set_non_blocking_mode */ - -#else - -int XX_httplib_set_non_blocking_mode( SOCKET sock ) { +#else /* _WIN32 */ int flags; - flags = fcntl(sock, F_GETFL, 0); + flags = fcntl( sock, F_GETFL, 0 ); fcntl( sock, F_SETFL, flags | O_NONBLOCK ); return 0; -} /* XX_httplib_set_non_blocking_mode */ +#endif /* _WIN32 */ -#endif /* _WIN32 */ +} /* XX_httplib_set_non_blocking_mode */ diff --git a/src/httplib_skip.c b/src/httplib_skip.c index 2ade3f35..aa2d5faf 100644 --- a/src/httplib_skip.c +++ b/src/httplib_skip.c @@ -22,13 +22,19 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -/* Simplified version of XX_httplib_skip_quoted without quote char - * and whitespace == delimiters */ +/* + * char *XX_httplib_skip( char **buf, const char *delimiters ); + * + * The function XX_httplib_skip is a simplified version of the function + * XX_httplib_skip_quoted() without a quote char and where delimiters are only + * whitespace. + */ + char *XX_httplib_skip( char **buf, const char *delimiters ) { return XX_httplib_skip_quoted( buf, delimiters, delimiters, 0 ); diff --git a/src/httplib_snprintf.c b/src/httplib_snprintf.c index a0726f5c..b4cf3cc8 100644 --- a/src/httplib_snprintf.c +++ b/src/httplib_snprintf.c @@ -22,18 +22,26 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #include "httplib_string.h" +/* + * void XX_httplib_snprintf( const struct httplib_connection *conn, int *truncated, char *buf, size_t buflen, const char *fmt, ... ); + * + * The function XX_httplib_snprintf() is an internal function to send a string + * to a connection. The string can be formated with a format string and + * parameters in the same way as the snprintf function works. + */ + void XX_httplib_snprintf( const struct httplib_connection *conn, int *truncated, char *buf, size_t buflen, const char *fmt, ... ) { va_list ap; - va_start(ap, fmt); - XX_httplib_vsnprintf(conn, truncated, buf, buflen, fmt, ap); - va_end(ap); + va_start( ap, fmt ); + XX_httplib_vsnprintf( conn, truncated, buf, buflen, fmt, ap ); + va_end( ap ); } /* XX_httplib_snprintf */ diff --git a/src/httplib_ssl_locking_callback.c b/src/httplib_ssl_locking_callback.c index 0dcf34ff..91bfb5b6 100644 --- a/src/httplib_ssl_locking_callback.c +++ b/src/httplib_ssl_locking_callback.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -45,8 +45,8 @@ void XX_httplib_ssl_locking_callback( int mode, int mutex_num, const char *file, UNUSED_PARAMETER(line); UNUSED_PARAMETER(file); - if ( mode & 1 ) httplib_pthread_mutex_lock( & XX_httplib_ssl_mutexes[mutex_num] ); - else httplib_pthread_mutex_unlock( & XX_httplib_ssl_mutexes[mutex_num] ); + if ( mode & 0x0001 ) httplib_pthread_mutex_lock( & XX_httplib_ssl_mutexes[mutex_num] ); + else httplib_pthread_mutex_unlock( & XX_httplib_ssl_mutexes[mutex_num] ); } /* XX_httplib_ssl_locking_callback */ diff --git a/src/httplib_start_thread.c b/src/httplib_start_thread.c index f42d05ed..57c2c51c 100644 --- a/src/httplib_start_thread.c +++ b/src/httplib_start_thread.c @@ -22,46 +22,49 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" +#if ! defined(USE_STACK_SIZE) || (USE_STACK_SIZE <= 0) +#undef USE_STACK_SIZE +#define USE_STACK_SIZE 0 +#endif /* USE_STACK_SIZE */ + +/* + * int httplib_start_thread( httplib_thread_func_t func, void *param ); + * + * The functiom httplib_start_thread() is a convenience function to help to + * start a detached thread. The function returns 0 if successful, or a non-zero + * value if an error occurs. An optional pointer parameter can be passed to the + * newly created thread. + */ + +int httplib_start_thread( httplib_thread_func_t func, void *param ) { + #if defined(_WIN32) -int httplib_start_thread(httplib_thread_func_t f, void *p) { + return ( (_beginthread( (void(__cdecl *)(void *))func, USE_STACK_SIZE, param ) == ((uintptr_t)(-1L))) ? -1 : 0 ); -#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) - /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 - */ - return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p) == ((uintptr_t)(-1L))) ? -1 : 0); -#else - return ( (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L))) ? -1 : 0); -#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ - -} /* httplib_start_thread */ - -#else - -int httplib_start_thread(httplib_thread_func_t func, void *param) { +#else /* _WIN32 */ pthread_t thread_id; pthread_attr_t attr; int result; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_init( & attr ); + pthread_attr_setdetachstate( & attr, PTHREAD_CREATE_DETACHED ); -#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) - /* Compile-time option to control stack size, - * e.g. -DUSE_STACK_SIZE=16384 */ - pthread_attr_setstacksize(&attr, USE_STACK_SIZE); -#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ +#if (USE_STACK_SIZE > 1) + pthread_attr_setstacksize( & attr, USE_STACK_SIZE ); +#endif /* USE_STACK_SIZE > 1 */ - result = pthread_create(&thread_id, &attr, func, param); - pthread_attr_destroy(&attr); + result = pthread_create( & thread_id, &attr, func, param ); + pthread_attr_destroy( & attr ); return result; -} /* httplib_start_thread */ #endif /* _WIN32 */ + +} /* httplib_start_thread */ diff --git a/src/httplib_strcasestr.c b/src/httplib_strcasestr.c index 4684ae44..4bcc2a72 100644 --- a/src/httplib_strcasestr.c +++ b/src/httplib_strcasestr.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" diff --git a/src/httplib_websocket_client_thread.c b/src/httplib_websocket_client_thread.c index db8346df..0696be99 100644 --- a/src/httplib_websocket_client_thread.c +++ b/src/httplib_websocket_client_thread.c @@ -22,48 +22,47 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #include "httplib_memory.h" /* - * ... XX_httplib_websocket_client_thread( void *data ); + * LIBHTTP_THREAD XX_httplib_websocket_client_thread( void *data ); * * The function XX_httplib_websocket_client_thread() is the worker thread which * connects as a client to a remote websocket server. */ #if defined(USE_WEBSOCKET) -#ifdef _WIN32 -unsigned __stdcall XX_httplib_websocket_client_thread( void *data ) { -#else /* _WIN32 */ -void * XX_httplib_websocket_client_thread(void *data) { -#endif /* _WIN32 */ - struct websocket_client_thread_data *cdata = (struct websocket_client_thread_data *)data; - XX_httplib_set_thread_name("ws-client"); +LIBHTTP_THREAD XX_httplib_websocket_client_thread( void *data ) { - if (cdata->conn->ctx) { - if (cdata->conn->ctx->callbacks.init_thread) { - /* 3 indicates a websocket client thread */ - /* TODO: check if conn->ctx can be set */ - cdata->conn->ctx->callbacks.init_thread(cdata->conn->ctx, 3); - } + struct websocket_client_thread_data *cdata; + + cdata = data; + + 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 ); } - 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 ); httplib_free( cdata ); + return LIBHTTP_THREAD_RETNULL; -#ifdef _WIN32 - return 0; -#else /* _WIN32 */ - return NULL; -#endif /* _WIN32 */ } /* XX_httplib_websocket_client_thread */ -#endif + +#endif /* USE_WEBSOCKET */ diff --git a/src/httplib_websocket_client_write.c b/src/httplib_websocket_client_write.c index 6f0ae3bb..1d4e027a 100644 --- a/src/httplib_websocket_client_write.c +++ b/src/httplib_websocket_client_write.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -36,23 +36,28 @@ static void mask_data( const char *in, size_t in_len, uint32_t masking_key, char /* * int httplib_websocket_client_write( struct httplib_connection *conn, int opcode, const char *data, size_t dataLen ); * - * The function httplib_websocket_client_write() is used to write as a client to a - * websocket server. + * The function httplib_websocket_client_write() is used to write as a client + * to a websocket server. The function returns -1 if an error occures, + * otherwise the amount of bytes written. */ int httplib_websocket_client_write( struct httplib_connection *conn, int opcode, const char *data, size_t dataLen ) { - int retval = -1; - char *masked_data = httplib_malloc( ((dataLen + 7) / 4) * 4 ); - uint32_t masking_key = (uint32_t)XX_httplib_get_random(); + int retval; + char *masked_data; + uint32_t masking_key; - if (masked_data == NULL) { - /* Return -1 in an error case */ - httplib_cry(conn, "Cannot allocate buffer for masked websocket response: Out of memory"); + retval = -1; + masked_data = httplib_malloc( ((dataLen + 7) / 4) * 4 ); + masking_key = (uint32_t) XX_httplib_get_random(); + + if ( masked_data == NULL ) { + + httplib_cry( conn, "Cannot allocate buffer for masked websocket response: Out of memory" ); return -1; } - mask_data(data, dataLen, masking_key, masked_data); + mask_data( data, dataLen, masking_key, masked_data ); retval = XX_httplib_websocket_write_exec( conn, opcode, masked_data, dataLen, masking_key ); httplib_free( masked_data ); @@ -61,8 +66,6 @@ int httplib_websocket_client_write( struct httplib_connection *conn, int opcode, } /* httplib_websocket_client_write */ - - /* * static void mask_data( const char *in, size_t in_len, uint32_t masking_key, char *out ); * @@ -72,19 +75,30 @@ int httplib_websocket_client_write( struct httplib_connection *conn, int opcode, static void mask_data( const char *in, size_t in_len, uint32_t masking_key, char *out ) { - size_t i = 0; + size_t i; i = 0; - if ((in_len > 3) && ((ptrdiff_t)in % 4) == 0) { - /* Convert in 32 bit words, if data is 4 byte aligned */ - while (i < (in_len - 3)) { + + if ( in_len > 3 && ((ptrdiff_t)in % 4) == 0 ) { + + /* + * Convert in 32 bit words, if data is 4 byte aligned + */ + + while ( i+3 < in_len ) { + *(uint32_t *)(void *)(out + i) = *(uint32_t *)(void *)(in + i) ^ masking_key; i += 4; } } - if (i != in_len) { - /* convert 1-3 remaining bytes if ((dataLen % 4) != 0)*/ - while (i < in_len) { + if ( i != in_len ) { + + /* + * convert 1-3 remaining bytes if ((dataLen % 4) != 0) + */ + + while ( i < in_len ) { + *(uint8_t *)(void *)(out + i) = *(uint8_t *)(void *)(in + i) ^ *(((uint8_t *)&masking_key) + (i % 4)); i++; } @@ -92,5 +106,4 @@ static void mask_data( const char *in, size_t in_len, uint32_t masking_key, char } /* mask_data */ - #endif /* !USE_WEBSOCKET */ diff --git a/src/httplib_websocket_write.c b/src/httplib_websocket_write.c index 4c6df92b..5c4bc52d 100644 --- a/src/httplib_websocket_write.c +++ b/src/httplib_websocket_write.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" diff --git a/src/httplib_websocket_write_exec.c b/src/httplib_websocket_write_exec.c index c57e73c6..14578e30 100644 --- a/src/httplib_websocket_write_exec.c +++ b/src/httplib_websocket_write_exec.c @@ -22,13 +22,13 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" /* - * int XX_httplib_websocket_write_exec( struct httplib_connection *conn, int opcode, const char *data, size_t dataLen, uint32_t masking_key ); + * int XX_httplib_websocket_write_exec( struct httplib_connection *conn, int opcode, const char *data, size_t data_len, uint32_t masking_key ); * * The function XX_httplib_websocket_write_exec() does the heavy lifting in * writing data over a websocket connectin to a remote peer. @@ -36,53 +36,84 @@ #if defined(USE_WEBSOCKET) -int XX_httplib_websocket_write_exec( struct httplib_connection *conn, int opcode, const char *data, size_t dataLen, uint32_t masking_key ) { +int XX_httplib_websocket_write_exec( struct httplib_connection *conn, int opcode, const char *data, size_t data_len, uint32_t masking_key ) { unsigned char header[14]; - size_t headerLen = 1; + size_t header_len; + int retval; + uint16_t len; + uint32_t len1; + uint32_t len2; - int retval = -1; + retval = -1; + header_len = 1; + header[0] = 0x80 + (opcode & 0xF); - header[0] = 0x80 + (opcode & 0xF); + /* + * Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 + */ - /* Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 */ - if (dataLen < 126) { - /* inline 7-bit length field */ - header[1] = (unsigned char)dataLen; - headerLen = 2; - } else if (dataLen <= 0xFFFF) { - /* 16-bit length field */ - uint16_t len = htons((uint16_t)dataLen); - header[1] = 126; - memcpy(header + 2, &len, 2); - headerLen = 4; - } else { - /* 64-bit length field */ - uint32_t len1 = htonl((uint64_t)dataLen >> 32); - uint32_t len2 = htonl(dataLen & 0xFFFFFFFF); - header[1] = 127; - memcpy(header + 2, &len1, 4); - memcpy(header + 6, &len2, 4); - headerLen = 10; + if ( data_len < 126 ) { + + /* + * inline 7-bit length field + */ + + header[1] = (unsigned char)data_len; + header_len = 2; + } + + else if ( data_len <= 65535 ) { + + /* + * 16-bit length field + */ + + len = htons( (uint16_t)data_len ); + header[1] = 126; + header_len = 4; + memcpy( header+2, & len, 2 ); + } + + else { + /* + * 64-bit length field + */ + + len1 = htonl( (uint64_t)data_len >> 32 ); + len2 = htonl( data_len & 0xFFFFFFFF ); + header[1] = 127; + header_len = 10; + memcpy( header + 2, & len1, 4 ); + memcpy( header + 6, & len2, 4 ); } - if (masking_key) { - /* add mask */ + if ( masking_key ) { + + /* + * add mask + */ + header[1] |= 0x80; - memcpy(header + headerLen, &masking_key, 4); - headerLen += 4; + memcpy( header + header_len, & masking_key, 4 ); + header_len += 4; } - /* Note that POSIX/Winsock's send() is threadsafe + /* + * Note that POSIX/Winsock's send() is threadsafe * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid - * but mongoose's httplib_printf/httplib_write is not (because of the loop in + * but LibHTTP's httplib_printf/httplib_write is not (because of the loop in * push(), although that is only a problem if the packet is large or - * outgoing buffer is full). */ - httplib_lock_connection(conn); - retval = httplib_write(conn, header, headerLen); - if (dataLen > 0) retval = httplib_write(conn, data, dataLen); - httplib_unlock_connection(conn); + * outgoing buffer is full). + */ + + httplib_lock_connection( conn ); + + retval = httplib_write( conn, header, header_len ); + if ( data_len > 0 ) retval = httplib_write( conn, data, data_len ); + + httplib_unlock_connection( conn ); return retval; diff --git a/src/httplib_write.c b/src/httplib_write.c index c525b1a7..6f3ea1a5 100644 --- a/src/httplib_write.c +++ b/src/httplib_write.c @@ -22,57 +22,76 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -int httplib_write( struct httplib_connection *conn, const void *buf, size_t len ) { +/* + * The function httplib_write() writes a number of characters over a + * connection. The amount of characters written is returned. If an error occurs + * the value 0 is returned. + * + * The function uses throtteling when necessary for a connection. + */ + +int httplib_write( struct httplib_connection *conn, const void *buffie, size_t lennie ) { time_t now; int64_t n; + int64_t len; int64_t total; int64_t allowed; + const char *buf; - if (conn == NULL) return 0; + if ( conn == NULL || buffie == NULL || lennie == 0 ) return 0; - if (conn->throttle > 0) { - if ((now = time(NULL)) != conn->last_throttle_time) { - conn->last_throttle_time = now; + buf = buffie; + len = (int64_t)lennie; + + if ( conn->throttle > 0 ) { + + now = time( NULL ); + + if ( now != conn->last_throttle_time ) { + + conn->last_throttle_time = now; conn->last_throttle_bytes = 0; } + allowed = conn->throttle - conn->last_throttle_bytes; - if (allowed > (int64_t)len) allowed = (int64_t)len; - if ((total = XX_httplib_push_all(conn->ctx, - NULL, - conn->client.sock, - conn->ssl, - (const char *)buf, - (int64_t)allowed)) == allowed) { - buf = (const char *)buf + total; + if ( allowed > len ) allowed = len; + + total = XX_httplib_push_all( conn->ctx, NULL, conn->client.sock, conn->ssl, buf, allowed ); + + if ( total == allowed ) { + + buf = buf + total; conn->last_throttle_bytes += total; - while (total < (int64_t)len && conn->ctx->stop_flag == 0) { - allowed = (conn->throttle > ((int64_t)len - total)) - ? (int64_t)len - total - : conn->throttle; - if ((n = XX_httplib_push_all(conn->ctx, - NULL, - conn->client.sock, - conn->ssl, - (const char *)buf, - (int64_t)allowed)) != allowed) { + + while ( total < len && conn->ctx->stop_flag == 0 ) { + + if ( conn->throttle > len-total ) allowed = len-total; + else allowed = conn->throttle; + + n = XX_httplib_push_all( conn->ctx, NULL, conn->client.sock, conn->ssl, buf, allowed ); + if ( n != allowed ) { + + if ( n > 0 ) total += n; break; } - sleep(1); + + sleep( 1 ); + conn->last_throttle_bytes = allowed; - conn->last_throttle_time = time(NULL); - buf = (const char *)buf + n; - total += n; + conn->last_throttle_time = time( NULL ); + buf = buf + n; + total += n; } } } - else total = XX_httplib_push_all(conn->ctx, NULL, conn->client.sock, conn->ssl, (const char *)buf, (int64_t)len); + else total = XX_httplib_push_all( conn->ctx, NULL, conn->client.sock, conn->ssl, buf, len ); return (int)total;