Loading admin/static/app.js +3 −1 Changes for admin/static/app.js: 3 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -93,7 +93,7 @@ const ProxyPlusAdmin = (() => { ? " (" + r.tls_pinned_fingerprints_sha256.length + " gepinnt)" : "") : "keine"; tr.innerHTML = `<td>${r.id}</td><td>${r.domain}</td><td>${r.path_prefix}</td><td>${r.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td>${tlsInfo}</td><td></td>`; tr.innerHTML = `<td>${r.id}</td><td>${r.domain}</td><td>${r.path_prefix}</td><td>${r.priority}</td><td>${r.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td>${tlsInfo}</td><td></td>`; addEditButton(tr, () => editRoute(r)); addDeleteButton(tr, () => deleteRoute(r.id)); body.appendChild(tr); Loading @@ -106,6 +106,7 @@ const ProxyPlusAdmin = (() => { document.getElementById("route-id").readOnly = true; document.getElementById("route-domain").value = r.domain; document.getElementById("route-path").value = r.path_prefix; document.getElementById("route-priority").value = r.priority; document.getElementById("route-upstream").value = r.upstream; document.getElementById("route-upstream-pool").value = r.upstream_pool; document.getElementById("route-permission").value = r.required_permission; Loading Loading @@ -404,6 +405,7 @@ const ProxyPlusAdmin = (() => { id: document.getElementById("route-id").value, domain: document.getElementById("route-domain").value || "*", path_prefix: document.getElementById("route-path").value, priority: parseInt(document.getElementById("route-priority").value, 10) || 0, upstream: document.getElementById("route-upstream").value, upstream_pool: document.getElementById("route-upstream-pool").value, required_permission: document.getElementById("route-permission").value, Loading admin/static/index.html +2 −1 Changes for admin/static/index.html: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -24,12 +24,13 @@ <table id="status-table"></table> <h2>HTTP-Routen</h2> <table id="http-routes"><thead><tr><th>ID</th><th>Domain</th><th>Pfad</th><th>Upstream</th><th>Pool</th><th>Berechtigung</th><th>TLS-Pruefung</th><th>Aktionen</th></tr></thead><tbody></tbody></table> <table id="http-routes"><thead><tr><th>ID</th><th>Domain</th><th>Pfad</th><th>Prioritaet</th><th>Upstream</th><th>Pool</th><th>Berechtigung</th><th>TLS-Pruefung</th><th>Aktionen</th></tr></thead><tbody></tbody></table> <button onclick="ProxyPlusAdmin.showNewRouteForm()">Neue Route</button> <div id="new-route-form" hidden> <input id="route-id" placeholder="ID"> <input id="route-domain" placeholder="Domain, * fuer alle"> <input id="route-path" placeholder="Pfad-Prefix, z.B. /app"> <input id="route-priority" type="number" step="1" value="0" placeholder="Prioritaet (kleiner = zuerst geprueft, Default 0)"> <input id="route-upstream" placeholder="Upstream (direkt), genau eine URL, z.B. http://10.0.0.5:8080"> <input id="route-upstream-pool" placeholder="oder Pool-ID fuer mehrere Ziele (statt Upstream)"> <input id="route-permission" placeholder="Berechtigung, z.B. http.proxy.use"> Loading src/api/JsonApiServer.cpp +2 −0 Changes for src/api/JsonApiServer.cpp: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -106,6 +106,7 @@ json_object *httpRouteToJson(const store::HttpRouteRecord &r) { json_object_object_add(item, "id", json_object_new_string(r.id.c_str())); json_object_object_add(item, "domain", json_object_new_string(r.domain.c_str())); json_object_object_add(item, "path_prefix", json_object_new_string(r.pathPrefix.c_str())); json_object_object_add(item, "priority", json_object_new_int(r.priority)); json_object_object_add(item, "upstream", json_object_new_string(r.upstream.c_str())); json_object_object_add(item, "upstream_pool", json_object_new_string(r.upstreamPool.c_str())); json_object_object_add(item, "required_permission", Loading @@ -124,6 +125,7 @@ std::string httpRouteFromJson(json_object *body, store::HttpRouteRecord &rec) { rec.id = getStringField(body, "id"); rec.domain = getStringField(body, "domain", "*"); rec.pathPrefix = getStringField(body, "path_prefix"); rec.priority = getIntField(body, "priority", 0); rec.upstream = getStringField(body, "upstream"); rec.upstreamPool = getStringField(body, "upstream_pool"); rec.requiredPermission = getStringField(body, "required_permission"); Loading src/http/HttpProxyServer.cpp +11 −27 Changes for src/http/HttpProxyServer.cpp: 11 added lines, 27 removed lines. Original line number Diff line number Diff line Loading @@ -293,36 +293,20 @@ void HttpProxyController::RequestEvent(libhttppp::HttpRequest &curreq, const int if (auto *v = hostHdr->getfirstValue()) requestHost = normalizeHost(v->getvalue()); } // The longer/more specific pathPrefix wins outright, regardless of domain // specificity -- e.g. "*" + "/blog" outranks "example.com" + "/" for a request to // example.com/blog, since "/blog" is the more specific match a config author clearly // meant to carve out of that domain's catch-all. Domain specificity is only a // tiebreak between routes whose pathPrefix ties in length (most commonly two // catch-all "/" routes, one for a specific domain and one wildcard -- the specific // one should win for its own domain there). const store::HttpRouteRecord *best = nullptr; bool bestIsDomainSpecific = false; auto routes = _configStore.listHttpRoutes(); for (const auto &route : routes) { if (!pathPrefixMatches(url, route.pathPrefix)) continue; bool wildcard = route.domain.empty() || route.domain == "*"; if (!wildcard && normalizeHost(route.domain) != requestHost) continue; bool domainSpecific = !wildcard; bool better = !best || route.pathPrefix.size() > best->pathPrefix.size() || (route.pathPrefix.size() == best->pathPrefix.size() && domainSpecific && !bestIsDomainSpecific); if (better) { best = &route; bestIsDomainSpecific = domainSpecific; } } // routes is a local vector of copies; keep 'best' valid by re-finding after the loop // is pointless churn -- instead copy the winning record out before routes goes away. // Domain is a hard filter, not a tiebreak: a route whose domain doesn't match this // request's Host header is never eligible, regardless of how specific its pathPrefix // is. Among the routes left, the first match wins -- ConfigStore::listHttpRoutes() // returns them in match-priority order (lowest HttpRouteRecord::priority first, id as // a tiebreak), so config-defined priority decides ties, not pathPrefix length. store::HttpRouteRecord matched; bool haveRoute = false; if (best) { matched = *best; for (const auto &route : _configStore.listHttpRoutes()) { bool wildcard = route.domain.empty() || route.domain == "*"; if (!wildcard && normalizeHost(route.domain) != requestHost) continue; if (!pathPrefixMatches(url, route.pathPrefix)) continue; matched = route; haveRoute = true; break; } if (!haveRoute) { Loading src/store/ConfigStore.cpp +56 −1 Changes for src/store/ConfigStore.cpp: 56 added lines, 1 removed line. Original line number Diff line number Diff line #include "ConfigStore.h" #include <algorithm> #include <cstring> #include <mutex> #include <stdexcept> #include <utility> #include <fcntl.h> #include <sys/stat.h> Loading Loading @@ -90,6 +92,8 @@ private: size_t _pos = 0; }; // Deliberately does not touch r.priority -- see the HttpRoutePriority comment in Records.h for // why that field is persisted as its own section instead of appended here. void encodeHttpRoute(ByteWriter &w, const HttpRouteRecord &r) { w.str(r.id); w.str(r.domain); Loading @@ -116,6 +120,16 @@ HttpRouteRecord decodeHttpRoute(ByteReader &r) { return rec; } void encodeHttpRoutePriority(ByteWriter &w, const std::string &routeId, int32_t priority) { w.str(routeId); w.u32(static_cast<uint32_t>(priority)); } std::pair<std::string, int32_t> decodeHttpRoutePriority(ByteReader &r) { std::string routeId = r.str(); int32_t priority = static_cast<int32_t>(r.u32()); return {routeId, priority}; } void encodeSmtpRule(ByteWriter &w, const SmtpRelayRuleRecord &r) { w.str(r.id); w.str(r.domain); Loading Loading @@ -417,6 +431,14 @@ void ConfigStore::applyRecord(RecordTable table, RecordOp op, const std::vector< _listenerTlsAssignments.erase(r.str()); } break; case RecordTable::HttpRoutePriority: if (op == RecordOp::Put) { auto [routeId, priority] = decodeHttpRoutePriority(r); _httpRoutePriorities[routeId] = priority; } else { _httpRoutePriorities.erase(r.str()); } break; } ++_version; } Loading @@ -427,6 +449,11 @@ void ConfigStore::putHttpRoute(const HttpRouteRecord &rec) { encodeHttpRoute(w, rec); appendWal(RecordTable::HttpRoute, RecordOp::Put, w.buffer()); _httpRoutes[rec.id] = rec; ByteWriter pw; encodeHttpRoutePriority(pw, rec.id, rec.priority); appendWal(RecordTable::HttpRoutePriority, RecordOp::Put, pw.buffer()); _httpRoutePriorities[rec.id] = rec.priority; } void ConfigStore::deleteHttpRoute(const std::string &id) { Loading @@ -435,12 +462,27 @@ void ConfigStore::deleteHttpRoute(const std::string &id) { w.str(id); appendWal(RecordTable::HttpRoute, RecordOp::Delete, w.buffer()); _httpRoutes.erase(id); ByteWriter pw; pw.str(id); appendWal(RecordTable::HttpRoutePriority, RecordOp::Delete, pw.buffer()); _httpRoutePriorities.erase(id); } std::vector<HttpRouteRecord> ConfigStore::listHttpRoutes() const { std::shared_lock lk(_mutex); std::vector<HttpRouteRecord> out; for (const auto &[id, rec] : _httpRoutes) out.push_back(rec); for (const auto &[id, rec] : _httpRoutes) { out.push_back(rec); auto pit = _httpRoutePriorities.find(id); out.back().priority = (pit != _httpRoutePriorities.end()) ? pit->second : 0; } // HttpProxyController::RequestEvent picks the first match off this list -- lowest priority // first, id as a stable tiebreak (most commonly every route sitting at the default 0). std::sort(out.begin(), out.end(), [](const HttpRouteRecord &a, const HttpRouteRecord &b) { if (a.priority != b.priority) return a.priority < b.priority; return a.id < b.id; }); return out; } Loading Loading @@ -659,6 +701,9 @@ std::vector<uint8_t> ConfigStore::snapshot() const { w.u32(static_cast<uint32_t>(_listenerTlsAssignments.size())); for (const auto &[key, rec] : _listenerTlsAssignments) encodeListenerTlsAssignment(w, rec); w.u32(static_cast<uint32_t>(_httpRoutePriorities.size())); for (const auto &[routeId, priority] : _httpRoutePriorities) encodeHttpRoutePriority(w, routeId, priority); return w.buffer(); } Loading @@ -680,6 +725,7 @@ bool ConfigStore::applySnapshotIfNewer(const std::vector<uint8_t> &blob) { std::unordered_map<std::string, UpstreamPoolRecord> upstreamPools; std::unordered_map<std::string, DkimKeyRecord> dkimKeys; std::unordered_map<std::string, ListenerTlsAssignmentRecord> listenerTlsAssignments; std::unordered_map<std::string, int32_t> httpRoutePriorities; uint32_t n = r.u32(); for (uint32_t i = 0; i < n; ++i) { Loading Loading @@ -734,6 +780,14 @@ bool ConfigStore::applySnapshotIfNewer(const std::vector<uint8_t> &blob) { listenerTlsAssignments[rec.listenerKey] = rec; } } // HTTP route priorities were added after listener TLS assignments; same guard. if (!r.atEnd()) { n = r.u32(); for (uint32_t i = 0; i < n; ++i) { auto [routeId, priority] = decodeHttpRoutePriority(r); httpRoutePriorities[routeId] = priority; } } _httpRoutes = std::move(httpRoutes); _smtpRelayRules = std::move(smtpRules); Loading @@ -743,6 +797,7 @@ bool ConfigStore::applySnapshotIfNewer(const std::vector<uint8_t> &blob) { _upstreamPools = std::move(upstreamPools); _dkimKeys = std::move(dkimKeys); _listenerTlsAssignments = std::move(listenerTlsAssignments); _httpRoutePriorities = std::move(httpRoutePriorities); _version = incomingVersion; return true; } Loading Loading
admin/static/app.js +3 −1 Changes for admin/static/app.js: 3 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -93,7 +93,7 @@ const ProxyPlusAdmin = (() => { ? " (" + r.tls_pinned_fingerprints_sha256.length + " gepinnt)" : "") : "keine"; tr.innerHTML = `<td>${r.id}</td><td>${r.domain}</td><td>${r.path_prefix}</td><td>${r.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td>${tlsInfo}</td><td></td>`; tr.innerHTML = `<td>${r.id}</td><td>${r.domain}</td><td>${r.path_prefix}</td><td>${r.priority}</td><td>${r.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td>${tlsInfo}</td><td></td>`; addEditButton(tr, () => editRoute(r)); addDeleteButton(tr, () => deleteRoute(r.id)); body.appendChild(tr); Loading @@ -106,6 +106,7 @@ const ProxyPlusAdmin = (() => { document.getElementById("route-id").readOnly = true; document.getElementById("route-domain").value = r.domain; document.getElementById("route-path").value = r.path_prefix; document.getElementById("route-priority").value = r.priority; document.getElementById("route-upstream").value = r.upstream; document.getElementById("route-upstream-pool").value = r.upstream_pool; document.getElementById("route-permission").value = r.required_permission; Loading Loading @@ -404,6 +405,7 @@ const ProxyPlusAdmin = (() => { id: document.getElementById("route-id").value, domain: document.getElementById("route-domain").value || "*", path_prefix: document.getElementById("route-path").value, priority: parseInt(document.getElementById("route-priority").value, 10) || 0, upstream: document.getElementById("route-upstream").value, upstream_pool: document.getElementById("route-upstream-pool").value, required_permission: document.getElementById("route-permission").value, Loading
admin/static/index.html +2 −1 Changes for admin/static/index.html: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -24,12 +24,13 @@ <table id="status-table"></table> <h2>HTTP-Routen</h2> <table id="http-routes"><thead><tr><th>ID</th><th>Domain</th><th>Pfad</th><th>Upstream</th><th>Pool</th><th>Berechtigung</th><th>TLS-Pruefung</th><th>Aktionen</th></tr></thead><tbody></tbody></table> <table id="http-routes"><thead><tr><th>ID</th><th>Domain</th><th>Pfad</th><th>Prioritaet</th><th>Upstream</th><th>Pool</th><th>Berechtigung</th><th>TLS-Pruefung</th><th>Aktionen</th></tr></thead><tbody></tbody></table> <button onclick="ProxyPlusAdmin.showNewRouteForm()">Neue Route</button> <div id="new-route-form" hidden> <input id="route-id" placeholder="ID"> <input id="route-domain" placeholder="Domain, * fuer alle"> <input id="route-path" placeholder="Pfad-Prefix, z.B. /app"> <input id="route-priority" type="number" step="1" value="0" placeholder="Prioritaet (kleiner = zuerst geprueft, Default 0)"> <input id="route-upstream" placeholder="Upstream (direkt), genau eine URL, z.B. http://10.0.0.5:8080"> <input id="route-upstream-pool" placeholder="oder Pool-ID fuer mehrere Ziele (statt Upstream)"> <input id="route-permission" placeholder="Berechtigung, z.B. http.proxy.use"> Loading
src/api/JsonApiServer.cpp +2 −0 Changes for src/api/JsonApiServer.cpp: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -106,6 +106,7 @@ json_object *httpRouteToJson(const store::HttpRouteRecord &r) { json_object_object_add(item, "id", json_object_new_string(r.id.c_str())); json_object_object_add(item, "domain", json_object_new_string(r.domain.c_str())); json_object_object_add(item, "path_prefix", json_object_new_string(r.pathPrefix.c_str())); json_object_object_add(item, "priority", json_object_new_int(r.priority)); json_object_object_add(item, "upstream", json_object_new_string(r.upstream.c_str())); json_object_object_add(item, "upstream_pool", json_object_new_string(r.upstreamPool.c_str())); json_object_object_add(item, "required_permission", Loading @@ -124,6 +125,7 @@ std::string httpRouteFromJson(json_object *body, store::HttpRouteRecord &rec) { rec.id = getStringField(body, "id"); rec.domain = getStringField(body, "domain", "*"); rec.pathPrefix = getStringField(body, "path_prefix"); rec.priority = getIntField(body, "priority", 0); rec.upstream = getStringField(body, "upstream"); rec.upstreamPool = getStringField(body, "upstream_pool"); rec.requiredPermission = getStringField(body, "required_permission"); Loading
src/http/HttpProxyServer.cpp +11 −27 Changes for src/http/HttpProxyServer.cpp: 11 added lines, 27 removed lines. Original line number Diff line number Diff line Loading @@ -293,36 +293,20 @@ void HttpProxyController::RequestEvent(libhttppp::HttpRequest &curreq, const int if (auto *v = hostHdr->getfirstValue()) requestHost = normalizeHost(v->getvalue()); } // The longer/more specific pathPrefix wins outright, regardless of domain // specificity -- e.g. "*" + "/blog" outranks "example.com" + "/" for a request to // example.com/blog, since "/blog" is the more specific match a config author clearly // meant to carve out of that domain's catch-all. Domain specificity is only a // tiebreak between routes whose pathPrefix ties in length (most commonly two // catch-all "/" routes, one for a specific domain and one wildcard -- the specific // one should win for its own domain there). const store::HttpRouteRecord *best = nullptr; bool bestIsDomainSpecific = false; auto routes = _configStore.listHttpRoutes(); for (const auto &route : routes) { if (!pathPrefixMatches(url, route.pathPrefix)) continue; bool wildcard = route.domain.empty() || route.domain == "*"; if (!wildcard && normalizeHost(route.domain) != requestHost) continue; bool domainSpecific = !wildcard; bool better = !best || route.pathPrefix.size() > best->pathPrefix.size() || (route.pathPrefix.size() == best->pathPrefix.size() && domainSpecific && !bestIsDomainSpecific); if (better) { best = &route; bestIsDomainSpecific = domainSpecific; } } // routes is a local vector of copies; keep 'best' valid by re-finding after the loop // is pointless churn -- instead copy the winning record out before routes goes away. // Domain is a hard filter, not a tiebreak: a route whose domain doesn't match this // request's Host header is never eligible, regardless of how specific its pathPrefix // is. Among the routes left, the first match wins -- ConfigStore::listHttpRoutes() // returns them in match-priority order (lowest HttpRouteRecord::priority first, id as // a tiebreak), so config-defined priority decides ties, not pathPrefix length. store::HttpRouteRecord matched; bool haveRoute = false; if (best) { matched = *best; for (const auto &route : _configStore.listHttpRoutes()) { bool wildcard = route.domain.empty() || route.domain == "*"; if (!wildcard && normalizeHost(route.domain) != requestHost) continue; if (!pathPrefixMatches(url, route.pathPrefix)) continue; matched = route; haveRoute = true; break; } if (!haveRoute) { Loading
src/store/ConfigStore.cpp +56 −1 Changes for src/store/ConfigStore.cpp: 56 added lines, 1 removed line. Original line number Diff line number Diff line #include "ConfigStore.h" #include <algorithm> #include <cstring> #include <mutex> #include <stdexcept> #include <utility> #include <fcntl.h> #include <sys/stat.h> Loading Loading @@ -90,6 +92,8 @@ private: size_t _pos = 0; }; // Deliberately does not touch r.priority -- see the HttpRoutePriority comment in Records.h for // why that field is persisted as its own section instead of appended here. void encodeHttpRoute(ByteWriter &w, const HttpRouteRecord &r) { w.str(r.id); w.str(r.domain); Loading @@ -116,6 +120,16 @@ HttpRouteRecord decodeHttpRoute(ByteReader &r) { return rec; } void encodeHttpRoutePriority(ByteWriter &w, const std::string &routeId, int32_t priority) { w.str(routeId); w.u32(static_cast<uint32_t>(priority)); } std::pair<std::string, int32_t> decodeHttpRoutePriority(ByteReader &r) { std::string routeId = r.str(); int32_t priority = static_cast<int32_t>(r.u32()); return {routeId, priority}; } void encodeSmtpRule(ByteWriter &w, const SmtpRelayRuleRecord &r) { w.str(r.id); w.str(r.domain); Loading Loading @@ -417,6 +431,14 @@ void ConfigStore::applyRecord(RecordTable table, RecordOp op, const std::vector< _listenerTlsAssignments.erase(r.str()); } break; case RecordTable::HttpRoutePriority: if (op == RecordOp::Put) { auto [routeId, priority] = decodeHttpRoutePriority(r); _httpRoutePriorities[routeId] = priority; } else { _httpRoutePriorities.erase(r.str()); } break; } ++_version; } Loading @@ -427,6 +449,11 @@ void ConfigStore::putHttpRoute(const HttpRouteRecord &rec) { encodeHttpRoute(w, rec); appendWal(RecordTable::HttpRoute, RecordOp::Put, w.buffer()); _httpRoutes[rec.id] = rec; ByteWriter pw; encodeHttpRoutePriority(pw, rec.id, rec.priority); appendWal(RecordTable::HttpRoutePriority, RecordOp::Put, pw.buffer()); _httpRoutePriorities[rec.id] = rec.priority; } void ConfigStore::deleteHttpRoute(const std::string &id) { Loading @@ -435,12 +462,27 @@ void ConfigStore::deleteHttpRoute(const std::string &id) { w.str(id); appendWal(RecordTable::HttpRoute, RecordOp::Delete, w.buffer()); _httpRoutes.erase(id); ByteWriter pw; pw.str(id); appendWal(RecordTable::HttpRoutePriority, RecordOp::Delete, pw.buffer()); _httpRoutePriorities.erase(id); } std::vector<HttpRouteRecord> ConfigStore::listHttpRoutes() const { std::shared_lock lk(_mutex); std::vector<HttpRouteRecord> out; for (const auto &[id, rec] : _httpRoutes) out.push_back(rec); for (const auto &[id, rec] : _httpRoutes) { out.push_back(rec); auto pit = _httpRoutePriorities.find(id); out.back().priority = (pit != _httpRoutePriorities.end()) ? pit->second : 0; } // HttpProxyController::RequestEvent picks the first match off this list -- lowest priority // first, id as a stable tiebreak (most commonly every route sitting at the default 0). std::sort(out.begin(), out.end(), [](const HttpRouteRecord &a, const HttpRouteRecord &b) { if (a.priority != b.priority) return a.priority < b.priority; return a.id < b.id; }); return out; } Loading Loading @@ -659,6 +701,9 @@ std::vector<uint8_t> ConfigStore::snapshot() const { w.u32(static_cast<uint32_t>(_listenerTlsAssignments.size())); for (const auto &[key, rec] : _listenerTlsAssignments) encodeListenerTlsAssignment(w, rec); w.u32(static_cast<uint32_t>(_httpRoutePriorities.size())); for (const auto &[routeId, priority] : _httpRoutePriorities) encodeHttpRoutePriority(w, routeId, priority); return w.buffer(); } Loading @@ -680,6 +725,7 @@ bool ConfigStore::applySnapshotIfNewer(const std::vector<uint8_t> &blob) { std::unordered_map<std::string, UpstreamPoolRecord> upstreamPools; std::unordered_map<std::string, DkimKeyRecord> dkimKeys; std::unordered_map<std::string, ListenerTlsAssignmentRecord> listenerTlsAssignments; std::unordered_map<std::string, int32_t> httpRoutePriorities; uint32_t n = r.u32(); for (uint32_t i = 0; i < n; ++i) { Loading Loading @@ -734,6 +780,14 @@ bool ConfigStore::applySnapshotIfNewer(const std::vector<uint8_t> &blob) { listenerTlsAssignments[rec.listenerKey] = rec; } } // HTTP route priorities were added after listener TLS assignments; same guard. if (!r.atEnd()) { n = r.u32(); for (uint32_t i = 0; i < n; ++i) { auto [routeId, priority] = decodeHttpRoutePriority(r); httpRoutePriorities[routeId] = priority; } } _httpRoutes = std::move(httpRoutes); _smtpRelayRules = std::move(smtpRules); Loading @@ -743,6 +797,7 @@ bool ConfigStore::applySnapshotIfNewer(const std::vector<uint8_t> &blob) { _upstreamPools = std::move(upstreamPools); _dkimKeys = std::move(dkimKeys); _listenerTlsAssignments = std::move(listenerTlsAssignments); _httpRoutePriorities = std::move(httpRoutePriorities); _version = incomingVersion; return true; } Loading