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

400
LINES

< > BotCompany Repo | #1029953 // Eleu Web Serving (transpilation test)

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

Libraryless. Click here for Pure Java version (5060L/35K).

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.
meta-comment {
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=\"http://tinybrain.de/" + parseSnippetID(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(f 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("http://tinybrain.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("http://tinybrain.de/" + 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();
}

} // end of comment

Author comment

Began life as a copy of #1002576

download  show line numbers  debug dex  old transpilations   

Travelled to 4 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt

No comments. add comment

Snippet ID: #1029953
Snippet name: Eleu Web Serving (transpilation test)
Eternal ID of this version: #1029953/8
Text MD5: a9ad973a1955d2df71541e9a71cc620f
Transpilation MD5: bd54f3ce43f95c2f528bdd2185b6e029
Author: stefan
Category: eleu
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-10-14 16:33:32
Source code size: 12363 bytes / 400 lines
Pitched / IR pitched: No / No
Views / Downloads: 110 / 161
Version history: 7 change(s)
Referenced in: [show references]