diff --git a/CMakeLists.txt b/CMakeLists.txt index da9fd2d..1aa16c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,26 @@ else() message(FATAL_ERROR "You must set `MODEM_TYPE` \"SCPC\" or \"TDMA\"!") endif() -add_compile_options(-Wall -Wextra -Wsign-conversion) +SET(PROJECT_GIT_REVISION "0") +FIND_PACKAGE(Git) +IF (GIT_FOUND) + EXECUTE_PROCESS ( + COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_HEAD + # ERROR_VARIABLE ERROR_RESULT + # RESULT_VARIABLE INFO_RESULT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + IF ( ${GIT_HEAD} MATCHES "^.+$" ) + STRING ( SUBSTRING ${GIT_HEAD} 0 8 VERSION_REVISION ) + SET ( PROJECT_GIT_REVISION ${VERSION_REVISION} ) + ENDIF() +ENDIF() + +add_compile_options(-Wall -Wextra -Wsign-conversion -DPROJECT_GIT_REVISION="${PROJECT_GIT_REVISION}") # максимальный размер тела запроса 200mb add_definitions(-DHTTP_MAX_PAYLOAD=200000000) diff --git a/front-generator/render-params.json b/front-generator/render-params.json index 0eb05bf..2fe6f10 100644 --- a/front-generator/render-params.json +++ b/front-generator/render-params.json @@ -213,7 +213,7 @@ "values": [{"label": "РРУ", "value": "false"}, {"label": "АРУ", "value": "true"}] }, {"widget": "number", "label": "Усиление, дБ", "name": "rxManualGain", "min": -40, "step": 0.01, "max": 40, "v_show": "paramRxtx.rxAgcEn === false"}, - {"widget": "watch", "label": "Текущее усиление", "model": "rxManualGain", "v_show": "paramRxtx.rxAgcEn === true"}, + {"widget": "watch", "label": "Текущее усиление", "model": "paramRxtx.rxManualGain", "v_show": "paramRxtx.rxAgcEn === true"}, {"widget": "checkbox", "label": "Инверсия спектра", "name": "rxSpectrumInversion"}, {"widget": "number", "label": "Центральная частота, КГц", "name": "rxCentralFreq", "min": 900000, "step": 0.01}, {"widget": "number", "label": "Символьная скорость, Бод", "name": "rxBaudrate", "min": 0, "step": 1}, diff --git a/front-generator/template/common/all-params-methods.js.j2 b/front-generator/template/common/all-params-methods.js.j2 index 5d373f5..e8fb0b5 100644 --- a/front-generator/template/common/all-params-methods.js.j2 +++ b/front-generator/template/common/all-params-methods.js.j2 @@ -12,7 +12,7 @@ } this.submitStatus.{{ g['group'] }} = true - fetch('/api/set/{{ g["group"] }}', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) + fetch('/api/set/{{ g["group"] }}', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query), credentials: 'same-origin' }) .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.update{{ g['group'] | title }}Settings(vals) }) .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) .finally(() => { this.submitStatus.{{ g['group'] }} = false }) diff --git a/front-generator/template/common/monitoring-methods.js.j2 b/front-generator/template/common/monitoring-methods.js.j2 index aff7510..e4e4ac4 100644 --- a/front-generator/template/common/monitoring-methods.js.j2 +++ b/front-generator/template/common/monitoring-methods.js.j2 @@ -93,7 +93,7 @@ resetPacketsStatistics() { fetch('/api/resetPacketStatistics', { - method: 'POST' + method: 'POST', credentials: 'same-origin' }).then(() => { this.statRx.packetsOk = 0 this.statRx.packetsBad = 0 diff --git a/front-generator/template/common/qos-methods.js.j2 b/front-generator/template/common/qos-methods.js.j2 index 9083e7c..3ae5fb4 100644 --- a/front-generator/template/common/qos-methods.js.j2 +++ b/front-generator/template/common/qos-methods.js.j2 @@ -60,7 +60,7 @@ headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(query) + body: JSON.stringify(query), credentials: 'same-origin' }).then(async (resp) => { this.submitStatusQos = false if (resp['error']) { throw new Error(resp['error']) } diff --git a/front-generator/template/main.html b/front-generator/template/main.html index fe4c60b..233319f 100644 --- a/front-generator/template/main.html +++ b/front-generator/template/main.html @@ -191,7 +191,7 @@ } } else { try { - let d = await fetch("/api/get/statistics") + let d = await fetch("/api/get/statistics", { credentials: 'same-origin' }) this.updateStatistics(await d.json()) } catch (e) { this.initState = "Ошибка обновления статистики" diff --git a/src/auth/jwt.cpp b/src/auth/jwt.cpp index 1067181..5a7f032 100644 --- a/src/auth/jwt.cpp +++ b/src/auth/jwt.cpp @@ -1,8 +1,15 @@ #include "jwt.h" #include - #include "utils.h" +#include + +static int64_t milliseconds() { + timeval tv{}; + gettimeofday(&tv,nullptr); + return ((tv.tv_sec * 1000000l) + tv.tv_usec) / 1000; +} + std::string http::auth::jwt::secretKey; void http::auth::jwt::generateSecretKey() { @@ -16,37 +23,45 @@ void http::auth::jwt::generateSecretKey() { } } -// payload, signature -static std::pair parseJwtFromCookie(const std::string& input) { - std::string val1, val2; - size_t dotPos = input.find('.'); - - // Если точка найдена - if (dotPos != std::string::npos) { - val1 = input.substr(0, dotPos); - - // Если в val1 есть еще точки, нужно найти последнюю точку - size_t lastDotPos = val1.find_last_of('.'); - if (lastDotPos != std::string::npos) { - val1 = val1.substr(0, lastDotPos + 1); - } - - val2 = input.substr(dotPos + 1); - } else { - // Точка не найдена, val1 - вся строка - val1 = input; - } - - return std::make_pair(http::utils::b64Decode(val1), val2); -} - http::auth::jwt::Jwt http::auth::jwt::Jwt::fromCookies(const std::string &cookie) { auto pc = utils::parseCookies(cookie); Jwt t; if (pc.find("auth") != pc.end()) { - auto tmp = parseJwtFromCookie(pc.at("auth")); - t.payload = tmp.first; - t.signature = tmp.second; + const auto auth = pc.at("auth"); + int firstDot = -1; + int secondDot = -1; + for (size_t i = 0; i < auth.size(); i++) { + if (auth[i] == '.') { + if (firstDot < 0) { firstDot = static_cast(i); } + else if (secondDot < 0) { secondDot = static_cast(i); } + else { + // так быть не должно + return t; + } + } + } + if (firstDot < 0 || secondDot < 0 || secondDot - firstDot == 0) { + // так тоже быть не должно + return t; + } + + try { + t.payload = auth.substr(0, firstDot); + t.signature = auth.substr(secondDot + 1); + t.lastUpdate = std::stol(auth.substr(firstDot + 1, secondDot - firstDot - 1)); + + // теперь проверим, что сигнатура верная, только тогда декодируем строку юзера + auto realSignature = utils::sha256(t.payload + std::to_string(t.lastUpdate) + secretKey); + if (t.signature != realSignature) { + t.payload.clear(); + } else { + t.payload = utils::b64Decode(t.payload); + } + } catch (std::exception& e) { + t.payload.clear(); + t.lastUpdate = 0; + t.signature.clear(); + } } return t; @@ -55,6 +70,7 @@ http::auth::jwt::Jwt http::auth::jwt::Jwt::fromCookies(const std::string &cookie http::auth::jwt::Jwt http::auth::jwt::Jwt::fromUser(const std::string &user) { Jwt t; t.payload = user; + t.lastUpdate = milliseconds(); return t; } @@ -63,18 +79,40 @@ bool http::auth::jwt::Jwt::isValid() { return false; } - auto realSignature = utils::sha256(this->payload + secretKey); - return signature == realSignature; + // проверка сигнатуры не нужна, она была на стадии парсинга куки + // auto realSignature = utils::sha256(utils::b64Encode(this->payload) + std::to_string(this->lastUpdate) + secretKey); + // if (signature != realSignature) { + // return false; + // } + + const auto currTime = milliseconds(); + + return currTime <= lastUpdate + SESSION_LIVE_MS && currTime >= lastUpdate; } std::string http::auth::jwt::Jwt::getUsername() { return payload; } -std::string http::auth::jwt::Jwt::asCookie() { - signature = utils::sha256(this->payload + secretKey); - auto val = utils::b64Encode(payload) + "." + signature; - return "auth=" + val + ";Path=/; Max-Age=86400; HttpOnly; SameSite=Lax"; +std::string http::auth::jwt::Jwt::asCookie(bool isSecure) { + this->lastUpdate = milliseconds(); + const auto uTime = std::to_string(this->lastUpdate); + const auto encodedPayload = utils::b64Encode(payload); + signature = utils::sha256(encodedPayload + uTime + secretKey); + const auto val = encodedPayload + "." + uTime + "." + signature; + std::string cookie = "auth="; + cookie += val; + cookie += ";Path=/; Max-Age="; + cookie += std::to_string(SESSION_LIVE_MS / 1000); + if (isSecure) { + cookie += "; Secure"; + } + cookie += "; HttpOnly; SameSite=Lax"; + return cookie; +} + +bool http::auth::jwt::Jwt::needUpdate() const { + return milliseconds() >= lastUpdate + SESSION_UPDATE_THRESHOLD; } http::auth::jwt::Jwt::~Jwt() = default; diff --git a/src/auth/jwt.h b/src/auth/jwt.h index 6559356..866d7c8 100644 --- a/src/auth/jwt.h +++ b/src/auth/jwt.h @@ -6,7 +6,10 @@ namespace http::auth::jwt { extern std::string secretKey; - constexpr const char* EMPTY_AUTH_COOKIE = "auth=;Path=/; Max-Age=86400; HttpOnly; SameSite=Lax";; + constexpr const char* EMPTY_AUTH_COOKIE = "auth=;Path=/; Max-Age=86400; HttpOnly; SameSite=Lax"; + + constexpr int64_t SESSION_LIVE_MS = 24 * 60 * 60 * 1000; // 24 часа + constexpr int64_t SESSION_UPDATE_THRESHOLD = 10 * 60 * 1000; // 10 минут void generateSecretKey(); @@ -17,6 +20,7 @@ namespace http::auth::jwt { */ class Jwt { std::string payload; + int64_t lastUpdate = 0; std::string signature; public: static Jwt fromCookies(const std::string& cookie); @@ -26,7 +30,9 @@ namespace http::auth::jwt { std::string getUsername(); - std::string asCookie(); + std::string asCookie(bool isSecure = false); + + bool needUpdate() const; ~Jwt(); }; diff --git a/src/auth/resources.cpp b/src/auth/resources.cpp index 50dede1..5b063df 100644 --- a/src/auth/resources.cpp +++ b/src/auth/resources.cpp @@ -44,12 +44,12 @@ http::auth::User::~User() = default; http::auth::AuthProvider::AuthProvider() = default; -std::shared_ptr http::auth::AuthProvider::doAuth(const std::string &username, const std::string &password, server::Reply &rep) { +std::shared_ptr http::auth::AuthProvider::doAuth(const std::string &username, const std::string &password, const server::Request &req, server::Reply &rep) { for (const auto& u: users) { if (u->username == username) { if (u->checkPassword(password)) { auto t = jwt::Jwt::fromUser(u->username); - rep.headers.push_back({.name = "Set-Cookie", .value = t.asCookie()}); + rep.headers.push_back({.name = "Set-Cookie", .value = t.asCookie(req.isSecure)}); return u; } BOOST_LOG_TRIVIAL(warning) << "http::auth::AuthProvider::doAuth(): Failed to login " << username << ", password: " << password << " (incorrect password)"; @@ -60,13 +60,17 @@ std::shared_ptr http::auth::AuthProvider::doAuth(const std::st return nullptr; } -std::shared_ptr http::auth::AuthProvider::getSession(const server::Request &req) { +std::shared_ptr http::auth::AuthProvider::getSession(const server::Request &req, server::Reply &rep) { auto t = jwt::Jwt::fromCookies(req.getHeaderValue("cookie")); if (t.isValid()) { const auto name = t.getUsername(); // токен валидный, ищем юзера for (auto& u: users) { if (u->username == name) { + // на всякий случай тут проверяем, что токен пора обновлять + if (t.needUpdate()) { + rep.headers.push_back({.name = "Set-Cookie", .value = t.asCookie(req.isSecure)}); + } return u; } } @@ -84,7 +88,7 @@ http::auth::AuthRequiredResource::AuthRequiredResource(const std::string &path, BasicResource(path), provider_(provider), generator_(std::move(generator)), perms(perms) {} void http::auth::AuthRequiredResource::handle(const server::Request &req, server::Reply &rep) { - if (auto user = this->provider_.getSession(req)) { + if (auto user = this->provider_.getSession(req, rep)) { if (user->checkPremisions(this->perms)) { this->generator_(req, rep); return; diff --git a/src/auth/resources.h b/src/auth/resources.h index 9f87144..8ce9677 100644 --- a/src/auth/resources.h +++ b/src/auth/resources.h @@ -70,20 +70,23 @@ namespace http::auth { * @note Класс устанавливает заголовок 'Set-Cookie' в ответе, и этот заголовок должен дойти до пользователя! */ class AuthProvider { + void updateSessionHook(); + public: AuthProvider(); /** * Авторизовать пользователя. * + * @param req * @param rep * @return true, в случае успешной авторизации */ - std::shared_ptr doAuth(const std::string &username, const std::string &password, server::Reply &rep); + std::shared_ptr doAuth(const std::string &username, const std::string &password, const server::Request &req, server::Reply &rep); std::vector> users; - std::shared_ptr getSession(const server::Request &req); + std::shared_ptr getSession(const server::Request &req, server::Reply &rep); ~AuthProvider(); }; diff --git a/src/main.cpp b/src/main.cpp index 346b306..b3ade5e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -147,7 +147,7 @@ public: void registerResources(http::server::Server& s) { s.resources.emplace_back(std::make_unique("/", [this](const auto& req, auto& rep) { - auto user = auth.getSession(req); + auto user = auth.getSession(req, rep); if (user == nullptr) { http::server::httpRedirect(rep, "/login"); } else { @@ -157,7 +157,7 @@ public: s.resources.emplace_back(std::make_unique("/login", [this](const auto& req, auto& rep) { if (req.method == "GET") { - auto user = auth.getSession(req); + auto user = auth.getSession(req, rep); if (user == nullptr) { sf->serve(LOGIN_HTML, rep); } else { @@ -172,7 +172,7 @@ public: boost::property_tree::ptree pt; read_json(is, pt); - auto u = auth.doAuth(pt.get("username"), pt.get("password"), rep); + auto u = auth.doAuth(pt.get("username"), pt.get("password"), req, rep); if (u == nullptr) { throw std::runtime_error("invalid session"); } @@ -206,10 +206,10 @@ public: s.resources.emplace_back(std::make_unique("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) { if (req.method != "GET") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); std::string result = R"({"mainState":)"; result += api->loadTerminalState(); @@ -222,10 +222,10 @@ public: s.resources.emplace_back(std::make_unique("/api/get/settings", this->auth, http::auth::User::WATCH_SETTINGS, [this](const auto& req, auto& rep) { if (req.method != "GET") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); std::string result = R"({"settings":)"; result += api->loadSettings(); @@ -236,10 +236,10 @@ public: s.resources.emplace_back(std::make_unique("/api/get/aboutFirmware", this->auth, 0, [this](const auto& req, auto& rep) { if (req.method != "GET") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); const auto result = api->loadFirmwareVersion(); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); @@ -248,11 +248,11 @@ public: s.resources.emplace_back(std::make_unique("/api/resetPacketStatistics", this->auth, http::auth::User::RESET_PACKET_STATISTICS, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); + return; } api->resetPacketStatistics(); rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); const std::string result = R"({"status":"ok")"; rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); @@ -261,10 +261,10 @@ public: s.resources.emplace_back(std::make_unique("/api/set/qos", this->auth, http::auth::User::SETUP_QOS, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); try { @@ -289,10 +289,10 @@ public: s.resources.emplace_back(std::make_unique("/api/set/buclnb", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); try { @@ -317,10 +317,10 @@ public: s.resources.emplace_back(std::make_unique("/api/set/cinc", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); try { @@ -345,10 +345,10 @@ public: s.resources.emplace_back(std::make_unique("/api/set/rxtx", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); try { @@ -373,10 +373,10 @@ public: s.resources.emplace_back(std::make_unique("/api/set/network", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); try { @@ -401,9 +401,9 @@ public: s.resources.emplace_back(std::make_unique("/api/reboot", this->auth, 0, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); const std::string result = R"({"status":"ok"})"; rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); @@ -413,9 +413,9 @@ public: s.resources.emplace_back(std::make_unique("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); + return; } rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); const std::string result = R"({"status":"ok"})"; rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); @@ -431,7 +431,6 @@ public: onUploadFirmware(req); rep.status = http::server::ok; - rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); std::string result = R"({"status":"ok","fwsize":)"; result += std::to_string(req.payload.size()); diff --git a/src/server/connection.cpp b/src/server/connection.cpp index 39f9d75..6a25f1d 100644 --- a/src/server/connection.cpp +++ b/src/server/connection.cpp @@ -6,10 +6,14 @@ namespace http::server { - const char* SERVER_HEADER_VALUE = "TerminalWebServer v0.1"; + const char* SERVER_HEADER_VALUE = "TerminalWebServer" +#ifdef PROJECT_GIT_REVISION + " " PROJECT_GIT_REVISION +#endif + ; Connection::Connection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler) - : socket_(std::move(socket)), connection_manager_(manager), request_handler_(std::move(handler)), request_(), reply_() { + : socket_(std::move(socket)), connection_manager_(manager), request_handler_(std::move(handler)), request_(false), reply_() { } void Connection::start() { @@ -51,7 +55,7 @@ namespace http::server { void Connection::doWrite() { reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())}); - if (request_.http_version_major == 1) { + if (request_.httpVersionMajor == 1) { reply_.headers.push_back({.name = "Connection", .value = "keep-alive"}); } @@ -71,7 +75,7 @@ namespace http::server { } SslConnection::SslConnection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler, const std::shared_ptr& ctx): - stream_(std::move(socket), *ctx), connection_manager_(manager), request_handler_(std::move(handler)), request_(), reply_() { + stream_(std::move(socket), *ctx), connection_manager_(manager), request_handler_(std::move(handler)), request_(true), reply_() { } void SslConnection::start() { @@ -125,7 +129,7 @@ namespace http::server { void SslConnection::doWrite() { reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())}); - if (request_.http_version_major == 1) { + if (request_.httpVersionMajor == 1) { reply_.headers.push_back({.name = "Connection", .value = "keep-alive"}); } diff --git a/src/server/request.hpp b/src/server/request.hpp index b3dbb1a..196d0a7 100644 --- a/src/server/request.hpp +++ b/src/server/request.hpp @@ -21,7 +21,7 @@ namespace http::server { /// A request received from a client. class Request { public: - Request(); + Request(bool secure); void reset(); std::string getHeaderValue(const std::string& headerName) const; @@ -29,9 +29,10 @@ namespace http::server { std::string method; std::string queryUri; std::unique_ptr url; - bool is_keep_alive{}; - int http_version_major{}; - int http_version_minor{}; + bool isKeepAlive{}; + const bool isSecure; + int httpVersionMajor{}; + int httpVersionMinor{}; std::vector
headers; std::vector payload; diff --git a/src/server/request_parser.cpp b/src/server/request_parser.cpp index a6b5ff7..21b8681 100644 --- a/src/server/request_parser.cpp +++ b/src/server/request_parser.cpp @@ -51,7 +51,7 @@ namespace http::server { Url::~Url() = default; - Request::Request() = default; + Request::Request(bool secure): isSecure(secure) {} void Request::reset() { method = ""; @@ -59,9 +59,9 @@ namespace http::server { if (url != nullptr) { url.reset(nullptr); } - is_keep_alive = false; - http_version_major = 0; - http_version_minor = 0; + isKeepAlive = false; + httpVersionMajor = 0; + httpVersionMinor = 0; headers.clear(); payload.clear(); } @@ -151,8 +151,8 @@ namespace http::server { } case http_version_slash: if (input == '/') { - req.http_version_major = 0; - req.http_version_minor = 0; + req.httpVersionMajor = 0; + req.httpVersionMinor = 0; state_ = http_version_major_start; return indeterminate; } else { @@ -160,7 +160,7 @@ namespace http::server { } case http_version_major_start: if (is_digit(input)) { - req.http_version_major = req.http_version_major * 10 + input - '0'; + req.httpVersionMajor = req.httpVersionMajor * 10 + input - '0'; state_ = http_version_major; return indeterminate; } else { @@ -171,14 +171,14 @@ namespace http::server { state_ = http_version_minor_start; return indeterminate; } else if (is_digit(input)) { - req.http_version_major = req.http_version_major * 10 + input - '0'; + req.httpVersionMajor = req.httpVersionMajor * 10 + input - '0'; return indeterminate; } else { return bad; } case http_version_minor_start: if (is_digit(input)) { - req.http_version_minor = req.http_version_minor * 10 + input - '0'; + req.httpVersionMinor = req.httpVersionMinor * 10 + input - '0'; state_ = http_version_minor; return indeterminate; } else { @@ -189,7 +189,7 @@ namespace http::server { state_ = expecting_newline_1; return indeterminate; } else if (is_digit(input)) { - req.http_version_minor = req.http_version_minor * 10 + input - '0'; + req.httpVersionMinor = req.httpVersionMinor * 10 + input - '0'; return indeterminate; } else { return bad; diff --git a/static/main-scpc.html b/static/main-scpc.html index 9b173d8..08ede8b 100644 --- a/static/main-scpc.html +++ b/static/main-scpc.html @@ -269,7 +269,7 @@ - +