[Webserver]httplib webserver+file upload(multipart,https)(Linux)Β 

πŸ‘‰πŸ» httplib μ›Ήμ„œλ²„μ—μ„œ multipart 파일 μ—…λ‘œλ“œλ₯Ό κ΅¬ν˜„ν•˜λŠ” μ„€λͺ…μž…λ‹ˆλ‹€.
This is a description of implementing multipart file uploads on an httplib web server.

πŸ‘‰πŸ» ν”„λ‘œμ νŠΈ 디렉토리내에 httplib.hνŒŒμΌμ„ λ‹€μš΄ λ°›μŠ΅λ‹ˆλ‹€.
Download the httplib.h file into the project directory.

βœ”οΈ httplib.h(0.42.0)

curl -L https://raw.githubusercontent.com/yhirose/cpp-httplib/master/httplib.h -o httplib.h

πŸ‘‰πŸ»Https server basic (https_upload_server.cpp)

βœ”οΈ μ•„λž˜λŠ” cpp ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 둜컬 μ„œλ²„λ‘œ νŒŒμΌμ„ μ—…λ‘œλ“œ ν•˜λŠ” μ„€λͺ…μž…λ‹ˆλ‹€.
The following is an explanation of uploading files from a C++ client to a local server.

βœ”οΈ κΉƒν—ˆλΈŒμ—μ„œ 파일 λ‹€μš΄λ‘œλ“œμ‹œ basic 디렉토리 내에 μžˆλŠ” 파일 μž…λ‹ˆλ‹€.
This is the file located in the basic directory when downloading from GitHub.

βœ”οΈ μΈμ¦μ„œλ₯Ό ν˜„μž¬ λ””λ ˆκ³ λ¦¬μ— λ³΅μ‚¬ν•©λ‹ˆλ‹€.
Copy the certificate to the current directory.

$ sudo cp /etc/letsencrypt/live/host.domain.org/fullchain.pem ./
$ sudo cp /etc/letsencrypt/live/host.domain.org/privkey.pem ./
# λ‚΄ κ³„μ •μœΌλ‘œ μ†Œμœ κΆŒ λ³€κ²½ / Change ownership to my account
$ sudo chown username:username *.pem  

βœ”οΈ μ½”λ“œ / Code

— https_upload_server.cpp

#define CPPHTTPLIB_OPENSSL_SUPPORT //
#include "../httplib.h"
#include <fstream>
#include <iostream>

int main() {
    //httplib::Server svr;
    httplib::SSLServer svr("./fullchain.pem", "./privkey.pem");

    if (!svr.is_valid()) {
        std::cerr << "SSL server setup failed\n";
        return 1;
    }

    svr.Post("/upload", [&](const httplib::Request& req, httplib::Response& res,
                            const httplib::ContentReader& content_reader) {
        if (!req.is_multipart_form_data()) {
            res.status = 400;
            res.set_content("Not multipart/form-data", "text/plain");
            return;
        }

        std::string filename;
        std::ofstream ofs;

        content_reader(
            [&](const httplib::FormData& item) {
                if (item.name != "file") return true;

                filename = httplib::sanitize_filename(item.filename);
                if (filename.empty()) {
                    res.status = 400;
                    res.set_content("Invalid filename", "text/plain");
                    return false;
                }
                // file upload directory
                ofs.open("../uploads/" + filename, std::ios::binary);
                if (!ofs) {
                    res.status = 500;
                    res.set_content("Failed to open file", "text/plain");
                    return false;
                }
                return true;
            },
            [&](const char* data, size_t len) {
                if (ofs) ofs.write(data, len);
                return true;
            }
        );

        if (ofs) ofs.close();

        res.set_content("Upload OK", "text/plain");
    });

    std::cout << "Upload server listening on https://0.0.0.0:5080\n";
    svr.listen("0.0.0.0", 5080);
}

— https_upload_client.cpp

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "../httplib.h"
#include <fstream>
#include <iostream>

int main() {
    // HTTPS Client
    httplib::SSLClient cli("host.domain.org", 5080);

    // μ—…λ‘œλ“œ ν•­λͺ© / Upload item
    httplib::UploadFormData item;
    item.name = "file";
    item.filename = "test.txt";
    item.content_type = "text/plain";

    std::ifstream ifs("test.txt", std::ios::binary);
    if (!ifs) {
        std::cerr << "cannot open file\n";
        return 1;
    }
    item.content.assign((std::istreambuf_iterator<char>(ifs)),
                         std::istreambuf_iterator<char>());

    httplib::UploadFormDataItems items;
    items.push_back(item);

    httplib::Headers headers;

    if (auto res = cli.Post("/upload", headers, items)) {
        std::cout << "status: " << res->status << "\n";
        std::cout << res->body << "\n";
    } else {
        std::cerr << "request failed\n";
    }
}

βœ”οΈ 컴파일 / Compiling

g++ https_upload_server.cpp -o https_upload_server -std=c++17 -pthread -lssl -lcrypto

βœ”οΈ μ„œλ²„ μ‹€ν–‰ / Run Server

./https_upload_server
μ„œλ²„μ‹€ν–‰ / Run Server

βœ”οΈ 컴파일 / Compiling

g++ https_upload_client.cpp -o https_client_server -std=c++17 -pthread -lssl -lcrypto

βœ”οΈ ν΄λΌμ΄μ–ΈνŠΈ μ‹€ν–‰ / Run Client

./https_upload_client
ν΄λΌμ΄μ–ΈνŠΈ μ‹€ν–‰ / Run Client

βœ”οΈ 파일 μ—…λ‘œλ“œ μƒνƒœ 확인 / Check file upload status

파일 μ—…λ‘œλ“œ μƒνƒœ 확인 / Check file upload status

πŸ‘‰πŸ»Https server html (https_upload_server.cpp)

βœ”οΈ μ•„λž˜λŠ” htmlνŒŒμΌμ—μ„œ νŒŒμΌμ„ μ—…λ‘œλ“œ ν•˜λŠ” μ„€λͺ…μž…λ‹ˆλ‹€.
Below is an explanation of how to upload a file in an HTML file.

βœ”οΈ κΉƒν—ˆλΈŒμ—μ„œ 파일 λ‹€μš΄λ‘œλ“œμ‹œ uploads_html 디렉토리 내에 μžˆλŠ” 파일 μž…λ‹ˆλ‹€.
This is the file located in the uploads_html directory when downloading from GitHub.

βœ”οΈ www 디렉토리가 μ—†μœΌλ©΄ 디렉토리λ₯Ό λ§Œλ“¦λ‹ˆλ‹€.
If the www directory does not exist, create the directory.

βœ”οΈ www 디렉토리 μ•ˆμ— κ°„λ‹¨ν•œ index.htmlνŒŒμΌμ„ λ§Œλ“¦λ‹ˆλ‹€.
Create a simple index.html file inside the www directory.

βœ”οΈ index.html

<!doctype html>
<html lang="ko">
    <head>
        <meta charset="utf-8" />
        <title>File Upload</title>
    </head>
    <body>
        <h1>파일 μ—…λ‘œλ“œ/File Upload</h1>
        <form action="/upload" method="post" enctype="multipart/form-data">
            <input type="file" name="file" />
            <button type="submit">μ—…λ‘œλ“œ/Upload</button>
        </form>
    </body>
</html>

βœ”οΈ μΈμ¦μ„œλ₯Ό ν˜„μž¬ 디렉토리에 λ³΅μ‚¬ν•©λ‹ˆλ‹€.
Copy the certificate to the current directory.

$ sudo cp /etc/letsencrypt/live/host.domain.org/fullchain.pem ./
$ sudo cp /etc/letsencrypt/live/host.domain.org/privkey.pem ./
# λ‚΄ κ³„μ •μœΌλ‘œ μ†Œμœ κΆŒ λ³€κ²½ / Change ownership to my account
$ sudo chown username:username *.pem  

βœ”οΈ μ½”λ“œ / Code

— https_upload_server.cpp

#define CPPHTTPLIB_OPENSSL_SUPPORT //
#include "../httplib.h"
#include <fstream>
#include <iostream>

int main() {
    //httplib::Server svr;
    httplib::SSLServer svr("./fullchain.pem", "./privkey.pem");

    if (!svr.is_valid()) {
        std::cerr << "SSL server setup failed\n";
        return 1;
    }
    // html upload form
    if (!svr.set_mount_point("/", "./www")) {
        std::cerr << "경둜 폴더λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€! 경둜λ₯Ό ν™•μΈν•˜μ„Έμš”./The path folder cannot be found! Please check the path." << std::endl;
        return 1;
    }

    svr.Post("/upload", [&](const httplib::Request& req, httplib::Response& res,
                            const httplib::ContentReader& content_reader) {
        if (!req.is_multipart_form_data()) {
            res.status = 400;
            res.set_content("Not multipart/form-data", "text/plain");
            return;
        }

        std::string filename;
        std::ofstream ofs;

        content_reader(
            [&](const httplib::FormData& item) {
                if (item.name != "file") return true;

                filename = httplib::sanitize_filename(item.filename);
                if (filename.empty()) {
                    res.status = 400;
                    res.set_content("Invalid filename", "text/plain");
                    return false;
                }
                // file upload directory
                ofs.open("../uploads/" + filename, std::ios::binary);
                if (!ofs) {
                    res.status = 500;
                    res.set_content("Failed to open file", "text/plain");
                    return false;
                }
                return true;
            },
            [&](const char* data, size_t len) {
                if (ofs) ofs.write(data, len);
                return true;
            }
        );

        if (ofs) ofs.close();

        res.set_content("Upload OK", "text/plain");
    });

    std::cout << "Upload server listening on https://0.0.0.0:5080\n";
    svr.listen("0.0.0.0", 5080);
}

βœ”οΈ 컴파일 / Compiling

g++ https_upload_server.cpp -o https_upload_server -std=c++17 -pthread -lssl -lcrypto

βœ”οΈ μ„œλ²„ μ‹€ν–‰ / Run Server

./https_upload_server
μ„œλ²„ μ‹€ν–‰ / Run Server

βœ”οΈ λΈŒλΌμš°μ € 접속 / Browser access

λΈŒλΌμš°μ € 접속 / Browser access
Upload OK

βœ”οΈ 파일 μ—…λ‘œλ“œ μƒνƒœ 확인 / Check file upload status

파일 μ—…λ‘œλ“œ μƒνƒœ 확인 / Check file upload status

Leave a Reply