cpp-httplib v0.42.0
DRAFT

S18. listen_after_bindで起動順序を制御する

普通はsvr.listen("0.0.0.0", 8080)でbindとlistenをまとめて行いますが、bindした直後に何か処理を差し挟みたいときは、2つを分けて呼べます。

bindとlistenを分ける

httplib::Server svr;

svr.Get("/", [](const auto &, auto &res) { res.set_content("ok", "text/plain"); });

if (!svr.bind_to_port("0.0.0.0", 8080)) {
  std::cerr << "bind failed" << std::endl;
  return 1;
}

// ここでbindは完了。まだacceptは始まっていない
drop_privileges();
signal_ready_to_parent_process();

svr.listen_after_bind(); // acceptループを開始
httplib::Server svr;

svr.Get("/", [](const auto &, auto &res) { res.set_content("ok", "text/plain"); });

if (!svr.bind_to_port("0.0.0.0", 8080)) {
  std::cerr << "bind failed" << std::endl;
  return 1;
}

// ここでbindは完了。まだacceptは始まっていない
drop_privileges();
signal_ready_to_parent_process();

svr.listen_after_bind(); // acceptループを開始

bind_to_port()でポートの確保までを行い、listen_after_bind()で実際の待ち受けを開始します。この2段階に分けることで、bindとacceptの間に処理を挟めます。

よくある用途

特権降格: 1023以下のポートにbindするにはroot権限が必要です。bindだけroot権限で行って、その後に一般ユーザーに降格すれば、以降のリクエスト処理は権限が落ちた状態で走ります。

svr.bind_to_port("0.0.0.0", 80);
drop_privileges();
svr.listen_after_bind();
svr.bind_to_port("0.0.0.0", 80);
drop_privileges();
svr.listen_after_bind();

起動完了通知: 親プロセスやsystemdに「準備完了」を通知してからacceptを開始できます。

テストの同期: テストコードで「サーバーがbindされた時点」を確実に捉えてからクライアントを動かせます。

戻り値のチェック

bind_to_port()は失敗するとfalseを返します。ポートが既に使われている場合などです。必ずチェックしてください。

if (!svr.bind_to_port("0.0.0.0", 8080)) {
  std::cerr << "port already in use" << std::endl;
  return 1;
}
if (!svr.bind_to_port("0.0.0.0", 8080)) {
  std::cerr << "port already in use" << std::endl;
  return 1;
}

listen_after_bind()はサーバーが停止するまでブロックし、正常終了ならtrueを返します。

Note: 空いているポートを自動で選びたいときはS17. ポートを動的に割り当てるを参照してください。こちらも内部ではbind_to_any_port() + listen_after_bind()の組み合わせです。

ESC