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

266
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 (31666L/210K).

1  
!7
2  
3  
// for sub-bots
4  
please include function serveRedirect.
5  
please include function serve403.
6  
please include function serve404.
7  
please include function serve500.
8  
please include function serveFile.
9  
please include function serveFileWithName.
10  
please include function serveFile_maxCache.
11  
please include function serveByteArray.
12  
please include function serveByteArray_maxCache.
13  
please include function serveInputStream.
14  
please include function servePartialContent.
15  
please include function serveRangeNotSatisfiable.
16  
17  
sclass WebSocketWithWebRequest extends WebSocket {
18  
  WebRequest request;
19  
  
20  
  *(NanoHTTPD.IHTTPSession session, WebRequest *request) {
21  
    super(session);
22  
  }
23  
  
24  
  IWebRequest webRequest() { ret request; }
25  
}
26  
27  
sclass WebRequest implements IWebRequest {
28  
  NanoHTTPD.IHTTPSession httpSession;
29  
  S uri, subURI;
30  
  SS params, files;
31  
  S cookie, clientIP;
32  
  Session session;
33  
  bool isHttps, noSpam;
34  
  
35  
  *(NanoHTTPD.IHTTPSession *httpSession, S *uri, SS *params) {
36  
    files = httpSession.getFiles();
37  
    clientIP =  getClientIPFromHeaders(httpSession.getHeaders());
38  
  }
39  
  
40  
  public S uri() { ret uri; }
41  
  public SS params() { ret params; }
42  
  public SS files() { ret files; }
43  
  
44  
  public SS headers() { ret httpSession.getHeaders(); }
45  
  
46  
  public S cookie() { ret cookie; }
47  
  
48  
  public bool isHttps() { ret isHttps; }
49  
  
50  
  public bool isPost() { ret httpSession.getMethod() == NanoHTTPD.Method.POST; }
51  
  
52  
  User loggedInUser() {
53  
    ret session == null ? null : session.user();
54  
  }
55  
  
56  
  S googleClientID() {
57  
    S domain = lower(domain());
58  
    File jsonFile = googleClientSecretFileForDomain(domain);
59  
    if (!fileExists(jsonFile)) null;
60  
    
61  
    Map map = decodeJSONMap(loadTextFile(jsonFile));
62  
    map = (Map) map.get("web");
63  
    ret (S) map.get("client_id");
64  
  }
65  
  
66  
  public void noSpam {
67  
    if (noSpam) ret;
68  
    set noSpam;
69  
    print("noSpam count: " + simpleSpamClientDetect2_markNoSpam(clientIP, uri) + " (" + clientIP + "/" + uri + ")");
70  
  }
71  
}
72  
73  
concept User > ConceptWithGlobalID {
74  
  long lastSeen;
75  
}
76  
77  
concept CookieUser > User {
78  
  S cookie;
79  
80  
  toString { ret "[cookieUser " + md5(cookie) + "]"; }
81  
}
82  
83  
concept GoogleUser > User {
84  
  long googleLogInDate;
85  
  S googleEmail, googleFirstName, googleLastName;
86  
  bool googleEmailVerified;
87  
88  
  toString { ret googleFirstName + " " + googleLastName; }
89  
}
90  
91  
concept Session {
92  
  S cookie;
93  
  User user;
94  
  long accesses;
95  
  
96  
  User user() {
97  
    if (user == null)
98  
      cset(this, user := uniq CookieUser(+cookie));
99  
    ret user;
100  
  }
101  
}
102  
103  
abstract sclass DynEleu extends DynPrintLogAndEnabled {
104  
  !include #1029492 // HTTP+HTTPS servers with WebSockets
105  
  
106  
  double clearSessionsInterval = 60.0;
107  
  double clearSessionsTimeout = 300.0;
108  
  switchable double maxModuleReloadWait = 20.0;
109  
  switchable double strayWebSocketDelay = 5.0;
110  
  
111  
  // Eleu-wide sessions... I don't think anyone uses these anymore
112  
  switchable bool useSessions;
113  
  
114  
  switchable bool shareCookiesBetweenApexDomains = true;
115  
  
116  
  // for noticing certificate changes in JavaX-Secret
117  
  transient FileWatchService watchService;
118  
  
119  
  void start {
120  
    super.start();
121  
    dbIndexing(Session, 'cookie, CookieUser, 'cookie, GoogleUser, 'googleEmail);
122  
    dm_restartOnFieldChange enabled();
123  
    if (!enabled) ret;
124  
    dm_startThread("Start Web Servers", r {
125  
      start_webServers(serverSocketFactory_botCompanyEtc());
126  
      onWebServersStarted();
127  
    });
128  
    dm_doEvery(clearSessionsInterval, r clearSessions);
129  
    
130  
    ownResource(serverSocketFactory_autoUpdate());
131  
  }
132  
  
133  
  void clearSessions {
134  
    Cl<Session> l = filter(list(Session),
135  
      s -> s.accesses <= 1 && elapsedSeconds_timestamp(s.created) >= clearSessionsTimeout);
136  
    if (nempty(l)) {
137  
      print("Dropping " + nSessions(l));
138  
      deleteConcepts(l);
139  
    }
140  
  }
141  
  
142  
  WebSocket newWebSocket(NanoHTTPD.IHTTPSession handshake) enter {
143  
    vmBus_send haveNewWebSocket(module(), handshake);
144  
    S domain = mapGet(handshake.getHeaders(), "host");
145  
    S uri = handshake.getUri();
146  
    SS params = handshake.getParms();
147  
    print("Making WebSocket. domain: " + domain + ", uri: " + uri);
148  
    WebRequest req = createWebRequest(handshake, uri, params);
149  
    WebSocket ws = new WebSocketWithWebRequest(handshake, req);
150  
    
151  
    S module = moduleForDomainAndURI(domain, uri);
152  
    if (nempty(module) && !dm_moduleIsStartingOrReloading(module)) pcall {
153  
      dm_callOpt(module, 'handleWebSocket, ws);
154  
    } else {
155  
      sleepSeconds(strayWebSocketDelay);
156  
      ws.close(); // Hopefully just a reload
157  
    }
158  
      
159  
    ret ws;
160  
  }
161  
  
162  
  O webServe(S uri, SS params) {
163  
    WebRequest req = new(NanoHTTPD.currentSession!, uri, params);
164  
    print("IP: " + req.clientIP);
165  
    
166  
    Pair<Int, Bool> spamCheck = spamBlocker.checkRequest(req.uri, req.clientIP);
167  
    if (spamCheck.b) {
168  
      print("Request from blocked IP: " + req.clientIP);
169  
      sleepSeconds(60.0);
170  
      ret print("go away");
171  
    }
172  
173  
    // fill more fields of WebRequest
174  
    
175  
    fillWebRequest(req);
176  
    
177  
    // log some
178  
    
179  
    printVars("webServe", +uri, userAgent := req.userAgent());
180  
    
181  
    // Serve Let's encrypt challenges
182  
    
183  
    if (startsWith(uri, "/.well-known/")) {
184  
      S fileName = "validation.txt";
185  
      /*if (swic(req.domain(), "www."))
186  
        fileName = "validation-www.txt";*/
187  
      ret loadTextFile(userDir(fileName));
188  
    }
189  
      
190  
    // Serve Google verification
191  
    
192  
    ifndef NoGoogleVerification
193  
    if (eqic(uri, "/google-verify"))
194  
      ret serveGoogleVerify(req);
195  
    endifndef
196  
      
197  
    S module = moduleForDomainAndURI(req.domain(), uri);
198  
    if (nempty(module)) {
199  
      if (sleepWhile(() -> dm_moduleIsStartingOrReloading(module), max := maxModuleReloadWait))
200  
        ret subBot_serve500("Reload taking too long");
201  
      ret eleu_callModuleHTMLMethod(module, req);
202  
    }
203  
    
204  
    ret subBot_serve404();
205  
  }
206  
207  
  // override me
208  
  S moduleForDomain(S domain) { null; }
209  
  
210  
  // or me
211  
  S moduleForDomainAndURI(S domain, S uri) { ret moduleForDomain(domain); }
212  
213  
  ifndef NoGoogleVerification
214  
  O serveGoogleVerify(WebRequest req) {
215  
    Payload payload = googleVerifyUserToken2(req.googleClientID(), req.params.get("token"));
216  
    S email = payload == null ? null : payload.getEmail();
217  
    if (empty(email)) ret print("google-verify", "No");
218  
    
219  
    // create/update an object for the user
220  
    GoogleUser user = uniqCI_sync GoogleUser(googleEmail := email);
221  
    cset(user,
222  
      googleEmailVerified := payload.getEmailVerified(),
223  
      googleFirstName := strOrNull(payload.get("given_name")),
224  
      googleLastName := strOrNull(payload.get("family_name")),
225  
      googleLogInDate := now());
226  
    
227  
    // link user to session
228  
    cset(req.session, +user);
229  
      
230  
    ret print("google-verify", payload.getEmail() + " " + (payload.getEmailVerified() ? "(verified)" : "(not verified)"));
231  
  }
232  
  endifndef
233  
234  
  ServeHttp_CookieHandler makeCookieHandler() {
235  
    new ServeHttp_CookieHandler h;
236  
    h.verbose = true;
237  
    h.shareCookiesBetweenApexDomains = shareCookiesBetweenApexDomains;
238  
    ret h;
239  
  }
240  
241  
  void fillWebRequest(WebRequest req) {  
242  
    req.cookie = makeCookieHandler().handle();
243  
    
244  
    req.isHttps = WebSocketHTTPD_current! == httpsServer;
245  
    
246  
    // Get session
247  
    req.session = nempty(req.cookie) && useSessions ? uniq Session(cookie := req.cookie) : null;
248  
    if (req.session != null)
249  
      cset(req.session, accesses := req.session.accesses+1);
250  
    print(session := req.session);
251  
  }
252  
  
253  
  void enhanceFrame(Container c) {
254  
    super.enhanceFrame(c);
255  
    internalFrameMenuItem(c, "Show blocked IPs", rEnterThread {
256  
      print("Blocked IPs: " + spamBlocker.blockedIPs);
257  
    });
258  
  }
259  
260  
  void onWebServersStarted() {}
261  
  
262  
  WebRequest createWebRequest(NanoHTTPD.IHTTPSession httpSession,
263  
    S uri, SS params) {
264  
    ret new WebRequest(httpSession, uri, params);
265  
  }
266  
} // end of module

Author comment

Began life as a copy of #1028671

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1029543
Snippet name: DynEleu [web server as module]
Eternal ID of this version: #1029543/63
Text MD5: 425b62a37a1b22e72b45bd79778f1cb6
Transpilation MD5: cfff9e7f6b6deee143f0ae79a9755588
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-10-04 01:29:35
Source code size: 7990 bytes / 266 lines
Pitched / IR pitched: No / No
Views / Downloads: 466 / 995
Version history: 62 change(s)
Referenced in: [show references]