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 |
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: | 389 / 605 |
| Version history: | 19 change(s) |
| Referenced in: | [show references] |