Skip to content

Commit 3fd99c8

Browse files
authored
Add options to store files directly to the file system and to omit duplicating files in memory (#257)
* Add options to store files directly to the file system and to omit duplicating files in memory * Delete accidentially added newline in http_resource.hpp * Removed unused function, fixed some code style and typos in comments * Removed asprintf in favor or string concatenation As asprintf is a gnu extension it should not be used in a multi plattform code. To generate the needed string, std::string is used As the finally called function mkstemp changes its first argument of type char * the concatenated string is still strdup-ed into a char * before handing it to mkstemp * Made http_utils::generate_random_upload_filename plattform independant Mkstemp is only available on windows. As there is no suitable plattform independant function to generate a unique filename within a specified directory, use mkstemp on linux and a combination of _mktemp_s and _sopen_s on windows. Furthermore the path separator differs on linux and windows so use a static const char which provides the appropriate path separator Additionally within this patch the function http_utils::generate_random_upload_filename uses an exception to reports errors rather than a empty string as return code * Input and output of http_utils::generate_random_upload_filename are const * Added options and functions for file upload to README.md * Use correct include and functions in generate_random_upload_filename to run on windows The current code won't compile on ApVeyor. So this is a an untested patch to include the correct headers and not use functions, which are not supported (or deprecated) on windows. * Add missing includes for windows, fixed wrong argument for _mktemp_s * Removed accidentially pushed debug code * Use a static const char* for the random file system template This is only used inside http_utils.cpp and therefor can be static * Make output of get_or_create_file_info const and introduce new setters The output of get_or_create_file_info should be const. As previously the result was used to direclty alter the content of the struct new setters to set the file_system_file_name and to grow the size are introduced (and used) * Use const variables for the webserver options * Added an example for file upload file_upload.cpp is a very basic example for a file upload server. It features a simple HTML input form with file upload (which supports multiple files). The uploaded files will be stored to the directory, which has to be given as command line parameter to the example program. All uploaded files will be printed in a table including their original name, the generated random filepath and the size. * Use a prefixed operator in for loop * Added safety check for use of strlen and some missing free()s Static code analysis complained about use of unprotected strlen. This changes adds a safety check directly before the us of strlen. Additionally some free()s (before some throws) where missing. * Changed file map to be a map of keys with a nested map of files There is the possibility to upload the same file with two different keys or to upload multiple (different) files with the same key within the same request. The current solution can only handle the second use case but not the first. This patch changes the file map to use a nested map of files within a map of keys to support both use cases. * Adjusted file_upload example to use new files map * Updated description of files map in README.md * Removed strlen from generate_random_upload_filename As Codacy static code analysis warns about the safety issue of strlen if a given string is not terminated, this is removed. The solution uses the size of the original string, which is just duplicated to a char* as the used function _mktemp_s requires a char* which will be changed by the function and the changed result is needed afterwards. * Use a pointer to std::ofstream in modded_request One of the workflows did not run successfully, as it is obviously not allowed to have a std::ofstream in a class if it is not a pointer or reference. This patch uses a pointer to the ofstream in the modded requests and adjusts all usage of the ofstream in webserver.cpp * Moved struct file_info_s to class file_info * Setters of class file_info are private and class webserver is a friend of this class As the file_info has to be changed (especially the file_size) in multiple iterations of the post iterator, this class cannot be fully immutable. To realise a interface without setters to the outside of the library these setters are private now. The only other classe, which uses these setters is the class webserver and is therefor declared as friend to the file_info class. * Use const reference as parameter for set_file_system_file_name * Don't create zero length files if no file is uploaded If an upload field is submitted without a file selected, the previous implementation would still create a random file with zero length. This is due to the fact, that the key would be present in the content, but the filename is empty. So a simple check, whether the filename is really present is introduced. This is done by simply looking at the very first sign of the const char* (which is given from libmicrohttpd). As strlen is considered unsafe and there is already a check, whether the const char* is not a nullptr, it is always safe to dereference and inspect the first char. * Updated README.md as file_info is now a class * Some code styling and small refactorings after review * Replaced std::exception with custom generateFilenameException Although there is no distinct error handling, if the filename generation throws an exception, it should be usable in the future and destinguishable from other common exceptions. Therefor the std::exception was replaced by generateFilenameException * Some more comments and code style fixes after review * Some more code style changes after review * Added error string to generateFilenameException Though it is not used at the moment the generateFilenameException features now a error string, which is set and can be used for debugging. * Added comment for c functions in generate_random_upload_filename Actually we should use standard C++ classes like ofstream for file operations. But as these do not feature some wanted functions, this is done by C functions. This patch adds some comments to explain the reasons for this decision. * Removed const qualifier from file_info::get_file_size() This is removed as the const qualifier is superfluous and generates warning on pedantic checks. * Added unit test for generate_random_upload_filename This is a first show on a unit test for the function generate_random_upload_filename. After calling the function, this unit test not only checks the returned filename, but also, whether the file exists. Maybe to be improved: - the filename template and the path_separator are currently duplicated - should there be a check, whether the removal of the temporary file worked? * Removed filesystem include from unit tests again, as it would require C++17 * Close open upload file after post processor is done If the file is not closed after the post processor is done, the last info of the file might not be written to disk until the calling application accesses the file. This could easily be reproduceable with uploading very small files ( < 100 Bytes). In this case the content of the file was missing for the calling process. * Added content_type and transfer_encoding to file_info The post iterator can set the content_type and the transfer_encoding of the uploaded file into the file_info. According to the documentation of libmicrohttpd this is sometimes unknown. In this case the values will be nullptr and therefor the file_info may be empty on this values. * Fixed file_upload example * Made filename_template and path_separator part of class http_utils As these two constant values are used in the unit tests they could no longer be static only in http_utils.cpp. So they are members of the class http_utils and therefor accessible in the unit tests * Added integration test for file upload Added an integration test for file upload which tests the upload with different settings of the webserver: - Upload to memory and disk - Upload to disk only - Upload to memory, duplicated in get_content and args - Upload to memory, only stored in args * Fixed integration test for file upload * Removed empty AUTO_TEST from file_upload integration test * Use internal values for http_ressource class in integration tests Avoid using pointers in class print_file_upload_resource in favour of internal values and according getter functions. Additionally simplified the code by wrapping lines and using the auto keyword instead of long map specifiers. * Added further integration tests for file upload Added the following integration tests - Upload of a file via PUT (then there is no post_processor used and the content of the file will only be available via get_content - Upload of two files - Upload of a file and an additional ordinary POST parameter * Added test_content_2 to configure script As the test_content_2 was missing in the configure script the file was not copy to the build/test directory and therefor the integration tests failed * Fixed memory leak in post_iterator If multiple files are uploaded, the ofstream to write the data to the file was created again (with new()) for the next file without deleting the previously created one. This resulted in a memory leak. * Some code style fixed on file upload integration test * Use map definition instead of auto in integration test Co-authored-by: Christian Tomahogh <ctomahogh@users.noreply.github.com>
1 parent bd559af commit 3fd99c8

20 files changed

+978
-13
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ For example, if your connection limit is “1”, a browser may open a first con
190190
* _.debug() and .no_debug():_ Enables debug messages from the library. `off` by default.
191191
* _.regex_checking() and .no_regex_checking():_ Enables pattern matching for endpoints. Read more [here](#registering-resources). `on` by default.
192192
* _.post_process() and .no_post_process():_ Enables/Disables the library to automatically parse the body of the http request as arguments if in querystring format. Read more [here](#parsing-requests). `on` by default.
193+
* _.put_processed_data_to_content() and .no_put_processed_data_to_content():_ Enables/Disables the library to copy parsed body data to the content or to only store it in the arguments map. `on` by default.
194+
* _.file_upload_target(**file_upload_target_T** file_upload_target):_ Controls, how the library stores uploaded files. Default value is `FILE_UPLOAD_MEMORY_ONLY`.
195+
* `FILE_UPLOAD_MEMORY_ONLY`: The content of the file is only stored in memory. Depending on `put_processed_data_to_content` only as part of the arguments map or additionally in the content.
196+
* `FILE_UPLOAD_DISK_ONLY`: The content of the file is stored only in the file system. The path is created from `file_upload_dir` and either a random name (if `generate_random_filename_on_upload` is true) or the actually uploaded file name.
197+
* `FILE_UPLOAD_MEMORY_AND_DISK`: The content of the file is stored in memory and on the file system.
198+
* _.file_upload_dir(**const std::string&** file_upload_dir):_ Specifies the directory to store all uploaded files. Default value is `/tmp`.
199+
* _.generate_random_filename_on_upload() and .no_generate_random_filename_on_upload():_ Enables/Disables the library to generate a unique and unused filename to store the uploaded file to. Otherwise the actually uploaded file name is used. `off` by default.
193200
* _.deferred()_ and _.no_deferred():_ Enables/Disables the ability for the server to suspend and resume connections. Simply put, it enables/disables the ability to use `deferred_response`. Read more [here](#building-responses-to-requests). `on` by default.
194201
* _.single_resource() and .no_single_resource:_ Sets or unsets the server in single resource mode. This limits all endpoints to be served from a single resource. The resultant is that the webserver will process the request matching to the endpoint skipping any complex semantic. Because of this, the option is incompatible with `regex_checking` and requires the resource to be registered against an empty endpoint or the root endpoint (`"/"`). The resource will also have to be registered as family. (For more information on resource registration, read more [here](#registering-resources)). `off` by default.
195202

@@ -553,6 +560,8 @@ The `http_request` class has a set of methods you will have access to when imple
553560
* _**const std::map<std::string, std::string, http::header_comparator>** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request.
554561
* _**const std::map<std::string, std::string, http::header_comparator>** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings).
555562
* _**const std::map<std::string, std::string, http::arg_comparator>** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default).
563+
* _**const std::map<std::string, file_info_s>** get_files() **const**:_ Returns information about all the uploaded files (if the files are stored to disk). This information includes the original file name, the size of the file and the path to the file in the file system.
564+
* _**const std::map<std::string, std::map<std::string, http::file_info>>** get_files() **const**:_ Returns information about all the uploaded files (if the files are stored to disk). This information includes the key (as identifier of the outer map), the original file name (as identifier of the inner map) and a class `file_info`, which includes the size of the file and the path to the file in the file system.
556565
* _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request.
557566
* _**bool** content_too_large() **const**:_ Returns `true` if the body length of the HTTP request sent by the client is longer than the max allowed on the server.
558567
* _**const std::string** get_querystring() **const**:_ Returns the `querystring` of the HTTP request.

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ AC_SUBST(EXT_LIB_PATH)
273273
AC_SUBST(EXT_LIBS)
274274

275275
AC_CONFIG_FILES([test/test_content:test/test_content])
276+
AC_CONFIG_FILES([test/test_content_2:test/test_content_2])
276277
AC_CONFIG_FILES([test/test_content_empty:test/test_content_empty])
277278
AC_CONFIG_FILES([test/cert.pem:test/cert.pem])
278279
AC_CONFIG_FILES([test/key.pem:test/key.pem])

examples/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
LDADD = $(top_builddir)/src/libhttpserver.la
2020
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/
2121
METASOURCES = AUTO
22-
noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator
22+
noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator file_upload
2323

2424
hello_world_SOURCES = hello_world.cpp
2525
service_SOURCES = service.cpp
@@ -41,3 +41,4 @@ minimal_ip_ban_SOURCES = minimal_ip_ban.cpp
4141
benchmark_select_SOURCES = benchmark_select.cpp
4242
benchmark_threads_SOURCES = benchmark_threads.cpp
4343
benchmark_nodelay_SOURCES = benchmark_nodelay.cpp
44+
file_upload_SOURCES = file_upload.cpp

examples/file_upload.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
USA
19+
*/
20+
21+
#include <iostream>
22+
#include <httpserver.hpp>
23+
24+
class file_upload_resource : public httpserver::http_resource {
25+
public:
26+
const std::shared_ptr<httpserver::http_response> render_GET(const httpserver::http_request&) {
27+
std::string get_response = "<html>\n";
28+
get_response += " <body>\n";
29+
get_response += " <form method=\"POST\" enctype=\"multipart/form-data\">\n";
30+
get_response += " <h1>Upload 1 (key is 'files', multiple files can be selected)</h1><br>\n";
31+
get_response += " <input type=\"file\" name=\"files\" multiple>\n";
32+
get_response += " <br><br>\n";
33+
get_response += " <h1>Upload 2 (key is 'files2', multiple files can be selected)</h1><br>\n";
34+
get_response += " <input type=\"file\" name=\"files2\" multiple><br><br>\n";
35+
get_response += " <input type=\"submit\" value=\"Upload\">\n";
36+
get_response += " </form>\n";
37+
get_response += " </body>\n";
38+
get_response += "</html>\n";
39+
40+
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response(get_response, 200, "text/html"));
41+
}
42+
43+
const std::shared_ptr<httpserver::http_response> render_POST(const httpserver::http_request& req) {
44+
std::string post_response = "<html>\n";
45+
post_response += "<head>\n";
46+
post_response += " <style>\n";
47+
post_response += " table, th, td {\n";
48+
post_response += " border: 1px solid black;\n";
49+
post_response += " border-collapse: collapse;\n";
50+
post_response += " }\n";
51+
post_response += " </style>\n";
52+
post_response += "</head>\n";
53+
post_response += "<body>\n";
54+
post_response += " Uploaded files:\n";
55+
post_response += " <br><br>\n";
56+
post_response += " <table>\n";
57+
post_response += " <tr>\n";
58+
post_response += " <th>Key</th>\n";
59+
post_response += " <th>Uploaded filename</th>\n";
60+
post_response += " <th>File system path</th>\n";
61+
post_response += " <th>File size</th>\n";
62+
post_response += " <th>Content type</th>\n";
63+
post_response += " <th>Transfer encoding</th>\n";
64+
post_response += " </tr>\n";
65+
66+
for (auto &file_key : req.get_files()) {
67+
for (auto &files : file_key.second) {
68+
post_response += " <tr><td>";
69+
post_response += file_key.first;
70+
post_response += "</td><td>";
71+
post_response += files.first;
72+
post_response += "</td><td>";
73+
post_response += files.second.get_file_system_file_name();
74+
post_response += "</td><td>";
75+
post_response += std::to_string(files.second.get_file_size());
76+
post_response += "</td><td>";
77+
post_response += files.second.get_content_type();
78+
post_response += "</td><td>";
79+
post_response += files.second.get_transfer_encoding();
80+
post_response += "</td></tr>\n";
81+
}
82+
}
83+
84+
post_response += " </table><br><br>\n";
85+
post_response += " <a href=\"/\">back</a>\n";
86+
post_response += "</body>\n</html>";
87+
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response(post_response, 201, "text/html"));
88+
}
89+
};
90+
91+
int main(int argc, char** argv) {
92+
// this example needs a directory as parameter
93+
if (2 != argc) {
94+
std::cout << "Usage: file_upload <upload_dir>" << std::endl;
95+
std::cout << std::endl;
96+
std::cout << " file_upload: writeable directory where uploaded files will be stored" << std::endl;
97+
return -1;
98+
}
99+
100+
std::cout << "CAUTION: this example will create files in the directory " << std::string(argv[1]) << std::endl;
101+
std::cout << "These files won't be deleted at termination" << std::endl;
102+
std::cout << "Please make sure, that the given directory exists and is writeable" << std::endl;
103+
104+
httpserver::webserver ws = httpserver::create_webserver(8080)
105+
.no_put_processed_data_to_content()
106+
.file_upload_dir(std::string(argv[1]))
107+
.generate_random_filename_on_upload()
108+
.file_upload_target(httpserver::FILE_UPLOAD_DISK_ONLY);
109+
110+
file_upload_resource fur;
111+
ws.register_resource("/", &fur);
112+
ws.start(true);
113+
114+
return 0;
115+
}
116+

src/Makefile.am

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/
2020
METASOURCES = AUTO
2121
lib_LTLIBRARIES = libhttpserver.la
22-
libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp details/http_endpoint.cpp
22+
libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp file_info.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp details/http_endpoint.cpp
2323
noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp gettext.h
24-
nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/basic_auth_fail_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp
24+
nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/file_info.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/basic_auth_fail_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp
2525

2626
AM_CXXFLAGS += -fPIC -Wall
2727

src/file_info.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011-2019 Sebastiano Merlino
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
USA
19+
*/
20+
21+
#include <string.h>
22+
#include "httpserver/file_info.hpp"
23+
24+
namespace httpserver {
25+
namespace http {
26+
27+
void file_info::set_file_system_file_name(const std::string& file_system_file_name) {
28+
_file_system_file_name = file_system_file_name;
29+
}
30+
31+
void file_info::set_content_type(const std::string& content_type) {
32+
_content_type = content_type;
33+
}
34+
35+
void file_info::set_transfer_encoding(const std::string& transfer_encoding) {
36+
_transfer_encoding = transfer_encoding;
37+
}
38+
39+
void file_info::grow_file_size(size_t additional_file_size) {
40+
_file_size += additional_file_size;
41+
}
42+
size_t file_info::get_file_size() const {
43+
return _file_size;
44+
}
45+
const std::string file_info::get_file_system_file_name() const {
46+
return _file_system_file_name;
47+
}
48+
const std::string file_info::get_content_type() const {
49+
return _content_type;
50+
}
51+
const std::string file_info::get_transfer_encoding() const {
52+
return _transfer_encoding;
53+
}
54+
55+
} // namespace http
56+
} // namespace httpserver

src/http_request.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ const std::map<std::string, std::string, http::arg_comparator> http_request::get
127127
return arguments;
128128
}
129129

130+
http::file_info& http_request::get_or_create_file_info(const std::string& key, const std::string& upload_file_name) {
131+
return files[key][upload_file_name];
132+
}
133+
130134
const std::string http_request::get_querystring() const {
131135
std::string querystring = "";
132136

src/http_utils.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
#if defined(_WIN32) && !defined(__CYGWIN__)
2424
#include <winsock2.h>
2525
#include <ws2tcpip.h>
26+
#include <io.h>
27+
#include <sys/stat.h>
28+
#include <fcntl.h>
29+
#include <share.h>
2630
#else // WIN32 check
2731
#include <arpa/inet.h>
2832
#include <netdb.h>
@@ -33,6 +37,7 @@
3337

3438
#include <stdio.h>
3539
#include <stdlib.h>
40+
#include <string.h>
3641
#include <fstream>
3742
#include <iomanip>
3843
#include <iterator>
@@ -198,6 +203,14 @@ const char* http_utils::http_post_encoding_multipart_formdata = MHD_HTTP_POST_EN
198203
const char* http_utils::application_octet_stream = "application/octet-stream";
199204
const char* http_utils::text_plain = "text/plain";
200205

206+
const char* http_utils::upload_filename_template = "libhttpserver.XXXXXX";
207+
208+
#if defined(_WIN32)
209+
const char http_utils::path_separator = '\\';
210+
#else // _WIN32
211+
const char http_utils::path_separator = '/';
212+
#endif // _WIN32
213+
201214
std::vector<std::string> http_utils::tokenize_url(const std::string& str, const char separator) {
202215
return string_utilities::string_split(str, separator);
203216
}
@@ -221,6 +234,45 @@ std::string http_utils::standardize_url(const std::string& url) {
221234
return result;
222235
}
223236

237+
const std::string http_utils::generate_random_upload_filename(const std::string& directory) {
238+
std::string filename = directory + http_utils::path_separator + http_utils::upload_filename_template;
239+
char *template_filename = strdup(filename.c_str());
240+
int fd = 0;
241+
242+
#if defined(_WIN32)
243+
// only function for win32 which creates unique filenames and can handle a given template including a path
244+
// all other functions like tmpnam() always create filenames in the 'temp' directory
245+
if (0 != _mktemp_s(template_filename, filename.size() + 1)) {
246+
free(template_filename);
247+
throw generateFilenameException("Failed to create unique filename");
248+
}
249+
250+
// as no existing file should be overwritten the operation should fail if the file already exists
251+
// fstream or ofstream classes don't feature such an option
252+
// with the function _sopen_s this can be achieved by setting the flag _O_EXCL
253+
if (0 != _sopen_s(&fd, template_filename, _O_CREAT | _O_EXCL | _O_NOINHERIT, _SH_DENYNO, _S_IREAD | _S_IWRITE)) {
254+
free(template_filename);
255+
throw generateFilenameException("Failed to create file");
256+
}
257+
if (fd == -1) {
258+
free(template_filename);
259+
throw generateFilenameException("File descriptor after successful _sopen_s is -1");
260+
}
261+
_close(fd);
262+
#else // _WIN32
263+
fd = mkstemp(template_filename);
264+
265+
if (fd == -1) {
266+
free(template_filename);
267+
throw generateFilenameException("Failed to create unique file");
268+
}
269+
close(fd);
270+
#endif // _WIN32
271+
std::string ret_filename = template_filename;
272+
free(template_filename);
273+
return ret_filename;
274+
}
275+
224276
std::string get_ip_str(const struct sockaddr *sa) {
225277
if (!sa) throw std::invalid_argument("socket pointer is null");
226278

src/httpserver.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "httpserver/http_resource.hpp"
3232
#include "httpserver/http_response.hpp"
3333
#include "httpserver/http_utils.hpp"
34+
#include "httpserver/file_info.hpp"
3435
#include "httpserver/string_response.hpp"
3536
#include "httpserver/webserver.hpp"
3637

0 commit comments

Comments
 (0)