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

238
LINES

< > BotCompany Repo | #1032107 // DynEleuMultiIP for multiple IPs, no https (e.g. in LAN)

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

Libraryless. Click here for Pure Java version (29713L/194K).

!7

// for sub-bots
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.
please include function serveInputStream.

sclass WebSocketWithWebRequest extends WebSocket {
  WebRequest request;
  
  *(NanoHTTPD.IHTTPSession session, WebRequest *request) {
    super(session);
  }
  
  IWebRequest webRequest() { ret request; }
}

sclass WebRequest implements IWebRequest {
  NanoHTTPD.IHTTPSession httpSession;
  S uri, subURI;
  SS params, files;
  S cookie, clientIP;
  Session session;
  bool isHttps, noSpam;
  
  *(NanoHTTPD.IHTTPSession *httpSession, S *uri, SS *params) {
    files = httpSession.getFiles();
    clientIP =  getClientIPFromHeaders(httpSession.getHeaders());
  }
  
  public S uri() { ret uri; }
  public SS params() { ret params; }
  public SS files() { ret files; }
  
  public SS headers() { ret httpSession.getHeaders(); }
  
  public S cookie() { ret cookie; }
  
  public bool isHttps() { ret isHttps; }
  
  User loggedInUser() {
    ret session == null ? null : session.user();
  }
  
  S googleClientID() {
    S domain = lower(domain());
    File jsonFile = googleClientSecretFileForDomain(domain);
    if (!fileExists(jsonFile)) null;
    
    Map map = decodeJSONMap(loadTextFile(jsonFile));
    map = (Map) map.get("web");
    ret (S) map.get("client_id");
  }
  
  public void noSpam {
    if (noSpam) ret;
    set noSpam;
    //print("noSpam count: " + simpleSpamClientDetect2_markNoSpam(clientIP, uri) + " (" + clientIP + "/" + uri + ")");
  }
}

concept User > ConceptWithGlobalID {
  long lastSeen;
}

concept CookieUser > User {
  S cookie;

  toString { ret "[cookieUser " + md5(cookie) + "]"; }
}

concept GoogleUser > User {
  long googleLogInDate;
  S googleEmail, googleFirstName, googleLastName;
  bool googleEmailVerified;

  toString { ret googleFirstName + " " + googleLastName; }
}

concept Session {
  S cookie;
  User user;
  long accesses;
  
  User user() {
    if (user == null)
      cset(this, user := uniq CookieUser(+cookie));
    ret user;
  }
}

abstract sclass DynEleuMultiIP extends DynPrintLogAndEnabled {
  transient autoDispose Set<WebSocketHTTPD> httpServers = syncLinkedHashSet();
  switchable int httpPort = 8000;
    
  double clearSessionsInterval = 60.0;
  double clearSessionsTimeout = 300.0;
  switchable double maxModuleReloadWait = 20.0;
  switchable double strayWebSocketDelay = 5.0;
  
  // Eleu-wide sessions... I don't think anyone uses these anymore
  switchable bool useSessions;
  
  void start {
    super.start();
    dbIndexing(Session, 'cookie, CookieUser, 'cookie, GoogleUser, 'googleEmail);
    dm_restartOnFieldChange enabled();
    if (!enabled) ret;
    dm_startThread("Start Web Servers", r {
      start_webServers(ipsToBindTo());
    });
    dm_doEvery(clearSessionsInterval, r clearSessions);
    
    //ownResource(serverSocketFactory_autoUpdate());
  }
  
  void clearSessions {
    Cl<Session> l = filter(list(Session),
      s -> s.accesses <= 1 && elapsedSeconds_timestamp(s.created) >= clearSessionsTimeout);
    if (nempty(l)) {
      print("Dropping " + nSessions(l));
      deleteConcepts(l);
    }
  }
  
  WebSocket newWebSocket(NanoHTTPD.IHTTPSession handshake) enter {
    vmBus_send haveNewWebSocket(module(), handshake);
    S domain = mapGet(handshake.getHeaders(), "host");
    S uri = handshake.getUri();
    SS params = handshake.getParms();
    print("Making WebSocket. domain: " + domain + ", uri: " + uri);
    WebRequest req = new(handshake, uri, params);
    WebSocket ws = new WebSocketWithWebRequest(handshake, req);
    
    S module = moduleForDomainAndURI(domain, uri);
    if (nempty(module) && !dm_moduleIsStartingOrReloading(module)) pcall {
      dm_callOpt(module, 'handleWebSocket, ws);
    } else {
      sleepSeconds(strayWebSocketDelay);
      ws.close(); // Hopefully just a reload
    }
      
    ret ws;
  }
  
  O webServe(S uri, SS params) {
    WebRequest req = new(NanoHTTPD.currentSession!, uri, params);
    print("IP: " + req.clientIP);
    
    /*Pair<Int, Bool> spamCheck = spamBlocker.checkRequest(req.uri, req.clientIP);
    if (spamCheck.b) {
      print("Request from blocked IP: " + req.clientIP);
      sleepSeconds(60.0);
      ret print("go away");
    }*/

    // fill more fields of WebRequest
    
    fillWebRequest(req);
    
    // log some
    
    printVars("webServe", +uri, userAgent := req.userAgent());
    
    // Serve Let's encrypt challenges
    
    if (startsWith(uri, "/.well-known/")) {
      S fileName = "validation.txt";
      /*if (swic(req.domain(), "www."))
        fileName = "validation-www.txt";*/
      ret loadTextFile(userDir(fileName));
    }
      
    S module = moduleForDomainAndURI(req.domain(), uri);
    if (nempty(module)) {
      if (sleepWhile(() -> dm_moduleIsStartingOrReloading(module), max := maxModuleReloadWait))
        ret subBot_serve500("Reload taking too long");
      ret eleu_callModuleHTMLMethod(module, req);
    }
    
    ret subBot_serve404();
  }

  // override me
  S moduleForDomain(S domain) { null; }
  
  // or me
  S moduleForDomainAndURI(S domain, S uri) { ret moduleForDomain(domain); }

  ServeHttp_CookieHandler makeCookieHandler() {
    ret nu ServeHttp_CookieHandler(verbose := true);
  }

  void fillWebRequest(WebRequest req) {  
    req.cookie = makeCookieHandler().handle();
    req.isHttps = false;
    
    // Get session
    req.session = nempty(req.cookie) && useSessions ? uniq Session(cookie := req.cookie) : null;
    if (req.session != null)
      cset(req.session, accesses := req.session.accesses+1);
    print(session := req.session);
  }
  
  /*void enhanceFrame(Container c) {
    super.enhanceFrame(c);
    internalFrameMenuItem(c, "Show blocked IPs", rEnterThread {
      print("Blocked IPs: " + spamBlocker.blockedIPs);
    });
  }*/

  // bind to all non-public IPs by default
  LS ipsToBindTo() {
    ret myLocalIPs();
  }
  
  void start_webServers(LS ips) {
    if (empty(ips))
      ret with print("No IPs to bind to");
      
    forEach_pcall(ips, ip -> {
      var httpd = new WebSocketHTTPD(ip, httpPort, lambda1 newWebSocket);
      httpd.enter = lambda0 enter;
      httpd.serveFunction = func(S uri, SS parms) { webServe(uri, parms) };
    
      ctex { httpd.start(); }
      httpServers.add(httpd);
      print("Web server started at http://" + ip + ":" + httpd.getPort());
    });
  }
} // end of module

Author comment

Began life as a copy of #1029543

download  show line numbers  debug dex  old transpilations   

Travelled to 3 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx

No comments. add comment

Snippet ID: #1032107
Snippet name: DynEleuMultiIP for multiple IPs, no https (e.g. in LAN)
Eternal ID of this version: #1032107/20
Text MD5: b5392dbc99baed6abefedbdd044868c6
Transpilation MD5: cf89701f52b57d0cc3ea07932c1ae1b4
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-08-14 01:33:12
Source code size: 6899 bytes / 238 lines
Pitched / IR pitched: No / No
Views / Downloads: 182 / 343
Version history: 19 change(s)
Referenced in: #1034167 - Standard Classes + Interfaces (LIVE, continuation of #1003674)