фича: автообновление сессии
This commit is contained in:
		| @@ -25,7 +25,26 @@ else() | |||||||
|     message(FATAL_ERROR "You must set `MODEM_TYPE` \"SCPC\" or \"TDMA\"!") |     message(FATAL_ERROR "You must set `MODEM_TYPE` \"SCPC\" or \"TDMA\"!") | ||||||
| endif() | 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 | # максимальный размер тела запроса 200mb | ||||||
| add_definitions(-DHTTP_MAX_PAYLOAD=200000000) | add_definitions(-DHTTP_MAX_PAYLOAD=200000000) | ||||||
|   | |||||||
| @@ -213,7 +213,7 @@ | |||||||
|                     "values": [{"label": "РРУ", "value": "false"}, {"label": "АРУ", "value": "true"}] |                     "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": "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": "checkbox", "label": "Инверсия спектра", "name": "rxSpectrumInversion"}, | ||||||
|                   {"widget": "number", "label": "Центральная частота, КГц", "name": "rxCentralFreq", "min": 900000, "step": 0.01}, |                   {"widget": "number", "label": "Центральная частота, КГц", "name": "rxCentralFreq", "min": 900000, "step": 0.01}, | ||||||
|                   {"widget": "number", "label": "Символьная скорость, Бод", "name": "rxBaudrate", "min": 0, "step": 1}, |                   {"widget": "number", "label": "Символьная скорость, Бод", "name": "rxBaudrate", "min": 0, "step": 1}, | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.{{ g['group'] }} = true |                 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) }) |                     .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}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.{{ g['group'] }} = false }) |                     .finally(() => { this.submitStatus.{{ g['group'] }} = false }) | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ | |||||||
|  |  | ||||||
|             resetPacketsStatistics() { |             resetPacketsStatistics() { | ||||||
|                 fetch('/api/resetPacketStatistics', { |                 fetch('/api/resetPacketStatistics', { | ||||||
|                     method: 'POST' |                     method: 'POST', credentials: 'same-origin' | ||||||
|                 }).then(() => { |                 }).then(() => { | ||||||
|                     this.statRx.packetsOk = 0 |                     this.statRx.packetsOk = 0 | ||||||
|                     this.statRx.packetsBad = 0 |                     this.statRx.packetsBad = 0 | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ | |||||||
|                     headers: { |                     headers: { | ||||||
|                         'Content-Type': 'application/json' |                         'Content-Type': 'application/json' | ||||||
|                     }, |                     }, | ||||||
|                     body: JSON.stringify(query) |                     body: JSON.stringify(query), credentials: 'same-origin' | ||||||
|                 }).then(async (resp) => { |                 }).then(async (resp) => { | ||||||
|                     this.submitStatusQos = false |                     this.submitStatusQos = false | ||||||
|                     if (resp['error']) { throw new Error(resp['error']) } |                     if (resp['error']) { throw new Error(resp['error']) } | ||||||
|   | |||||||
| @@ -191,7 +191,7 @@ | |||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     try { |                     try { | ||||||
|                         let d = await fetch("/api/get/statistics") |                         let d = await fetch("/api/get/statistics", { credentials: 'same-origin' }) | ||||||
|                         this.updateStatistics(await d.json()) |                         this.updateStatistics(await d.json()) | ||||||
|                     } catch (e) { |                     } catch (e) { | ||||||
|                         this.initState = "Ошибка обновления статистики" |                         this.initState = "Ошибка обновления статистики" | ||||||
|   | |||||||
							
								
								
									
										106
									
								
								src/auth/jwt.cpp
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/auth/jwt.cpp
									
									
									
									
									
								
							| @@ -1,8 +1,15 @@ | |||||||
| #include "jwt.h" | #include "jwt.h" | ||||||
| #include <random> | #include <random> | ||||||
|  |  | ||||||
| #include "utils.h" | #include "utils.h" | ||||||
|  |  | ||||||
|  | #include <sys/time.h> | ||||||
|  |  | ||||||
|  | static int64_t milliseconds() { | ||||||
|  |     timeval tv{}; | ||||||
|  |     gettimeofday(&tv,nullptr); | ||||||
|  |     return ((tv.tv_sec * 1000000l) + tv.tv_usec) / 1000; | ||||||
|  | } | ||||||
|  |  | ||||||
| std::string http::auth::jwt::secretKey; | std::string http::auth::jwt::secretKey; | ||||||
|  |  | ||||||
| void http::auth::jwt::generateSecretKey() { | void http::auth::jwt::generateSecretKey() { | ||||||
| @@ -16,37 +23,45 @@ void http::auth::jwt::generateSecretKey() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // payload, signature |  | ||||||
| static std::pair<std::string, std::string> 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) { | http::auth::jwt::Jwt http::auth::jwt::Jwt::fromCookies(const std::string &cookie) { | ||||||
|     auto pc = utils::parseCookies(cookie); |     auto pc = utils::parseCookies(cookie); | ||||||
|     Jwt t; |     Jwt t; | ||||||
|     if (pc.find("auth") != pc.end()) { |     if (pc.find("auth") != pc.end()) { | ||||||
|         auto tmp = parseJwtFromCookie(pc.at("auth")); |         const auto auth = pc.at("auth"); | ||||||
|         t.payload = tmp.first; |         int firstDot = -1; | ||||||
|         t.signature = tmp.second; |         int secondDot = -1; | ||||||
|  |         for (size_t i = 0; i < auth.size(); i++) { | ||||||
|  |             if (auth[i] == '.') { | ||||||
|  |                 if (firstDot < 0) { firstDot = static_cast<int>(i); } | ||||||
|  |                 else if (secondDot < 0) { secondDot = static_cast<int>(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; |     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) { | http::auth::jwt::Jwt http::auth::jwt::Jwt::fromUser(const std::string &user) { | ||||||
|     Jwt t; |     Jwt t; | ||||||
|     t.payload = user; |     t.payload = user; | ||||||
|  |     t.lastUpdate = milliseconds(); | ||||||
|     return t; |     return t; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -63,18 +79,40 @@ bool http::auth::jwt::Jwt::isValid() { | |||||||
|         return false; |         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() { | std::string http::auth::jwt::Jwt::getUsername() { | ||||||
|     return payload; |     return payload; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::string http::auth::jwt::Jwt::asCookie() { | std::string http::auth::jwt::Jwt::asCookie(bool isSecure) { | ||||||
|     signature = utils::sha256(this->payload + secretKey); |     this->lastUpdate = milliseconds(); | ||||||
|     auto val = utils::b64Encode(payload) + "." + signature; |     const auto uTime = std::to_string(this->lastUpdate); | ||||||
|     return "auth=" + val + ";Path=/; Max-Age=86400; HttpOnly; SameSite=Lax"; |     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; | http::auth::jwt::Jwt::~Jwt() = default; | ||||||
|   | |||||||
| @@ -6,7 +6,10 @@ | |||||||
| namespace http::auth::jwt { | namespace http::auth::jwt { | ||||||
|     extern std::string secretKey; |     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(); |     void generateSecretKey(); | ||||||
|  |  | ||||||
| @@ -17,6 +20,7 @@ namespace http::auth::jwt { | |||||||
|      */ |      */ | ||||||
|     class Jwt { |     class Jwt { | ||||||
|         std::string payload; |         std::string payload; | ||||||
|  |         int64_t lastUpdate = 0; | ||||||
|         std::string signature; |         std::string signature; | ||||||
|     public: |     public: | ||||||
|         static Jwt fromCookies(const std::string& cookie); |         static Jwt fromCookies(const std::string& cookie); | ||||||
| @@ -26,7 +30,9 @@ namespace http::auth::jwt { | |||||||
|  |  | ||||||
|         std::string getUsername(); |         std::string getUsername(); | ||||||
|  |  | ||||||
|         std::string asCookie(); |         std::string asCookie(bool isSecure = false); | ||||||
|  |  | ||||||
|  |         bool needUpdate() const; | ||||||
|  |  | ||||||
|         ~Jwt(); |         ~Jwt(); | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -44,12 +44,12 @@ http::auth::User::~User() = default; | |||||||
|  |  | ||||||
| http::auth::AuthProvider::AuthProvider() = default; | http::auth::AuthProvider::AuthProvider() = default; | ||||||
|  |  | ||||||
| std::shared_ptr<http::auth::User> http::auth::AuthProvider::doAuth(const std::string &username, const std::string &password, server::Reply &rep) { | std::shared_ptr<http::auth::User> http::auth::AuthProvider::doAuth(const std::string &username, const std::string &password, const server::Request &req, server::Reply &rep) { | ||||||
|     for (const auto& u: users) { |     for (const auto& u: users) { | ||||||
|         if (u->username == username) { |         if (u->username == username) { | ||||||
|             if (u->checkPassword(password)) { |             if (u->checkPassword(password)) { | ||||||
|                 auto t = jwt::Jwt::fromUser(u->username); |                 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; |                 return u; | ||||||
|             } |             } | ||||||
|             BOOST_LOG_TRIVIAL(warning) << "http::auth::AuthProvider::doAuth(): Failed to login " << username << ", password: " << password << " (incorrect password)"; |             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::User> http::auth::AuthProvider::doAuth(const std::st | |||||||
|     return nullptr; |     return nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<http::auth::User> http::auth::AuthProvider::getSession(const server::Request &req) { | std::shared_ptr<http::auth::User> http::auth::AuthProvider::getSession(const server::Request &req, server::Reply &rep) { | ||||||
|     auto t = jwt::Jwt::fromCookies(req.getHeaderValue("cookie")); |     auto t = jwt::Jwt::fromCookies(req.getHeaderValue("cookie")); | ||||||
|     if (t.isValid()) { |     if (t.isValid()) { | ||||||
|         const auto name = t.getUsername(); |         const auto name = t.getUsername(); | ||||||
|         // токен валидный, ищем юзера |         // токен валидный, ищем юзера | ||||||
|         for (auto& u: users) { |         for (auto& u: users) { | ||||||
|             if (u->username == name) { |             if (u->username == name) { | ||||||
|  |                 // на всякий случай тут проверяем, что токен пора обновлять | ||||||
|  |                 if (t.needUpdate()) { | ||||||
|  |                     rep.headers.push_back({.name = "Set-Cookie", .value = t.asCookie(req.isSecure)}); | ||||||
|  |                 } | ||||||
|                 return u; |                 return u; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -84,7 +88,7 @@ http::auth::AuthRequiredResource::AuthRequiredResource(const std::string &path, | |||||||
|     BasicResource(path), provider_(provider), generator_(std::move(generator)), perms(perms) {} |     BasicResource(path), provider_(provider), generator_(std::move(generator)), perms(perms) {} | ||||||
|  |  | ||||||
| void http::auth::AuthRequiredResource::handle(const server::Request &req, server::Reply &rep) { | 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)) { |         if (user->checkPremisions(this->perms)) { | ||||||
|             this->generator_(req, rep); |             this->generator_(req, rep); | ||||||
|             return; |             return; | ||||||
|   | |||||||
| @@ -70,20 +70,23 @@ namespace http::auth { | |||||||
|     * @note Класс устанавливает заголовок 'Set-Cookie' в ответе, и этот заголовок должен дойти до пользователя! |     * @note Класс устанавливает заголовок 'Set-Cookie' в ответе, и этот заголовок должен дойти до пользователя! | ||||||
|     */ |     */ | ||||||
|     class AuthProvider { |     class AuthProvider { | ||||||
|  |         void updateSessionHook(); | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         AuthProvider(); |         AuthProvider(); | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * Авторизовать пользователя. |          * Авторизовать пользователя. | ||||||
|          * |          * | ||||||
|  |          * @param req | ||||||
|          * @param rep |          * @param rep | ||||||
|          * @return true, в случае успешной авторизации |          * @return true, в случае успешной авторизации | ||||||
|          */ |          */ | ||||||
|         std::shared_ptr<http::auth::User> doAuth(const std::string &username, const std::string &password, server::Reply &rep); |         std::shared_ptr<http::auth::User> doAuth(const std::string &username, const std::string &password, const server::Request &req, server::Reply &rep); | ||||||
|  |  | ||||||
|         std::vector<std::shared_ptr<User>> users; |         std::vector<std::shared_ptr<User>> users; | ||||||
|  |  | ||||||
|         std::shared_ptr<User> getSession(const server::Request &req); |         std::shared_ptr<User> getSession(const server::Request &req, server::Reply &rep); | ||||||
|  |  | ||||||
|         ~AuthProvider(); |         ~AuthProvider(); | ||||||
|     }; |     }; | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/main.cpp
									
									
									
									
									
								
							| @@ -147,7 +147,7 @@ public: | |||||||
|  |  | ||||||
|     void registerResources(http::server::Server& s) { |     void registerResources(http::server::Server& s) { | ||||||
|         s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/", [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/", [this](const auto& req, auto& rep) { | ||||||
|             auto user = auth.getSession(req); |             auto user = auth.getSession(req, rep); | ||||||
|             if (user == nullptr) { |             if (user == nullptr) { | ||||||
|                 http::server::httpRedirect(rep, "/login"); |                 http::server::httpRedirect(rep, "/login"); | ||||||
|             } else { |             } else { | ||||||
| @@ -157,7 +157,7 @@ public: | |||||||
|  |  | ||||||
|         s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/login", [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/login", [this](const auto& req, auto& rep) { | ||||||
|             if (req.method == "GET") { |             if (req.method == "GET") { | ||||||
|                 auto user = auth.getSession(req); |                 auto user = auth.getSession(req, rep); | ||||||
|                 if (user == nullptr) { |                 if (user == nullptr) { | ||||||
|                     sf->serve(LOGIN_HTML, rep); |                     sf->serve(LOGIN_HTML, rep); | ||||||
|                 } else { |                 } else { | ||||||
| @@ -172,7 +172,7 @@ public: | |||||||
|                     boost::property_tree::ptree pt; |                     boost::property_tree::ptree pt; | ||||||
|                     read_json(is, pt); |                     read_json(is, pt); | ||||||
|  |  | ||||||
|                     auto u = auth.doAuth(pt.get<std::string>("username"), pt.get<std::string>("password"), rep); |                     auto u = auth.doAuth(pt.get<std::string>("username"), pt.get<std::string>("password"), req, rep); | ||||||
|                     if (u == nullptr) { |                     if (u == nullptr) { | ||||||
|                         throw std::runtime_error("invalid session"); |                         throw std::runtime_error("invalid session"); | ||||||
|                     } |                     } | ||||||
| @@ -206,10 +206,10 @@ public: | |||||||
|         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "GET") { |             if (req.method != "GET") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|             std::string result = R"({"mainState":)"; |             std::string result = R"({"mainState":)"; | ||||||
|             result += api->loadTerminalState(); |             result += api->loadTerminalState(); | ||||||
| @@ -222,10 +222,10 @@ public: | |||||||
|         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/settings", this->auth, http::auth::User::WATCH_SETTINGS, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/settings", this->auth, http::auth::User::WATCH_SETTINGS, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "GET") { |             if (req.method != "GET") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|             std::string result = R"({"settings":)"; |             std::string result = R"({"settings":)"; | ||||||
|             result += api->loadSettings(); |             result += api->loadSettings(); | ||||||
| @@ -236,10 +236,10 @@ public: | |||||||
|         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/aboutFirmware", this->auth, 0, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/aboutFirmware", this->auth, 0, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "GET") { |             if (req.method != "GET") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|             const auto result = api->loadFirmwareVersion(); |             const auto result = api->loadFirmwareVersion(); | ||||||
|             rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); |             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<http::auth::AuthRequiredResource>("/api/resetPacketStatistics", this->auth, http::auth::User::RESET_PACKET_STATISTICS, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetPacketStatistics", this->auth, http::auth::User::RESET_PACKET_STATISTICS, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "POST") { |             if (req.method != "POST") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|             api->resetPacketStatistics(); |             api->resetPacketStatistics(); | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|             const std::string result = R"({"status":"ok")"; |             const std::string result = R"({"status":"ok")"; | ||||||
|             rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); |             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<http::auth::AuthRequiredResource>("/api/set/qos", this->auth, http::auth::User::SETUP_QOS, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/qos", this->auth, http::auth::User::SETUP_QOS, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "POST") { |             if (req.method != "POST") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|  |  | ||||||
|             try { |             try { | ||||||
| @@ -289,10 +289,10 @@ public: | |||||||
|         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/buclnb", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/buclnb", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "POST") { |             if (req.method != "POST") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|  |  | ||||||
|             try { |             try { | ||||||
| @@ -317,10 +317,10 @@ public: | |||||||
|         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/cinc", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/cinc", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "POST") { |             if (req.method != "POST") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|  |  | ||||||
|             try { |             try { | ||||||
| @@ -345,10 +345,10 @@ public: | |||||||
|         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/rxtx", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/rxtx", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "POST") { |             if (req.method != "POST") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|  |  | ||||||
|             try { |             try { | ||||||
| @@ -373,10 +373,10 @@ public: | |||||||
|         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/network", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/network", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "POST") { |             if (req.method != "POST") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|  |  | ||||||
|             try { |             try { | ||||||
| @@ -401,9 +401,9 @@ public: | |||||||
|         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/reboot", this->auth, 0, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/reboot", this->auth, 0, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "POST") { |             if (req.method != "POST") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|             const std::string result = R"({"status":"ok"})"; |             const std::string result = R"({"status":"ok"})"; | ||||||
|             rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); |             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<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { |         s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { | ||||||
|             if (req.method != "POST") { |             if (req.method != "POST") { | ||||||
|                 http::server::stockReply(http::server::bad_request, rep); |                 http::server::stockReply(http::server::bad_request, rep); | ||||||
|  |                 return; | ||||||
|             } |             } | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|             const std::string result = R"({"status":"ok"})"; |             const std::string result = R"({"status":"ok"})"; | ||||||
|             rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); |             rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); | ||||||
| @@ -431,7 +431,6 @@ public: | |||||||
|             onUploadFirmware(req); |             onUploadFirmware(req); | ||||||
|  |  | ||||||
|             rep.status = http::server::ok; |             rep.status = http::server::ok; | ||||||
|             rep.headers.clear(); |  | ||||||
|             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); |             rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); | ||||||
|             std::string result = R"({"status":"ok","fwsize":)"; |             std::string result = R"({"status":"ok","fwsize":)"; | ||||||
|             result += std::to_string(req.payload.size()); |             result += std::to_string(req.payload.size()); | ||||||
|   | |||||||
| @@ -6,10 +6,14 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| namespace http::server { | 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) |     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() { |     void Connection::start() { | ||||||
| @@ -51,7 +55,7 @@ namespace http::server { | |||||||
|     void Connection::doWrite() { |     void Connection::doWrite() { | ||||||
|         reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); |         reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); | ||||||
|         reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())}); |         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"}); |             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<boost::asio::ssl::context>& ctx): |     SslConnection::SslConnection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler, const std::shared_ptr<boost::asio::ssl::context>& 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() { |     void SslConnection::start() { | ||||||
| @@ -125,7 +129,7 @@ namespace http::server { | |||||||
|     void SslConnection::doWrite() { |     void SslConnection::doWrite() { | ||||||
|         reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); |         reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); | ||||||
|         reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())}); |         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"}); |             reply_.headers.push_back({.name = "Connection", .value = "keep-alive"}); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ namespace http::server { | |||||||
|     /// A request received from a client. |     /// A request received from a client. | ||||||
|     class Request { |     class Request { | ||||||
|     public: |     public: | ||||||
|         Request(); |         Request(bool secure); | ||||||
|  |  | ||||||
|         void reset(); |         void reset(); | ||||||
|         std::string getHeaderValue(const std::string& headerName) const; |         std::string getHeaderValue(const std::string& headerName) const; | ||||||
| @@ -29,9 +29,10 @@ namespace http::server { | |||||||
|         std::string method; |         std::string method; | ||||||
|         std::string queryUri; |         std::string queryUri; | ||||||
|         std::unique_ptr<Url> url; |         std::unique_ptr<Url> url; | ||||||
|         bool is_keep_alive{}; |         bool isKeepAlive{}; | ||||||
|         int http_version_major{}; |         const bool isSecure; | ||||||
|         int http_version_minor{}; |         int httpVersionMajor{}; | ||||||
|  |         int httpVersionMinor{}; | ||||||
|         std::vector<header> headers; |         std::vector<header> headers; | ||||||
|         std::vector<char> payload; |         std::vector<char> payload; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ namespace http::server { | |||||||
|  |  | ||||||
|     Url::~Url() = default; |     Url::~Url() = default; | ||||||
|  |  | ||||||
|     Request::Request() = default; |     Request::Request(bool secure): isSecure(secure) {} | ||||||
|  |  | ||||||
|     void Request::reset() { |     void Request::reset() { | ||||||
|         method = ""; |         method = ""; | ||||||
| @@ -59,9 +59,9 @@ namespace http::server { | |||||||
|         if (url != nullptr) { |         if (url != nullptr) { | ||||||
|             url.reset(nullptr); |             url.reset(nullptr); | ||||||
|         } |         } | ||||||
|         is_keep_alive = false; |         isKeepAlive = false; | ||||||
|         http_version_major = 0; |         httpVersionMajor = 0; | ||||||
|         http_version_minor = 0; |         httpVersionMinor = 0; | ||||||
|         headers.clear(); |         headers.clear(); | ||||||
|         payload.clear(); |         payload.clear(); | ||||||
|     } |     } | ||||||
| @@ -151,8 +151,8 @@ namespace http::server { | |||||||
|                 } |                 } | ||||||
|             case http_version_slash: |             case http_version_slash: | ||||||
|                 if (input == '/') { |                 if (input == '/') { | ||||||
|                     req.http_version_major = 0; |                     req.httpVersionMajor = 0; | ||||||
|                     req.http_version_minor = 0; |                     req.httpVersionMinor = 0; | ||||||
|                     state_ = http_version_major_start; |                     state_ = http_version_major_start; | ||||||
|                     return indeterminate; |                     return indeterminate; | ||||||
|                 } else { |                 } else { | ||||||
| @@ -160,7 +160,7 @@ namespace http::server { | |||||||
|                 } |                 } | ||||||
|             case http_version_major_start: |             case http_version_major_start: | ||||||
|                 if (is_digit(input)) { |                 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; |                     state_ = http_version_major; | ||||||
|                     return indeterminate; |                     return indeterminate; | ||||||
|                 } else { |                 } else { | ||||||
| @@ -171,14 +171,14 @@ namespace http::server { | |||||||
|                     state_ = http_version_minor_start; |                     state_ = http_version_minor_start; | ||||||
|                     return indeterminate; |                     return indeterminate; | ||||||
|                 } else if (is_digit(input)) { |                 } 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; |                     return indeterminate; | ||||||
|                 } else { |                 } else { | ||||||
|                     return bad; |                     return bad; | ||||||
|                 } |                 } | ||||||
|             case http_version_minor_start: |             case http_version_minor_start: | ||||||
|                 if (is_digit(input)) { |                 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; |                     state_ = http_version_minor; | ||||||
|                     return indeterminate; |                     return indeterminate; | ||||||
|                 } else { |                 } else { | ||||||
| @@ -189,7 +189,7 @@ namespace http::server { | |||||||
|                     state_ = expecting_newline_1; |                     state_ = expecting_newline_1; | ||||||
|                     return indeterminate; |                     return indeterminate; | ||||||
|                 } else if (is_digit(input)) { |                 } 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; |                     return indeterminate; | ||||||
|                 } else { |                 } else { | ||||||
|                     return bad; |                     return bad; | ||||||
|   | |||||||
| @@ -269,7 +269,7 @@ | |||||||
|                         </select> |                         </select> | ||||||
|                     </label> |                     </label> | ||||||
|                     <label v-show="paramRxtx.rxAgcEn === false"><span>Усиление, дБ</span><input type="number" v-model="paramRxtx.rxManualGain" min="-40" max="40" step="0.01"/></label> |                     <label v-show="paramRxtx.rxAgcEn === false"><span>Усиление, дБ</span><input type="number" v-model="paramRxtx.rxManualGain" min="-40" max="40" step="0.01"/></label> | ||||||
|                     <label v-show="paramRxtx.rxAgcEn === true"><span>Текущее усиление</span><input type="text" readonly v-model="rxManualGain"/></label> |                     <label v-show="paramRxtx.rxAgcEn === true"><span>Текущее усиление</span><input type="text" readonly v-model="paramRxtx.rxManualGain"/></label> | ||||||
|                     <label> |                     <label> | ||||||
|                         <span>Инверсия спектра</span> |                         <span>Инверсия спектра</span> | ||||||
|                         <span class="toggle-input"><input type="checkbox" v-model="paramRxtx.rxSpectrumInversion" /><span class="slider"></span></span> |                         <span class="toggle-input"><input type="checkbox" v-model="paramRxtx.rxSpectrumInversion" /><span class="slider"></span></span> | ||||||
| @@ -774,7 +774,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.rxtx = true |                 this.submitStatus.rxtx = true | ||||||
|                 fetch('/api/set/rxtx', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) |                 fetch('/api/set/rxtx', {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.updateRxtxSettings(vals) }) |                     .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateRxtxSettings(vals) }) | ||||||
|                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.rxtx = false }) |                     .finally(() => { this.submitStatus.rxtx = false }) | ||||||
| @@ -793,7 +793,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.cinc = true |                 this.submitStatus.cinc = true | ||||||
|                 fetch('/api/set/cinc', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) |                 fetch('/api/set/cinc', {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.updateCincSettings(vals) }) |                     .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateCincSettings(vals) }) | ||||||
|                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.cinc = false }) |                     .finally(() => { this.submitStatus.cinc = false }) | ||||||
| @@ -812,7 +812,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.buclnb = true |                 this.submitStatus.buclnb = true | ||||||
|                 fetch('/api/set/buclnb', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) |                 fetch('/api/set/buclnb', {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.updateBuclnbSettings(vals) }) |                     .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateBuclnbSettings(vals) }) | ||||||
|                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.buclnb = false }) |                     .finally(() => { this.submitStatus.buclnb = false }) | ||||||
| @@ -826,7 +826,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.tcpaccel = true |                 this.submitStatus.tcpaccel = true | ||||||
|                 fetch('/api/set/tcpaccel', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) |                 fetch('/api/set/tcpaccel', {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.updateTcpaccelSettings(vals) }) |                     .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateTcpaccelSettings(vals) }) | ||||||
|                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.tcpaccel = false }) |                     .finally(() => { this.submitStatus.tcpaccel = false }) | ||||||
| @@ -843,7 +843,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.network = true |                 this.submitStatus.network = true | ||||||
|                 fetch('/api/set/network', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) |                 fetch('/api/set/network', {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.updateNetworkSettings(vals) }) |                     .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateNetworkSettings(vals) }) | ||||||
|                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.network = false }) |                     .finally(() => { this.submitStatus.network = false }) | ||||||
| @@ -1001,7 +1001,7 @@ | |||||||
|  |  | ||||||
|             resetPacketsStatistics() { |             resetPacketsStatistics() { | ||||||
|                 fetch('/api/resetPacketStatistics', { |                 fetch('/api/resetPacketStatistics', { | ||||||
|                     method: 'POST' |                     method: 'POST', credentials: 'same-origin' | ||||||
|                 }).then(() => { |                 }).then(() => { | ||||||
|                     this.statRx.packetsOk = 0 |                     this.statRx.packetsOk = 0 | ||||||
|                     this.statRx.packetsBad = 0 |                     this.statRx.packetsBad = 0 | ||||||
| @@ -1075,7 +1075,7 @@ | |||||||
|                     headers: { |                     headers: { | ||||||
|                         'Content-Type': 'application/json' |                         'Content-Type': 'application/json' | ||||||
|                     }, |                     }, | ||||||
|                     body: JSON.stringify(query) |                     body: JSON.stringify(query), credentials: 'same-origin' | ||||||
|                 }).then(async (resp) => { |                 }).then(async (resp) => { | ||||||
|                     this.submitStatusQos = false |                     this.submitStatusQos = false | ||||||
|                     if (resp['error']) { throw new Error(resp['error']) } |                     if (resp['error']) { throw new Error(resp['error']) } | ||||||
| @@ -1327,7 +1327,7 @@ | |||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     try { |                     try { | ||||||
|                         let d = await fetch("/api/get/statistics") |                         let d = await fetch("/api/get/statistics", { credentials: 'same-origin' }) | ||||||
|                         this.updateStatistics(await d.json()) |                         this.updateStatistics(await d.json()) | ||||||
|                     } catch (e) { |                     } catch (e) { | ||||||
|                         this.initState = "Ошибка обновления статистики" |                         this.initState = "Ошибка обновления статистики" | ||||||
|   | |||||||
| @@ -435,7 +435,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.rxtx = true |                 this.submitStatus.rxtx = true | ||||||
|                 fetch('/api/set/rxtx', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) |                 fetch('/api/set/rxtx', {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.updateRxtxSettings(vals) }) |                     .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateRxtxSettings(vals) }) | ||||||
|                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.rxtx = false }) |                     .finally(() => { this.submitStatus.rxtx = false }) | ||||||
| @@ -454,7 +454,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.buclnb = true |                 this.submitStatus.buclnb = true | ||||||
|                 fetch('/api/set/buclnb', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) |                 fetch('/api/set/buclnb', {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.updateBuclnbSettings(vals) }) |                     .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateBuclnbSettings(vals) }) | ||||||
|                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.buclnb = false }) |                     .finally(() => { this.submitStatus.buclnb = false }) | ||||||
| @@ -471,7 +471,7 @@ | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 this.submitStatus.network = true |                 this.submitStatus.network = true | ||||||
|                 fetch('/api/set/network', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query) }) |                 fetch('/api/set/network', {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.updateNetworkSettings(vals) }) |                     .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateNetworkSettings(vals) }) | ||||||
|                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) |                     .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) | ||||||
|                     .finally(() => { this.submitStatus.network = false }) |                     .finally(() => { this.submitStatus.network = false }) | ||||||
| @@ -585,7 +585,7 @@ | |||||||
|  |  | ||||||
|             resetPacketsStatistics() { |             resetPacketsStatistics() { | ||||||
|                 fetch('/api/resetPacketStatistics', { |                 fetch('/api/resetPacketStatistics', { | ||||||
|                     method: 'POST' |                     method: 'POST', credentials: 'same-origin' | ||||||
|                 }).then(() => { |                 }).then(() => { | ||||||
|                     this.statRx.packetsOk = 0 |                     this.statRx.packetsOk = 0 | ||||||
|                     this.statRx.packetsBad = 0 |                     this.statRx.packetsBad = 0 | ||||||
| @@ -684,7 +684,7 @@ | |||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     try { |                     try { | ||||||
|                         let d = await fetch("/api/get/statistics") |                         let d = await fetch("/api/get/statistics", { credentials: 'same-origin' }) | ||||||
|                         this.updateStatistics(await d.json()) |                         this.updateStatistics(await d.json()) | ||||||
|                     } catch (e) { |                     } catch (e) { | ||||||
|                         this.initState = "Ошибка обновления статистики" |                         this.initState = "Ошибка обновления статистики" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user