Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

396
LINES

< > BotCompany Repo | #1002576 // Eleu Web Serving (Include)

JavaX fragment (include) [tags: use-pretranspiled]

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>&nbsp; &nbsp; " + 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: 1186 / 2668
Version history: 129 change(s)
Referenced in: #1002017 - Eleutheria Main, including Slack Bot (LIVE)
#1002576 - Eleu Web Serving (Include)
#1002591 - Eleu Sister Main for tinybrain.de
#1002638 - Web Log Bot (Web)
#1013896 - Eleutheria Main for butter.botcompany.de + Stefan's OS (LIVE)
#1022823 - Eleutheria Main for butter.botcompany.de as Dyn Module [dev.]
#1023951 - agi.blue Standalone with GUI [OK]
#1024026 - Eleutheria Main for butter.botcompany.de (backup without Stefan's OS)
#1029953 - Eleu Web Serving (transpilation test)
#3000202 - Answer for stefanreich (>> T conversion bot)
#3000238 - Answer for stefanreich (>> t power bot)
#3000382 - Answer for ferdie (>> t = 1, f = 0)
#3000383 - Answer for funkoverflow (>> t=1, f=0 okay)
#3000475 - Smart Bot's answer to: !fresh recentlyChangedSnippets()