Compare commits

..

5 Commits

Author SHA1 Message Date
yhirose 68fa9bce0f Release v0.37.1 2026-03-09 20:51:37 -04:00
yhirose 1f34c541b0 Add more books 2026-03-09 19:30:18 -04:00
yhirose e41ec36274 Fix handling of malformed Content-Length in open_stream and add tests 2026-03-08 22:30:20 -04:00
yhirose 7489fd3a8b Remove 32-bit limitation (#2388)
* Remove 32-bit limitation

* Fix build problems

* Add 32-bit disclaimer and fix MSVC x86 warnings

- Move 32-bit warning to top of README with strong disclaimer
- Add static_cast<size_t> to fix truncation warnings on 32-bit MSVC

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:07:21 -05:00
yhirose ab3098f18b Update README 2026-03-06 18:50:23 -05:00
16 changed files with 401 additions and 44 deletions
+36
View File
@@ -0,0 +1,36 @@
name: 32-bit Build Test
on:
push:
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
jobs:
test-win32:
name: Windows 32-bit (MSVC x86)
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Build (Win32)
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
cl /std:c++14 /EHsc /W4 /WX /c /Fo:NUL test\test_32bit_build.cpp
test-arm32:
name: ARM 32-bit (cross-compile)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install cross compiler
run: sudo apt-get update && sudo apt-get install -y g++-arm-linux-gnueabihf
- name: Build (ARM 32-bit)
run: arm-linux-gnueabihf-g++ -std=c++11 -Wall -Wextra -Wno-psabi -Werror -c -o /dev/null test/test_32bit_build.cpp
+2 -3
View File
@@ -40,7 +40,7 @@ jobs:
clang-format --version
cd test && make style_check
build-error-check-on-32bit:
build-and-test-on-32bit:
runs-on: ubuntu-latest
if: >
(github.event_name == 'push') ||
@@ -64,9 +64,8 @@ jobs:
libssl-dev${{ matrix.config.arch_suffix }} libcurl4-openssl-dev${{ matrix.config.arch_suffix }} \
zlib1g-dev${{ matrix.config.arch_suffix }} libbrotli-dev${{ matrix.config.arch_suffix }} \
libzstd-dev${{ matrix.config.arch_suffix }}
- name: build and run tests (expect failure)
- name: build and run tests
run: cd test && make test EXTRA_CXXFLAGS="${{ matrix.config.arch_flags }}"
continue-on-error: true
ubuntu:
runs-on: ubuntu-latest
-3
View File
@@ -173,9 +173,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
message(WARNING "The target is Windows but CMAKE_SYSTEM_VERSION is not set, the default system version is set to Windows 10.")
endif()
endif()
if(CMAKE_SIZEOF_VOID_P LESS 8)
message(WARNING "Pointer size ${CMAKE_SIZEOF_VOID_P} is not supported. Please use a 64-bit compiler.")
endif()
# Set some variables that are used in-tree and while building based on our options
set(HTTPLIB_IS_COMPILED ${HTTPLIB_COMPILE})
+4 -1
View File
@@ -5,11 +5,14 @@
A C++11 single-file header-only cross platform HTTP/HTTPS library.<br>
It's extremely easy to set up. Just include the **[httplib.h](https://raw.githubusercontent.com/yhirose/cpp-httplib/refs/heads/master/httplib.h)** file in your code!
**Learn more in the [official documentation](https://yhirose.github.io/cpp-httplib/)**.
Learn more in the [official documentation](https://yhirose.github.io/cpp-httplib/) (built with [docs-gen](https://github.com/yhirose/docs-gen)).
> [!IMPORTANT]
> This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.
> [!WARNING]
> 32-bit platforms are **NOT supported**. Use at your own risk. The library may compile on 32-bit targets, but no security review has been conducted for 32-bit environments. Integer truncation and other 32-bit-specific issues may exist. **Security reports that only affect 32-bit platforms will be closed without action.** The maintainer does not have access to 32-bit environments for testing or fixing issues. CI includes basic compile checks only, not functional or security testing.
## Main Features
- HTTP Server/Client
+11 -1
View File
@@ -4,7 +4,7 @@ langs = ["en", "ja"]
[site]
title = "cpp-httplib"
version = "0.37.0"
version = "0.37.1"
hostname = "https://yhirose.github.io"
base_path = "/cpp-httplib"
footer_message = "© 2026 Yuji Hirose. All rights reserved."
@@ -14,6 +14,16 @@ label = "Tour"
path = "tour/"
icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/></svg>'
[[nav]]
label = "Cookbook"
path = "cookbook/"
icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>'
[[nav]]
label = "LLM App"
path = "llm-app/"
icon_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>'
[[nav]]
label = "GitHub"
url = "https://github.com/yhirose/cpp-httplib"
+90 -3
View File
@@ -1,8 +1,95 @@
---
title: "Cookbook"
order: 1
order: 0
---
This section is under construction.
A collection of recipes that answer "How do I...?" questions. Each recipe is self-contained — read only what you need.
Check back soon for a collection of recipes organized by topic.
## Client
### Basics
- Get the response body as a string / save to a file
- Send and receive JSON
- Set default headers (`set_default_headers`)
- Follow redirects (`set_follow_location`)
### Authentication
- Use Basic authentication (`set_basic_auth`)
- Call an API with a Bearer token
### File Upload
- Upload a file as multipart form data (`make_file_provider`)
- POST a file as raw binary (`make_file_body`)
- Send the body with chunked transfer (Content Provider)
### Streaming & Progress
- Receive a response as a stream
- Use the progress callback (`set_progress`)
### Connection & Performance
- Set timeouts (`set_connection_timeout` / `set_read_timeout`)
- Set an overall timeout (`set_max_timeout`)
- Understand connection reuse and Keep-Alive behavior
- Enable compression (`set_compress` / `set_decompress`)
- Send requests through a proxy (`set_proxy`)
### Error Handling & Debugging
- Handle error codes (`Result::error()`)
- Handle SSL errors (`ssl_error()` / `ssl_backend_error()`)
- Set up client logging (`set_logger` / `set_error_logger`)
## Server
### Basics
- Register GET / POST / PUT / DELETE handlers
- Receive JSON requests and return JSON responses
- Use path parameters (`/users/:id`)
- Set up a static file server (`set_mount_point`)
### Streaming & Files
- Stream a large file in the response (`ContentProvider`)
- Return a file download response (`Content-Disposition`)
- Receive multipart data as a stream (`ContentReader`)
- Compress responses (gzip)
### Handler Chain
- Add pre-processing to all routes (Pre-routing handler)
- Add response headers with a Post-routing handler (CORS, etc.)
- Authenticate per route with a Pre-request handler (`matched_route`)
- Pass data between handlers with `res.user_data`
### Error Handling & Debugging
- Return custom error pages (`set_error_handler`)
- Catch exceptions (`set_exception_handler`)
- Log requests (Logger)
- Detect client disconnection (`req.is_connection_closed()`)
### Operations & Tuning
- Assign a port dynamically (`bind_to_any_port`)
- Control startup order with `listen_after_bind`
- Shut down gracefully (`stop()` and signal handling)
- Tune Keep-Alive (`set_keep_alive_max_count` / `set_keep_alive_timeout`)
- Configure the thread pool (`new_task_queue`)
## TLS / Security
- Choosing between OpenSSL, mbedTLS, and wolfSSL (build-time `#define` differences)
- Control SSL certificate verification (disable, custom CA, custom callback)
- Use a custom certificate verification callback (`set_server_certificate_verifier`)
- Set up an SSL/TLS server (certificate and private key)
- Configure mTLS (mutual TLS with client certificates)
- Access the peer certificate on the server (`req.peer_cert()` / SNI)
## SSE
- Implement an SSE server
- Use event names to distinguish event types
- Handle reconnection (`Last-Event-ID`)
- Receive SSE events on the client
## WebSocket
- Implement a WebSocket echo server and client
- Configure heartbeats (`set_websocket_ping_interval`)
- Handle connection close
- Send and receive binary frames
+1
View File
@@ -19,3 +19,4 @@ Under the hood, it uses blocking I/O with a thread pool. It's not built for hand
- [A Tour of cpp-httplib](tour/) — A step-by-step tutorial covering the basics. Start here if you're new
- [Cookbook](cookbook/) — A collection of recipes organized by topic. Jump to whatever you need
- [Building a Desktop LLM App](llm-app/) — A hands-on guide to building a desktop app with llama.cpp, step by step
+22
View File
@@ -0,0 +1,22 @@
---
title: "Building a Desktop LLM App with cpp-httplib"
order: 0
---
Build an LLM-powered translation desktop app step by step, learning both the server and client sides of cpp-httplib along the way. Translation is just an example — swap it out to build your own summarizer, code generator, chatbot, or any other LLM application.
## Dependencies
- [llama.cpp](https://github.com/ggml-org/llama.cpp) — LLM inference engine
- [nlohmann/json](https://github.com/nlohmann/json) — JSON parser (header-only)
- [webview/webview](https://github.com/webview/webview) — WebView wrapper (header-only)
- [cpp-httplib](https://github.com/yhirose/cpp-httplib) — HTTP server/client (header-only)
## Chapters
1. **Embed llama.cpp and create a REST API** — Start with a simple API that accepts text via POST and returns a translation as JSON
2. **Add token streaming with SSE** — Stream translation results token by token using the standard LLM API approach
3. **Add model discovery and download** — Use the client to search and download GGUF models from Hugging Face
4. **Add a Web UI** — Serve a translation UI with static file hosting, making the app accessible from a browser
5. **Turn it into a desktop app with WebView** — Wrap the web app with webview/webview to create an Electron-like desktop application
6. **Code reading: llama.cpp's server implementation** — Compare your implementation with production-quality code and learn from the differences
+1 -1
View File
@@ -1,6 +1,6 @@
---
title: "A Tour of cpp-httplib"
order: 1
order: 0
---
This is a step-by-step tutorial that walks you through the basics of cpp-httplib. Each chapter builds on the previous one, so please read them in order starting from Chapter 1.
+90 -3
View File
@@ -1,8 +1,95 @@
---
title: "Cookbook"
order: 1
order: 0
---
This section is under construction.
「〇〇をするには?」という問いに答えるレシピ集です。各レシピは独立しているので、必要なページだけ読めます。
Check back soon for a collection of recipes organized by topic.
## クライアント
### 基本
- レスポンスボディを文字列で取得する / ファイルに保存する
- JSON を送受信する
- デフォルトヘッダーを設定する(`set_default_headers`
- リダイレクトを追従する(`set_follow_location`
### 認証
- Basic 認証を使う(`set_basic_auth`
- Bearer トークンで API を呼ぶ
### ファイル送信
- ファイルをマルチパートフォームとしてアップロードする(`make_file_provider`
- ファイルを生バイナリとして POST する(`make_file_body`
- チャンク転送でボディを送る(Content Provider
### ストリーミング・進捗
- レスポンスをストリーミングで受信する
- 進捗コールバックを使う(`set_progress`
### 接続・パフォーマンス
- タイムアウトを設定する(`set_connection_timeout` / `set_read_timeout`
- 全体タイムアウトを設定する(`set_max_timeout`
- 接続の再利用と Keep-Alive の挙動を理解する
- 圧縮を有効にする(`set_compress` / `set_decompress`
- プロキシを経由してリクエストを送る(`set_proxy`
### エラー処理・デバッグ
- エラーコードをハンドリングする(`Result::error()`
- SSL エラーをハンドリングする(`ssl_error()` / `ssl_backend_error()`
- クライアントにログを設定する(`set_logger` / `set_error_logger`
## サーバー
### 基本
- GET / POST / PUT / DELETE ハンドラを登録する
- JSON リクエストを受け取り JSON レスポンスを返す
- パスパラメーターを使う(`/users/:id`
- 静的ファイルサーバーを設定する(`set_mount_point`
### ストリーミング・ファイル
- 大きなファイルをストリーミングで返す(`ContentProvider`
- ファイルダウンロードレスポンスを返す(`Content-Disposition`
- マルチパートデータをストリーミングで受け取る(`ContentReader`
- レスポンスを圧縮して返す(gzip)
### ハンドラチェーン
- 全ルートに共通の前処理をする(Pre-routing handler
- Post-routing handler でレスポンスヘッダーを追加する(CORS など)
- Pre-request handler でルート単位の認証を行う(`matched_route`
- `res.user_data` でハンドラ間データを渡す
### エラー処理・デバッグ
- カスタムエラーページを返す(`set_error_handler`
- 例外をキャッチする(`set_exception_handler`
- リクエストをログに記録する(Logger)
- クライアントが切断したか検出する(`req.is_connection_closed()`
### 運用・チューニング
- ポートを動的に割り当てる(`bind_to_any_port`
- `listen_after_bind` で起動順序を制御する
- グレースフルシャットダウンする(`stop()` とシグナルハンドリング)
- Keep-Alive を調整する(`set_keep_alive_max_count` / `set_keep_alive_timeout`
- マルチスレッド数を設定する(`new_task_queue`
## TLS / セキュリティ
- OpenSSL・mbedTLS・wolfSSL の選択指針(ビルド時の `#define` の違い)
- SSL 証明書の検証を制御する(証明書の無効化・カスタム CA・カスタムコールバック)
- カスタム証明書検証コールバックを使う(`set_server_certificate_verifier`
- SSL/TLS サーバーを立ち上げる(証明書・秘密鍵の設定)
- mTLS(クライアント証明書による相互認証)を設定する
- サーバー側でピア証明書を参照する(`req.peer_cert()` / SNI
## SSE
- SSE サーバーを実装する
- SSE でイベント名を使い分ける
- SSE の再接続を処理する(`Last-Event-ID`
- SSE をクライアントで受信する
## WebSocket
- WebSocket エコーサーバー/クライアントを実装する
- ハートビートを設定する(`set_websocket_ping_interval`
- 接続クローズをハンドリングする
- バイナリフレームを送受信する
+1
View File
@@ -19,3 +19,4 @@ HTTPSも使えます。OpenSSLやmbedTLSをリンクするだけで、サーバ
- [A Tour of cpp-httplib](tour/) — 基本を順を追って学べるチュートリアル。初めての方はここから
- [Cookbook](cookbook/) — 目的別のレシピ集。必要なトピックから読めます
- [Building a Desktop LLM App](llm-app/) — llama.cpp を組み込んだデスクトップアプリを段階的に構築する実践ガイド
+22
View File
@@ -0,0 +1,22 @@
---
title: "Building a Desktop LLM App with cpp-httplib"
order: 0
---
llama.cpp を組み込んだ LLM 翻訳デスクトップアプリを段階的に構築しながら、cpp-httplib のサーバー・クライアント両面の使い方を実践的に学びます。翻訳は一例であり、この部分を差し替えることで要約・コード生成・チャットボットなど自分のアプリに応用できます。
## 依存ライブラリ
- [llama.cpp](https://github.com/ggml-org/llama.cpp) — LLM 推論エンジン
- [nlohmann/json](https://github.com/nlohmann/json) — JSON パーサー(ヘッダーオンリー)
- [webview/webview](https://github.com/webview/webview) — WebView ラッパー(ヘッダーオンリー)
- [cpp-httplib](https://github.com/yhirose/cpp-httplib) — HTTP サーバー/クライアント(ヘッダーオンリー)
## 章立て
1. **llama.cpp を組み込んで REST API を作る** — テキストを POST すると翻訳結果を JSON で返すシンプルな API から始める
2. **SSE でトークンストリーミングを追加する** — 翻訳結果をトークン単位で逐次返す LLM API 標準の方式を実装する
3. **モデルの取得・管理機能を追加する** — Hugging Face から GGUF モデルを検索・ダウンロードするクライアント機能を実装する
4. **Web UI を追加する** — 静的ファイル配信で翻訳 UI をホストし、ブラウザから操作できるようにする
5. **WebView でデスクトップアプリ化する** — webview/webview で包み、Electron 的なデスクトップアプリとして動作させる
6. **llama.cpp 本家のサーバー実装をコードリーディング** — 自分で作ったものとプロダクション品質のコードを比較して学ぶ
+1 -1
View File
@@ -1,6 +1,6 @@
---
title: "A Tour of cpp-httplib"
order: 1
order: 0
---
cpp-httplibの基本を、順番に学んでいくチュートリアルです。各章は前の章の内容を踏まえて進む構成なので、1章から順に読んでください。
+15 -28
View File
@@ -8,28 +8,8 @@
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.37.0"
#define CPPHTTPLIB_VERSION_NUM "0x002500"
/*
* Platform compatibility check
*/
#if defined(_WIN32) && !defined(_WIN64)
#if defined(_MSC_VER)
#pragma message( \
"cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler.")
#else
#warning \
"cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler."
#endif
#elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ < 8
#warning \
"cpp-httplib doesn't support 32-bit platforms. Please use a 64-bit compiler."
#elif defined(__SIZEOF_SIZE_T__) && __SIZEOF_SIZE_T__ < 8
#warning \
"cpp-httplib doesn't support platforms where size_t is less than 64 bits."
#endif
#define CPPHTTPLIB_VERSION "0.37.1"
#define CPPHTTPLIB_VERSION_NUM "0x002501"
#ifdef _WIN32
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
@@ -2801,7 +2781,7 @@ inline size_t get_header_value_u64(const Headers &headers,
std::advance(it, static_cast<ssize_t>(id));
if (it != rng.second) {
if (is_numeric(it->second)) {
return std::strtoull(it->second.data(), nullptr, 10);
return static_cast<size_t>(std::strtoull(it->second.data(), nullptr, 10));
} else {
is_invalid_value = true;
}
@@ -8241,7 +8221,8 @@ get_range_offset_and_length(Range r, size_t content_length) {
assert(r.first <= r.second &&
r.second < static_cast<ssize_t>(content_length));
(void)(content_length);
return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
return std::make_pair(static_cast<size_t>(r.first),
static_cast<size_t>(r.second - r.first) + 1);
}
inline std::string make_content_range_header_field(
@@ -12433,11 +12414,17 @@ ClientImpl::open_stream(const std::string &method, const std::string &path,
handle.body_reader_.stream = handle.stream_;
handle.body_reader_.payload_max_length = payload_max_length_;
auto content_length_str = handle.response->get_header_value("Content-Length");
if (!content_length_str.empty()) {
if (handle.response->has_header("Content-Length")) {
bool is_invalid = false;
auto content_length = detail::get_header_value_u64(
handle.response->headers, "Content-Length", 0, 0, is_invalid);
if (is_invalid) {
handle.error = Error::Read;
handle.response.reset();
return handle;
}
handle.body_reader_.has_content_length = true;
handle.body_reader_.content_length =
static_cast<size_t>(std::stoull(content_length_str));
handle.body_reader_.content_length = content_length;
}
auto transfer_encoding =
+96
View File
@@ -13712,6 +13712,102 @@ TEST_F(OpenStreamTest, ProhibitedTrailersAreIgnored_Stream) {
EXPECT_EQ(std::string(""), handle.response->get_header_value("X-Allowed"));
}
static std::thread serve_single_response(int port,
const std::string &response) {
return std::thread([port, response] {
auto srv = ::socket(AF_INET, SOCK_STREAM, 0);
default_socket_options(srv);
detail::set_socket_opt_time(srv, SOL_SOCKET, SO_RCVTIMEO, 5, 0);
detail::set_socket_opt_time(srv, SOL_SOCKET, SO_SNDTIMEO, 5, 0);
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(static_cast<uint16_t>(port));
::inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
int opt = 1;
::setsockopt(srv, SOL_SOCKET, SO_REUSEADDR,
#ifdef _WIN32
reinterpret_cast<const char *>(&opt),
#else
&opt,
#endif
sizeof(opt));
::bind(srv, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
::listen(srv, 1);
sockaddr_in cli_addr{};
socklen_t cli_len = sizeof(cli_addr);
auto cli = ::accept(srv, reinterpret_cast<sockaddr *>(&cli_addr), &cli_len);
if (cli != INVALID_SOCKET) {
char buf[4096];
::recv(cli, buf, sizeof(buf), 0);
::send(cli,
#ifdef _WIN32
static_cast<const char *>(response.c_str()),
static_cast<int>(response.size()),
#else
response.c_str(), response.size(),
#endif
0);
detail::close_socket(cli);
}
detail::close_socket(srv);
});
}
TEST(OpenStreamMalformedContentLength, InvalidArgument) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
auto server_thread =
serve_single_response(PORT + 2, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: not-a-number\r\n"
"Connection: close\r\n"
"\r\n"
"hello");
std::this_thread::sleep_for(std::chrono::milliseconds(200));
Client cli("127.0.0.1", PORT + 2);
auto handle = cli.open_stream("GET", "/");
EXPECT_FALSE(handle.is_valid());
server_thread.join();
}
TEST(OpenStreamMalformedContentLength, OutOfRange) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
auto server_thread = serve_single_response(
PORT + 2, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 99999999999999999999999999\r\n"
"Connection: close\r\n"
"\r\n"
"hello");
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// Before the fix, std::stoull would throw std::out_of_range here and
// crash the process. After the fix, strtoull silently clamps to
// ULLONG_MAX so the stream opens without crashing. The important thing
// is that the process does NOT terminate.
Client cli("127.0.0.1", PORT + 2);
auto handle = cli.open_stream("GET", "/");
EXPECT_TRUE(handle.is_valid());
server_thread.join();
}
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
TEST_F(OpenStreamTest, Gzip) {
Client cli("127.0.0.1", port_);
+9
View File
@@ -0,0 +1,9 @@
#include "../httplib.h"
int main() {
httplib::Server svr;
httplib::Client cli("localhost", 8080);
(void)svr;
(void)cli;
return 0;
}