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).

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  
15  
sclass WebSocketWithWebRequest extends WebSocket {
16  
  WebRequest request;
17  
  
18  
  *(NanoHTTPD.IHTTPSession session, WebRequest *request) {
19  
    super(session);
20  
  }
21  
  
22  
  IWebRequest webRequest() { ret request; }
23  
}
24  
25  
sclass WebRequest implements IWebRequest {
26  
  NanoHTTPD.IHTTPSession httpSession;
27  
  S uri, subURI;
28  
  SS params, files;
29  
  S cookie, clientIP;
30  
  Session session;
31  
  bool isHttps, noSpam;
32  
  
33  
  *(NanoHTTPD.IHTTPSession *httpSession, S *uri, SS *params) {
34  
    files = httpSession.getFiles();
35  
    clientIP =  getClientIPFromHeaders(httpSession.getHeaders());
36  
  }
37  
  
38  
  public S uri() { ret uri; }
39  
  public SS params() { ret params; }
40  
  public SS files() { ret files; }
41  
  
42  
  public SS headers() { ret httpSession.getHeaders(); }
43  
  
44  
  public S cookie() { ret cookie; }
45  
  
46  
  public bool isHttps() { ret isHttps; }
47  
  
48  
  User loggedInUser() {
49  
    ret session == null ? null : session.user();
50  
  }
51  
  
52  
  S googleClientID() {
53  
    S domain = lower(domain());
54  
    File jsonFile = googleClientSecretFileForDomain(domain);
55  
    if (!fileExists(jsonFile)) null;
56  
    
57  
    Map map = decodeJSONMap(loadTextFile(jsonFile));
58  
    map = (Map) map.get("web");
59  
    ret (S) map.get("client_id");
60  
  }
61  
  
62  
  public void noSpam {
63  
    if (noSpam) ret;
64  
    set noSpam;
65  
    //print("noSpam count: " + simpleSpamClientDetect2_markNoSpam(clientIP, uri) + " (" + clientIP + "/" + uri + ")");
66  
  }
67  
}
68  
69  
concept User > ConceptWithGlobalID {
70  
  long lastSeen;
71  
}
72  
73  
concept CookieUser > User {
74  
  S cookie;
75  
76  
  toString { ret "[cookieUser " + md5(cookie) + "]"; }
77  
}
78  
79  
concept GoogleUser > User {
80  
  long googleLogInDate;
81  
  S googleEmail, googleFirstName, googleLastName;
82  
  bool googleEmailVerified;
83  
84  
  toString { ret googleFirstName + " " + googleLastName; }
85  
}
86  
87  
concept Session {
88  
  S cookie;
89  
  User user;
90  
  long accesses;
91  
  
92  
  User user() {
93  
    if (user == null)
94  
      cset(this, user := uniq CookieUser(+cookie));
95  
    ret user;
96  
  }
97  
}
98  
99  
abstract sclass DynEleuMultiIP extends DynPrintLogAndEnabled {
100  
  transient autoDispose Set<WebSocketHTTPD> httpServers = syncLinkedHashSet();
101  
  switchable int httpPort = 8000;
102  
    
103  
  double clearSessionsInterval = 60.0;
104  
  double clearSessionsTimeout = 300.0;
105  
  switchable double maxModuleReloadWait = 20.0;
106  
  switchable double strayWebSocketDelay = 5.0;
107  
  
108  
  // Eleu-wide sessions... I don't think anyone uses these anymore
109  
  switchable bool useSessions;
110  
  
111  
  void start {
112  
    super.start();
113  
    dbIndexing(Session, 'cookie, CookieUser, 'cookie, GoogleUser, 'googleEmail);
114  
    dm_restartOnFieldChange enabled();
115  
    if (!enabled) ret;
116  
    dm_startThread("Start Web Servers", r {
117  
      start_webServers(ipsToBindTo());
118  
    });
119  
    dm_doEvery(clearSessionsInterval, r clearSessions);
120  
    
121  
    //ownResource(serverSocketFactory_autoUpdate());
122  
  }
123  
  
124  
  void clearSessions {
125  
    Cl<Session> l = filter(list(Session),
126  
      s -> s.accesses <= 1 && elapsedSeconds_timestamp(s.created) >= clearSessionsTimeout);
127  
    if (nempty(l)) {
128  
      print("Dropping " + nSessions(l));
129  
      deleteConcepts(l);
130  
    }
131  
  }
132  
  
133  
  WebSocket newWebSocket(NanoHTTPD.IHTTPSession handshake) enter {
134  
    vmBus_send haveNewWebSocket(module(), handshake);
135  
    S domain = mapGet(handshake.getHeaders(), "host");
136  
    S uri = handshake.getUri();
137  
    SS params = handshake.getParms();
138  
    print("Making WebSocket. domain: " + domain + ", uri: " + uri);
139  
    WebRequest req = new(handshake, uri, params);
140  
    WebSocket ws = new WebSocketWithWebRequest(handshake, req);
141  
    
142  
    S module = moduleForDomainAndURI(domain, uri);
143  
    if (nempty(module) && !dm_moduleIsStartingOrReloading(module)) pcall {
144  
      dm_callOpt(module, 'handleWebSocket, ws);
145  
    } else {
146  
      sleepSeconds(strayWebSocketDelay);
147  
      ws.close(); // Hopefully just a reload
148  
    }
149  
      
150  
    ret ws;
151  
  }
152  
  
153  
  O webServe(S uri, SS params) {
154  
    WebRequest req = new(NanoHTTPD.currentSession!, uri, params);
155  
    print("IP: " + req.clientIP);
156  
    
157  
    /*Pair<Int, Bool> spamCheck = spamBlocker.checkRequest(req.uri, req.clientIP);
158  
    if (spamCheck.b) {
159  
      print("Request from blocked IP: " + req.clientIP);
160  
      sleepSeconds(60.0);
161  
      ret print("go away");
162  
    }*/
163  
164  
    // fill more fields of WebRequest
165  
    
166  
    fillWebRequest(req);
167  
    
168  
    // log some
169  
    
170  
    printVars("webServe", +uri, userAgent := req.userAgent());
171  
    
172  
    // Serve Let's encrypt challenges
173  
    
174  
    if (startsWith(uri, "/.well-known/")) {
175  
      S fileName = "validation.txt";
176  
      /*if (swic(req.domain(), "www."))
177  
        fileName = "validation-www.txt";*/
178  
      ret loadTextFile(userDir(fileName));
179  
    }
180  
      
181  
    S module = moduleForDomainAndURI(req.domain(), uri);
182  
    if (nempty(module)) {
183  
      if (sleepWhile(() -> dm_moduleIsStartingOrReloading(module), max := maxModuleReloadWait))
184  
        ret subBot_serve500("Reload taking too long");
185  
      ret eleu_callModuleHTMLMethod(module, req);
186  
    }
187  
    
188  
    ret subBot_serve404();
189  
  }
190  
191  
  // override me
192  
  S moduleForDomain(S domain) { null; }
193  
  
194  
  // or me
195  
  S moduleForDomainAndURI(S domain, S uri) { ret moduleForDomain(domain); }
196  
197  
  ServeHttp_CookieHandler makeCookieHandler() {
198  
    ret nu ServeHttp_CookieHandler(verbose := true);
199  
  }
200  
201  
  void fillWebRequest(WebRequest req) {  
202  
    req.cookie = makeCookieHandler().handle();
203  
    req.isHttps = false;
204  
    
205  
    // Get session
206  
    req.session = nempty(req.cookie) && useSessions ? uniq Session(cookie := req.cookie) : null;
207  
    if (req.session != null)
208  
      cset(req.session, accesses := req.session.accesses+1);
209  
    print(session := req.session);
210  
  }
211  
  
212  
  /*void enhanceFrame(Container c) {
213  
    super.enhanceFrame(c);
214  
    internalFrameMenuItem(c, "Show blocked IPs", rEnterThread {
215  
      print("Blocked IPs: " + spamBlocker.blockedIPs);
216  
    });
217  
  }*/
218  
219  
  // bind to all non-public IPs by default
220  
  LS ipsToBindTo() {
221  
    ret myLocalIPs();
222  
  }
223  
  
224  
  void start_webServers(LS ips) {
225  
    if (empty(ips))
226  
      ret with print("No IPs to bind to");
227  
      
228  
    forEach_pcall(ips, ip -> {
229  
      var httpd = new WebSocketHTTPD(ip, httpPort, lambda1 newWebSocket);
230  
      httpd.enter = lambda0 enter;
231  
      httpd.serveFunction = func(S uri, SS parms) { webServe(uri, parms) };
232  
    
233  
      ctex { httpd.start(); }
234  
      httpServers.add(httpd);
235  
      print("Web server started at http://" + ip + ":" + httpd.getPort());
236  
    });
237  
  }
238  
} // 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: 183 / 345
Version history: 19 change(s)
Referenced in: [show references]