static Set<S> blockedIPs = synchroHashSet(); // important: cleaned regularly in #1009210 static Set<S> whiteListedIPs = synchroHashSet(); //static int blockedIPDelay = -1; // forever static int blockedIPDelay = 60; // seconds //static int spamInterval = 60000; //static double spamPerSecondThreshold = 2.0; // per IP + URI // for sub-bots to call through reflection please include function serveRedirect. please include function serve403. please include function serve404. please include function serve500. please include function serveFile. please include function serveFileWithName. please include function serveFile_maxCache. please include function serveByteArray. please include function serveByteArray_maxCache. static volatile int hitCount; // These are speaking URLs like "/images/" static new SS botNames; // see e.g. #1013896 static S anonymousDialogID = "web-anon"; static S webLogProgramID = "#1002576"; static S webLog = "webLog"; static int recentWebLogMax = 1000; static new LinkedList<Map> recentWebLog; static S homePageBotID = "#1002771"; static void webInit() { readLocally("hitCount"); readLocally("homePageBotID"); //readLocally("blockedIPs"); //set NanoHTTPD_debug; } static new Object webLock; static NanoHTTPD.Response serve(S uri, NanoHTTPD.Method method, Map<S,S> header, Map<S,S> params, Map<S,S> files) { final long startTime = sysNow(); temp tempAfterwards(r { print(formatDateAndTime() + " | " + (sysNow()-startTime) + " ms") }); SS headers = getSession().getHeaders(); S clientIP = headers.get("remote-addr"); Pair<Int, Bool> pair = simpleSpamClientDetect2(clientIP, uri); //Pair<S> pair = pair(clientIP, uri); //int ipCount = toInt(ipsAndUris.get(pair)); //ipsAndUris.put(pair, ipCount+1); bool whiteListed = whiteListedIPs.contains(clientIP); if (whiteListed) pair.b = false; bool bad = eq(uri, "/simulate-bad-client"); //int spamMax = iround(spamPerSecondThreshold*spamInterval/1000); int ipCount = pair.a; if (!bad && pair.b) { print("Blocking IP " + clientIP + ", count reached: " + ipCount); addBlockedIP(clientIP); bad = true; } if (blockedIPs.contains(clientIP)) { bad = true; if (blockedIPDelay < 0) print(formatDateAndTime() + " | Blocked IP!! " + clientIP + " " + l(nanoHttpd_badClients())); else { print(formatDateAndTime() + " | Blocked IP!! " + clientIP + (blockedIPDelay != 0 ? " Delaying " + blockedIPDelay + "s" : "")); //sleepSeconds(blockedIPDelay); } //ret serveHTML("No"); } if (bad) { NanoHTTPD.IHTTPSession httpSession = NanoHTTPD.currentSession!; if (httpSession == null || blockedIPDelay >= 0) { if (httpSession == null) print("No HTML session?"); sleepSeconds(blockedIPDelay); ret serve500("go away"); } httpSession.badClient(true); null; } S host = dropFrom(header.get("host"), ":"); print(gmtWithSeconds() + ": Serving HTTP " + quote(headers.get("host") + /*colonPortUnless80(subBot_currentPort())*/ + uri) + " to " + clientIP + " (anti-spam count: " + ipCount + "/" + simpleSpamClientDetect2_spamMax + (whiteListed ? ", white-listed" : "") + ")"); int hitID; synchronized(webLock) { ++hitCount; hitID = hitCount; if ((hitCount % 1000) == 1) { hitCount = (hitCount+999)/1000*1000; saveLocally("hitCount"); hitCount = hitID; } } pcall { webLog(litmap("time", now(), "request", hitID, "uri", uri, "method", str(method), "header", header, "params", params, "files", files)); } try { NanoHTTPD.Response response = serveNoLog(uri, method, header, params, files); // log that we responded pcall { webLog(litmap("time", now(), "response", hitID)); } ret response; } catch (Throwable e) { // log that there was an error pcall { webLog(litmap("time", now(), "response-error", hitID, getStackTrace(e))); } throw asRuntimeException(e); } } static NanoHTTPD.Response serveNoLog(S uri, NanoHTTPD.Method method, Map<S,S> header, Map<S,S> params, Map<S,S> files) { actualURI.set(uri); if (nempty(files)) print("URI: " + uri + ", files: " + files); uploadFiles.set(files); if (!startsWith(uri, "/")) ret serve404("Bad URI"); uri = dropPrefixMandatory("/", uri); if (!loaded) { int numLoaded = l(bots), total = l(botIDs); // query dispatcher for number of loaded sub-bots S lbi = loadingBotID; if (sameSnippetID(lbi, "#1002317")) { int[] x = (int[]) callOpt(loadingBot, "numLoaded"); if (x != null) { numLoaded += x[0]; total += x[1]; } lbi = or((S) getOpt(loadingBot, "loadingBotID"), lbi); } S s = "LOADING, PLEASE TRY AGAIN. " + numLoaded + "/" + total + " bots loaded "; if (lbi != null) s += " (loading bot " + formatSnippetID(lbi) /*+ " - " + getSnippetTitle_overBot(lbi)*/ + ")"; ret serve500(s); } // count and set cookie O visitorsBot = getBot("#1002157"); if (visitorsBot != null) callHtmlMethod(visitorsBot, "/"); setDialogIDForWeb(); if (eq(uri, "") || eq(uri, "raw")) ret serveHomePage("/", params); S host = dropFrom(header.get("host"), ":"); if (domainIsUnderOneOf(host, "code.botcompany.de", "tomii.me")) ret serveHomePage(uri, params); // Blog Bot handles it if (eq(uri, "favicon.ico")) ret serveFile(loadLibrary(or2(trimLoadTextFile(javaxDataDir("eleu-favicon.txt")), #1101146)), "image/x-icon"); int i = uri.indexOf('/'); if (i < 0) { uri += "/"; i = l(uri)-1; } S firstPart = uri.substring(0, i); //print("uri: " + uri + ", i: " + i + ", firstPart: " + firstPart); if (botNames.containsKey(firstPart)) { uri = dropPrefix("#", botNames.get(firstPart)) + addPrefix("/", substring(uri, i)); i = uri.indexOf('/'); firstPart = i >= 0 ? uri.substring(0, i) : uri; //print("now firstPart: " + firstPart + ", uri: " + uri + ", i: " + i); } S rest = i >= 0 ? substr(uri, i) : "/"; // rest always starts with "/" if (possibleGlobalID(toLower(firstPart))) { ret serveBot(#1007510, "/raw/" + uri, params); /*AIConcept c = aiConceptsMap_cached().get(toLower(firstPart)); if (c != null) ret serveHTML("Concept found: " + c.globalID + " - " + c.name); else ret serveHTML("Concept not found: " + toLower(firstPart));*/ } if (!isInteger(firstPart)) ret serveHomePage("/" + uri, params); ret serveBot(firstPart, rest, params); } static NanoHTTPD.Response serveBot(S botID, S subUri, Map<S, S> params) { sleepWhile(() -> isTrue_callOpt(getDispatcher(), 'isReloading, botID)); Class bot = getBot(botID); boolean raw = subUri.equals("/raw") || subUri.startsWith("/raw/"); if (raw) { subUri = substr(subUri, "/raw".length()); if (subUri.length() == 0) subUri = "/"; } if (bot != null) { //print("Bot " + botID + " methods: " + structure(allMethodNames(bot))); O botOut; try { botOut = callExtendedHtmlMethod(bot, subUri, params); } catch e { callOpt(bot, 'print, "Error while serving " + subUri + ": " + stackTraceToString(e)); throw rethrow(e); } if (botOut instanceof NanoHTTPD.Response) ret (NanoHTTPD.Response) botOut; if (eq(getClassName(botOut), NanoHTTPD.Response.class.getName())) fail("Wrong realm"); S botHtml = str(botOut); S html; if (raw) html = unnull(botHtml); else { if (botHtml == null) botHtml = "Bot has no HTML output."; S name = getSnippetTitle(botID); S title = htmlencode(name) + " [" + formatSnippetID(botID) + "]"; html = botHtml; Pair<S> pTitle = hextracttag(html, 'title); if (pairBNotNull(pTitle)) title = join(contentsOfContainerTag(htmlTok(pTitle.b))); html = pTitle.a; html = "<html><head><title>" + title + "</title></head>" + "<body><h3>Bot <a href=\"" + snippetLink(botID) + "\">" + formatSnippetID(botID) + "</a> - " + htmlencode(name) + "</h3>\n" + botHtml + "\n</body></html>"; } ret serveHTMLNoCache(html); } ret serve404(); } static NanoHTTPD.Response serveHomePage(S uri, Map<S, S> params) { if (nempty(homePageBotID)) { O bot = getBot(homePageBotID); if (bot != null) /*pcall*/ { ret serveHTMLNoCache(callHtmlMethod2(bot, uri, params)); } } ret serveHTML(botsListCache!); } static TimedCache<S> botsListCache = new(r serveBotsList, 10.0); static S serveBotsList() { new StringBuilder buf; buf.append("<html>"); buf.append("<head><title>TinyBrain Chat Bots</title></head>"); buf.append("<body>"); buf.append("<h3><a href=" + htmlQuote("https://botcompany.de") + ">TinyBrain</a> Bots</h3>"); buf.append("<ul>"); L<S> botsToList = litlist(getProgramID()); O dispatcher = getDispatcher(); if (dispatcher != null) botsToList.addAll((L) call(dispatcher, "getSubBotIDs")); botsToList.addAll(activeBots); for (S botID : botsToList) { botID = formatSnippetID(botID); S parsedID = "" + parseSnippetID(botID); S title = "?"; pcall { title = getSnippetTitle_overBot(botID); } S name = botID + " - " + htmlencode(title); buf.append("<li><a href=" + htmlQuote(parsedID) + ">" + name + "</a> ("); buf.append("<a href=" + htmlQuote(snippetLink(parsedID)) + ">source</a>)</li>"); } buf.append("</ul>"); buf.append("<p>Hit count: " + hitCount + "</p>"); buf.append("<p>Last interactions:</p>"); int maxInteractionsToPrint = 10; for (int i = l(history)-1; i >= 0 && i >= l(history)-maxInteractionsToPrint; i--) { Map m = history.get(i); buf.append("<p>" + htmlencode(m.get("question")) + "<br> " + htmlencode(m.get("answer")) + "</p>"); } buf.append("</body>"); buf.append("</html>"); ret str(buf); } // for sub-bots to call static NanoHTTPD.IHTTPSession getSession() { ret NanoHTTPD.currentSession.get(); } // for sub-bots to call static O getCookies() { NanoHTTPD.IHTTPSession session = NanoHTTPD.currentSession.get(); NanoHTTPD.CookieHandler cookies = session.getCookies(); ret cookies; } static void setDialogIDForWeb() { O session = getSession(); O cookieHandler = getCookies(); S cookie = cast call(cookieHandler, "read", "cookie"); //print("Web answer cookie: " + cookie); //print("Web answer question: " + quote(s)); S dialogID = nempty(cookie) ? "web-" + hashCookie(cookie) : anonymousDialogID; main.dialogID.set(dialogID); } static S webAnswer(S s) { ret webAnswer(s, true); } static S webAnswer(S s, boolean log) { setDialogIDForWeb(); originalLine.set(s); // drop the attn prefix ("!") s = dropPrefix("!", s).trim(); attn.set(true); dedicated.set(true); // user name is empty for now S a = null; pcall { a = answer(s, false); // generalRelease = false, all bots print("Web answer: " + quote(a)); if (empty(a)) a = "Hello :)"; } if (empty(a)) a = "(no response)"; if (log) { S user = getDialogID(); Map logEntry = litmap("question", s, "user", user, "answer", a); historyAdd(logEntry); } ret a; } static S hashCookie(S cookie) { ret md5(cookie).substring(0, 10); } static void webLog(Map data) { synchronized(webLock) { logStructure(getProgramFile(webLogProgramID, webLog), data); if (recentWebLogMax > 0) { if (l(recentWebLog) >= recentWebLogMax) recentWebLog.remove(0); recentWebLog.add(data); } } } static L<Map> getRecentWebLog() { synchronized(webLock) { ret cloneList(recentWebLog); } } static synchronized void addBlockedIP(S ip) { blockedIPs.add(ip); //save("blockedIPs"); } static synchronized void removeBlockedIP(S ip) { blockedIPs.remove(ip); //save("blockedIPs"); } static synchronized void clearBlockedIPs() { blockedIPs.clear(); }
download show line numbers debug dex old transpilations
Travelled to 16 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1002576 |
Snippet name: | Eleu Web Serving (Include) |
Eternal ID of this version: | #1002576/130 |
Text MD5: | 2883bbcc8b0d27fde6205483dc26bfde |
Author: | stefan |
Category: | eleu |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-03-30 18:46:01 |
Source code size: | 12289 bytes / 396 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 1185 / 2667 |
Version history: | 129 change(s) |
Referenced in: | [show references] |