25 changes: 23 additions & 2 deletions API.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Ulfius API Documentation

- [Header file](#header-file)
- [Use Ulfius in a C program](#use-ulfius-in-a-c-program)
- [Header file](#header-file)
- [Build options](#build-options)
- [Return values](#return-values)
- [Memory management](#memory-management)
- [Webservice initialization](#webservice-initialization)
Expand Down Expand Up @@ -59,14 +61,33 @@
- [Update existing programs from Ulfius 2.0 to 2.1](#update-existing-programs-from-ulfius-20-to-21)
- [Update existing programs from Ulfius 1.x to 2.0](#update-existing-programs-from-ulfius-1x-to-20)

## Header file <a name="header-file"></a>
## Use Ulfius in a C program <a name="use-ulfius-in-a-c-program"></a>

### Header file <a name="header-file"></a>

Include file `ulfius.h` in your source file:

```C
#include <ulfius.h>
```

### Build options <a name="build-options"></a>

You can use `pkg-config` to provide the compile and link options for Ulfius:

```shell
$ # compile flags
$ pkg-config --cflags libulfius
-I/usr/include
$ # linker flags
$ pkg-config --libs libulfius
-L/usr/lib -lulfius -lorcania -lyder
```

If you don't or can't have pkg-config for the build, you can set the linker options `-lulfius -lorcania -lyder`.

The options `-lorcania` and `-lyder` are not necessary if you don't directly use Orcania or Yder functions. But in doubt, add them anyway.

On your linker command, add Ulfius as a dependency library, e.g. `-lulfius` for gcc.

## Return values <a name="return-values"></a>
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Ulfius Changelog

## 2.7.4

- Add `void` parameter to functions with no param
- Fix bug when malformed HTTP requests are sent, thanks Jeremy Brown!
- Remove yder flag from `libulfius.pc` when yder is disabled
- Avoid Time-of-check time-of-use filesystem race condition, assume `fopen` result is enough

## 2.7.3

- Add `ULFIUS_CHECK_VERSION` macro (Thanks Oliv3)
Expand Down
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ set(PROJECT_HOMEPAGE_URL "https://github.com/babelouest/ulfius/")
set(PROJECT_BUGREPORT_PATH "https://github.com/babelouest/ulfius/issues")
set(LIBRARY_VERSION_MAJOR "2")
set(LIBRARY_VERSION_MINOR "7")
set(LIBRARY_VERSION_PATCH "3")
set(LIBRARY_VERSION_PATCH "4")

set(PROJECT_VERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}.${LIBRARY_VERSION_PATCH}")
set(PROJECT_VERSION_MAJOR ${LIBRARY_VERSION_MAJOR})
Expand Down Expand Up @@ -281,7 +281,9 @@ endif ()
option(WITH_YDER "Use Yder library to log messages" ON)
option(SEARCH_YDER "Search for Yder library" ON)

set(LIB_YDER "")
if (WITH_YDER)
set(LIB_YDER "-lyder")
set(U_DISABLE_YDER OFF)
set(SEARCH_ORCANIA OFF CACHE BOOL "Force to false") # Avoid to search and download orcania during yder search and download

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,4 @@ Example callback functions are available in the folder [example_callbacks](https
I'm open for questions and suggestions, feel free to open an [issue](https://github.com/babelouest/ulfius/issues), a [pull request](https://github.com/babelouest/ulfius/pulls) or send me an [e-mail](mailto:mail@babelouest.org) if you feel like it!
You can visit the IRC channel #ulfius on the [Freenode](https://freenode.net/) network.
You can visit the IRC channel #ulfius on the [Libera.​Chat](https://libera.chat/) network.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
*
* Static file server with compression Ulfius callback
*
* Copyright 2020 Nicolas Mora <mail@babelouest.org>
* Copyright 2020-2021 Nicolas Mora <mail@babelouest.org>
*
* Version 20201213
* Version 20210906
*
* The MIT License (MIT)
*
Expand Down Expand Up @@ -189,24 +189,22 @@ static int callback_static_file_uncompressed (const struct _u_request * request,

file_path = msprintf("%s/%s", ((struct _u_compressed_inmemory_website_config *)user_data)->files_path, file_requested);

if (access(file_path, F_OK) != -1) {
f = fopen (file_path, "rb");
if (f) {
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
f = fopen (file_path, "rb");
if (f) {
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);

content_type = u_map_get_case(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, get_filename_ext(file_requested));
if (content_type == NULL) {
content_type = u_map_get(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, "*");
y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
}
u_map_put(response->map_header, "Content-Type", content_type);
u_map_copy_into(response->map_header, &((struct _u_compressed_inmemory_website_config *)user_data)->map_header);
content_type = u_map_get_case(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, get_filename_ext(file_requested));
if (content_type == NULL) {
content_type = u_map_get(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, "*");
y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
}
u_map_put(response->map_header, "Content-Type", content_type);
u_map_copy_into(response->map_header, &((struct _u_compressed_inmemory_website_config *)user_data)->map_header);

if (ulfius_set_stream_response(response, 200, callback_static_file_uncompressed_stream, callback_static_file_uncompressed_stream_free, length, CHUNK, f) != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error ulfius_set_stream_response");
}
if (ulfius_set_stream_response(response, 200, callback_static_file_uncompressed_stream, callback_static_file_uncompressed_stream_free, length, CHUNK, f) != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error ulfius_set_stream_response");
}
} else {
if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
Expand Down Expand Up @@ -358,99 +356,97 @@ int callback_static_compressed_inmemory_website (const struct _u_request * reque
} else {
file_path = msprintf("%s/%s", ((struct _u_compressed_inmemory_website_config *)user_data)->files_path, file_requested);

if (access(file_path, F_OK) != -1) {
if (!pthread_mutex_lock(&config->lock)) {
f = fopen (file_path, "rb");
if (f) {
fseek (f, 0, SEEK_END);
offset = length = ftell (f);
fseek (f, 0, SEEK_SET);

if ((file_content_orig = file_content = o_malloc(length)) != NULL && (data_zip = o_malloc((2*length)+20)) != NULL) {
defstream.zalloc = u_zalloc;
defstream.zfree = u_zfree;
defstream.opaque = Z_NULL;
defstream.avail_in = (uInt)length;
defstream.next_in = (Bytef *)file_content;
while ((read_length = fread(file_content, sizeof(char), offset, f))) {
file_content += read_length;
offset -= read_length;
}
if (!pthread_mutex_lock(&config->lock)) {
f = fopen (file_path, "rb");
if (f) {
fseek (f, 0, SEEK_END);
offset = length = ftell (f);
fseek (f, 0, SEEK_SET);

if ((file_content_orig = file_content = o_malloc(length)) != NULL && (data_zip = o_malloc((2*length)+20)) != NULL) {
defstream.zalloc = u_zalloc;
defstream.zfree = u_zfree;
defstream.opaque = Z_NULL;
defstream.avail_in = (uInt)length;
defstream.next_in = (Bytef *)file_content;
while ((read_length = fread(file_content, sizeof(char), offset, f))) {
file_content += read_length;
offset -= read_length;
}

if (compress_mode == U_COMPRESS_GZIP) {
if (deflateInit2(&defstream,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
U_GZIP_WINDOW_BITS | U_GZIP_ENCODING,
8,
Z_DEFAULT_STRATEGY) != Z_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (gzip)");
ret = U_CALLBACK_ERROR;
}
} else {
if (deflateInit(&defstream, Z_BEST_COMPRESSION) != Z_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (deflate)");
if (compress_mode == U_COMPRESS_GZIP) {
if (deflateInit2(&defstream,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
U_GZIP_WINDOW_BITS | U_GZIP_ENCODING,
8,
Z_DEFAULT_STRATEGY) != Z_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (gzip)");
ret = U_CALLBACK_ERROR;
}
} else {
if (deflateInit(&defstream, Z_BEST_COMPRESSION) != Z_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (deflate)");
ret = U_CALLBACK_ERROR;
}
}
if (ret == U_CALLBACK_CONTINUE) {
do {
if ((data_zip = o_realloc(data_zip, data_zip_len+_U_W_BLOCK_SIZE)) != NULL) {
defstream.avail_out = _U_W_BLOCK_SIZE;
defstream.next_out = ((Bytef *)data_zip)+data_zip_len;
switch ((res = deflate(&defstream, Z_FINISH))) {
case Z_OK:
case Z_STREAM_END:
case Z_BUF_ERROR:
break;
default:
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflate %d", res);
ret = U_CALLBACK_ERROR;
break;
}
data_zip_len += _U_W_BLOCK_SIZE - defstream.avail_out;
} else {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resources for data_zip");
ret = U_CALLBACK_ERROR;
}
}
} while (U_CALLBACK_CONTINUE == ret && defstream.avail_out == 0);

if (ret == U_CALLBACK_CONTINUE) {
do {
if ((data_zip = o_realloc(data_zip, data_zip_len+_U_W_BLOCK_SIZE)) != NULL) {
defstream.avail_out = _U_W_BLOCK_SIZE;
defstream.next_out = ((Bytef *)data_zip)+data_zip_len;
switch ((res = deflate(&defstream, Z_FINISH))) {
case Z_OK:
case Z_STREAM_END:
case Z_BUF_ERROR:
break;
default:
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflate %d", res);
ret = U_CALLBACK_ERROR;
break;
}
data_zip_len += _U_W_BLOCK_SIZE - defstream.avail_out;
} else {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resources for data_zip");
ret = U_CALLBACK_ERROR;
if (compress_mode == U_COMPRESS_GZIP) {
if (config->allow_cache_compressed) {
u_map_put_binary(&config->gzip_files, file_requested, data_zip, 0, defstream.total_out);
}
} while (U_CALLBACK_CONTINUE == ret && defstream.avail_out == 0);

if (ret == U_CALLBACK_CONTINUE) {
if (compress_mode == U_COMPRESS_GZIP) {
if (config->allow_cache_compressed) {
u_map_put_binary(&config->gzip_files, file_requested, data_zip, 0, defstream.total_out);
}
ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
} else {
if (config->allow_cache_compressed) {
u_map_put_binary(&config->deflate_files, file_requested, data_zip, 0, defstream.total_out);
}
ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
} else {
if (config->allow_cache_compressed) {
u_map_put_binary(&config->deflate_files, file_requested, data_zip, 0, defstream.total_out);
}
u_map_put(response->map_header, U_CONTENT_HEADER, compress_mode==U_COMPRESS_GZIP?U_ACCEPT_GZIP:U_ACCEPT_DEFLATE);
ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
}
u_map_put(response->map_header, U_CONTENT_HEADER, compress_mode==U_COMPRESS_GZIP?U_ACCEPT_GZIP:U_ACCEPT_DEFLATE);
}
deflateEnd(&defstream);
o_free(data_zip);
} else {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resource for file_content or data_zip");
ret = U_CALLBACK_ERROR;
}
o_free(file_content_orig);
fclose(f);
deflateEnd(&defstream);
o_free(data_zip);
} else {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resource for file_content or data_zip");
ret = U_CALLBACK_ERROR;
}
pthread_mutex_unlock(&config->lock);
o_free(file_content_orig);
fclose(f);
} else {
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error pthread_lock_mutex");
ret = U_CALLBACK_ERROR;
if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
ret = U_CALLBACK_IGNORE;
} else {
ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
response->status = 302;
}
}
pthread_mutex_unlock(&config->lock);
} else {
if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
ret = U_CALLBACK_IGNORE;
} else {
ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
response->status = 302;
}
y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error pthread_lock_mutex");
ret = U_CALLBACK_ERROR;
}
o_free(file_path);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
*
* Static file server with compression Ulfius callback
*
* Copyright 2020 Nicolas Mora <mail@babelouest.org>
* Copyright 2020-2021 Nicolas Mora <mail@babelouest.org>
*
* Version 20201213
* Version 20210906
*
* The MIT License (MIT)
*
Expand Down
14 changes: 8 additions & 6 deletions example_programs/test_u_map/test_u_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ int put_file_content_in_map (struct _u_map * map, const char * file_path, uint64
FILE * f;
int res = U_OK;

if (access(file_path, F_OK) != -1 && map != NULL) {
if (map != NULL) {
f = fopen (file_path, "rb");
if (f) {
fseek (f, 0, SEEK_END);
Expand All @@ -70,13 +70,15 @@ int put_file_content_in_map (struct _u_map * map, const char * file_path, uint64
fread (buffer, 1, length, f);
}
fclose (f);
}

if (buffer) {
res = u_map_put_binary(map,file_path, (char *)buffer, offset, length);
o_free(buffer);
if (buffer) {
res = u_map_put_binary(map,file_path, (char *)buffer, offset, length);
o_free(buffer);
} else {
res = U_ERROR;
}
} else {
res = U_ERROR;
res = U_ERROR_PARAMS;
}
} else {
res = U_ERROR_PARAMS;
Expand Down
10 changes: 5 additions & 5 deletions include/ulfius.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,12 @@ void u_free(void * data);
* The function ulfius_send_request_close must be called when ulfius send request functions are no longer needed
* @return U_OK on success
*/
int ulfius_global_init();
int ulfius_global_init(void);

/**
* Close global parameters
*/
void ulfius_global_close();
void ulfius_global_close(void);

/**
* @}
Expand Down Expand Up @@ -640,7 +640,7 @@ int ulfius_remove_endpoint_by_val(struct _u_instance * u_instance, const char *
* ulfius_empty_endpoint
* @return empty endpoint that goes at the end of an endpoint list
*/
const struct _u_endpoint * ulfius_empty_endpoint();
const struct _u_endpoint * ulfius_empty_endpoint(void);

/**
* ulfius_copy_endpoint
Expand Down Expand Up @@ -2081,7 +2081,7 @@ struct _websocket_handler {
*/

#ifndef U_DISABLE_GNUTLS
/*
/**
* ulfius_export_client_certificate_pem
* Exports the client certificate using PEM format
* @param request struct _u_request used
Expand All @@ -2090,7 +2090,7 @@ struct _websocket_handler {
*/
char * ulfius_export_client_certificate_pem(const struct _u_request * request);

/*
/**
* ulfius_import_client_certificate_pem
* Imports the client certificate using PEM format
* @param request struct _u_request used
Expand Down
Loading