Pistache для C++: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Pistache для C++: REST-фреймворк нового поколения

Pistache — это современная, легковесная и высокопроизводительная библиотека для создания REST API на языке C++. Разработанная с нуля с использованием возможностей C++11/14/17, она предоставляет разработчикам чистый, интуитивно понятный интерфейс для построения HTTP-серверов и клиентов. В отличие от громоздких решений вроде Boost.Asio, Pistache предлагает лаконичный API, который делает процесс создания веб-сервисов на C++ таким же простым, как на Python или Node.js, но при этом сохраняет всю мощь и производительность нативного кода.

Зачем нужен Pistache? В современном мире микро-сервисной архитектуры и интернета вещей (IoT) часто требуется создавать легковесные, быстрые и надёжные HTTP-серверы. C++ идеально подходит для таких задач благодаря своей производительности, но традиционно написание HTTP-сервера на C++ было сложным и трудоёмким процессом. Pistache решает эту проблему, предоставляя готовые абстракции для маршрутизации, обработки запросов, парсинга параметров, управления сессиями и многого другого. Библиотека активно используется в продакшене для построения REST API, веб-хуков, микросервисов и даже в качестве основы для более сложных сетевых приложений.

Ключевые особенности Pistache включают: асинхронную обработку запросов на основе epoll (Linux) или kqueue (macOS), поддержку HTTP/1.1, встроенный парсинг query-параметров и JSON (через сторонние библиотеки), гибкую систему маршрутизации с поддержкой параметров пути, автоматическое управление памятью через умные указатели, и полную потокобезопасность. Всё это делает Pistache отличным выбором для проектов, где важна как скорость разработки, так и скорость выполнения.

Установка Pistache

Pistache не устанавливается через pip или npm, так как это нативная C++ библиотека. Установка производится через системные пакетные менеджеры или сборкой из исходников.

Linux (Ubuntu/Debian)

# Установка зависимостей
sudo apt-get update
sudo apt-get install -y libpistache-dev cmake g++ libssl-dev

# Проверка установки
dpkg -l | grep pistache

macOS (Homebrew)

brew install pistache

Сборка из исходников (кросс-платформенный способ)

git clone https://github.com/pistacheio/pistache.git
cd pistache
mkdir build && cd build
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ..
make -j$(nproc)
sudo make install

Быстрый старт: минимальный сервер

Создадим простейший HTTP-сервер, который отвечает "Hello, World!" на любой GET-запрос.

#include <pistache/endpoint.h>
#include <pistache/http.h>
#include <iostream>

using namespace Pistache;

class HelloHandler : public Http::Handler {
public:
    HTTP_PROTOTYPE(HelloHandler)

    void onRequest(const Http::Request& request, Http::ResponseWriter writer) override {
        writer.send(Http::Code::Ok, "Hello, World!\n");
    }
};

int main() {
    // Создаём эндпоинт на порту 9080
    Address addr(Ipv4::any(), Port(9080));
    auto opts = Http::Endpoint::options().threads(1);
    Http::Endpoint server(addr);
    server.init(opts);
    server.setHandler(std::make_shared<HelloHandler>());
    server.serve();

    return 0;
}

Компиляция: g++ -std=c++17 -lpistache -lpthread -o server server.cpp

Запустите сервер и откройте в браузере http://localhost:9080 — вы увидите приветствие.

Основные классы и методы Pistache

Рассмотрим ключевые компоненты библиотеки с примерами использования.

1. Http::Endpoint — точка входа сервера

Сигнатура: Http::Endpoint(const Address& addr);

Что делает: Создаёт HTTP-сервер, привязанный к указанному адресу и порту. Управляет жизненным циклом сервера.

#include <pistache/endpoint.h>
using namespace Pistache;

int main() {
    Address addr(Ipv4::any(), Port(8080));
    Http::Endpoint server(addr);
    server.init(Http::Endpoint::options().threads(4));
    // ... настройка обработчиков ...
    server.shutdown();
    return 0;
}

2. Http::Handler — базовый обработчик запросов

Сигнатура: class Handler { virtual void onRequest(const Request&, ResponseWriter) = 0; };

Что делает: Абстрактный класс для обработки входящих HTTP-запросов. Все пользовательские обработчики должны наследоваться от него.

class MyHandler : public Http::Handler {
public:
    HTTP_PROTOTYPE(MyHandler)  // Макрос для клонирования

    void onRequest(const Http::Request& request, Http::ResponseWriter writer) override {
        writer.send(Http::Code::Ok, "Response from MyHandler");
    }
};

3. Http::Request — объект запроса

Сигнатура: class Request { std::string resource() const; std::string query() const; };

Что делает: Представляет входящий HTTP-запрос. Предоставляет доступ к URI, заголовкам, query-параметрам и телу запроса.

void onRequest(const Http::Request& req, Http::ResponseWriter writer) {
    std::string path = req.resource();        // "/api/users"
    std::string query = req.query();          // "?id=123&name=John"
    std::string body = req.body();            // Тело запроса (для POST/PUT)
    std::string userAgent = req.headers().get("User-Agent").value();
    
    writer.send(Http::Code::Ok, "Path: " + path);
}

4. Http::ResponseWriter — объект для отправки ответа

Сигнатура: void send(Http::Code code, const std::string& body);

Что делает: Предоставляет методы для отправки HTTP-ответа клиенту. Поддерживает различные форматы: строки, JSON, файлы, потоки.

void onRequest(const Http::Request& req, Http::ResponseWriter writer) {
    // Отправка JSON
    writer.send(Http::Code::Ok, R"({"status": "ok", "message": "Hello"})", MIME(Application, Json));
    
    // Отправка с заголовками
    writer.headers().add("X-Custom-Header", "value");
    writer.send(Http::Code::Created, "Resource created");
    
    // Отправка файла
    writer.send(Http::Code::Ok, "file.txt");
}

5. Http::Router — маршрутизация запросов

Сигнатура: void get(const std::string& route, RouteHandler handler);

Что делает: Позволяет регистрировать обработчики для конкретных HTTP-методов и путей. Поддерживает параметры в пути.

#include <pistache/router.h>

using namespace Pistache;

class ApiRouter {
public:
    static void setup(Rest::Router& router) {
        // GET /users
        router.get("/users", [](const Rest::Request& req, Http::ResponseWriter writer) {
            writer.send(Http::Code::Ok, "List of users");
        });
        
        // GET /users/:id — параметр в пути
        router.get("/users/:id", [](const Rest::Request& req, Http::ResponseWriter writer) {
            auto id = req.param(":id").as<int>();
            writer.send(Http::Code::Ok, "User ID: " + std::to_string(id));
        });
        
        // POST /users
        router.post("/users", [](const Rest::Request& req, Http::ResponseWriter writer) {
            writer.send(Http::Code::Created, "User created");
        });
    }
};

6. Rest::Request — расширенный запрос с параметрами

Сигнатура: template<typename T> T param(const std::string& name) const;

Что делает: Расширяет Http::Request, добавляя удобный доступ к параметрам маршрута, query-параметрам и телу запроса.

void handler(const Rest::Request& req, Http::ResponseWriter writer) {
    // Параметр из пути
    int userId = req.param(":id").as<int>();
    
    // Query-параметр
    auto nameOpt = req.query().get("name");
    std::string name = nameOpt.value_or("default");
    
    // Парсинг JSON тела
    auto json = nlohmann::json::parse(req.body());
    std::string email = json["email"];
    
    writer.send(Http::Code::Ok, "User " + std::to_string(userId) + ": " + name);
}

7. Http::Cookie — работа с куки

Сигнатура: Cookie(const std::string& name, const std::string& value);

Что делает: Позволяет устанавливать и читать HTTP-куки в запросах и ответах.

void onRequest(const Http::Request& req, Http::ResponseWriter writer) {
    // Чтение куки из запроса
    auto cookie = req.cookie("session_id");
    if (cookie) {
        std::cout << "Session: " << cookie->value << std::endl;
    }
    
    // Установка куки в ответе
    Http::Cookie authCookie("token", "abc123");
    authCookie.setHttpOnly(true);
    authCookie.setMaxAge(3600);  // 1 час
    writer.cookies().add(authCookie);
    
    writer.send(Http::Code::Ok, "Cookie set");
}

8. Http::Mime — работа с MIME-типами

Сигнатура: MIME(Application, Json) или MIME(Text, Plain)

Что делает: Предоставляет константы и утилиты для работы с MIME-типами содержимого.

void onRequest(const Http::Request& req, Http::ResponseWriter writer) {
    // Отправка JSON
    writer.send(Http::Code::Ok, "{}", MIME(Application, Json));
    
    // Отправка HTML
    writer.send(Http::Code::Ok, "<h1>Title</h1>", MIME(Text, Html));
    
    // Отправка XML
    writer.send(Http::Code::Ok, "<root/>", MIME(Application, Xml));
    
    // Отправка произвольного бинарного контента
    writer.send(Http::Code::Ok, "binary data", MIME(Application, OctetStream));
}

9. Http::Async — асинхронные операции

Сигнатура: template<typename Func> void async(Func func);

Что делает: Позволяет выполнять длительные операции асинхронно, не блокируя обработку других запросов.

void onRequest(const Http::Request& req, Http::ResponseWriter writer) {
    // Асинхронная обработка
    Http::Async::async([writer = std::move(writer)]() mutable {
        // Длительная операция (например, запрос к БД)
        std::this_thread::sleep_for(std::chrono::seconds(2));
        writer.send(Http::Code::Ok, "Async response");
    });
}

10. Http::Endpoint::options — настройки сервера

Сигнатура: static Options threads(int count);

Что делает: Позволяет конфигурировать сервер: количество потоков, таймауты, максимальный размер запроса и т.д.

auto opts = Http::Endpoint::options()
    .threads(8)                          // Количество потоков
    .maxRequestSize(1024 * 1024)         // Максимальный размер запроса (1 MB)
    .maxResponseSize(10 * 1024 * 1024)   // Максимальный размер ответа (10 MB)
    .flags(Tcp::Options::ReuseAddr);     // Флаги сокета

server.init(opts);

Реальный практический пример: REST API для управления задачами

Создадим полноценный REST API для управления списком задач (To-Do list) с поддержкой CRUD операций.

#include <pistache/endpoint.h>
#include <pistache/router.h>
#include <pistache/http.h>
#include <nlohmann/json.hpp>
#include <vector>
#include <mutex>
#include <algorithm>

using namespace Pistache;
using json = nlohmann::json;

// Модель задачи
struct Task {
    int id;
    std::string title;
    bool completed;
};

// Хранилище задач (в памяти)
class TaskRepository {
private:
    std::vector<Task> tasks;
    int nextId = 1;
    std::mutex mtx;

public:
    TaskRepository() {
        // Добавим несколько тестовых задач
        tasks.push_back({nextId++, "Изучить Pistache", false});
        tasks.push_back({nextId++, "Написать REST API", false});
        tasks.push_back({nextId++, "Задеплоить на сервер", true});
    }

    std::vector<Task> getAll() {
        std::lock_guard<std::mutex> lock(mtx);
        return tasks;
    }

    std::optional<Task> getById(int id) {
        std::lock_guard<std::mutex> lock(mtx);
        auto it = std::find_if(tasks.begin(), tasks.end(), 
            [id](const Task& t) { return t.id == id; });
        if (it != tasks.end()) return *it;
        return std::nullopt;
    }

    Task create(const std::string& title) {
        std::lock_guard<std::mutex> lock(mtx);
        Task newTask{nextId++, title, false};
        tasks.push_back(new                    

Рекомендаций