1
0
mirror of synced 2025-04-20 11:47:43 +03:00

Changed set_file_content to accept only a regular file path.

This commit is contained in:
yhirose 2024-09-09 19:59:18 -04:00
parent 3f2922b3fa
commit 7ab9c119ef
3 changed files with 90 additions and 91 deletions

164
README.md
View File

@ -97,37 +97,33 @@ int main(void)
Server svr; Server svr;
svr.Get("/hi", [](const Request& req, Response& res) { svr.Get("/hi", [](const Request &req, Response &res) {
res.set_content("Hello World!", "text/plain"); res.set_content("Hello World!", "text/plain");
}); });
// Match the request path against a regular expression // Match the request path against a regular expression
// and extract its captures // and extract its captures
svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) { svr.Get(R"(/numbers/(\d+))", [&](const Request &req, Response &res) {
auto numbers = req.matches[1]; auto numbers = req.matches[1];
res.set_content(numbers, "text/plain"); res.set_content(numbers, "text/plain");
}); });
// Capture the second segment of the request path as "id" path param // Capture the second segment of the request path as "id" path param
svr.Get("/users/:id", [&](const Request& req, Response& res) { svr.Get("/users/:id", [&](const Request &req, Response &res) {
auto user_id = req.path_params.at("id"); auto user_id = req.path_params.at("id");
res.set_content(user_id, "text/plain"); res.set_content(user_id, "text/plain");
}); });
// Extract values from HTTP headers and URL query params // Extract values from HTTP headers and URL query params
svr.Get("/body-header-param", [](const Request& req, Response& res) { svr.Get("/body-header-param", [](const Request &req, Response &res) {
if (req.has_header("Content-Length")) { if (req.has_header("Content-Length")) {
auto val = req.get_header_value("Content-Length"); auto val = req.get_header_value("Content-Length");
} }
if (req.has_param("key")) { if (req.has_param("key")) { auto val = req.get_param_value("key"); }
auto val = req.get_param_value("key");
}
res.set_content(req.body, "text/plain"); res.set_content(req.body, "text/plain");
}); });
svr.Get("/stop", [&](const Request& req, Response& res) { svr.Get("/stop", [&](const Request &req, Response &res) { svr.stop(); });
svr.stop();
});
svr.listen("localhost", 1234); svr.listen("localhost", 1234);
} }
@ -276,7 +272,7 @@ svr.set_post_routing_handler([](const auto& req, auto& res) {
svr.Post("/multipart", [&](const auto& req, auto& res) { svr.Post("/multipart", [&](const auto& req, auto& res) {
auto size = req.files.size(); auto size = req.files.size();
auto ret = req.has_file("name1"); auto ret = req.has_file("name1");
const auto& file = req.get_file_value("name1"); const auto &file = req.get_file_value("name1");
// file.filename; // file.filename;
// file.content_type; // file.content_type;
// file.content; // file.content;
@ -288,10 +284,10 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
```cpp ```cpp
svr.Post("/content_receiver", svr.Post("/content_receiver",
[&](const Request &req, Response &res, const ContentReader &content_reader) { [&](const Request &req, Response &res, const ContentReader &content_reader) {
if (req.is_multipart_form_data()) { if (req.is_multipart_form_data()) {
// NOTE: `content_reader` is blocking until every form data field is read // NOTE: `content_reader` is blocking until every form data field is read
MultipartFormDataItems files; MultipartFormDataItems files;
content_reader( content_reader(
[&](const MultipartFormData &file) { [&](const MultipartFormData &file) {
files.push_back(file); files.push_back(file);
return true; return true;
@ -300,13 +296,13 @@ svr.Post("/content_receiver",
files.back().content.append(data, data_length); files.back().content.append(data, data_length);
return true; return true;
}); });
} else { } else {
std::string body; std::string body;
content_reader([&](const char *data, size_t data_length) { content_reader([&](const char *data, size_t data_length) {
body.append(data, data_length); body.append(data, data_length);
return true; return true;
}); });
} }
}); });
``` ```
@ -319,14 +315,14 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
auto data = new std::string("abcdefg"); auto data = new std::string("abcdefg");
res.set_content_provider( res.set_content_provider(
data->size(), // Content length data->size(), // Content length
"text/plain", // Content type "text/plain", // Content type
[&, data](size_t offset, size_t length, DataSink &sink) { [&, data](size_t offset, size_t length, DataSink &sink) {
const auto &d = *data; const auto &d = *data;
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE)); sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
return true; // return 'false' if you want to cancel the process. return true; // return 'false' if you want to cancel the process.
}, },
[data](bool success) { delete data; }); [data](bool success) { delete data; });
}); });
``` ```
@ -335,17 +331,17 @@ Without content length:
```cpp ```cpp
svr.Get("/stream", [&](const Request &req, Response &res) { svr.Get("/stream", [&](const Request &req, Response &res) {
res.set_content_provider( res.set_content_provider(
"text/plain", // Content type "text/plain", // Content type
[&](size_t offset, DataSink &sink) { [&](size_t offset, DataSink &sink) {
if (/* there is still data */) { if (/* there is still data */) {
std::vector<char> data; std::vector<char> data;
// prepare data... // prepare data...
sink.write(data.data(), data.size()); sink.write(data.data(), data.size());
} else { } else {
sink.done(); // No more data sink.done(); // No more data
} }
return true; // return 'false' if you want to cancel the process. return true; // return 'false' if you want to cancel the process.
}); });
}); });
``` ```
@ -354,15 +350,13 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
```cpp ```cpp
svr.Get("/chunked", [&](const Request& req, Response& res) { svr.Get("/chunked", [&](const Request& req, Response& res) {
res.set_chunked_content_provider( res.set_chunked_content_provider(
"text/plain", "text/plain", [](size_t offset, DataSink &sink) {
[](size_t offset, DataSink &sink) { sink.write("123", 3);
sink.write("123", 3); sink.write("345", 3);
sink.write("345", 3); sink.write("789", 3);
sink.write("789", 3); sink.done(); // No more data
sink.done(); // No more data return true; // return 'false' if you want to cancel the process.
return true; // return 'false' if you want to cancel the process. });
}
);
}); });
``` ```
@ -371,24 +365,21 @@ With trailer:
```cpp ```cpp
svr.Get("/chunked", [&](const Request& req, Response& res) { svr.Get("/chunked", [&](const Request& req, Response& res) {
res.set_header("Trailer", "Dummy1, Dummy2"); res.set_header("Trailer", "Dummy1, Dummy2");
res.set_chunked_content_provider( res.set_chunked_content_provider("text/plain", [](size_t offset,
"text/plain", DataSink &sink) {
[](size_t offset, DataSink &sink) { sink.write("123", 3);
sink.write("123", 3); sink.write("345", 3);
sink.write("345", 3); sink.write("789", 3);
sink.write("789", 3); sink.done_with_trailer({{"Dummy1", "DummyVal1"}, {"Dummy2", "DummyVal2"}});
sink.done_with_trailer({ return true;
{"Dummy1", "DummyVal1"}, });
{"Dummy2", "DummyVal2"}
});
return true;
}
);
}); });
``` ```
### Send file content ### Send file content
We can set a file path for the response body. It's a user's responsibility to pass a valid regular file path. If the path doesn't exist, or a directory path, cpp-httplib throws an exception.
```cpp ```cpp
svr.Get("/content", [&](const Request &req, Response &res) { svr.Get("/content", [&](const Request &req, Response &res) {
res.set_file_content("./path/to/conent.html"); res.set_file_content("./path/to/conent.html");
@ -452,7 +443,8 @@ Please see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/e
If you want to set the thread count at runtime, there is no convenient way... But here is how. If you want to set the thread count at runtime, there is no convenient way... But here is how.
```cpp ```cpp
svr.new_task_queue = [] { return new ThreadPool(12); }; svr.new_task_queue = [] {
return new ThreadPool(12); };
``` ```
You can also provide an optional parameter to limit the maximum number You can also provide an optional parameter to limit the maximum number
@ -460,7 +452,8 @@ of pending requests, i.e. requests `accept()`ed by the listener but
still waiting to be serviced by worker threads. still waiting to be serviced by worker threads.
```cpp ```cpp
svr.new_task_queue = [] { return new ThreadPool(/*num_threads=*/12, /*max_queued_requests=*/18); }; svr.new_task_queue = [] {
return new ThreadPool(/*num_threads=*/12, /*max_queued_requests=*/18); };
``` ```
Default limit is 0 (unlimited). Once the limit is reached, the listener Default limit is 0 (unlimited). Once the limit is reached, the listener
@ -473,9 +466,7 @@ You can supply your own thread pool implementation according to your need.
```cpp ```cpp
class YourThreadPoolTaskQueue : public TaskQueue { class YourThreadPoolTaskQueue : public TaskQueue {
public: public:
YourThreadPoolTaskQueue(size_t n) { YourThreadPoolTaskQueue(size_t n) { pool_.start_with_thread_count(n); }
pool_.start_with_thread_count(n);
}
virtual bool enqueue(std::function<void()> fn) override { virtual bool enqueue(std::function<void()> fn) override {
/* Return true if the task was actually enqueued, or false /* Return true if the task was actually enqueued, or false
@ -483,9 +474,7 @@ public:
return pool_.enqueue(fn); return pool_.enqueue(fn);
} }
virtual void shutdown() override { virtual void shutdown() override { pool_.shutdown_gracefully(); }
pool_.shutdown_gracefully();
}
private: private:
YourThreadPool pool_; YourThreadPool pool_;
@ -648,8 +637,8 @@ std::string body;
auto res = cli.Get("/large-data", auto res = cli.Get("/large-data",
[&](const char *data, size_t data_length) { [&](const char *data, size_t data_length) {
body.append(data, data_length); body.append(data, data_length);
return true; return true;
}); });
``` ```
@ -659,12 +648,12 @@ std::string body;
auto res = cli.Get( auto res = cli.Get(
"/stream", Headers(), "/stream", Headers(),
[&](const Response &response) { [&](const Response &response) {
EXPECT_EQ(StatusCode::OK_200, response.status); EXPECT_EQ(StatusCode::OK_200, response.status);
return true; // return 'false' if you want to cancel the request. return true; // return 'false' if you want to cancel the request.
}, },
[&](const char *data, size_t data_length) { [&](const char *data, size_t data_length) {
body.append(data, data_length); body.append(data, data_length);
return true; // return 'false' if you want to cancel the request. return true; // return 'false' if you want to cancel the request.
}); });
``` ```
@ -676,8 +665,8 @@ std::string body = ...;
auto res = cli.Post( auto res = cli.Post(
"/stream", body.size(), "/stream", body.size(),
[](size_t offset, size_t length, DataSink &sink) { [](size_t offset, size_t length, DataSink &sink) {
sink.write(body.data() + offset, length); sink.write(body.data() + offset, length);
return true; // return 'false' if you want to cancel the request. return true; // return 'false' if you want to cancel the request.
}, },
"text/plain"); "text/plain");
``` ```
@ -688,11 +677,11 @@ auto res = cli.Post(
auto res = cli.Post( auto res = cli.Post(
"/stream", "/stream",
[](size_t offset, DataSink &sink) { [](size_t offset, DataSink &sink) {
sink.os << "chunked data 1"; sink.os << "chunked data 1";
sink.os << "chunked data 2"; sink.os << "chunked data 2";
sink.os << "chunked data 3"; sink.os << "chunked data 3";
sink.done(); sink.done();
return true; // return 'false' if you want to cancel the request. return true; // return 'false' if you want to cancel the request.
}, },
"text/plain"); "text/plain");
``` ```
@ -704,9 +693,8 @@ httplib::Client cli(url, port);
// prints: 0 / 000 bytes => 50% complete // prints: 0 / 000 bytes => 50% complete
auto res = cli.Get("/", [](uint64_t len, uint64_t total) { auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
printf("%lld / %lld bytes => %d%% complete\n", printf("%lld / %lld bytes => %d%% complete\n", len, total,
len, total, (int)(len * 100 / total));
(int)(len*100/total));
return true; // return 'false' if you want to cancel the request. return true; // return 'false' if you want to cancel the request.
} }
); );
@ -904,8 +892,8 @@ g++ 4.8 and below cannot build this library since `<regex>` in the versions are
Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32_LEAN_AND_MEAN` beforehand. Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32_LEAN_AND_MEAN` beforehand.
```cpp ```cpp
#include <httplib.h>
#include <Windows.h> #include <Windows.h>
#include <httplib.h>
``` ```
```cpp ```cpp

View File

@ -5752,12 +5752,21 @@ inline void Response::set_chunked_content_provider(
inline void Response::set_file_content(const std::string &path, inline void Response::set_file_content(const std::string &path,
const std::string &content_type) { const std::string &content_type) {
file_content_path_ = path; detail::FileStat stat(dir);
file_content_content_type_ = content_type; if (stat.is_file(path)) {
file_content_path_ = path;
file_content_content_type_ = content_type;
return;
}
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
std::string msg = "'" + path + "' is not a regular file.";
throw std::invalid_argument(msg);
#endif
} }
inline void Response::set_file_content(const std::string &path) { inline void Response::set_file_content(const std::string &path) {
file_content_path_ = path; return set_file_content(path, std::string());
} }
// Result implementation // Result implementation

View File

@ -2288,6 +2288,8 @@ protected:
{ {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
cli_.enable_server_certificate_verification(false); cli_.enable_server_certificate_verification(false);
#else
#error no ssl
#endif #endif
} }