tlsd 2.11.1 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/tlsd.js +134 -201
package/package.json
CHANGED
package/tlsd.js
CHANGED
|
@@ -8,6 +8,11 @@ const connect = require( "connect" );
|
|
|
8
8
|
const websocket = require( "websocket" );
|
|
9
9
|
const serveStatic = require( "serve-static" )
|
|
10
10
|
|
|
11
|
+
const body_parser = require( "body-parser" ).json( { limit: "10mb" } );
|
|
12
|
+
const compression = require( "compression" )();
|
|
13
|
+
const cors = require( "cors" )();
|
|
14
|
+
const queryString = require( "querystring" );
|
|
15
|
+
|
|
11
16
|
require( "sleepless" ).globalize();
|
|
12
17
|
|
|
13
18
|
const L = log5.mkLog( "TLSD: " );
|
|
@@ -31,7 +36,7 @@ function usage() {
|
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
let seq = 0;
|
|
34
|
-
|
|
39
|
+
function next_seq() {
|
|
35
40
|
seq += 1;
|
|
36
41
|
return seq;
|
|
37
42
|
}
|
|
@@ -39,7 +44,11 @@ const next_seq = function() {
|
|
|
39
44
|
|
|
40
45
|
// Handles incoming RPC msgs.
|
|
41
46
|
// Tries to load the rpc handler module from root and if successful, passes the msg to it
|
|
42
|
-
|
|
47
|
+
// xport:
|
|
48
|
+
// { type: "WS", transport: { ... } }
|
|
49
|
+
// { type: "GET", transport: { req, res } }
|
|
50
|
+
// { type: "POST", transport: { req, res } }
|
|
51
|
+
function rpc_handler( root, msg, transport, _okay, _fail ) {
|
|
43
52
|
|
|
44
53
|
let ll = toInt( msg.log_level );
|
|
45
54
|
if( ll > 0 ) {
|
|
@@ -55,15 +64,15 @@ const rpc_handler = function( root, msg, xport, _okay, _fail ) {
|
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
V( "RPC " + o2j( msg ).abbr( 70 ) );
|
|
58
|
-
D( "----------->>> "+
|
|
67
|
+
D( "----------->>> "+transport.type+" "+o2j( msg, null, 2 ) );
|
|
59
68
|
|
|
60
69
|
const okay = data => {
|
|
61
|
-
D( "<<<=========== "+
|
|
70
|
+
D( "<<<=========== "+transport.type+" OKAY "+o2j( data, null, 2 ) );
|
|
62
71
|
_okay( data );
|
|
63
72
|
};
|
|
64
73
|
|
|
65
74
|
const fail = error => {
|
|
66
|
-
D( "<<<*********** "+
|
|
75
|
+
D( "<<<*********** "+transport.type+" ERROR "+o2j( error, null, 2 ) );
|
|
67
76
|
_fail( error );
|
|
68
77
|
};
|
|
69
78
|
|
|
@@ -75,7 +84,7 @@ const rpc_handler = function( root, msg, xport, _okay, _fail ) {
|
|
|
75
84
|
try {
|
|
76
85
|
const mod = require( root + "/rpc" );
|
|
77
86
|
try {
|
|
78
|
-
mod( msg, okay, ouch );
|
|
87
|
+
mod( msg, okay, ouch, transport );
|
|
79
88
|
} catch( err ) {
|
|
80
89
|
E( (err instanceof Error ) ? err.stack : err );
|
|
81
90
|
ouch( "RPC handler exception" );
|
|
@@ -89,8 +98,8 @@ const rpc_handler = function( root, msg, xport, _okay, _fail ) {
|
|
|
89
98
|
|
|
90
99
|
// Glue function to call rpc handler module and then return response
|
|
91
100
|
// via the websockets msg object
|
|
92
|
-
|
|
93
|
-
rpc_handler( root, msg.msg, "WS", data => {
|
|
101
|
+
function ws_msg_handler( root, msg, connection ) {
|
|
102
|
+
rpc_handler( root, msg.msg, { type: "WS", transport: connection }, data => {
|
|
94
103
|
msg.reply( data );
|
|
95
104
|
}, error => {
|
|
96
105
|
msg.error( { error } );
|
|
@@ -99,115 +108,113 @@ const ws_msg_handler = function( root, msg ) {
|
|
|
99
108
|
};
|
|
100
109
|
|
|
101
110
|
|
|
102
|
-
//
|
|
103
|
-
// XXX This has got to be inefficient at best, as it's creating a
|
|
104
|
-
// connect app on every request. No idea how much of this gets optimized
|
|
105
|
-
// out by V8.
|
|
106
|
-
const basic_handler = function( root ) {
|
|
107
|
-
|
|
108
|
-
const app = connect();
|
|
109
|
-
|
|
110
|
-
app.use( require( "body-parser" ).json( { limit: "10mb" } ) );
|
|
111
|
-
app.use( require( "compression" )() );
|
|
112
|
-
app.use( require( "cors" )() ); // allow requests from other domains
|
|
113
|
-
|
|
114
|
-
// populate req.query
|
|
115
|
-
app.use( function( req, res, next ) {
|
|
116
|
-
req.query = require( "querystring" ).parse( req._parsedUrl.query );
|
|
117
|
-
next();
|
|
118
|
-
} );
|
|
119
|
-
|
|
120
|
-
// logger
|
|
121
|
-
app.use( function( req, res, next ) {
|
|
122
|
-
const host = req.headers[ "host" ];
|
|
123
|
-
let { method, url, query, body } = req;
|
|
124
|
-
url = url.split( "?" ).shift();
|
|
125
|
-
I( host + ": " + method + " " + o2j(url) + " " + o2j( query, null, 2 ) + "/" + o2j( body, null, 2 ) );
|
|
126
|
-
next();
|
|
127
|
-
} );
|
|
128
|
-
|
|
129
|
-
// Intercept and service POST requests for "/rpc"
|
|
130
|
-
app.use( function( req, res, next ) {
|
|
131
|
-
|
|
132
|
-
let { method, url, query, body } = req;
|
|
133
|
-
|
|
134
|
-
url = url.split( "?" ).shift();
|
|
135
|
-
|
|
136
|
-
// if not an rpc request, pass on to static
|
|
137
|
-
if( url != "/rpc" && url != "/rpc/" ) {
|
|
138
|
-
next();
|
|
139
|
-
return
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Only allow POSTS to /rpc in tlsd mode.
|
|
143
|
-
// In dev mode, GET is allowed as well with query args.
|
|
144
|
-
let input = body;
|
|
145
|
-
if( method != "POST" ) {
|
|
146
|
-
if( ! ( method == "GET" && dev_mode ) ) {
|
|
147
|
-
W( "GET /rpc is disallowed in tlsd mode" );
|
|
148
|
-
res.writeHead( 405 ); // Method Not Allowed
|
|
149
|
-
res.end();
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
input = query;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Set up callbacks
|
|
156
|
-
const done = ( error, data ) => {
|
|
157
|
-
res.writeHead( 200, {
|
|
158
|
-
"Content-Type": "application/json",
|
|
159
|
-
"Cache-Control": "no-store",
|
|
160
|
-
});
|
|
161
|
-
res.write( o2j( data ) );
|
|
162
|
-
res.end();
|
|
163
|
-
};
|
|
164
|
-
const okay = ( data ) => { done( null, data ); };
|
|
165
|
-
// XXX wrong
|
|
166
|
-
const fail = ( error, body ) => { done( error, body ); };
|
|
111
|
+
// -----------------------
|
|
167
112
|
|
|
168
|
-
// Summon the rpc handler for the domain root.
|
|
169
|
-
rpc_handler( root, input, "REST", okay, fail );
|
|
170
113
|
|
|
171
|
-
|
|
114
|
+
// Handler that populates the req.query var with what's in query args
|
|
115
|
+
function populate_query( req, res, next ) {
|
|
116
|
+
req.query = queryString.parse( req._parsedUrl.query );
|
|
117
|
+
next();
|
|
118
|
+
}
|
|
172
119
|
|
|
120
|
+
// Simple logging handler
|
|
121
|
+
function logger( req, res, next ) {
|
|
122
|
+
const host = req.headers[ "host" ];
|
|
123
|
+
let { method, url, query, body } = req;
|
|
124
|
+
url = url.split( "?" ).shift();
|
|
125
|
+
I( host + ": " + method + " " + o2j( url ) + " " + o2j( query, null, 2 ) + "/" + o2j( body, null, 2 ) );
|
|
126
|
+
next();
|
|
127
|
+
}
|
|
173
128
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
129
|
+
// Creates and returns a handler function that intercepts and services
|
|
130
|
+
// POST requests to "/rpc" endpoint.
|
|
131
|
+
function rpc_post( root ) {
|
|
132
|
+
return function( req, res, next ) {
|
|
133
|
+
let { method, url, query, body } = req;
|
|
179
134
|
|
|
135
|
+
url = url.split( "?" ).shift();
|
|
136
|
+
if( url != "/rpc" && url != "/rpc/" ) {
|
|
137
|
+
next();
|
|
138
|
+
return
|
|
139
|
+
}
|
|
180
140
|
|
|
181
|
-
|
|
182
|
-
|
|
141
|
+
// Only allow POSTS in TLS mode.
|
|
142
|
+
// In dev mode, GET is allowed as well with query args.
|
|
143
|
+
let input = body;
|
|
144
|
+
if( method != "POST" ) {
|
|
145
|
+
if( ! ( method == "GET" && dev_mode ) ) {
|
|
146
|
+
W( "GET /rpc is disallowed in TLS mode" );
|
|
147
|
+
res.writeHead( 405 ); // Method Not Allowed
|
|
148
|
+
res.end();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
input = query;
|
|
152
|
+
}
|
|
183
153
|
|
|
154
|
+
// Set up callbacks
|
|
155
|
+
const done = ( error, data ) => {
|
|
156
|
+
res.writeHead( 200, {
|
|
157
|
+
"Content-Type": "application/json",
|
|
158
|
+
"Cache-Control": "no-store",
|
|
159
|
+
});
|
|
160
|
+
res.write( o2j( data ) );
|
|
161
|
+
res.end();
|
|
162
|
+
};
|
|
163
|
+
const okay = ( data ) => { done( null, data ); };
|
|
164
|
+
// XXX wrong
|
|
165
|
+
const fail = ( error, body ) => { done( error, body ); };
|
|
166
|
+
|
|
167
|
+
// Summon the rpc handler for the domain root.
|
|
168
|
+
rpc_handler( root, input, { type, method, transport: { req, res, } } , okay, fail );
|
|
169
|
+
};
|
|
170
|
+
}
|
|
184
171
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
172
|
+
// Handler for tlsd's own static files.
|
|
173
|
+
// This is so that "GET /rpc/rpc.js" can return the client-side code for
|
|
174
|
+
// sending WS RPC messages (similar to how socket.io works).
|
|
175
|
+
const rpc_static = serveStatic( __dirname + "/rpc_static" );
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
// Creates and returns a handler that checks for "/404" at root.
|
|
179
|
+
// If found, it redirects to it, otherwise it responds with 404.
|
|
180
|
+
function not_found( root ) {
|
|
181
|
+
return function( req, res, next ) {
|
|
182
|
+
// Can't just do a redirect here because if /404 doesn't exist, it will
|
|
183
|
+
// just go into a redirect loop. So test to see if the dir exists first.
|
|
184
|
+
let path = root + "/static/404";
|
|
185
|
+
is_dir( path, so => {
|
|
186
|
+
if( so ) {
|
|
187
|
+
let p404 = "/404/?original_path=" + encodeURIComponent( req.url );
|
|
188
|
+
res.writeHead( 302, { "Location": p404, } ); // redirect to path
|
|
197
189
|
} else {
|
|
198
|
-
|
|
190
|
+
res.writeHead( 404 );
|
|
199
191
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
192
|
+
res.end();
|
|
193
|
+
} );
|
|
194
|
+
}
|
|
195
|
+
}
|
|
203
196
|
|
|
197
|
+
// Create and return a connect app/function that implements
|
|
198
|
+
// default (basic) functionality.
|
|
199
|
+
function basic_handler( root ) {
|
|
200
|
+
const app = connect();
|
|
201
|
+
app.use( body_parser );
|
|
202
|
+
app.use( compression );
|
|
203
|
+
app.use( cors ); // allow requests from other domains
|
|
204
|
+
app.use( populate_query );
|
|
205
|
+
app.use( logger );
|
|
206
|
+
app.use( rpc_post( root ) );
|
|
207
|
+
app.use( rpc_static );
|
|
208
|
+
app.use( serveStatic( root + "/static" ) ); // static files for domain
|
|
209
|
+
app.use( not_found( root ) );
|
|
204
210
|
return app;
|
|
205
|
-
|
|
206
211
|
}
|
|
207
212
|
|
|
208
213
|
|
|
214
|
+
const cached_basic_handlers = {};
|
|
215
|
+
|
|
209
216
|
// Handle REST calls (as opposed to websocket messages)
|
|
210
|
-
|
|
217
|
+
function rest_handler( root, req, rsp ) {
|
|
211
218
|
|
|
212
219
|
I( req.headers[ "host" ] + ": " + req.method + " " + req.url );
|
|
213
220
|
D( "rest_handler root: " + root );
|
|
@@ -231,21 +238,31 @@ const rest_handler = function( root, req, rsp ) {
|
|
|
231
238
|
D( "Using custom REST handler loaded from "+root );
|
|
232
239
|
call( handler );
|
|
233
240
|
} catch( err ) {
|
|
234
|
-
//
|
|
235
|
-
|
|
241
|
+
// attempt to load custom handler threw an exception
|
|
242
|
+
|
|
243
|
+
let handler = cached_basic_handlers[ root ];
|
|
244
|
+
if( ! handler ) {
|
|
245
|
+
handler = basic_handler( root );
|
|
246
|
+
cached_basic_handlers[ root ] = handler;
|
|
247
|
+
}
|
|
248
|
+
|
|
236
249
|
D( "Using basic REST handler" );
|
|
237
250
|
call( handler );
|
|
238
251
|
}
|
|
239
252
|
};
|
|
240
253
|
|
|
241
254
|
|
|
255
|
+
// -----------------------
|
|
256
|
+
|
|
257
|
+
|
|
242
258
|
// tracked websocket connections
|
|
243
|
-
const ws_connections = {};
|
|
259
|
+
//const ws_connections = {};
|
|
244
260
|
|
|
245
261
|
|
|
246
|
-
|
|
262
|
+
// Associate a websocket message handler (msg_handler) to a webserver instance (server)
|
|
263
|
+
function ws_attach( server, msg_handler ) {
|
|
247
264
|
|
|
248
|
-
const wsd = new websocket.server( { httpServer, autoAcceptConnections: false, } );
|
|
265
|
+
const wsd = new websocket.server( { httpServer: server, autoAcceptConnections: false, } );
|
|
249
266
|
|
|
250
267
|
wsd.on( "request", function( wsreq ) {
|
|
251
268
|
V( "WS: connection request from "+wsreq.remoteAddress+" "+wsreq.resource )
|
|
@@ -254,7 +271,7 @@ const ws_attach = function( httpServer, msg_handler ) {
|
|
|
254
271
|
|
|
255
272
|
const socket = wsreq.accept( null, wsreq.origin );
|
|
256
273
|
|
|
257
|
-
const name = "ws-
|
|
274
|
+
const name = "ws-conn-"+next_seq(); // XXX just use the websocket id
|
|
258
275
|
|
|
259
276
|
// send msg to connected client
|
|
260
277
|
const send = function( msg , cb ) {
|
|
@@ -273,7 +290,7 @@ const ws_attach = function( httpServer, msg_handler ) {
|
|
|
273
290
|
|
|
274
291
|
socket.on("close", function() {
|
|
275
292
|
D( "WS: disconnect" );
|
|
276
|
-
delete ws_connections[ name ]; // remove from tracked connections
|
|
293
|
+
//delete ws_connections[ name ]; // remove from tracked connections
|
|
277
294
|
});
|
|
278
295
|
|
|
279
296
|
// incoming msgs from client come through here
|
|
@@ -305,7 +322,7 @@ const ws_attach = function( httpServer, msg_handler ) {
|
|
|
305
322
|
|
|
306
323
|
D( "WS: connected: "+name );
|
|
307
324
|
|
|
308
|
-
ws_connections[ name ] = conn; // add to tracked connections
|
|
325
|
+
//ws_connections[ name ] = conn; // add to tracked connections
|
|
309
326
|
|
|
310
327
|
} );
|
|
311
328
|
|
|
@@ -316,84 +333,6 @@ const ws_attach = function( httpServer, msg_handler ) {
|
|
|
316
333
|
};
|
|
317
334
|
|
|
318
335
|
|
|
319
|
-
|
|
320
|
-
/*
|
|
321
|
-
const build_sass = function( path ) {
|
|
322
|
-
const sass = require( "sass" );
|
|
323
|
-
const result = sass.compile( path );
|
|
324
|
-
fs.writeFileSync( path + ".css", result.css, "utf8" );
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
const auto_builders = [
|
|
328
|
-
[ /\.(sass|scss)$/, build_sass ],
|
|
329
|
-
];
|
|
330
|
-
|
|
331
|
-
// Do auto-build processes (dev mode only)
|
|
332
|
-
const auto_build = function( paths ) {
|
|
333
|
-
|
|
334
|
-
for( let path of paths ) {
|
|
335
|
-
for( const bldr of auto_builders ) {
|
|
336
|
-
if( bldr[ 0 ].test( path ) ) {
|
|
337
|
-
bldr[ 1 ]( path );
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
// Engage the filesystem watcher (dev mode only)
|
|
347
|
-
const enable_fs_watch = function( root ) {
|
|
348
|
-
|
|
349
|
-
let paths_noted = {};
|
|
350
|
-
|
|
351
|
-
let hot_reload_tid = null;
|
|
352
|
-
let hot_reload_count = 0;
|
|
353
|
-
|
|
354
|
-
function hot_reload_tick() {
|
|
355
|
-
hot_reload_count -= 1; // reduce count by 1
|
|
356
|
-
if( hot_reload_count <= 0 ) {
|
|
357
|
-
// counter reached 0
|
|
358
|
-
clearInterval( hot_reload_tid ); // turn off the ticker
|
|
359
|
-
hot_reload_tid = null;
|
|
360
|
-
|
|
361
|
-
auto_build( Object.keys( paths_noted ) );
|
|
362
|
-
paths_noted = {};
|
|
363
|
-
|
|
364
|
-
// send RELOAD msg to all client that have an active ws connection
|
|
365
|
-
for( let conn of Object.values( ws_connections ) ) {
|
|
366
|
-
conn.send( { msg: "RELOAD" } ); // send RELOAD msg to browser
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function note( evt, path ) {
|
|
372
|
-
// A change of some sort was seen
|
|
373
|
-
// Hundreds of these can happen very rapidly, so the timer stuff
|
|
374
|
-
// above is used to delay sending the RELOAD command until
|
|
375
|
-
// a second or so has passed without any more calls to this function
|
|
376
|
-
|
|
377
|
-
paths_noted[ path ] = evt; // XXX
|
|
378
|
-
//D( "noted fs change: [" + evt + "] " + path );
|
|
379
|
-
|
|
380
|
-
hot_reload_count = 5;
|
|
381
|
-
if( hot_reload_tid === null ) {
|
|
382
|
-
// ticker isn't running, so start it
|
|
383
|
-
hot_reload_tid = setInterval( hot_reload_tick, 200 );
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
// tell OS to start watching for any changes in root
|
|
389
|
-
fs.watch( root, { recursive: true, }, function( evt, path ) {
|
|
390
|
-
note( evt, root + "/" + path );
|
|
391
|
-
} );
|
|
392
|
-
|
|
393
|
-
};
|
|
394
|
-
*/
|
|
395
|
-
|
|
396
|
-
|
|
397
336
|
// -----------------------
|
|
398
337
|
|
|
399
338
|
const argv = process.argv;
|
|
@@ -415,14 +354,13 @@ if( argv.length == 2 ) {
|
|
|
415
354
|
maintainerEmail: MAINTAINER_EMAIL,
|
|
416
355
|
configDir: "./greenlock.d",
|
|
417
356
|
cluster: false,
|
|
418
|
-
//notify
|
|
419
357
|
} ).ready( glx => {
|
|
420
358
|
|
|
421
|
-
var
|
|
359
|
+
var server = glx.httpsServer();
|
|
422
360
|
|
|
423
|
-
ws_attach(
|
|
361
|
+
ws_attach( server, ( msg, connection, domain ) => {
|
|
424
362
|
const root = path.resolve( DOMAINS_ROOT + "/" + domain );
|
|
425
|
-
ws_msg_handler( root, msg );
|
|
363
|
+
ws_msg_handler( root, msg, connection );
|
|
426
364
|
} );
|
|
427
365
|
|
|
428
366
|
glx.serveApp( ( req, res ) => {
|
|
@@ -454,22 +392,17 @@ if( argv.length == 5 ) {
|
|
|
454
392
|
V( "SITE_ROOT: " + SITE_ROOT );
|
|
455
393
|
V( "PORT: " + PORT );
|
|
456
394
|
|
|
457
|
-
const
|
|
395
|
+
const server = http.createServer( ( req, res ) => {
|
|
458
396
|
rest_handler( SITE_ROOT, req, res );
|
|
459
397
|
} );
|
|
460
398
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
ws_attach( httpd, ( msg, conn, domain ) => {
|
|
465
|
-
ws_msg_handler( SITE_ROOT, msg );
|
|
466
|
-
} );
|
|
399
|
+
ws_attach( server, ( msg, conn, domain ) => {
|
|
400
|
+
ws_msg_handler( SITE_ROOT, msg );
|
|
401
|
+
} );
|
|
467
402
|
|
|
468
|
-
|
|
469
|
-
// enable_fs_watch( SITE_ROOT );
|
|
403
|
+
server.listen( toInt( PORT ), () => {
|
|
470
404
|
|
|
471
405
|
I( "Listening on " + PORT + " & serving from " + SITE_ROOT );
|
|
472
|
-
|
|
473
406
|
} );
|
|
474
407
|
|
|
475
408
|
} else {
|