mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-04-12 03:38:30 +00:00
Add Cookbook C01-C19 (draft)
This commit is contained in:
60
docs-src/pages/ja/cookbook/c01-get-response-body.md
Normal file
60
docs-src/pages/ja/cookbook/c01-get-response-body.md
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "C01. レスポンスボディを取得する / ファイルに保存する"
|
||||
order: 1
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
## 文字列として取得する
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
auto res = cli.Get("/hello");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
`res->body`は`std::string`なので、そのまま使えます。レスポンス全体がメモリに読み込まれます。
|
||||
|
||||
> **Warning:** 大きなファイルを`res->body`で受け取ると、まるごとメモリに載ってしまいます。サイズが大きい場合は次の`ContentReceiver`を使いましょう。
|
||||
|
||||
## ファイルに保存する
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
std::ofstream ofs("output.bin", std::ios::binary);
|
||||
if (!ofs) {
|
||||
std::cerr << "Failed to open file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto res = cli.Get("/large-file",
|
||||
[&](const char *data, size_t len) {
|
||||
ofs.write(data, len);
|
||||
return static_cast<bool>(ofs);
|
||||
});
|
||||
```
|
||||
|
||||
`ContentReceiver`を使うと、データをチャンクごとに受け取れます。ボディ全体をメモリに溜めずにファイルへ書き出せるので、大きなファイルのダウンロードにぴったりです。
|
||||
|
||||
コールバックから`false`を返すと、ダウンロードを途中で止められます。上の例では`ofs`への書き込みが失敗したら自動的に中断します。
|
||||
|
||||
> **Detail:** ダウンロード前にContent-Lengthなどのレスポンスヘッダーを確認したいときは、`ResponseHandler`を組み合わせましょう。
|
||||
>
|
||||
> ```cpp
|
||||
> auto res = cli.Get("/large-file",
|
||||
> [](const httplib::Response &res) {
|
||||
> auto len = res.get_header_value("Content-Length");
|
||||
> std::cout << "Size: " << len << std::endl;
|
||||
> return true; // falseを返すとダウンロードを中止
|
||||
> },
|
||||
> [&](const char *data, size_t len) {
|
||||
> ofs.write(data, len);
|
||||
> return static_cast<bool>(ofs);
|
||||
> });
|
||||
> ```
|
||||
>
|
||||
> `ResponseHandler`はヘッダー受信後、ボディ受信前に呼ばれます。`false`を返せばダウンロード自体をスキップできます。
|
||||
|
||||
> ダウンロードの進捗を表示したい場合はC11. 進捗コールバックを使うを参照してください。
|
||||
36
docs-src/pages/ja/cookbook/c02-json.md
Normal file
36
docs-src/pages/ja/cookbook/c02-json.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: "C02. JSONを送受信する"
|
||||
order: 2
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
cpp-httplibにはJSONパーサーが含まれていません。JSONの組み立てや解析には[nlohmann/json](https://github.com/nlohmann/json)などのライブラリを使ってください。ここでは`nlohmann/json`を例に説明します。
|
||||
|
||||
## JSONを送信する
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
nlohmann::json j = {{"name", "Alice"}, {"age", 30}};
|
||||
auto res = cli.Post("/api/users", j.dump(), "application/json");
|
||||
```
|
||||
|
||||
`Post()`の第2引数にJSON文字列、第3引数にContent-Typeを渡します。`Put()`や`Patch()`でも同じ形です。
|
||||
|
||||
> **Warning:** 第3引数のContent-Typeを省略すると、サーバー側でJSONとして認識されないことがあります。`"application/json"`を必ず指定しましょう。
|
||||
|
||||
## JSONレスポンスを受け取る
|
||||
|
||||
```cpp
|
||||
auto res = cli.Get("/api/users/1");
|
||||
if (res && res->status == 200) {
|
||||
auto j = nlohmann::json::parse(res->body);
|
||||
std::cout << j["name"] << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
`res->body`は`std::string`なので、そのままJSONライブラリに渡せます。
|
||||
|
||||
> **Note:** サーバーがエラー時にHTMLを返すことがあります。ステータスコードを確認してからパースすると安全です。また、APIによっては`Accept: application/json`ヘッダーが必要です。JSON APIを繰り返し呼ぶならC03. デフォルトヘッダーを設定するが便利です。
|
||||
|
||||
> サーバー側でJSONを受け取って返す方法はS02. JSONリクエストを受け取りJSONレスポンスを返すを参照してください。
|
||||
55
docs-src/pages/ja/cookbook/c03-default-headers.md
Normal file
55
docs-src/pages/ja/cookbook/c03-default-headers.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "C03. デフォルトヘッダーを設定する"
|
||||
order: 3
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
同じヘッダーを毎回のリクエストに付けたいときは、`set_default_headers()`を使います。一度設定すれば、そのクライアントから送るすべてのリクエストに自動で付与されます。
|
||||
|
||||
## 基本の使い方
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
|
||||
cli.set_default_headers({
|
||||
{"Accept", "application/json"},
|
||||
{"User-Agent", "my-app/1.0"},
|
||||
});
|
||||
|
||||
auto res = cli.Get("/users");
|
||||
```
|
||||
|
||||
`Accept`や`User-Agent`のように、APIを呼ぶたびに必要なヘッダーをまとめて登録できます。リクエストごとに指定する手間が省けます。
|
||||
|
||||
## Bearerトークンを毎回付ける
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
|
||||
cli.set_default_headers({
|
||||
{"Authorization", "Bearer " + token},
|
||||
{"Accept", "application/json"},
|
||||
});
|
||||
|
||||
auto res1 = cli.Get("/me");
|
||||
auto res2 = cli.Get("/projects");
|
||||
```
|
||||
|
||||
認証トークンを一度セットしておけば、以降のリクエストで自動的に送られます。複数のエンドポイントを叩くAPIクライアントを書くときに便利です。
|
||||
|
||||
> **Note:** `set_default_headers()`は既存のデフォルトヘッダーを**上書き**します。あとから1つだけ追加したい場合でも、全体を渡し直してください。
|
||||
|
||||
## リクエスト単位のヘッダーと組み合わせる
|
||||
|
||||
デフォルトヘッダーを設定していても、個別のリクエストで追加のヘッダーを渡せます。
|
||||
|
||||
```cpp
|
||||
httplib::Headers headers = {
|
||||
{"X-Request-ID", "abc-123"},
|
||||
};
|
||||
auto res = cli.Get("/users", headers);
|
||||
```
|
||||
|
||||
リクエスト単位で渡したヘッダーはデフォルトヘッダーに**追加**されます。両方がサーバーに送られます。
|
||||
|
||||
> Bearerトークンを使った認証の詳細はC06. BearerトークンでAPIを呼ぶを参照してください。
|
||||
38
docs-src/pages/ja/cookbook/c04-follow-location.md
Normal file
38
docs-src/pages/ja/cookbook/c04-follow-location.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: "C04. リダイレクトを追従する"
|
||||
order: 4
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
cpp-httplibはデフォルトではリダイレクト(HTTP 3xx)を追従しません。サーバーから`302 Found`が返ってきても、そのままステータスコード302のレスポンスとして受け取ります。
|
||||
|
||||
自動で追従してほしいときは、`set_follow_location(true)`を呼びましょう。
|
||||
|
||||
## リダイレクトを追従する
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://example.com");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/old-path");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
`set_follow_location(true)`を設定すると、`Location`ヘッダーを見て新しいURLに自動でリクエストを投げ直します。最終的なレスポンスが`res`に入ります。
|
||||
|
||||
## HTTPからHTTPSへのリダイレクト
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://example.com");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/");
|
||||
```
|
||||
|
||||
多くのサイトはHTTPアクセスをHTTPSへリダイレクトします。`set_follow_location(true)`を有効にしておけば、こうしたケースも透過的に扱えます。スキームやホストが変わっても自動で追従します。
|
||||
|
||||
> **Warning:** HTTPSへのリダイレクトを追従するには、cpp-httplibをOpenSSL(または他のTLSバックエンド)付きでビルドしておく必要があります。TLSサポートがないと、HTTPSへのリダイレクトは失敗します。
|
||||
|
||||
> **Note:** リダイレクトを追従すると、リクエストの実行時間は伸びます。タイムアウトの設定はC12. タイムアウトを設定するを参照してください。
|
||||
46
docs-src/pages/ja/cookbook/c05-basic-auth.md
Normal file
46
docs-src/pages/ja/cookbook/c05-basic-auth.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: "C05. Basic認証を使う"
|
||||
order: 5
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
Basic認証が必要なエンドポイントには、`set_basic_auth()`でユーザー名とパスワードを渡します。cpp-httplibが自動で`Authorization: Basic ...`ヘッダーを組み立ててくれます。
|
||||
|
||||
## 基本の使い方
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_basic_auth("alice", "s3cret");
|
||||
|
||||
auto res = cli.Get("/private");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
一度設定すれば、そのクライアントから送るすべてのリクエストに認証情報が付きます。毎回ヘッダーを組み立てる必要はありません。
|
||||
|
||||
## リクエスト単位で使う
|
||||
|
||||
特定のリクエストだけに認証情報を付けたいときは、Headersを直接渡す方法もあります。
|
||||
|
||||
```cpp
|
||||
httplib::Headers headers = {
|
||||
httplib::make_basic_authentication_header("alice", "s3cret"),
|
||||
};
|
||||
auto res = cli.Get("/private", headers);
|
||||
```
|
||||
|
||||
`make_basic_authentication_header()`がBase64エンコード済みのヘッダーを作ってくれます。
|
||||
|
||||
> **Warning:** Basic認証は資格情報をBase64で**エンコード**するだけで、暗号化しません。必ずHTTPS経由で使ってください。平文HTTPで使うと、パスワードがネットワーク上を丸見えで流れます。
|
||||
|
||||
## Digest認証
|
||||
|
||||
より安全なDigest認証を使いたいときは`set_digest_auth()`を使います。こちらはOpenSSL(または他のTLSバックエンド)付きでビルドしたときだけ利用できます。
|
||||
|
||||
```cpp
|
||||
cli.set_digest_auth("alice", "s3cret");
|
||||
```
|
||||
|
||||
> BearerトークンでAPIを呼びたい場合はC06. BearerトークンでAPIを呼ぶを参照してください。
|
||||
50
docs-src/pages/ja/cookbook/c06-bearer-token.md
Normal file
50
docs-src/pages/ja/cookbook/c06-bearer-token.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: "C06. BearerトークンでAPIを呼ぶ"
|
||||
order: 6
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
OAuth 2.0やモダンなWeb APIでよく使われるBearerトークン認証には、`set_bearer_token_auth()`を使います。トークンを渡すと、cpp-httplibが`Authorization: Bearer <token>`ヘッダーを自動で組み立ててくれます。
|
||||
|
||||
## 基本の使い方
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_bearer_token_auth("eyJhbGciOiJIUzI1NiIs...");
|
||||
|
||||
auto res = cli.Get("/me");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
一度設定すれば、以降のリクエストすべてにトークンが付きます。GitHub APIやSlack API、自前のOAuthサービスなど、トークンベースのAPIを叩くときの定番です。
|
||||
|
||||
## リクエスト単位で使う
|
||||
|
||||
特定のリクエストだけにトークンを付けたい、あるいはリクエストごとに違うトークンを使いたいときは、Headersで直接渡せます。
|
||||
|
||||
```cpp
|
||||
httplib::Headers headers = {
|
||||
httplib::make_bearer_token_authentication_header(token),
|
||||
};
|
||||
auto res = cli.Get("/me", headers);
|
||||
```
|
||||
|
||||
`make_bearer_token_authentication_header()`が`Authorization`ヘッダーを組み立ててくれます。
|
||||
|
||||
## トークンをリフレッシュする
|
||||
|
||||
トークンの有効期限が切れたら、新しいトークンで`set_bearer_token_auth()`を呼び直すだけで更新できます。
|
||||
|
||||
```cpp
|
||||
if (res && res->status == 401) {
|
||||
auto new_token = refresh_token();
|
||||
cli.set_bearer_token_auth(new_token);
|
||||
res = cli.Get("/me");
|
||||
}
|
||||
```
|
||||
|
||||
> **Warning:** Bearerトークンはそれ自体が認証情報です。必ずHTTPS経由で送ってください。また、ソースコードや設定ファイルにトークンをハードコードしないようにしましょう。
|
||||
|
||||
> 複数のヘッダーをまとめて設定したいときはC03. デフォルトヘッダーを設定するも便利です。
|
||||
52
docs-src/pages/ja/cookbook/c07-multipart-upload.md
Normal file
52
docs-src/pages/ja/cookbook/c07-multipart-upload.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "C07. ファイルをマルチパートフォームとしてアップロードする"
|
||||
order: 7
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
HTMLフォームの`<input type="file">`と同じ形式でファイルを送りたいときは、マルチパートフォーム(`multipart/form-data`)を使います。cpp-httplibは`UploadFormDataItems`と`FormDataProviderItems`の2種類のAPIを用意しています。使い分けの基準は**ファイルサイズ**です。
|
||||
|
||||
## 小さなファイルを送る
|
||||
|
||||
ファイル内容をメモリに読み込んでから送る方法です。サイズが小さいなら、これが一番シンプルです。
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
std::ifstream ifs("avatar.png", std::ios::binary);
|
||||
std::string content((std::istreambuf_iterator<char>(ifs)),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
httplib::UploadFormDataItems items = {
|
||||
{"name", "Alice", "", ""},
|
||||
{"avatar", content, "avatar.png", "image/png"},
|
||||
};
|
||||
|
||||
auto res = cli.Post("/upload", items);
|
||||
```
|
||||
|
||||
`UploadFormData`の各要素は`{name, content, filename, content_type}`の4つです。テキストフィールドなら`filename`と`content_type`を空文字にしておきます。
|
||||
|
||||
## 大きなファイルをストリーミングで送る
|
||||
|
||||
ファイル全体をメモリに載せずに、チャンクごとに送りたいときは`make_file_provider()`を使います。内部でファイルを少しずつ読みながら送信するので、巨大なファイルでもメモリを圧迫しません。
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
httplib::UploadFormDataItems items = {
|
||||
{"name", "Alice", "", ""},
|
||||
};
|
||||
|
||||
httplib::FormDataProviderItems provider_items = {
|
||||
httplib::make_file_provider("video", "large-video.mp4", "", "video/mp4"),
|
||||
};
|
||||
|
||||
auto res = cli.Post("/upload", httplib::Headers{}, items, provider_items);
|
||||
```
|
||||
|
||||
`make_file_provider()`の引数は`(フォーム名, ファイルパス, ファイル名, Content-Type)`です。ファイル名を空にするとファイルパスがそのまま使われます。
|
||||
|
||||
> **Note:** `UploadFormDataItems`と`FormDataProviderItems`は同じリクエスト内で併用できます。テキストフィールドは`UploadFormDataItems`、ファイルは`FormDataProviderItems`、という使い分けがきれいです。
|
||||
|
||||
> アップロードの進捗を表示したい場合はC11. 進捗コールバックを使うを参照してください。
|
||||
34
docs-src/pages/ja/cookbook/c08-post-file-body.md
Normal file
34
docs-src/pages/ja/cookbook/c08-post-file-body.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "C08. ファイルを生バイナリとしてPOSTする"
|
||||
order: 8
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
マルチパートではなく、ファイルの中身をそのままリクエストボディとして送りたいときがあります。S3互換APIへのアップロードや、生の画像データを受け付けるエンドポイントなどです。このときは`make_file_body()`を使いましょう。
|
||||
|
||||
## 基本の使い方
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://storage.example.com");
|
||||
|
||||
auto [size, provider] = httplib::make_file_body("backup.tar.gz");
|
||||
if (size == 0) {
|
||||
std::cerr << "Failed to open file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto res = cli.Put("/bucket/backup.tar.gz", size,
|
||||
provider, "application/gzip");
|
||||
```
|
||||
|
||||
`make_file_body()`はファイルサイズと`ContentProvider`のペアを返します。そのまま`Post()`や`Put()`に渡せば、ファイルの中身がリクエストボディとしてそのまま送られます。
|
||||
|
||||
`ContentProvider`はファイルをチャンクごとに読み込むので、巨大なファイルでもメモリに全体を載せません。
|
||||
|
||||
## ファイルが開けなかったとき
|
||||
|
||||
`make_file_body()`は開けなかった場合、`size`を`0`、`provider`を空の関数オブジェクトとして返します。そのまま送信するとおかしな結果になるので、必ず`size`をチェックしてください。
|
||||
|
||||
> **Warning:** `make_file_body()`はContent-Lengthを最初に確定させる必要があるため、ファイルサイズをあらかじめ取得します。送信中にファイルサイズが変わる可能性がある場合は、このAPIには向きません。
|
||||
|
||||
> マルチパート形式で送りたい場合はC07. ファイルをマルチパートフォームとしてアップロードするを参照してください。
|
||||
47
docs-src/pages/ja/cookbook/c09-chunked-upload.md
Normal file
47
docs-src/pages/ja/cookbook/c09-chunked-upload.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "C09. チャンク転送でボディを送る"
|
||||
order: 9
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
送信するボディのサイズが事前にわからないとき、たとえばリアルタイムに生成されるデータや別のストリームから流し込むデータを送りたいときは、`ContentProviderWithoutLength`を使います。HTTPのチャンク転送エンコーディング(chunked transfer-encoding)として送信されます。
|
||||
|
||||
## 基本の使い方
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
auto res = cli.Post("/stream",
|
||||
[&](size_t offset, httplib::DataSink &sink) {
|
||||
std::string chunk = produce_next_chunk();
|
||||
if (chunk.empty()) {
|
||||
sink.done(); // 送信終了
|
||||
return true;
|
||||
}
|
||||
return sink.write(chunk.data(), chunk.size());
|
||||
},
|
||||
"application/octet-stream");
|
||||
```
|
||||
|
||||
ラムダは「次のチャンクを作って`sink.write()`で送る」だけです。データがもう無くなったら`sink.done()`を呼べば送信が完了します。
|
||||
|
||||
## サイズがわかっている場合
|
||||
|
||||
送信するボディの**合計サイズが事前にわかっている**ときは、`ContentProvider`(`size_t offset, size_t length, DataSink &sink`を取るタイプ)と合計サイズを渡す別のオーバーロードを使います。
|
||||
|
||||
```cpp
|
||||
size_t total_size = get_total_size();
|
||||
|
||||
auto res = cli.Post("/upload", total_size,
|
||||
[&](size_t offset, size_t length, httplib::DataSink &sink) {
|
||||
auto data = read_range(offset, length);
|
||||
return sink.write(data.data(), data.size());
|
||||
},
|
||||
"application/octet-stream");
|
||||
```
|
||||
|
||||
サイズがわかっているとContent-Lengthヘッダーが付くので、サーバー側で進捗を把握しやすくなります。可能ならこちらを使いましょう。
|
||||
|
||||
> **Detail:** `sink.write()`は書き込みが成功したかどうかを`bool`で返します。`false`が返ったら回線が切れています。ラムダはそのまま`false`を返して終了しましょう。
|
||||
|
||||
> ファイルをそのまま送るだけなら、`make_file_body()`が便利です。C08. ファイルを生バイナリとしてPOSTするを参照してください。
|
||||
52
docs-src/pages/ja/cookbook/c10-stream-response.md
Normal file
52
docs-src/pages/ja/cookbook/c10-stream-response.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "C10. レスポンスをストリーミングで受信する"
|
||||
order: 10
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
レスポンスのボディをチャンクごとに受け取りたいときは、`ContentReceiver`を使います。大きなファイルを扱うときはもちろん、NDJSON(改行区切りJSON)やログストリームのように「届いた分だけ先に処理したい」ケースでも便利です。
|
||||
|
||||
## チャンクごとに処理する
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
auto res = cli.Get("/logs/stream",
|
||||
[](const char *data, size_t len) {
|
||||
std::cout.write(data, len);
|
||||
std::cout.flush();
|
||||
return true; // falseを返すと受信を中止
|
||||
});
|
||||
```
|
||||
|
||||
サーバーから届いたデータが、到着した順にラムダに渡されます。コールバックが`false`を返すとダウンロードを途中で止められます。
|
||||
|
||||
## NDJSONを行単位でパースする
|
||||
|
||||
バッファを使って改行区切りのJSONを1行ずつ処理する例です。
|
||||
|
||||
```cpp
|
||||
std::string buffer;
|
||||
|
||||
auto res = cli.Get("/events",
|
||||
[&](const char *data, size_t len) {
|
||||
buffer.append(data, len);
|
||||
size_t pos;
|
||||
while ((pos = buffer.find('\n')) != std::string::npos) {
|
||||
auto line = buffer.substr(0, pos);
|
||||
buffer.erase(0, pos + 1);
|
||||
if (!line.empty()) {
|
||||
auto j = nlohmann::json::parse(line);
|
||||
handle_event(j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
```
|
||||
|
||||
バッファに貯めながら、改行が見つかるたびに1行を取り出してパースします。ストリーミングAPIをリアルタイムに処理する基本パターンです。
|
||||
|
||||
> **Warning:** `ContentReceiver`を渡すと、`res->body`は**空のまま**になります。ボディは自分でコールバック内で保存するか処理するかしてください。
|
||||
|
||||
> ダウンロードの進捗を知りたい場合はC11. 進捗コールバックを使うと組み合わせましょう。
|
||||
> Server-Sent Events(SSE)を扱うときはE04. SSEをクライアントで受信するも参考になります。
|
||||
59
docs-src/pages/ja/cookbook/c11-progress-callback.md
Normal file
59
docs-src/pages/ja/cookbook/c11-progress-callback.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "C11. 進捗コールバックを使う"
|
||||
order: 11
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
ダウンロードやアップロードの進捗を表示したいときは、`DownloadProgress`または`UploadProgress`コールバックを渡します。どちらも`(current, total)`の2引数を取る関数オブジェクトです。
|
||||
|
||||
## ダウンロードの進捗
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
auto res = cli.Get("/large-file",
|
||||
[](size_t current, size_t total) {
|
||||
auto percent = (total > 0) ? (current * 100 / total) : 0;
|
||||
std::cout << "\rDownloading: " << percent << "% ("
|
||||
<< current << "/" << total << ")" << std::flush;
|
||||
return true; // falseを返すとダウンロードを中止
|
||||
});
|
||||
std::cout << std::endl;
|
||||
```
|
||||
|
||||
コールバックはデータを受信するたびに呼ばれます。`total`はContent-Lengthから取得した値で、サーバーが送ってこない場合は`0`になることがあります。その場合は進捗率が計算できないので、受信済みバイト数だけを表示するのが無難です。
|
||||
|
||||
## アップロードの進捗
|
||||
|
||||
アップロード側も同じ形です。`Post()`や`Put()`の最後の引数に`UploadProgress`を渡します。
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
std::string body = load_large_body();
|
||||
|
||||
auto res = cli.Post("/upload", body, "application/octet-stream",
|
||||
[](size_t current, size_t total) {
|
||||
auto percent = current * 100 / total;
|
||||
std::cout << "\rUploading: " << percent << "%" << std::flush;
|
||||
return true;
|
||||
});
|
||||
std::cout << std::endl;
|
||||
```
|
||||
|
||||
## 中断する
|
||||
|
||||
コールバックから`false`を返すと、転送を中止できます。UI側で「キャンセル」ボタンが押されたら`false`を返す、といった使い方ができます。
|
||||
|
||||
```cpp
|
||||
std::atomic<bool> cancelled{false};
|
||||
|
||||
auto res = cli.Get("/large-file",
|
||||
[&](size_t current, size_t total) {
|
||||
return !cancelled.load();
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** `ContentReceiver`と進捗コールバックは同時に使えます。ファイルに書き出しながら進捗を表示したいときは、両方を渡しましょう。
|
||||
|
||||
> ファイル保存と組み合わせる具体例はC01. レスポンスボディを取得する / ファイルに保存するも参照してください。
|
||||
50
docs-src/pages/ja/cookbook/c12-timeouts.md
Normal file
50
docs-src/pages/ja/cookbook/c12-timeouts.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: "C12. タイムアウトを設定する"
|
||||
order: 12
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
クライアントには3種類のタイムアウトがあります。それぞれ別々に設定できます。
|
||||
|
||||
| 種類 | API | デフォルト | 意味 |
|
||||
| --- | --- | --- | --- |
|
||||
| 接続タイムアウト | `set_connection_timeout` | 300秒 | TCP接続の確立までの待ち時間 |
|
||||
| 読み取りタイムアウト | `set_read_timeout` | 300秒 | レスポンスを受信する際の1回の`recv`待ち時間 |
|
||||
| 書き込みタイムアウト | `set_write_timeout` | 5秒 | リクエストを送信する際の1回の`send`待ち時間 |
|
||||
|
||||
## 基本の使い方
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
cli.set_connection_timeout(5, 0); // 5秒
|
||||
cli.set_read_timeout(10, 0); // 10秒
|
||||
cli.set_write_timeout(10, 0); // 10秒
|
||||
|
||||
auto res = cli.Get("/api/data");
|
||||
```
|
||||
|
||||
秒数とマイクロ秒を2引数で渡します。細かい指定が不要なら第2引数は省略できます。
|
||||
|
||||
## `std::chrono`で指定する
|
||||
|
||||
`std::chrono`の期間を直接渡すオーバーロードもあります。こちらのほうが読みやすいのでおすすめです。
|
||||
|
||||
```cpp
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
cli.set_connection_timeout(5s);
|
||||
cli.set_read_timeout(10s);
|
||||
cli.set_write_timeout(500ms);
|
||||
```
|
||||
|
||||
## デフォルトでは300秒と長めな点に注意
|
||||
|
||||
接続タイムアウトと読み取りタイムアウトはデフォルトで**300秒(5分)**です。サーバーが反応しない場合、このままだと5分待たされます。短めに設定したほうが良いことが多いです。
|
||||
|
||||
```cpp
|
||||
cli.set_connection_timeout(3s);
|
||||
cli.set_read_timeout(10s);
|
||||
```
|
||||
|
||||
> **Warning:** 読み取りタイムアウトは「1回の受信待ち」に対するタイムアウトです。大きなファイルのダウンロードで途中ずっとデータが流れている限り、リクエスト全体で30分かかっても発火しません。リクエスト全体の時間制限を設けたい場合はC13. 全体タイムアウトを設定するを使ってください。
|
||||
42
docs-src/pages/ja/cookbook/c13-max-timeout.md
Normal file
42
docs-src/pages/ja/cookbook/c13-max-timeout.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: "C13. 全体タイムアウトを設定する"
|
||||
order: 13
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
C12. タイムアウトを設定するで紹介した3種類のタイムアウトは、いずれも「1回の`send`や`recv`」に対するものです。リクエスト全体の所要時間に上限を設けたい場合は、`set_max_timeout()`を使います。
|
||||
|
||||
## 基本の使い方
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
cli.set_max_timeout(5000); // 5秒(ミリ秒単位)
|
||||
|
||||
auto res = cli.Get("/slow-endpoint");
|
||||
```
|
||||
|
||||
ミリ秒単位で指定します。接続、送信、受信をすべて含めて、リクエスト全体が指定時間を超えたら打ち切られます。
|
||||
|
||||
## `std::chrono`で指定する
|
||||
|
||||
こちらも`std::chrono`の期間を受け取るオーバーロードがあります。
|
||||
|
||||
```cpp
|
||||
using namespace std::chrono_literals;
|
||||
cli.set_max_timeout(5s);
|
||||
```
|
||||
|
||||
## どう使い分けるか
|
||||
|
||||
`set_read_timeout`は「データが来ない時間」のタイムアウトなので、少しずつデータが流れ続ける状況では発火しません。たとえば1秒ごとに1バイト届くようなエンドポイントは、`set_read_timeout`をいくら短くしてもタイムアウトしません。
|
||||
|
||||
一方、`set_max_timeout`は「経過時間」に対する上限なので、こうしたケースでも確実に止められます。外部APIを叩くときや、ユーザーを待たせすぎたくないときに重宝します。
|
||||
|
||||
```cpp
|
||||
cli.set_connection_timeout(3s);
|
||||
cli.set_read_timeout(10s);
|
||||
cli.set_max_timeout(30s); // 全体で30秒を超えたら中断
|
||||
```
|
||||
|
||||
> **Note:** `set_max_timeout()`は通常のタイムアウトと併用できます。短期的な無反応は`set_read_timeout`で、長時間の処理は`set_max_timeout`で、という二段構えにするのが安全です。
|
||||
53
docs-src/pages/ja/cookbook/c14-keep-alive.md
Normal file
53
docs-src/pages/ja/cookbook/c14-keep-alive.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "C14. 接続の再利用とKeep-Aliveの挙動を理解する"
|
||||
order: 14
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
`httplib::Client`は同じインスタンスで複数回リクエストを送ると、TCP接続を自動的に再利用します。HTTP/1.1のKeep-Aliveが有効に働くので、TCPハンドシェイクやTLSハンドシェイクのオーバーヘッドを毎回払わずに済みます。
|
||||
|
||||
## 接続は自動で使い回される
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
|
||||
auto res1 = cli.Get("/users/1");
|
||||
auto res2 = cli.Get("/users/2"); // 同じ接続を再利用
|
||||
auto res3 = cli.Get("/users/3"); // 同じ接続を再利用
|
||||
```
|
||||
|
||||
特別な設定は要りません。`cli`を使い回すだけで、内部的には同じソケットで通信が続きます。とくにHTTPSでは、TLSハンドシェイクのコストが大きいので効果が顕著です。
|
||||
|
||||
## Keep-Aliveを明示的にオフにする
|
||||
|
||||
毎回新しい接続を張り直したい場合は、`set_keep_alive(false)`を呼びます。テスト目的などで使うことがあります。
|
||||
|
||||
```cpp
|
||||
cli.set_keep_alive(false);
|
||||
```
|
||||
|
||||
ただし、普段はオン(デフォルト)のままで問題ありません。
|
||||
|
||||
## リクエストごとに`Client`を作らない
|
||||
|
||||
1回のリクエストのたびに`Client`をスコープから抜けて破棄すると、接続の再利用は効きません。ループの外でインスタンスを作り、中で使い回しましょう。
|
||||
|
||||
```cpp
|
||||
// NG: 毎回接続が切れる
|
||||
for (auto id : ids) {
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.Get("/users/" + id);
|
||||
}
|
||||
|
||||
// OK: 接続が再利用される
|
||||
httplib::Client cli("https://api.example.com");
|
||||
for (auto id : ids) {
|
||||
cli.Get("/users/" + id);
|
||||
}
|
||||
```
|
||||
|
||||
## 並行リクエスト
|
||||
|
||||
複数のスレッドから並行にリクエストを送りたいときは、スレッドごとに別々の`Client`インスタンスを持つのが無難です。1つの`Client`は1本のTCP接続を使い回すので、同じインスタンスに複数スレッドから同時にリクエストを投げると、結局どこかで直列化されます。
|
||||
|
||||
> **Note:** サーバー側のKeep-Aliveタイムアウトを超えると、サーバーが接続を切ります。その場合cpp-httplibは自動で再接続して再試行するので、アプリケーションコードで気にする必要はありません。
|
||||
47
docs-src/pages/ja/cookbook/c15-compression.md
Normal file
47
docs-src/pages/ja/cookbook/c15-compression.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "C15. 圧縮を有効にする"
|
||||
order: 15
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
cpp-httplibは送信時の圧縮と受信時の解凍をサポートしています。ただし、zlibまたはBrotliを有効にしてビルドしておく必要があります。
|
||||
|
||||
## ビルド時の準備
|
||||
|
||||
圧縮機能を使うには、`httplib.h`をインクルードする前に次のマクロを定義しておきます。
|
||||
|
||||
```cpp
|
||||
#define CPPHTTPLIB_ZLIB_SUPPORT // gzip / deflate
|
||||
#define CPPHTTPLIB_BROTLI_SUPPORT // brotli
|
||||
#include <httplib.h>
|
||||
```
|
||||
|
||||
リンク時に`zlib`や`brotli`のライブラリも必要です。
|
||||
|
||||
## リクエストボディを圧縮して送る
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_compress(true);
|
||||
|
||||
std::string big_payload = build_payload();
|
||||
auto res = cli.Post("/api/data", big_payload, "application/json");
|
||||
```
|
||||
|
||||
`set_compress(true)`を呼んでおくと、POSTやPUTのリクエストボディがgzipで圧縮されて送信されます。サーバー側が対応している必要があります。
|
||||
|
||||
## レスポンスを解凍する
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_decompress(true); // デフォルトで有効
|
||||
|
||||
auto res = cli.Get("/api/data");
|
||||
std::cout << res->body << std::endl;
|
||||
```
|
||||
|
||||
`set_decompress(true)`を呼ぶと、サーバーが`Content-Encoding: gzip`などで圧縮したレスポンスを自動で解凍してくれます。`res->body`には解凍済みのデータが入ります。
|
||||
|
||||
デフォルトで有効なので、通常は何もしなくても解凍されます。あえて生の圧縮データを触りたいときだけ`set_decompress(false)`にしましょう。
|
||||
|
||||
> **Warning:** `CPPHTTPLIB_ZLIB_SUPPORT`を定義せずにビルドすると、`set_compress()`や`set_decompress()`を呼んでも何も起こりません。マクロの定義を忘れていないか、最初に確認しましょう。
|
||||
52
docs-src/pages/ja/cookbook/c16-proxy.md
Normal file
52
docs-src/pages/ja/cookbook/c16-proxy.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "C16. プロキシを経由してリクエストを送る"
|
||||
order: 16
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
社内ネットワークや特定の経路を通したい場合、HTTPプロキシを経由してリクエストを送れます。`set_proxy()`でプロキシのホストとポートを指定するだけです。
|
||||
|
||||
## 基本の使い方
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_proxy("proxy.internal", 8080);
|
||||
|
||||
auto res = cli.Get("/users");
|
||||
```
|
||||
|
||||
プロキシ経由でリクエストが送られます。HTTPSの場合はCONNECTメソッドでトンネルが張られるので、cpp-httplib側で特別な設定は要りません。
|
||||
|
||||
## プロキシに認証を設定する
|
||||
|
||||
プロキシ自体が認証を要求する場合は、`set_proxy_basic_auth()`や`set_proxy_bearer_token_auth()`を使います。
|
||||
|
||||
```cpp
|
||||
cli.set_proxy("proxy.internal", 8080);
|
||||
cli.set_proxy_basic_auth("user", "password");
|
||||
```
|
||||
|
||||
```cpp
|
||||
cli.set_proxy_bearer_token_auth("token");
|
||||
```
|
||||
|
||||
OpenSSL(または他のTLSバックエンド)付きでビルドしていれば、Digest認証も使えます。
|
||||
|
||||
```cpp
|
||||
cli.set_proxy_digest_auth("user", "password");
|
||||
```
|
||||
|
||||
## エンドのサーバー認証と組み合わせる
|
||||
|
||||
プロキシ認証と、エンドサーバーへの認証(C05やC06)は別物です。両方が必要なら、両方設定します。
|
||||
|
||||
```cpp
|
||||
cli.set_proxy("proxy.internal", 8080);
|
||||
cli.set_proxy_basic_auth("proxy-user", "proxy-pass");
|
||||
|
||||
cli.set_bearer_token_auth("api-token"); // エンドサーバー向け
|
||||
```
|
||||
|
||||
プロキシには`Proxy-Authorization`、エンドサーバーには`Authorization`ヘッダーが送られます。
|
||||
|
||||
> **Note:** 環境変数の`HTTP_PROXY`や`HTTPS_PROXY`は自動的には読まれません。必要ならアプリケーション側で読み取って`set_proxy()`に渡してください。
|
||||
63
docs-src/pages/ja/cookbook/c17-error-codes.md
Normal file
63
docs-src/pages/ja/cookbook/c17-error-codes.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: "C17. エラーコードをハンドリングする"
|
||||
order: 17
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
`cli.Get()`や`cli.Post()`は`Result`型を返します。リクエストが失敗したとき(サーバーに到達できなかった、タイムアウトしたなど)、返り値は「falsy」になります。詳しい原因を知りたい場合は`Result::error()`を使います。
|
||||
|
||||
## 基本の判定
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
auto res = cli.Get("/api/data");
|
||||
|
||||
if (res) {
|
||||
// リクエストが送れて、レスポンスも受け取れた
|
||||
std::cout << "status: " << res->status << std::endl;
|
||||
} else {
|
||||
// ネットワーク層で失敗した
|
||||
std::cerr << "error: " << httplib::to_string(res.error()) << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
`if (res)`で成功・失敗を判定し、失敗時は`res.error()`で`httplib::Error`列挙値を取り出せます。`to_string()`に渡すと人間が読める文字列になります。
|
||||
|
||||
## 代表的なエラー
|
||||
|
||||
| 値 | 意味 |
|
||||
| --- | --- |
|
||||
| `Error::Connection` | サーバーに接続できなかった |
|
||||
| `Error::ConnectionTimeout` | 接続タイムアウト(`set_connection_timeout`) |
|
||||
| `Error::Read` / `Error::Write` | 送受信中のエラー |
|
||||
| `Error::Timeout` | `set_max_timeout`で設定した全体タイムアウト |
|
||||
| `Error::ExceedRedirectCount` | リダイレクト回数が上限を超えた |
|
||||
| `Error::SSLConnection` | TLSハンドシェイクに失敗 |
|
||||
| `Error::SSLServerVerification` | サーバー証明書の検証に失敗 |
|
||||
| `Error::Canceled` | 進捗コールバックから`false`が返された |
|
||||
|
||||
## ステータスコードとの使い分け
|
||||
|
||||
`res`が truthy でも、HTTPステータスコードが4xxや5xxのこともあります。この2つは別物です。
|
||||
|
||||
```cpp
|
||||
auto res = cli.Get("/api/data");
|
||||
if (!res) {
|
||||
// ネットワークエラー(そもそもレスポンスを受け取れていない)
|
||||
std::cerr << "network error: " << httplib::to_string(res.error()) << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (res->status >= 400) {
|
||||
// HTTPエラー(レスポンスは受け取った)
|
||||
std::cerr << "http error: " << res->status << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 正常系
|
||||
std::cout << res->body << std::endl;
|
||||
```
|
||||
|
||||
ネットワーク層のエラーは`res.error()`、HTTPのエラーは`res->status`、と頭の中で分けておきましょう。
|
||||
|
||||
> SSL関連のエラーをさらに詳しく調べたい場合はC18. SSLエラーをハンドリングするを参照してください。
|
||||
53
docs-src/pages/ja/cookbook/c18-ssl-errors.md
Normal file
53
docs-src/pages/ja/cookbook/c18-ssl-errors.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "C18. SSLエラーをハンドリングする"
|
||||
order: 18
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
HTTPSリクエストで失敗したとき、`res.error()`は`Error::SSLConnection`や`Error::SSLServerVerification`といった値を返します。ただ、これだけだと原因の切り分けが難しいこともあります。そんなときは`Result::ssl_error()`と`Result::ssl_backend_error()`が役に立ちます。
|
||||
|
||||
## SSLエラーの詳細を取得する
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
auto res = cli.Get("/");
|
||||
|
||||
if (!res) {
|
||||
auto err = res.error();
|
||||
std::cerr << "error: " << httplib::to_string(err) << std::endl;
|
||||
|
||||
if (err == httplib::Error::SSLConnection ||
|
||||
err == httplib::Error::SSLServerVerification) {
|
||||
std::cerr << "ssl_error: " << res.ssl_error() << std::endl;
|
||||
std::cerr << "ssl_backend_error: " << res.ssl_backend_error() << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ssl_error()`はSSLライブラリが返したエラーコード(OpenSSLの`SSL_get_error()`の値など)、`ssl_backend_error()`はバックエンドがさらに詳しく提供するエラー値です。OpenSSLなら`ERR_get_error()`の値が入ります。
|
||||
|
||||
## OpenSSLのエラーを文字列化する
|
||||
|
||||
`ssl_backend_error()`で取得した値を、OpenSSLの`ERR_error_string()`で文字列にするとデバッグに便利です。
|
||||
|
||||
```cpp
|
||||
#include <openssl/err.h>
|
||||
|
||||
if (res.ssl_backend_error() != 0) {
|
||||
char buf[256];
|
||||
ERR_error_string_n(res.ssl_backend_error(), buf, sizeof(buf));
|
||||
std::cerr << "openssl: " << buf << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
## よくある原因
|
||||
|
||||
| 症状 | ありがちな原因 |
|
||||
| --- | --- |
|
||||
| `SSLServerVerification` | CA証明書のパスが通っていない、自己署名証明書 |
|
||||
| `SSLServerHostnameVerification` | 証明書のCN/SANとホスト名が一致しない |
|
||||
| `SSLConnection` | TLSバージョンの不一致、対応スイートが無い |
|
||||
|
||||
> **Note:** `ssl_backend_error()`は以前は`ssl_openssl_error()`と呼ばれていました。後者はdeprecatedで、現在は`ssl_backend_error()`を使ってください。
|
||||
|
||||
> 証明書の検証設定を変えたい場合はT02. SSL証明書の検証を制御するを参照してください。
|
||||
57
docs-src/pages/ja/cookbook/c19-client-logger.md
Normal file
57
docs-src/pages/ja/cookbook/c19-client-logger.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "C19. クライアントにログを設定する"
|
||||
order: 19
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
クライアントから送ったリクエストと受け取ったレスポンスをログに残したいときは、`set_logger()`を使います。エラーだけを拾いたいなら`set_error_logger()`が別に用意されています。
|
||||
|
||||
## リクエストとレスポンスをログに残す
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
|
||||
cli.set_logger([](const httplib::Request &req, const httplib::Response &res) {
|
||||
std::cout << req.method << " " << req.path
|
||||
<< " -> " << res.status << std::endl;
|
||||
});
|
||||
|
||||
auto res = cli.Get("/users");
|
||||
```
|
||||
|
||||
`set_logger()`に渡したコールバックは、リクエストが完了するたびに呼ばれます。リクエストとレスポンスの両方を引数で受け取れるので、メソッドやパス、ステータスコード、ヘッダー、ボディなど好きな情報をログに残せます。
|
||||
|
||||
## エラーだけを拾う
|
||||
|
||||
ネットワーク層のエラーが起きたとき(`Error::Connection`など)は、`set_logger()`は呼ばれません。レスポンスが無いからです。こうしたエラーを拾いたいときは`set_error_logger()`を使います。
|
||||
|
||||
```cpp
|
||||
cli.set_error_logger([](const httplib::Error &err, const httplib::Request *req) {
|
||||
std::cerr << "error: " << httplib::to_string(err);
|
||||
if (req) {
|
||||
std::cerr << " (" << req->method << " " << req->path << ")";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
第2引数の`req`はヌルポインタのこともあります。リクエストを組み立てる前の段階で失敗した場合です。使う前に必ずヌルチェックしてください。
|
||||
|
||||
## 両方を組み合わせる
|
||||
|
||||
成功時は通常のログ、失敗時はエラーログ、という2本立てにすると便利です。
|
||||
|
||||
```cpp
|
||||
cli.set_logger([](const auto &req, const auto &res) {
|
||||
std::cout << "[ok] " << req.method << " " << req.path
|
||||
<< " " << res.status << std::endl;
|
||||
});
|
||||
|
||||
cli.set_error_logger([](const auto &err, const auto *req) {
|
||||
std::cerr << "[ng] " << httplib::to_string(err);
|
||||
if (req) std::cerr << " " << req->method << " " << req->path;
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** ログコールバックはリクエスト処理と同じスレッドで同期的に呼ばれます。重い処理を入れるとリクエストがその分遅くなるので、必要なら別スレッドのキューに流しましょう。
|
||||
@@ -4,93 +4,93 @@ order: 0
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
「〇〇をするには?」という問いに答えるレシピ集です。各レシピは独立しているので、必要なページだけ読めます。
|
||||
「〇〇をするには?」という問いに答えるレシピ集です。各レシピは独立しているので、必要なページだけ読めます。基本的な使い方は[Tour](../tour/)で紹介しています。
|
||||
|
||||
## クライアント
|
||||
|
||||
### 基本
|
||||
- レスポンスボディを文字列で取得する / ファイルに保存する
|
||||
- JSON を送受信する
|
||||
- デフォルトヘッダーを設定する(`set_default_headers`)
|
||||
- リダイレクトを追従する(`set_follow_location`)
|
||||
- [C01. レスポンスボディを取得する / ファイルに保存する](c01-get-response-body)
|
||||
- [C02. JSONを送受信する](c02-json)
|
||||
- [C03. デフォルトヘッダーを設定する](c03-default-headers)
|
||||
- [C04. リダイレクトを追従する](c04-follow-location)
|
||||
|
||||
### 認証
|
||||
- Basic 認証を使う(`set_basic_auth`)
|
||||
- Bearer トークンで API を呼ぶ
|
||||
- [C05. Basic認証を使う](c05-basic-auth)
|
||||
- [C06. BearerトークンでAPIを呼ぶ](c06-bearer-token)
|
||||
|
||||
### ファイル送信
|
||||
- ファイルをマルチパートフォームとしてアップロードする(`make_file_provider`)
|
||||
- ファイルを生バイナリとして POST する(`make_file_body`)
|
||||
- チャンク転送でボディを送る(Content Provider)
|
||||
- [C07. ファイルをマルチパートフォームとしてアップロードする](c07-multipart-upload)
|
||||
- [C08. ファイルを生バイナリとしてPOSTする](c08-post-file-body)
|
||||
- [C09. チャンク転送でボディを送る](c09-chunked-upload)
|
||||
|
||||
### ストリーミング・進捗
|
||||
- レスポンスをストリーミングで受信する
|
||||
- 進捗コールバックを使う(`DownloadProgress` / `UploadProgress`)
|
||||
- [C10. レスポンスをストリーミングで受信する](c10-stream-response)
|
||||
- [C11. 進捗コールバックを使う](c11-progress-callback)
|
||||
|
||||
### 接続・パフォーマンス
|
||||
- タイムアウトを設定する(`set_connection_timeout` / `set_read_timeout`)
|
||||
- 全体タイムアウトを設定する(`set_max_timeout`)
|
||||
- 接続の再利用と Keep-Alive の挙動を理解する
|
||||
- 圧縮を有効にする(`set_compress` / `set_decompress`)
|
||||
- プロキシを経由してリクエストを送る(`set_proxy`)
|
||||
- [C12. タイムアウトを設定する](c12-timeouts)
|
||||
- [C13. 全体タイムアウトを設定する](c13-max-timeout)
|
||||
- [C14. 接続の再利用とKeep-Aliveの挙動を理解する](c14-keep-alive)
|
||||
- [C15. 圧縮を有効にする](c15-compression)
|
||||
- [C16. プロキシを経由してリクエストを送る](c16-proxy)
|
||||
|
||||
### エラー処理・デバッグ
|
||||
- エラーコードをハンドリングする(`Result::error()`)
|
||||
- SSL エラーをハンドリングする(`ssl_error()` / `ssl_backend_error()`)
|
||||
- クライアントにログを設定する(`set_logger` / `set_error_logger`)
|
||||
- [C17. エラーコードをハンドリングする](c17-error-codes)
|
||||
- [C18. SSLエラーをハンドリングする](c18-ssl-errors)
|
||||
- [C19. クライアントにログを設定する](c19-client-logger)
|
||||
|
||||
## サーバー
|
||||
|
||||
### 基本
|
||||
- GET / POST / PUT / DELETE ハンドラを登録する
|
||||
- JSON リクエストを受け取り JSON レスポンスを返す
|
||||
- パスパラメーターを使う(`/users/:id`)
|
||||
- 静的ファイルサーバーを設定する(`set_mount_point`)
|
||||
- S01. GET / POST / PUT / DELETEハンドラを登録する
|
||||
- S02. JSONリクエストを受け取りJSONレスポンスを返す
|
||||
- S03. パスパラメーターを使う(`/users/:id`)
|
||||
- S04. 静的ファイルサーバーを設定する(`set_mount_point`)
|
||||
|
||||
### ストリーミング・ファイル
|
||||
- 大きなファイルをストリーミングで返す(`ContentProvider`)
|
||||
- ファイルダウンロードレスポンスを返す(`Content-Disposition`)
|
||||
- マルチパートデータをストリーミングで受け取る(`ContentReader`)
|
||||
- レスポンスを圧縮して返す(gzip)
|
||||
- S05. 大きなファイルをストリーミングで返す(`ContentProvider`)
|
||||
- S06. ファイルダウンロードレスポンスを返す(`Content-Disposition`)
|
||||
- S07. マルチパートデータをストリーミングで受け取る(`ContentReader`)
|
||||
- S08. レスポンスを圧縮して返す(gzip)
|
||||
|
||||
### ハンドラチェーン
|
||||
- 全ルートに共通の前処理をする(Pre-routing handler)
|
||||
- Post-routing handler でレスポンスヘッダーを追加する(CORS など)
|
||||
- Pre-request handler でルート単位の認証を行う(`matched_route`)
|
||||
- `res.user_data` でハンドラ間データを渡す
|
||||
- S09. 全ルートに共通の前処理をする(Pre-routing handler)
|
||||
- S10. Post-routing handlerでレスポンスヘッダーを追加する(CORSなど)
|
||||
- S11. Pre-request handlerでルート単位の認証を行う(`matched_route`)
|
||||
- S12. `res.user_data`でハンドラ間データを渡す
|
||||
|
||||
### エラー処理・デバッグ
|
||||
- カスタムエラーページを返す(`set_error_handler`)
|
||||
- 例外をキャッチする(`set_exception_handler`)
|
||||
- リクエストをログに記録する(Logger)
|
||||
- クライアントが切断したか検出する(`req.is_connection_closed()`)
|
||||
- S13. カスタムエラーページを返す(`set_error_handler`)
|
||||
- S14. 例外をキャッチする(`set_exception_handler`)
|
||||
- S15. リクエストをログに記録する(Logger)
|
||||
- S16. クライアントが切断したか検出する(`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`)
|
||||
- S17. ポートを動的に割り当てる(`bind_to_any_port`)
|
||||
- S18. `listen_after_bind`で起動順序を制御する
|
||||
- S19. グレースフルシャットダウンする(`stop()`とシグナルハンドリング)
|
||||
- S20. Keep-Aliveを調整する(`set_keep_alive_max_count` / `set_keep_alive_timeout`)
|
||||
- S21. マルチスレッド数を設定する(`new_task_queue`)
|
||||
- S22. Unix domain socketで通信する(`set_address_family(AF_UNIX)`)
|
||||
|
||||
## TLS / セキュリティ
|
||||
|
||||
- OpenSSL・mbedTLS・wolfSSL の選択指針(ビルド時の `#define` の違い)
|
||||
- SSL 証明書の検証を制御する(証明書の無効化・カスタム CA・カスタムコールバック)
|
||||
- カスタム証明書検証コールバックを使う(`set_server_certificate_verifier`)
|
||||
- SSL/TLS サーバーを立ち上げる(証明書・秘密鍵の設定)
|
||||
- mTLS(クライアント証明書による相互認証)を設定する
|
||||
- サーバー側でピア証明書を参照する(`req.peer_cert()` / SNI)
|
||||
- T01. OpenSSL・mbedTLS・wolfSSLの選択指針(ビルド時の`#define`の違い)
|
||||
- T02. SSL証明書の検証を制御する(無効化・カスタムCA・カスタムコールバック)
|
||||
- T03. SSL/TLSサーバーを立ち上げる(証明書・秘密鍵の設定)
|
||||
- T04. mTLS(クライアント証明書による相互認証)を設定する
|
||||
- T05. サーバー側でピア証明書を参照する(`req.peer_cert()` / SNI)
|
||||
|
||||
## SSE
|
||||
|
||||
- SSE サーバーを実装する
|
||||
- SSE でイベント名を使い分ける
|
||||
- SSE の再接続を処理する(`Last-Event-ID`)
|
||||
- SSE をクライアントで受信する
|
||||
- E01. SSEサーバーを実装する
|
||||
- E02. SSEでイベント名を使い分ける
|
||||
- E03. SSEの再接続を処理する(`Last-Event-ID`)
|
||||
- E04. SSEをクライアントで受信する
|
||||
|
||||
## WebSocket
|
||||
|
||||
- WebSocket エコーサーバー/クライアントを実装する
|
||||
- ハートビートを設定する(`set_websocket_ping_interval` / `CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND`)
|
||||
- 接続クローズをハンドリングする
|
||||
- バイナリフレームを送受信する
|
||||
- W01. WebSocketエコーサーバー/クライアントを実装する
|
||||
- W02. ハートビートを設定する(`set_websocket_ping_interval`)
|
||||
- W03. 接続クローズをハンドリングする
|
||||
- W04. バイナリフレームを送受信する
|
||||
|
||||
Reference in New Issue
Block a user