C++11 単一ファイル ヘッダーのみのクロス プラットフォーム HTTP/HTTPS ライブラリ。
セットアップは非常に簡単です。あなたのコードにhttplib.hファイルを含めるだけです!
注: このライブラリは「ブロッキング」ソケット I/O を使用します。「非ブロッキング」ソケットI / Oを持つライブラリを探しているなら、これはあなたが望むものではありません。
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "path/to/httplib.h"
// HTTP
httplib::Server svr;
// HTTPS
httplib::SSLServer svr;
svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
res.set_content("Hello World!", "text/plain");
});
svr.listen("0.0.0.0", 8080);
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "path/to/httplib.h"
// HTTP
httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co");
// HTTPS
httplib::Client cli("https://cpp-httplib-server.yhirose.repl.co");
auto res = cli.Get("/hi");
res->status;
res->body;
SSL サポートは、 で使用できます。 リンクする必要があります。
CPPHTTPLIB_OPENSSL_SUPPORT
libssl
libcrypto
注: cpp-httplib は現在、バージョン 1.1.1 および 3.0 のみをサポートしています。
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "path/to/httplib.h"
// Server
httplib::SSLServer svr("./cert.pem", "./key.pem");
// Client
httplib::Client cli("https://localhost:1234"); // scheme + host
httplib::SSLClient cli("localhost:1234"); // host
httplib::SSLClient cli("localhost", 1234); // host, port
// Use your CA bundle
cli.set_ca_cert_path("./ca-bundle.crt");
// Disable cert verification
cli.enable_server_certificate_verification(false);
注: SSL を使用する場合、一部のオペレーティング・システムでは SIGPIPE はメッセージ単位でしか抑制できないため、すべての場合に SIGPIPE を回避することは不可能に思えますが、OpenSSL ライブラリーの内部通信のためにそうさせる方法はありません。プログラムが SIGPIPE で終了しないようにする必要がある場合、唯一の完全に一般的な方法は、SIGPIPE が自分で処理または無視するためのシグナルハンドラを設定することです。
#include <httplib.h>
int main(void)
{
using namespace httplib;
Server svr;
svr.Get("/hi", [](const Request& req, Response& res) {
res.set_content("Hello World!", "text/plain");
});
svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) {
auto numbers = req.matches[1];
res.set_content(numbers, "text/plain");
});
svr.Get("/body-header-param", [](const Request& req, Response& res) {
if (req.has_header("Content-Length")) {
auto val = req.get_header_value("Content-Length");
}
if (req.has_param("key")) {
auto val = req.get_param_value("key");
}
res.set_content(req.body, "text/plain");
});
svr.Get("/stop", [&](const Request& req, Response& res) {
svr.stop();
});
svr.listen("localhost", 1234);
}
Post、、およびメソッドもサポートされています。
Put
Delete
Options
int port = svr.bind_to_any_port("0.0.0.0");
svr.listen_after_bind();
// Mount / to ./www directory
auto ret = svr.set_mount_point("/", "./www");
if (!ret) {
// The specified base directory doesn't exist...
}
// Mount /public to ./www directory
ret = svr.set_mount_point("/public", "./www");
// Mount /public to ./www1 and ./www2 directories
ret = svr.set_mount_point("/public", "./www1"); // 1st order to search
ret = svr.set_mount_point("/public", "./www2"); // 2nd order to search
// Remove mount /
ret = svr.remove_mount_point("/");
// Remove mount /public
ret = svr.remove_mount_point("/public");
// User defined file extension and MIME type mappings
svr.set_file_extension_and_mimetype_mapping("cc", "text/x-c");
svr.set_file_extension_and_mimetype_mapping("cpp", "text/x-c");
svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");
組み込みのマッピングを次に示します。
延長 | MIME タイプ | 延長 | MIME タイプ |
---|---|---|---|
.css | テキスト/CSS | ティッカー | オーディオ/MPEG |
.csv | テキスト/CSV | ウェバ | オーディオ/ウェブ |
.txt | テキスト/プレーン | .wav | オーディオ/ウェーブ |
ティッカー | テキスト/VTT | .otf | フォント/OTF |
html, htm | テキスト/HTML | .ttf | フォント/TTF |
ティッカー | 画像/APNG | ウォフ | フォント/ウォフ |
アビフ | 画像/アビフ | ウォフ2 | フォント/woff2 |
.bmp | 画像/BMP | .7z | アプリケーション/X-7Z圧縮 |
.gif | 画像/GIF | 原子 | アプリケーション/アトム+XML |
.png | 画像/PNG | アプリケーション/PDF | |
.svg | 画像/SVG+XML | MJS, JS | アプリケーション/ジャバスクリプト |
.webp | 画像/ウェブ | ジェイソン | アプリケーション/JSON |
.ico | 画像/Xアイコン | .rss | アプリケーション/RSS+XML |
.tif | 画像/ティフ | .tar | アプリケーション/X-tar |
.tiff | 画像/ティフ | xhtml, xht | application/xhtml+xml |
JPEG, JPG | 画像/JPEG | ティッカー | application/xslt+xml |
.mp4 | ビデオ/MP4 | .xml | アプリケーション/XML |
.mpeg | ビデオ/MPEG | .gz | アプリケーション/gzip |
.webm | ビデオ/ウェブ | .zip | アプリケーション/郵便番号 |
.mp3 | オーディオ/MP3 | ワズム | アプリケーション/WASM |
// The handler is called right before the response is sent to a client
svr.set_file_request_handler([](const Request &req, Response &res) {
...
});
注: これらの静的ファイル サーバー メソッドはスレッド セーフではありません。
svr.set_logger([](const auto& req, const auto& res) {
your_logger(req, res);
});
svr.set_error_handler([](const auto& req, auto& res) {
auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
char buf[BUFSIZ];
snprintf(buf, sizeof(buf), fmt, res.status);
res.set_content(buf, "text/html");
});
例外ハンドラーは、ユーザー ルーティング ハンドラーがエラーをスローした場合に呼び出されます。
svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) {
auto fmt = "<h1>Error 500</h1><p>%s</p>";
char buf[BUFSIZ];
try {
std::rethrow_exception(ep);
} catch (std::exception &e) {
snprintf(buf, sizeof(buf), fmt, e.what());
} catch (...) { // See the following NOTE
snprintf(buf, sizeof(buf), fmt, "Unknown Exception");
}
res.set_content(buf, "text/html");
res.status = 500;
});
注: 再スローされた例外ポインターのブロックを指定しない場合、キャッチされない例外によってサーバーがクラッシュします。注意してください!
catch (...)
svr.set_pre_routing_handler([](const auto& req, auto& res) {
if (req.path == "/hello") {
res.set_content("world", "text/html");
return Server::HandlerResponse::Handled;
}
return Server::HandlerResponse::Unhandled;
});
svr.set_post_routing_handler([](const auto& req, auto& res) {
res.set_header("ADDITIONAL_HEADER", "value");
});
svr.Post("/multipart", [&](const auto& req, auto& res) {
auto size = req.files.size();
auto ret = req.has_file("name1");
const auto& file = req.get_file_value("name1");
// file.filename;
// file.content_type;
// file.content;
});
svr.Post("/content_receiver",
[&](const Request &req, Response &res, const ContentReader &content_reader) {
if (req.is_multipart_form_data()) {
// NOTE: `content_reader` is blocking until every form data field is read
MultipartFormDataItems files;
content_reader(
[&](const MultipartFormData &file) {
files.push_back(file);
return true;
},
[&](const char *data, size_t data_length) {
files.back().content.append(data, data_length);
return true;
});
} else {
std::string body;
content_reader([&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
}
});
const size_t DATA_CHUNK_SIZE = 4;
svr.Get("/stream", [&](const Request &req, Response &res) {
auto data = new std::string("abcdefg");
res.set_content_provider(
data->size(), // Content length
"text/plain", // Content type
[data](size_t offset, size_t length, DataSink &sink) {
const auto &d = *data;
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
return true; // return 'false' if you want to cancel the process.
},
[data](bool success) { delete data; });
});
コンテンツの長さなし:
svr.Get("/stream", [&](const Request &req, Response &res) {
res.set_content_provider(
"text/plain", // Content type
[&](size_t offset, DataSink &sink) {
if (/* there is still data */) {
std::vector<char> data;
// prepare data...
sink.write(data.data(), data.size());
} else {
sink.done(); // No more data
}
return true; // return 'false' if you want to cancel the process.
});
});
svr.Get("/chunked", [&](const Request& req, Response& res) {
res.set_chunked_content_provider(
"text/plain",
[](size_t offset, DataSink &sink) {
sink.write("123", 3);
sink.write("345", 3);
sink.write("789", 3);
sink.done(); // No more data
return true; // return 'false' if you want to cancel the process.
}
);
});
既定では、サーバーはヘッダーの応答を送信します。
100 Continue
Expect: 100-continue
// Send a '417 Expectation Failed' response.
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
return 417;
});
// Send a final status without reading the message body.
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
return res.status = 401;
});
svr.set_keep_alive_max_count(2); // Default is 5
svr.set_keep_alive_timeout(10); // Default is 5
svr.set_read_timeout(5, 0); // 5 seconds
svr.set_write_timeout(5, 0); // 5 seconds
svr.set_idle_interval(0, 100000); // 100 milliseconds
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB
ThreadPoolは既定のタスク キューとして使用され、既定のスレッド数は 8 または です。で変更できます。
std::thread::hardware_concurrency()
CPPHTTPLIB_THREAD_POOL_COUNT
実行時にスレッド数を設定したい場合は、便利な方法はありません...しかし、ここに方法があります。
svr.new_task_queue = [] { return new ThreadPool(12); };
必要に応じて、独自のスレッドプール実装を提供できます。
class YourThreadPoolTaskQueue : public TaskQueue {
public:
YourThreadPoolTaskQueue(size_t n) {
pool_.start_with_thread_count(n);
}
virtual void enqueue(std::function<void()> fn) override {
pool_.enqueue(fn);
}
virtual void shutdown() override {
pool_.shutdown_gracefully();
}
private:
YourThreadPool pool_;
};
svr.new_task_queue = [] {
return new YourThreadPoolTaskQueue(12);
};
#include <httplib.h>
#include <iostream>
int main(void)
{
httplib::Client cli("localhost", 1234);
if (auto res = cli.Get("/hi")) {
if (res->status == 200) {
std::cout << res->body << std::endl;
}
} else {
auto err = res.error();
std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
}
}
注:スキームホストポート文字列を持つコンストラクタがサポートされるようになりました!
httplib::Client cli("localhost");
httplib::Client cli("localhost:8080");
httplib::Client cli("http://localhost");
httplib::Client cli("http://localhost:8080");
httplib::Client cli("https://localhost");
httplib::SSLClient cli("localhost");
からのエラーのリストは次のとおりです。
Result::error()
enum Error {
Success = 0,
Unknown,
Connection,
BindIPAddress,
Read,
Write,
ExceedRedirectCount,
Canceled,
SSLConnection,
SSLLoadingCerts,
SSLServerVerification,
UnsupportedMultipartBoundaryChars,
Compression,
ConnectionTimeout,
};
httplib::Headers headers = {
{ "Accept-Encoding", "gzip, deflate" }
};
auto res = cli.Get("/hi", headers);
又は
cli.set_default_headers({
{ "Accept-Encoding", "gzip, deflate" }
});
auto res = cli.Get("/hi");
res = cli.Post("/post", "text", "text/plain");
res = cli.Post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded");
httplib::Params params;
params.emplace("name", "john");
params.emplace("note", "coder");
auto res = cli.Post("/post", params);
又は
httplib::Params params{
{ "name", "john" },
{ "note", "coder" }
};
auto res = cli.Post("/post", params);
httplib::MultipartFormDataItems items = {
{ "text1", "text default", "", "" },
{ "text2", "aωb", "", "" },
{ "file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" },
{ "file2", "{\n \"world\", true\n}\n", "world.json", "application/json" },
{ "file3", "", "", "application/octet-stream" },
};
auto res = cli.Post("/multipart", items);
res = cli.Put("/resource/foo", "text", "text/plain");
res = cli.Delete("/resource/foo");
res = cli.Options("*");
res = cli.Options("/resource/foo");
cli.set_connection_timeout(0, 300000); // 300 milliseconds
cli.set_read_timeout(5, 0); // 5 seconds
cli.set_write_timeout(5, 0); // 5 seconds
std::string body;
auto res = cli.Get("/large-data",
[&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
std::string body;
auto res = cli.Get(
"/stream", Headers(),
[&](const Response &response) {
EXPECT_EQ(200, response.status);
return true; // return 'false' if you want to cancel the request.
},
[&](const char *data, size_t data_length) {
body.append(data, data_length);
return true; // return 'false' if you want to cancel the request.
});
std::string body = ...;
auto res = cli.Post(
"/stream", body.size(),
[](size_t offset, size_t length, DataSink &sink) {
sink.write(body.data() + offset, length);
return true; // return 'false' if you want to cancel the request.
},
"text/plain");
auto res = cli.Post(
"/stream",
[](size_t offset, DataSink &sink) {
sink.os << "chunked data 1";
sink.os << "chunked data 2";
sink.os << "chunked data 3";
sink.done();
return true; // return 'false' if you want to cancel the request.
},
"text/plain");
httplib::Client client(url, port);
// prints: 0 / 000 bytes => 50% complete
auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
printf("%lld / %lld bytes => %d%% complete\n",
len, total,
(int)(len*100/total));
return true; // return 'false' if you want to cancel the request.
}
);
// Basic Authentication
cli.set_basic_auth("user", "pass");
// Digest Authentication
cli.set_digest_auth("user", "pass");
// Bearer Token Authentication
cli.set_bearer_token_auth("token");
NOTE: OpenSSL is required for Digest Authentication.
cli.set_proxy("host", port);
// Basic Authentication
cli.set_proxy_basic_auth("user", "pass");
// Digest Authentication
cli.set_proxy_digest_auth("user", "pass");
// Bearer Token Authentication
cli.set_proxy_bearer_token_auth("pass");
NOTE: OpenSSL is required for Digest Authentication.
httplib::Client cli("httpbin.org");
auto res = cli.Get("/range/32", {
httplib::make_range_header({{1, 10}}) // 'Range: bytes=1-10'
});
// res->status should be 206.
// res->body should be "bcdefghijk".
httplib::make_range_header({{1, 10}, {20, -1}}) // 'Range: bytes=1-10, 20-'
httplib::make_range_header({{100, 199}, {500, 599}}) // 'Range: bytes=100-199, 500-599'
httplib::make_range_header({{0, 0}, {-1, 1}}) // 'Range: bytes=0-0, -1'
httplib::Client cli("localhost", 1234);
cli.Get("/hello"); // with "Connection: close"
cli.set_keep_alive(true);
cli.Get("/world");
cli.set_keep_alive(false);
cli.Get("/last-request"); // with "Connection: close"
httplib::Client cli("yahoo.com");
auto res = cli.Get("/");
res->status; // 301
cli.set_follow_location(true);
res = cli.Get("/");
res->status; // 200
NOTE: This feature is not available on Windows, yet.
cli.set_interface("eth0"); // Interface name, IP address or host name
The server can apply compression to the following MIME type contents:
'gzip' compression is available with . should be linked.
CPPHTTPLIB_ZLIB_SUPPORT
libz
Brotli compression is available with . Necessary libraries should be linked. Please see https://github.com/google/brotli for more detail.
CPPHTTPLIB_BROTLI_SUPPORT
cli.set_compress(true);
res = cli.Post("/resource/foo", "...", "text/plain");
cli.set_decompress(false);
res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
res->body; // Compressed data
poll
select
selectsystem call is used as default since it's more widely supported. If you want to let cpp-httplib use instead, you can do so with .
poll
CPPHTTPLIB_USE_POLL
$ ./split.py -h
usage: split.py [-h] [-e EXTENSION] [-o OUT]
This script splits httplib.h into .h and .cc parts.
optional arguments:
-h, --help show this help message and exit
-e EXTENSION, --extension EXTENSION
extension of the implementation file (default: cc)
-o OUT, --out OUT where to write the files (default: out)
$ ./split.py
Wrote out/httplib.h and out/httplib.cc
G ++ 4.8以下では、バージョンが壊れているため、このライブラリをビルドできません。
<regex>
前に含めるか、事前に定義して含めます。
httplib.h
Windows.h
Windows.h
WIN32_LEAN_AND_MEAN
#include <httplib.h>
#include <Windows.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <httplib.h>
注: cpp-httplib は公式には最新の Visual Studio のみをサポートしています。以前のバージョンのVisual Studioで動作する可能性がありますが、確認できなくなりました。プル要求は、C++11 準拠に違反しない限り、古いバージョンの Visual Studio では常に歓迎されます。
注: Windows 8 以前、Visual Studio 2013 以前、および Windows 上の Cygwin はサポートされていません。
MITライセンス(© 2022 広瀬 雄二)
これらの人々は、このライブラリを単純なおもちゃからまったく別のレベルに磨くために多大な貢献をしました!