!7 do not include class WebRequest. concept Domain { S domain; S moduleLibID; S proxyDestination; S proxyCookieChecker; // a module lib ID bool proxyRewritePort; bool mustBeEnabled; toString { ret super.toString() + " " + domain; } } // Eleu is so important, we don't use a shared lib module Eleu3 > DynEleu { transient CRUD domainCRUD; switchable bool verbose; switchable bool debugNanoHTTPD; switchable S sameSite; {} { enabled = false; } start { NanoHTTPD_debug = debugNanoHTTPD; //bootstrapDBFrom(#1029618); set flag defaultDefaultClassFinder_debug. dbIndexingCI(Domain, 'domain); //assertNempty(list(Domain)); uniqCI Domain(domain := ""); domainCRUD = new CRUD(Domain); //dm_registerAs eleuWithCRUD(); } visualize { var c = jtabs( "Main", super.visualize(), "Domains" := domainCRUD.visualize()); tablePopupMenu(domainCRUD.table(), voidfunc(JPopupMenu menu, int row) { Domain d = domainCRUD.selected(), ret if null; if (nempty(d.moduleLibID)) addMenuItem(menu, "Load module", rThreadEnter { dm_makeOrShow(d.moduleLibID) }); }); ret c; } @Override S moduleForDomain(S domain) { domain = dropAfterColon(domain); // drop port number for (Domain d : domainsInMatchingOrder()) { if (matchingDomain(d, domain)) { S mod = findMod(d); if (verbose) print("Domain matched: " + domain + " / " + d.domain + " => " + mod); try answer mod; } else if (verbose) print("Domain not matched: " + domain + " / " + d.domain); } ret findMod(defaultDomain()); } Domain defaultDomain() { ret conceptWhereCI Domain(domain := ""); } S findMod(Domain d) { if (d == null || empty(d.moduleLibID)) null; ret d.mustBeEnabled ? dm_findModuleWithParams(d.moduleLibID, enabled := true) : dm_findModule(d.moduleLibID); } ServeHttp_CookieHandler makeCookieHandler() { ServeHttp_CookieHandler cookieHandler = super.makeCookieHandler(); cookieHandler.metaParams = appendPrefixIfNempty("SameSite=", sameSite); ret cookieHandler; } void onWebServersStarted :: after { forEach(webServers(), ws -> { print("Augmenting web server: " + ws); ws.collectHeaderLines = true; // session is an instance of NanoHTTPD.IHTTPSession ws.specialHandling = session -> ctex { temp enter(); S domain = dropPortFromHost(mapGet(session.getHeaders(), "host")); Domain d = first(domainsInMatchingOrder(), _d -> matchingDomain(_d, domain)); printVars(+domain, +d); if (d == null || empty(d.proxyDestination)) false; print("Handling proxy request to " + d.proxyDestination); HostAndPort hap = new(d.proxyDestination); if (nempty(d.proxyCookieChecker)) { S cookieChecker = dm_findModule(d.proxyCookieChecker); if (empty(cookieChecker)) fail(print("Cookie checker " + d.proxyCookieChecker + " not found, aborting request")); NanoHTTPD.CookieHandler cookies = session.getCookies(); if (!isTrue(dm_call(cookieChecker, "checkCookie", cookies.read("cookie"), domain))) fail(print("Cookie checker disapproves")); else print("Cookie checker approves. URI: " + session.getUri()); } new HandleProxyRequest hpr; hpr.hap = hap; hpr.session = session; hpr.rewritePort = d.proxyRewritePort; hpr.customizeRequest = proxyRequest -> { proxyRequest.setOut = out -> { proxyRequest.setOut_base(out); proxyRequest.outProxied = out; // mod here }; proxyRequest.setOutOut = outOut -> { proxyRequest.setOutOut_base(outOut); proxyRequest.outOutProxied = outOut; // mod here }; }; vmBus_send handlingProxyRequest(me(), hpr); ret true with hpr.run(); }; }); } // onWebServersStarted bool matchingDomain(Domain d, S domain) { ret domainIsUnder_extended(domain, d.domain); } class WebRequest2 extends WebRequest { *(NanoHTTPD.IHTTPSession httpSession, S uri, SS params) { super(httpSession, uri, params); } // TODO: change uri etc /*public NanoHTTPD.Response proxy(S url, bool rewriteHost, bool rewritePort) { url = dropPrefix("http://", url); if (startsWith(url, "https://")) new HandleProxyRequest hpr; hpr.hap = hap; hpr.session = httpSession; hpr.rewriteHost = rewriteHost; hpr.rewritePort = rewritePort; hpr.run(); null; }*/ } @Override WebRequest createWebRequest(NanoHTTPD.IHTTPSession httpSession, S uri, SS params) { ret new WebRequest2(httpSession, uri, params); } // API Cl domainsInMatchingOrder() { ret sortByCalculatedFieldICDesc( filter(list(Domain), d -> !eqic(d.domain, "")), d -> reversed(d.domain)); } void addDomain(S domain, S moduleLibID, bool mustBeEnabled) { uniq Domain(+domain, +moduleLibID, +mustBeEnabled); } }