Commit c90f2220 authored by jan.koester's avatar jan.koester
Browse files

test

parent bb2102cf
Loading
Loading
Loading
Loading
+2 −1
Changes for admin/static/app.js: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -83,7 +83,7 @@ const ProxyPlusAdmin = (() => {
            ? " (" + r.tls_pinned_fingerprints_sha256.length + " gepinnt)"
            : "")
        : "keine";
      tr.innerHTML = `<td>${r.id}</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.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td>${tlsInfo}</td><td></td>`;
      addDeleteButton(tr, () => deleteRoute(r.id));
      body.appendChild(tr);
    }
@@ -214,6 +214,7 @@ const ProxyPlusAdmin = (() => {
        .filter(Boolean);
      await api("POST", "/api/v1/http/routes", {
        id: document.getElementById("route-id").value,
        domain: document.getElementById("route-domain").value || "*",
        path_prefix: document.getElementById("route-path").value,
        upstream: document.getElementById("route-upstream").value,
        upstream_pool: document.getElementById("route-upstream-pool").value,
+2 −1
Changes for admin/static/index.html: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -24,10 +24,11 @@
  <table id="status-table"></table>

  <h2>HTTP-Routen</h2>
  <table id="http-routes"><thead><tr><th>ID</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>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-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)">
+2 −0
Changes for src/api/JsonApiServer.cpp: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -198,6 +198,7 @@ void JsonApiController::registerRoutes() {
             for (const auto &r : _configStore.listHttpRoutes()) {
                 JsonGuard item(json_object_new_object());
                 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, "upstream", json_object_new_string(r.upstream.c_str()));
                 json_object_object_add(item, "upstream_pool", json_object_new_string(r.upstreamPool.c_str()));
@@ -223,6 +224,7 @@ void JsonApiController::registerRoutes() {
             if (!body.obj) return jsonError("invalid JSON body");
             store::HttpRouteRecord rec;
             rec.id = getStringField(body, "id");
             rec.domain = getStringField(body, "domain", "*");
             rec.pathPrefix = getStringField(body, "path_prefix");
             rec.upstream = getStringField(body, "upstream");
             rec.upstreamPool = getStringField(body, "upstream_pool");
+31 −3
Changes for src/http/HttpProxyServer.cpp: 31 added lines, 3 removed lines.
Original line number Diff line number Diff line
#include "HttpProxyServer.h"

#include <algorithm>
#include <cctype>
#include <iostream>
#include <set>

@@ -47,6 +49,16 @@ std::vector<uint8_t> parseHexFingerprint(const std::string &hex) {
    return out;
}

// Normalizes a Host header for route matching: strips the ":port" suffix (routes match on
// hostname only) and lowercases (DNS names are case-insensitive, but stored/typed route
// domains and incoming headers won't reliably agree on case otherwise).
std::string normalizeHost(const std::string &hostHeader) {
    std::string host = hostHeader.substr(0, hostHeader.find(':'));
    std::transform(host.begin(), host.end(), host.begin(),
                    [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
    return host;
}

netplus::TlsTrustPolicy trustPolicyFor(const store::HttpRouteRecord &route) {
    netplus::TlsTrustPolicy policy;
    policy.verifyPeer = route.tlsVerifyUpstream;
@@ -231,13 +243,29 @@ void HttpProxyController::RequestEvent(libhttppp::HttpRequest &curreq, const int

        if (method == "GET" && tryServeAcmeChallenge(curreq, url)) return;

        std::string requestHost;
        if (auto *hostHdr = curreq.getHeaderData("host")) {
            if (auto *v = hostHdr->getfirstValue()) requestHost = normalizeHost(v->getvalue());
        }

        // Prefer an exact domain match over a wildcard ("*"/empty = any Host) one regardless
        // of path length, so e.g. "example.com" + "/" outranks "*" + "/app" for a request to
        // example.com/app -- only among routes of the same domain-specificity does the longer
        // pathPrefix win.
        const store::HttpRouteRecord *best = nullptr;
        bool bestIsDomainSpecific = false;
        auto routes = _configStore.listHttpRoutes();
        for (const auto &route : routes) {
            if (url.rfind(route.pathPrefix, 0) == 0) {
                if (!best || route.pathPrefix.size() > best->pathPrefix.size()) {
            if (url.rfind(route.pathPrefix, 0) != 0) continue;
            bool wildcard = route.domain.empty() || route.domain == "*";
            if (!wildcard && normalizeHost(route.domain) != requestHost) continue;
            bool domainSpecific = !wildcard;
            bool better = !best || (domainSpecific && !bestIsDomainSpecific) ||
                          (domainSpecific == bestIsDomainSpecific &&
                           route.pathPrefix.size() > best->pathPrefix.size());
            if (better) {
                best = &route;
                }
                bestIsDomainSpecific = domainSpecific;
            }
        }
        // routes is a local vector of copies; keep 'best' valid by re-finding after the loop
+2 −0
Changes for src/store/ConfigStore.cpp: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ private:

void encodeHttpRoute(ByteWriter &w, const HttpRouteRecord &r) {
    w.str(r.id);
    w.str(r.domain);
    w.str(r.pathPrefix);
    w.str(r.upstream);
    w.str(r.requiredPermission);
@@ -103,6 +104,7 @@ void encodeHttpRoute(ByteWriter &w, const HttpRouteRecord &r) {
HttpRouteRecord decodeHttpRoute(ByteReader &r) {
    HttpRouteRecord rec;
    rec.id = r.str();
    rec.domain = r.str();
    rec.pathPrefix = r.str();
    rec.upstream = r.str();
    rec.requiredPermission = r.str();
Loading