【C++】httplib.hを使ったwebサーバの作成

# はじめに
以前にhttplib.hを使ったHTTPサーバ/クライアントを作成した。
　※以前の記事：[C++ HTTPサーバー/クライアントを作る](https://qiita.com/hi_to_san/items/454b32416cbe9ca1626f)

今回はhttplib.hを使ってブラウザ表示できるwebサーバを作ってみた。

# httplib.hのダウンロード/コピー
以下のgithubからダウンロードしてくる。
https://github.com/yhirose/cpp-httplib

もしくは、Googleがyhiroseさんの実装を取り込んだGoogle gitからダウンロードする。
[chromium.googlesource.com/httplib.h](https://chromium.googlesource.com/crashpad/crashpad/+/a7859e9bc63e54e972906745cbcc1b8543a4fe10/third_party/cpp-httplib/cpp-httplib/httplib.h)

<details>
    <summary>※補足 (2023/8/25追記)</summary>
g++のバージョンによっては起動すると、

```
terminate called after throwing an instance of 'std::regex_error'
```
となり、regexでエラーとなってしまうようです。
@hiroaki_yamadaさんから教えていただきました。

regexはC++ 11から追加された機能になるため、バージョン4.9以上にすれば、動作すると思います。
以下は参考ページになります。
* [C++11へのコンパイラ対応状況](https://www.torutk.com/projects/swe/wiki/C++11%E3%81%B8%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E5%AF%BE%E5%BF%9C%E7%8A%B6%E6%B3%81)
* [cpprefjp - C++日本語リファレンス](https://cpprefjp.github.io/implementation.html)

今回の動作環境は9.4.0でした。
</details>

# ディレクトリ構成
httplib.h と同じ階層にserverSample.cppを置く。
htmlディレクトリはブラウザで表示させるファイル置き場。
```
├── CMakeLists.txt
├── html
│   ├── index.html
│   └── page1
│       └── page.html
├── httplib.h
└── serverSample.cpp
```

# 実装
Readme.mdのServer Exampleを参考にwebサーバアプリを作成する。

<details>
    <summary>実装は以下になる。（ここをクリック）</summary>

```c++:serverSample.cpp
#include "httplib.h"
#include <iostream>

class FileReader {
public:
    int Read(std::string file_path) {
        std::ifstream ifs(file_path);
        std::string str = "";
        record_data = "";

        if (ifs.fail()) {
            std::cerr << "Failed to open file." << std::endl;
            return -1;
        }
        while (getline(ifs, str)) {
            record_data += str;
        }
        return 0;
    }
    std::string GetRecord() { return record_data; }
private:
    std::string record_data;
};

int main(void)
{
    using namespace httplib;
    Server svr;
    FileReader reader;

    std::cout << "start Server" << std::endl;

    std::string file_path = "../html";

    svr.Get("/", [&](const Request& req, Response& res) {
        reader.Read(file_path + "/index.html");
        std::string html = reader.GetRecord();
        res.set_content(html, "text/html");
    });

    svr.Get(R"(/(.+))", [&](const Request& req, Response& res) {
        reader.Read(file_path + req.target);
        std::string html = reader.GetRecord();
        res.set_content(html, "text/html");
    });

    svr.listen("127.0.0.1", 8080);
}
```
```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# set the project name and version
project(httpSample CXX)

# find package
find_package(Threads REQUIRED)

# compile executable file
add_executable(serverSample serverSample.cpp)

# module, library, target link
target_link_libraries(serverSample PUBLIC "-pthread")
```

</details>

# 実装解説
* class FileReader
    * htmlファイルを読み込むためのクラス
* `int main(void)`関数の`std::string file_path = "../html";`
    * html置き場のディレクトリパスを指定する。
    * 以下のコマンドでbuildディレクトリに実行ファイルを生成する。
        ```
        cmake -Bbuild -H.
        cmake --build build
        ```
    * buildディレクトリからアプリを実行したため`"../html"`を指定している。
* `res.set_content(html, "text/html");`
    * text/htmlを指定する。
    * ※ここの値は状況に応じてcssやjavascriptに変える必要がある。
* `svr.Get("/", [&](const Request& req, Response& res) {`
    * Get methodで/を要求された時はhtml/index.htmlをブラウザに表示させる。
* `svr.Get(R"(/(.+))", [&](const Request& req, Response& res) {`
    * R"(/(.+))"は正規表現。
    * /以降のパスを要求された時はhtml配下のhtmlファイルをブラウザに表示させる。

# ※補足 (2023/8/25追記)
@yhiroseさんから教えていただきました。 > まさかの作者さんからでした。
今回のhtmlのディレクトリにアクセスさせるため、
```c++:serverSample.cpp
std::string file_path = "../html";
```
を`svr.Get(R"(/(.+))"`内の
```c++:serverSample.cpp
reader.Read(file_path + req.target);
```
に設定していました。

頂いたコメントによると、以下のcpp-httplibのStatic File Server機能を使用できるようです。
```c++:
svr.set_mount_point("/", "../html")
```
これについて、再度実装してみました。

物凄く実装が綺麗になりました。

```c++:serverSample.cpp
#include "httplib.h"
#include <iostream>

int main(void)
{
    using namespace httplib;
    Server svr;

    std::cout << "start Server" << std::endl;
    svr.set_mount_point("/", "../html");
    svr.listen("127.0.0.1", 8080);
}
```
これだけでWebサーバを作成できました。
簡単で驚きました。

Googleがアップしていたのが、2017年の更新。
この時はまだStatic File Server機能は追加されてませんでした。
※ご本人様のGithubを確認せずに、こちらを使ってました。。

ご本人様のGithubを確認したら、2023年の更新。
set_mount_pointすごぃ。。。
改めて記事にしてまとめます。

# 余談
PCスペックによってはレスポンスが遅く、htmlにリンクしたjavascriptがブラウザに表示できないことがある。

# さいごに
以上で終了です。
メモ程度ですが、ご参考になれば幸いです。