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

209
LINES

< > BotCompany Repo | #1029543 - DynEleu [web server as module]

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

Uses 1658K of libraries. Click here for Pure Java version (26591L/185K).

!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 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) {}
  
  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 DynEleu extends DynPrintLogAndEnabled {
  !include #1029492 // HTTP+HTTPS servers with WebSockets
  
  double clearSessionsInterval = 60.0;
  double clearSessionsTimeout = 300.0;
  switchable double maxModuleReloadWait = 20.0;
  
  // for noticing certificate changes in JavaX-Secret
  transient FileWatchService watchService;
  
  void start {
    super.start();
    dbIndexing(Session, 'cookie, CookieUser, 'cookie, GoogleUser, 'googleEmail);
    dm_restartOnFieldChange enabled();
    if (!enabled) ret;
    thread "Start Web Servers" {
      start_webServers(serverSocketFactory_botCompanyEtc());
    }
    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 {
    S domain = mapGet(handshake.getHeaders(), "host");
    S uri = getString uri(handshake);
    print("Making WebSocket. domain: " + domain + ", uri: " + uri);
    WebSocket ws = new(handshake);
    
    S module = moduleForDomainAndURI(domain, uri);
    if (nempty(module) && !dm_moduleIsStartingOrReloading(module)) pcall {
      dm_callOpt(module, 'handleWebSocket, ws);
    } else
      ws.close(); // Hopefully just a reload
      
    ret ws;
  }
  
  O webServe(S uri, SS params) {
    WebRequest req = new(NanoHTTPD.currentSession!, uri, params);
    req.files = serveHttp_files();
    req.clientIP = serveHttp_clientIP();
    
    Pair<Int, Bool> spamCheck = spamBlocker.checkRequest(req.uri, req.clientIP);
    if (spamCheck.b) {
      sleepSeconds(60.0);
      ret print("go away");
    }
    printVars("webServe", +uri);

    // 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));
    }
      
    // fill more fields of WebRequest
    
    req.cookie = makeCookieHandler().handle();
    
    req.isHttps = WebSocketHTTPD_current! == httpsServer;
    
    // Get session
    req.session = nempty(req.cookie) ? uniq Session(cookie := req.cookie) : null;
    if (req.session != null)
      cset(req.session, accesses := req.session.accesses+1);
    print(session := req.session);

    // Serve Google verification
    
    if (eqic(uri, "/google-verify"))
      ret serveGoogleVerify(req);
      
    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); }

  O serveGoogleVerify(WebRequest req) {
    Payload payload = googleVerifyUserToken2(req.googleClientID(), req.params.get("token"));
    S email = payload == null ? null : payload.getEmail();
    if (empty(email)) ret print("google-verify", "No");
    
    // create/update an object for the user
    GoogleUser user = uniqCI_sync GoogleUser(googleEmail := email);
    cset(user,
      googleEmailVerified := payload.getEmailVerified(),
      googleFirstName := strOrNull(payload.get("given_name")),
      googleLastName := strOrNull(payload.get("family_name")),
      googleLogInDate := now());
    
    // link user to session
    cset(req.session, +user);
      
    ret print("google-verify", payload.getEmail() + " " + (payload.getEmailVerified() ? "(verified)" : "(not verified)"));
  }

  ServeHttp_CookieHandler makeCookieHandler() {
    ret nu ServeHttp_CookieHandler(verbose := true);
  }
} // end of module

Author comment

Began life as a copy of #1028671

download  show line numbers  debug dex   

Travelled to 6 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, xrpafgyirdlv

No comments. add comment

Snippet ID: #1029543
Snippet name: DynEleu [web server as module]
Eternal ID of this version: #1029543/35
Text MD5: eef3def9c06b1c7588c54305d9351afb
Transpilation MD5: ddcf65a0ebac3e886088f530af00bd73
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-02-22 19:59:00
Source code size: 6247 bytes / 209 lines
Pitched / IR pitched: No / No
Views / Downloads: 161 / 373
Version history: 34 change(s)
Referenced in: [show references]

Formerly at http://tinybrain.de/1029543 & http://1029543.tinybrain.de