Здравствуйте, MTD, Вы писали:
MTD>Здравствуйте, pkl, Вы писали:
pkl>>Входной HTTP/1.1 протокол парсится с минимальным кол-вом копирований данных. pkl>>Распарсенный HTTP представляет собой просто набор указателей на разные места во входном буфере, куда данные залетели изначально. pkl>>Нет привязки к boost или другой сетевой библиотеке, на нижнем уровне лежит класс TCP сервера, реализованный на epoll, но можно подменить что угодно своё с похожим API. pkl>>Написано по мотивам внутренностей nginx. Минимум new/delete, всё переиспользуется.
MTD>Крайне интересно звучит. Какие планы по развитию или чисто just for fun? Обработчики запросов где и как обрабатываются?
Just For Fun конечно, я есть частное лицо, а не организация. Планы могут быть когда ты можешь их гарантировать, а что может гарантировать частное лицо без договора.
Примерные планы — юзать как основное сетевое двигло в своих fun-проектах, дорабатывать по мере надобности, рефакторить как накатит волна.
TCPServerEpoll — это шаблонный класс, куда нужно передать класс фабрики ресурсов. Когда приходит TCP-коннект, то TCPServerEpoll лезет в фабрику ресурсов и говорит "дай объект коннекта для типа 70", где 70 — это то что указал юзер при открытии порта на прослушивание. Объект может быть new или взят из списка свободных (кеш).
Фабрика понимает, что 70 — это HTTP коннект, например. Там их всего несколько типов из коробки, есть например примитивный "bin" протокол для общения демонов между собой. Все коннекты наследуют класс Connection, методы которого eventRead(), eventWrite(), eventClose() и дёргает TCPServerEpoll при соответствующих событиях.
Далее внутри каждого конкретного дочернего типа Connection есть свои eventRead() и т.п. Но общее одно — сам читай из дескриптора данные куда тебе надо.
Преследуя политику минимального копирования данных, TCPServerEpoll не пытается сам читать данные во временные буферы, которые потом отдаёт абстрактному коннекшну. Сделано как в nginx: пускай коннекшн сам решает надо ли ему вообще читать эти данные и пускай сам читает СРАЗУ в тот буфер где они будут нужны и столько сколько нужно, а не занимается копированием из нашего буфера. Поэтому в ConnectionHTTP например, в зависимости от состояния (что мы сейчас пытаемся прочитать — хидеры или BODY) данные читаются в разные буферы и т.п. Вот стартовая процедура чтения данных в ConnectionHTTP https://github.com/pavelkolodin/fir/blob/master/net/connection_http.h#L285 — она читает и разбирает headers.
Есть понятие Responder. Это тип "отвечальщик" — там "хендлеры" и живут ( пример Responder ) Responder передаётся как шаблонный в фабрику. Когда ConnectionHTTP допринимал запрос и понял что запрос нормальный, он говорит фабрике — дай Responder, я буду отвечать! Далее делается что-то типа responder->dataHTTP(this) и ответ отправляется обратно. У Responder должны быть методы dataHTTP, dataWS, dataBin, closeHTTP, closeWS, closeBin иначе не скомпилицца. Это и есть хендлеры. В них тебе дают голые headers, cookies, body и делай с ними что хочешь. Есть там какой-то ответный буфер, туда можно запилить ответ и вернуться, а ConnectionHTTP сам отправит ответ.
ConnectionHTTP на конечных автоматах — данные могут приходить любыми кусочками.
Некоторые штуки не реализованы — например нет поддержки gzip, нет HTTP Range_requests, нет ещё какой-то важной фигни. Зато из коробки есть miltipart/form-data -- можно запощенные HTTP-формочки парсить.
ConnectionWebSocket реализован тоже на конечных автоматах, работает стабильно. В нём есть пара лишних копирований данных (из-за непоняток с вопросами владения буферами). Также в нём отсутствует пара каких-то второстепенных моментов, типа отработки PING\PONG, хотя может и запилено — не помню. В продакшене на http://fintank.ru этот ConnectionWebSocket вроде стабильно работает часами, но я бы над ним ещё поработал.
В целом fir надо немного отрефакторить, там часть написана когда я ещё малолетним дебилом был — под c++14 надо причёсывать, про кеш CPU подумать, lock-free внедрить, мьютексов дурацких повыкидывать если есть...
Здравствуйте, pkl, Вы писали:
pkl>В целом fir надо немного отрефакторить, там часть написана когда я ещё малолетним дебилом был — под c++14 надо причёсывать, про кеш CPU подумать, lock-free внедрить, мьютексов дурацких повыкидывать если есть...
прежде всего автотесты нужны.
автотесты — это программки, которые говорят OK/FAIL при запуске. а те тесты, что в репе лежат, лучше удалить
Re: RESTinio 0.4: header-only C++14 библиотека с HTTP/Websock
Завершены работы над версией 0.4. В ней мы доделали многое из того, что хотели иметь в RESTinio-1.0, и теперь можно рассматривать RESTinio-0.4 как стабильную бета версию. Полный список изменений можно увидеть тут.
Поддержка Boost::ASIO. Boost::ASIO, начиная с версии 1.66, включает в себя изменения, сделанные ранее в stand-alone версии ASIO, поэтому стало возможным добавить интеграцию с Boost версией ASIO, которую мы собираемся поддерживать и в дальнейшем. Подробнее смотрите в документации.
Добавлена поддержка Boost::Regex в качестве regex engine для express-маршрутизатора.
В интерфейс класса для заголовка запроса добавлены функции доступа к структуре request target: /path, ?request, #fragment. Таким образом, если получен запрос с request target: /weather/temperature?from=2018-01-01&to=2018-01-22#Celsius, то можно получить доступ к /weather/temperature (path), from=2018-01-01&to=2018-01-22 (query) and Celsius (fragment).
Изменения в политике матчинга путей в express-маршрутизаторе. Теперь для матчинга берется только path-часть от исходного request-target. Т.е. для запроса на /weather/temperature?from=2018-01-01&to=2018-01-22 для матчинга будет браться только /weather/temperature (path). Хотя это изменение противоречит оригинальной expressjs, для нас такой подход кажется более естесственным.
Основной репозиторий RESTinio находится на bitbucket (есть github зеркало).
Мы обновили свою легковесную C++14 библиотеку для встраивания HTTP-входа в C++ приложения до версии 0.4.3.
Основные изменения в RESTinio со времени последнего анонса:
новые варианты restinio::run(), позволяющие задать внешний asio::io_context для RESTinio;
добавлена поддержка sendfile (https://stiffstream.com/en/docs/restinio/0.4/sendfile.html);
request_id теперь доступен внутри обработчика входящего HTTP-запроса;
плюс несколько мелких изменений и улучшений в коде самого RESTinio.
Мы создавали RESTinio для того, чтобы иметь возможность асинхронной обработки входящих запросов в случаях, когда для формирования ответа нужно обратиться к медленно отвечающему стороннему сервису. Иногда обращения к таким сторонним сервисам нужно делать посредством HTTP. Для таких целей широко используется Си-шная библиотека libcurl. Подружить асинхронную обработку входящих запросов посредством RESTinio с асинхронной обработкой исходящих запросов посредством libcurl можно несколькими способами. Подробнее эту тему мой коллега раскрыл в небольшой серии статей: часть 1, часть 2, часть 3.
Развитие RESTinio продолжается. У нас есть свои идеи о том, что можно было бы добавить в следующих версиях библиотеки. Но нам было бы очень интересно услышать пожелания от тех, кто смотрел на RESTinio, но еще не начал её использовать:
что бы вам хотелось увидеть в RESTinio?
чего не хотелось бы видеть?
что останавливает вас от использования RESTinio?
Добавлена концепция преобразователя данных, которая реализована для компрессии/декомпрессии данных с помощью библиотеки zlib. Подробнее смотри: Compression (defalate, gzip).
router->http_get(R"(/port/:id/latest)",
[](auto req, auto params) {
auto resp = req->create_response();
resp.append_header_date_field()
.append_header(restinio::http_field::content_type, "application/json");
restinio::transforms::zlib::gzip_body_appender(resp)
.append(load_port_data(params["id"]))
.complete();
return resp.done();
});
Добавлена функция value_or(), с помощью которой можно получить значение параметра из key-value контейнера RESTinio с подстановкой значения по-умолчанию. Подробне смотри: Get values from RESTinio key-value containers.
Ранее приходилось писать так:
Набор cmake-файлов для сборки и использования RESTinio улучшены в соответствии с рекомендациями по modern cmake. Подробне смотри: CMake.
RESTinio доступен через vcpkg. Теперь вы можете использовать RESTinio с помощью команды вида
$ vcpkg install restinio
Часть функций API переработана так, чтобы получать аргументы в виде string_view вместо ссылки на std::string, что должно уменьшить количество обращений к динамической памяти при работе с RESTinio.
нагляднее про позиционирование и возможное применение. Совсем коротко напомним: RESTinio развивается как инструмент для прикручивания REST API к уже существующему C++ному коду. Особенно, если прикладная обработка запросов занимается сотни/тысячи/десятки тысяч миллисекунд (как в задачах по обработке больших объемов данных) и, поэтому нужен асинхронный HTTP-сервер, который не будет блокировать IO-контексты на время прикладной обработки запроса.
Еще поднималась тема nginx-а перед приложением с RESTinio внутри. Этот сценарий выставления REST API наружу настолько очевиден, что его не было смысла даже обсуждать. Как раз для демо-страничка для shrimp-а подобным образом и организована: наружу смотрит nginx с SSL-ем, а за ним стоит C++ное приложение с RESTinio внутри.
Re[2]: Пример проекта, который использует RESTinio
Здравствуйте, so5team, Вы писали:
S> Поддержка Boost::ASIO. Boost::ASIO, начиная с версии 1.66, включает в себя изменения, сделанные ранее в stand-alone версии ASIO, поэтому стало возможным добавить интеграцию с Boost версией ASIO, которую мы собираемся поддерживать и в дальнейшем. Подробнее смотрите в документации.
А Boost.Beast не смотрели, можете что нибудь рассказать, про сравнение с этой библиотекой.
Здравствуйте, Igore, Вы писали:
I>А Boost.Beast не смотрели, можете что нибудь рассказать, про сравнение с этой библиотекой.
По неполиткорректному мнению одного из участников нашей команды, Boost.Beast -- это какая хрень незаслуженно распиаренный и слишком низкоуровневый конструктор, который может быть полезен всего двум категориям пользователей:
разработчикам более высокоуровневых и дружественных пользователю инструментов (такого же уровня, как RESTinio, CROW, Pistache, RestBed и пр.), которые не хотят самостоятельно реализовывать поддержку HTTP-протокола. Мы, например, используем http_parser из Node.js, кто-то может предпочесть Boost.Beast;
разработчикам, которые ради производительности и ресурсоемкости вынуждены работать с HTTP на самом низком уровне, но которые не хотят делать это вручную.
Всем остальным, кому нужно просто добавить HTTP в свой C++ный код и кто не хочет писать простыни кода, лучше держаться от Boost.Beast подальше. Если хотите наглядности в подтверждении этой мысли, то просто поищите пример HelloWorld для Boost.Beast и для RESTinio (или для CROW, RestBed или C++REST SDK). А еще лучше, какой-нибудь пример, который использует что-то вроде Express.js для роутинга запросов. Что-то вроде вот этого примера из нашего README:
#include <restinio/all.hpp>
using namespace restinio;
template<typename T>
std::ostream & operator<<(std::ostream & to, const optional_t<T> & v) {
if(v) to << *v;
return to;
}
int main() {
// Create express router for our service.auto router = std::make_unique<router::express_router_t<>>();
router->http_get(
R"(/data/meter/:meter_id(\d+))",
[](auto req, auto params) {
const auto qp = parse_query(req->header().query());
return req->create_response()
.set_body(
fmt::format("meter_id={} (year={}/mon={}/day={})",
cast_to<int>(params["meter_id"]),
opt_value<int>(qp, "year"),
opt_value<int>(qp, "mon"),
opt_value<int>(qp, "day")))
.done();
});
router->non_matched_request_handler(
[](auto req){
return req->create_response( 404, "Not found").connection_close().done();
});
// Launching a server with custom traits.struct my_server_traits : public default_single_thread_traits_t {
using request_handler_t = restinio::router::express_router_t<>;
};
restinio::run(
restinio::on_this_thread<my_server_traits>()
.address("localhost")
.request_handler(std::move(router)));
return 0;
}
Возможно, тогда станет лучше понятно, что Boost.Beast -- это для мазохистов специфических задач, а RESTinio и ему подобные -- для обычных разработчиков.
Re[2]: fir: другая HTTP/WebSocket либа с zero-copy внутри без п
Добавлены нотификаторы о статусе записи данных. Нужно определить был ли ответ отправлен и каков статус записи данных в сокет? Тогда используйте нотификаторы:
Автор Boost.Beast будет делать доклад на CppCon-2018 для которого он подготовил небольшой пример на базе Boost.Beast-а.
Мы, для сравнения выразительности и удобства использования двух библиотек, сделали тот же самый пример, но на базе RESTinio:
Здравствуйте, so5team, Вы писали:
S>Автор Boost.Beast будет делать доклад на CppCon-2018 для которого он подготовил небольшой пример на базе Boost.Beast-а. S>Мы, для сравнения выразительности и удобства использования двух библиотек, сделали тот же самый пример, но на базе RESTinio:
Здравствуйте, kaa.python, Вы писали:
S>>Автор Boost.Beast будет делать доклад на CppCon-2018 для которого он подготовил небольшой пример на базе Boost.Beast-а. S>>Мы, для сравнения выразительности и удобства использования двух библиотек, сделали тот же самый пример, но на базе RESTinio:
KP>Осталось рассказать об этом на CppCon
Пока участвовать в заграничных конференциях у нас не получается по ряду более чем объективных причин. Поэтому пока распространяем информацию посредством Интернета.
Re: v.0.5.0: возможность работы с кастомными версиями http-parser
Со времени последнего анонса в RESTinio было исправлено несколько ошибок и было добавлено несколько новых фич. В частности, в версии 0.5.0 добавлена возможность работы RESTinio с кастомизированными версиями библиотеки http-parser. Это позволяет реализовать обработку в RESTinio нестандартных HTTP-методов.
Допустим, что следует написать REST-сервис, который должен реагировать на нестандартные методы ENCODE и DECODE. Для этого потребуется:
сделать форк http-parser, в котором реализуется распознавание методов ENCODE и DECODE (в результате чего в кастомизированном http-parser появляются значения HTTP_ENCODE и HTTP_DECODE);
определить в своем коде две константы типа restinio::http_method_id_t:
указать имя собственного типа с методом from_nodejs в свойствах HTTP-сервера:
struct my_server_traits : public restinio::default_traits_t {
using http_methods_mapper_t = my_http_methods_mapping;
};
После этого можно будет использовать константы http_encode и http_decode при работе с RESTinio, например:
auto make_request_handler() {
auto router = std::make_unique< restinio::router::express_router_t<> >();
router->add_handler(http_encode, "/data", [](auto req, auto params) {...});
router->add_handler(http_decode, "/data", [](auto req, auto params) {...});
...
return router;
}
Примечание. Для реализации этой функциональности из RESTinio был удален тип http_method_t, а вместо него введен тип http_method_id_t. Если в вашем коде использовался http_method_t, то при обновлении на v.0.5.0 вам придется внести изменения в свой код.
Также в версии 0.5.0 был удален ряд вещей, которые были помечены как "deprecated" в предыдущих версиях RESTinio.
Здравствуйте, Denis Ivlev, Вы писали:
S>>Со времени последнего анонса в RESTinio было исправлено несколько ошибок
DI>Прочитал как "наделано ошибок". Простите)
Да вы своим ослоумием и "полезными" комментариями здесь резко выделяетесь, так что продолжайте. Публике нужны развлечения.