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]

1  
static Set<S> blockedIPs = synchroHashSet(); // important: cleaned regularly in #1009210
2  
static Set<S> whiteListedIPs = synchroHashSet();
3  
4  
//static int blockedIPDelay = -1; // forever
5  
static int blockedIPDelay = 60; // seconds
6  
7  
//static int spamInterval = 60000;
8  
//static double spamPerSecondThreshold = 2.0; // per IP + URI
9  
10  
// for sub-bots to call through reflection
11  
please include function serveRedirect.
12  
please include function serve403.
13  
please include function serve404.
14  
please include function serve500.
15  
please include function serveFile.
16  
please include function serveFileWithName.
17  
please include function serveFile_maxCache.
18  
please include function serveByteArray.
19  
please include function serveByteArray_maxCache.
20  
21  
static volatile int hitCount;
22  
23  
// These are speaking URLs like "/images/"
24  
static new SS botNames; // see e.g. #1013896
25  
26  
static S anonymousDialogID = "web-anon";
27  
28  
static S webLogProgramID = "#1002576";
29  
static S webLog = "webLog";
30  
31  
static int recentWebLogMax = 1000;
32  
static new LinkedList<Map> recentWebLog;
33  
34  
static S homePageBotID = "#1002771";
35  
36  
static void webInit() {
37  
  readLocally("hitCount");
38  
  readLocally("homePageBotID");
39  
  //readLocally("blockedIPs");
40  
  //set NanoHTTPD_debug;
41  
}
42  
43  
static new Object webLock;
44  
45  
static NanoHTTPD.Response serve(S uri, NanoHTTPD.Method method,
46  
  Map<S,S> header, Map<S,S> params, Map<S,S> files) {
47  
  
48  
  final long startTime = sysNow();
49  
  temp tempAfterwards(r { print(formatDateAndTime() + " | " + (sysNow()-startTime) + " ms") });
50  
  
51  
  SS headers = getSession().getHeaders();
52  
  S clientIP = headers.get("remote-addr");
53  
  
54  
  Pair<Int, Bool> pair = simpleSpamClientDetect2(clientIP, uri);
55  
  //Pair<S> pair = pair(clientIP, uri);
56  
  //int ipCount = toInt(ipsAndUris.get(pair));
57  
  //ipsAndUris.put(pair, ipCount+1);
58  
59  
  bool whiteListed = whiteListedIPs.contains(clientIP);
60  
  if (whiteListed) pair.b = false;
61  
  
62  
  bool bad = eq(uri, "/simulate-bad-client");
63  
  //int spamMax = iround(spamPerSecondThreshold*spamInterval/1000);
64  
  int ipCount = pair.a;
65  
  
66  
  if (!bad && pair.b) {
67  
    print("Blocking IP " + clientIP + ", count reached: " + ipCount);
68  
    addBlockedIP(clientIP);
69  
    bad = true;
70  
  }
71  
  
72  
  if (blockedIPs.contains(clientIP)) {
73  
    bad = true;
74  
    if (blockedIPDelay < 0)
75  
      print(formatDateAndTime() + " | Blocked IP!! " + clientIP + " " + l(nanoHttpd_badClients()));
76  
    else {
77  
      print(formatDateAndTime() + " | Blocked IP!! " + clientIP + (blockedIPDelay != 0 ? " Delaying " + blockedIPDelay + "s" : ""));
78  
      //sleepSeconds(blockedIPDelay);
79  
    }
80  
    //ret serveHTML("No");
81  
  }
82  
  
83  
  if (bad) {
84  
    NanoHTTPD.IHTTPSession httpSession = NanoHTTPD.currentSession!;
85  
    if (httpSession == null || blockedIPDelay >= 0) {
86  
      if (httpSession == null) print("No HTML session?");
87  
      sleepSeconds(blockedIPDelay);
88  
      ret serve500("go away");
89  
    }
90  
    httpSession.badClient(true);
91  
    null;
92  
  }
93  
  
94  
  S host = dropFrom(header.get("host"), ":");
95  
  
96  
  print(gmtWithSeconds() + ": Serving HTTP " + quote(headers.get("host") + /*colonPortUnless80(subBot_currentPort())*/ + uri) + " to " + clientIP + " (anti-spam count: " + ipCount + "/" + simpleSpamClientDetect2_spamMax + (whiteListed ? ", white-listed" : "") + ")");
97  
  
98  
  int hitID;
99  
  
100  
  synchronized(webLock) {
101  
    ++hitCount;
102  
    hitID = hitCount;
103  
    if ((hitCount % 1000) == 1) {
104  
      hitCount = (hitCount+999)/1000*1000;
105  
      saveLocally("hitCount");
106  
      hitCount = hitID;
107  
    }
108  
  }
109  
  
110  
  pcall { webLog(litmap("time", now(), "request", hitID, "uri", uri, "method", str(method), "header", header, "params", params, "files", files)); }
111  
  
112  
  try {
113  
    NanoHTTPD.Response response = serveNoLog(uri, method, header, params, files);
114  
    
115  
    // log that we responded
116  
    pcall { webLog(litmap("time", now(), "response", hitID)); }
117  
    
118  
    ret response;
119  
  } catch (Throwable e) {
120  
    // log that there was an error
121  
    pcall { webLog(litmap("time", now(), "response-error", hitID, getStackTrace(e))); }
122  
    throw asRuntimeException(e);
123  
  }
124  
}
125  
  
126  
static NanoHTTPD.Response serveNoLog(S uri, NanoHTTPD.Method method,
127  
  Map<S,S> header, Map<S,S> params, Map<S,S> files) {
128  
  
129  
  actualURI.set(uri);
130  
  if (nempty(files))
131  
    print("URI: " + uri + ", files: " + files);
132  
  uploadFiles.set(files);
133  
  if (!startsWith(uri, "/")) ret serve404("Bad URI");
134  
  uri = dropPrefixMandatory("/", uri);
135  
  
136  
  if (!loaded) {
137  
    int numLoaded = l(bots), total = l(botIDs);
138  
    
139  
    // query dispatcher for number of loaded sub-bots
140  
    
141  
    S lbi = loadingBotID;
142  
    if (sameSnippetID(lbi, "#1002317")) {
143  
      int[] x = (int[]) callOpt(loadingBot, "numLoaded");
144  
      if (x != null) {
145  
        numLoaded += x[0];
146  
        total += x[1];
147  
      }
148  
      
149  
      lbi = or((S) getOpt(loadingBot, "loadingBotID"), lbi);
150  
    }
151  
    
152  
    S s = "LOADING, PLEASE TRY AGAIN. " + numLoaded + "/" + total + " bots loaded ";
153  
    if (lbi != null) 
154  
      s +=  " (loading bot " + formatSnippetID(lbi) /*+ " - " + getSnippetTitle_overBot(lbi)*/ + ")";
155  
    
156  
    ret serve500(s);
157  
  }
158  
  
159  
  // count and set cookie
160  
  O visitorsBot = getBot("#1002157");
161  
  if (visitorsBot != null)
162  
    callHtmlMethod(visitorsBot, "/");
163  
164  
  setDialogIDForWeb();
165  
  
166  
  if (eq(uri, "") || eq(uri, "raw"))
167  
    ret serveHomePage("/", params);
168  
    
169  
  S host = dropFrom(header.get("host"), ":");
170  
  
171  
  if (domainIsUnderOneOf(host, "code.botcompany.de", "tomii.me"))
172  
    ret serveHomePage(uri, params); // Blog Bot handles it
173  
    
174  
  if (eq(uri, "favicon.ico"))
175  
    ret serveFile(loadLibrary(or2(trimLoadTextFile(javaxDataDir("eleu-favicon.txt")), #1101146)), "image/x-icon");
176  
177  
  int i = uri.indexOf('/');
178  
  if (i < 0) { uri += "/"; i = l(uri)-1; }
179  
  S firstPart = uri.substring(0, i);
180  
  
181  
  //print("uri: " + uri + ", i: " + i + ", firstPart: " + firstPart);
182  
  if (botNames.containsKey(firstPart)) {
183  
    uri = dropPrefix("#", botNames.get(firstPart))
184  
      + addPrefix("/", substring(uri, i));
185  
    i = uri.indexOf('/');
186  
    firstPart = i >= 0 ? uri.substring(0, i) : uri;
187  
    //print("now firstPart: " + firstPart + ", uri: " + uri + ", i: " + i);
188  
  }
189  
  
190  
  S rest = i >= 0 ? substr(uri, i) : "/"; // rest always starts with "/"
191  
  
192  
  if (possibleGlobalID(toLower(firstPart))) {
193  
    ret serveBot(#1007510, "/raw/" + uri, params);
194  
    /*AIConcept c = aiConceptsMap_cached().get(toLower(firstPart));
195  
    if (c != null)
196  
      ret serveHTML("Concept found: " + c.globalID + " - " + c.name);
197  
    else
198  
      ret serveHTML("Concept not found: " + toLower(firstPart));*/
199  
  }
200  
201  
  if (!isInteger(firstPart))
202  
    ret serveHomePage("/" + uri, params);
203  
    
204  
  ret serveBot(firstPart, rest, params);
205  
}
206  
207  
static NanoHTTPD.Response serveBot(S botID, S subUri, Map<S, S> params) {
208  
  sleepWhile(() -> isTrue_callOpt(getDispatcher(), 'isReloading, botID));
209  
  
210  
  Class bot = getBot(botID);
211  
  boolean raw = subUri.equals("/raw") || subUri.startsWith("/raw/");
212  
  if (raw) {
213  
    subUri = substr(subUri, "/raw".length());
214  
    if (subUri.length() == 0) subUri = "/";
215  
  }
216  
  if (bot != null) {
217  
    //print("Bot " + botID + " methods: " + structure(allMethodNames(bot)));
218  
    O botOut;
219  
    try {
220  
      botOut = callExtendedHtmlMethod(bot, subUri, params);
221  
    } catch e {
222  
      callOpt(bot, 'print, "Error while serving " + subUri + ": " + stackTraceToString(e));
223  
      throw rethrow(e);
224  
    }
225  
    
226  
    if (botOut instanceof NanoHTTPD.Response)
227  
      ret (NanoHTTPD.Response) botOut;
228  
    if (eq(getClassName(botOut), NanoHTTPD.Response.class.getName()))
229  
      fail("Wrong realm");
230  
      
231  
    S botHtml = str(botOut);
232  
    S html;
233  
    if (raw) html = unnull(botHtml);
234  
    else {
235  
      if (botHtml == null)
236  
        botHtml = "Bot has no HTML output.";
237  
      S name = getSnippetTitle(botID);
238  
      S title = htmlencode(name) + " [" + formatSnippetID(botID) + "]";
239  
      
240  
      html = botHtml;
241  
      Pair<S> pTitle = hextracttag(html, 'title);
242  
      if (pairBNotNull(pTitle)) title = join(contentsOfContainerTag(htmlTok(pTitle.b)));
243  
      html = pTitle.a;
244  
      html = "<html><head><title>" + title + "</title></head>" +
245  
        "<body><h3>Bot <a href=\"" + snippetLink(botID) + "\">" + formatSnippetID(botID) + "</a> - " + htmlencode(name) + "</h3>\n" + botHtml +
246  
        "\n</body></html>";
247  
    }
248  
    ret serveHTMLNoCache(html);
249  
  }
250  
  
251  
  ret serve404();
252  
}
253  
254  
static NanoHTTPD.Response serveHomePage(S uri, Map<S, S> params) {
255  
  if (nempty(homePageBotID)) {
256  
    O bot = getBot(homePageBotID);
257  
    if (bot != null) /*pcall*/ {
258  
      ret serveHTMLNoCache(callHtmlMethod2(bot, uri, params));
259  
    }
260  
  }
261  
262  
  ret serveHTML(botsListCache!);
263  
}
264  
265  
static TimedCache<S> botsListCache = new(r serveBotsList, 10.0);
266  
267  
static S serveBotsList() {
268  
  new StringBuilder buf;
269  
  buf.append("<html>");
270  
  buf.append("<head><title>TinyBrain Chat Bots</title></head>");
271  
  buf.append("<body>");
272  
  buf.append("<h3><a href=" + htmlQuote("https://botcompany.de") + ">TinyBrain</a> Bots</h3>");
273  
  buf.append("<ul>");
274  
  
275  
  L<S> botsToList = litlist(getProgramID());
276  
  O dispatcher = getDispatcher();
277  
  if (dispatcher != null)
278  
    botsToList.addAll((L) call(dispatcher, "getSubBotIDs"));
279  
  botsToList.addAll(activeBots);
280  
  
281  
  for (S botID : botsToList) {
282  
    botID = formatSnippetID(botID);
283  
    S parsedID = "" + parseSnippetID(botID);
284  
    S title = "?";
285  
    pcall { title = getSnippetTitle_overBot(botID); }
286  
    S name = botID + " - " + htmlencode(title);
287  
    buf.append("<li><a href=" + htmlQuote(parsedID) + ">" + name + "</a> (");
288  
    buf.append("<a href=" + htmlQuote(snippetLink(parsedID)) + ">source</a>)</li>");
289  
  }
290  
  buf.append("</ul>");
291  
  buf.append("<p>Hit count: " + hitCount + "</p>");
292  
  
293  
  buf.append("<p>Last interactions:</p>");
294  
  
295  
  int maxInteractionsToPrint = 10;
296  
  
297  
  for (int i = l(history)-1; i >= 0 && i >= l(history)-maxInteractionsToPrint; i--) {
298  
    Map m = history.get(i);
299  
    buf.append("<p>" + htmlencode(m.get("question")) + "<br>&nbsp; &nbsp; " + htmlencode(m.get("answer")) + "</p>");
300  
  }
301  
  
302  
  buf.append("</body>");
303  
  buf.append("</html>");
304  
  ret str(buf);
305  
}
306  
307  
// for sub-bots to call
308  
static NanoHTTPD.IHTTPSession getSession() {
309  
  ret NanoHTTPD.currentSession.get();
310  
}
311  
312  
// for sub-bots to call
313  
static O getCookies() {
314  
  NanoHTTPD.IHTTPSession session = NanoHTTPD.currentSession.get();
315  
  NanoHTTPD.CookieHandler cookies = session.getCookies();
316  
  ret cookies;
317  
}
318  
319  
static void setDialogIDForWeb() {
320  
  O session = getSession();
321  
  O cookieHandler = getCookies();
322  
  S cookie = cast call(cookieHandler, "read", "cookie");
323  
  //print("Web answer cookie: " + cookie);
324  
  //print("Web answer question: " + quote(s));
325  
  S dialogID = nempty(cookie) ? "web-" + hashCookie(cookie) : anonymousDialogID;
326  
  main.dialogID.set(dialogID);
327  
}
328  
329  
static S webAnswer(S s) {
330  
  ret webAnswer(s, true);
331  
}
332  
333  
static S webAnswer(S s, boolean log) {
334  
  setDialogIDForWeb();
335  
  
336  
  originalLine.set(s);
337  
  
338  
  // drop the attn prefix ("!")
339  
  s = dropPrefix("!", s).trim();
340  
  
341  
  attn.set(true);
342  
  dedicated.set(true);
343  
  // user name is empty for now
344  
        
345  
  S a = null;
346  
  pcall {
347  
    a = answer(s, false); // generalRelease = false, all bots
348  
    print("Web answer: " + quote(a));
349  
    if (empty(a))
350  
      a = "Hello :)";
351  
  }
352  
  if (empty(a)) a = "(no response)";
353  
  
354  
  if (log) {
355  
    S user = getDialogID();
356  
    Map logEntry = litmap("question", s, "user", user, "answer", a);
357  
    historyAdd(logEntry);
358  
  }
359  
  
360  
  ret a;
361  
}
362  
363  
static S hashCookie(S cookie) {
364  
  ret md5(cookie).substring(0, 10);
365  
}
366  
367  
static void webLog(Map data) {
368  
  synchronized(webLock) {
369  
    logStructure(getProgramFile(webLogProgramID, webLog), data);
370  
    if (recentWebLogMax > 0) {
371  
      if (l(recentWebLog) >= recentWebLogMax)
372  
        recentWebLog.remove(0);
373  
      recentWebLog.add(data);
374  
    }
375  
  }
376  
}
377  
378  
static L<Map> getRecentWebLog() {
379  
  synchronized(webLock) {
380  
    ret cloneList(recentWebLog);
381  
  }
382  
}
383  
384  
static synchronized void addBlockedIP(S ip) {
385  
  blockedIPs.add(ip);
386  
  //save("blockedIPs");
387  
}
388  
389  
static synchronized void removeBlockedIP(S ip) {
390  
  blockedIPs.remove(ip);
391  
  //save("blockedIPs");
392  
}
393  
394  
static synchronized void clearBlockedIPs() {
395  
  blockedIPs.clear();
396  
}

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: 1141 / 2613
Version history: 129 change(s)
Referenced in: [show references]