tlsd 2.11.1 → 2.11.2

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/tlsd.js +121 -192
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tlsd",
3
- "version": "2.11.1",
3
+ "version": "2.11.2",
4
4
  "description": "A server for web app prototyping with HTTPS and Websockets",
5
5
  "main": "tlsd.js",
6
6
  "bin": {
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
- const next_seq = function() {
39
+ function next_seq() {
35
40
  seq += 1;
36
41
  return seq;
37
42
  }
@@ -39,7 +44,7 @@ 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
- const rpc_handler = function( root, msg, xport, _okay, _fail ) {
47
+ function rpc_handler( root, msg, xport, _okay, _fail ) {
43
48
 
44
49
  let ll = toInt( msg.log_level );
45
50
  if( ll > 0 ) {
@@ -89,7 +94,7 @@ const rpc_handler = function( root, msg, xport, _okay, _fail ) {
89
94
 
90
95
  // Glue function to call rpc handler module and then return response
91
96
  // via the websockets msg object
92
- const ws_msg_handler = function( root, msg ) {
97
+ function ws_msg_handler( root, msg ) {
93
98
  rpc_handler( root, msg.msg, "WS", data => {
94
99
  msg.reply( data );
95
100
  }, error => {
@@ -99,115 +104,113 @@ const ws_msg_handler = function( root, msg ) {
99
104
  };
100
105
 
101
106
 
102
- // Return a handler function that implements default (basic) functionality.
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 ); };
107
+ // -----------------------
167
108
 
168
- // Summon the rpc handler for the domain root.
169
- rpc_handler( root, input, "REST", okay, fail );
170
109
 
171
- } );
110
+ // Handler that populates the req.query var with what's in query args
111
+ function populate_query( req, res, next ) {
112
+ req.query = queryString.parse( req._parsedUrl.query );
113
+ next();
114
+ }
172
115
 
116
+ // Simple logging handler
117
+ function logger( req, res, next ) {
118
+ const host = req.headers[ "host" ];
119
+ let { method, url, query, body } = req;
120
+ url = url.split( "?" ).shift();
121
+ I( host + ": " + method + " " + o2j( url ) + " " + o2j( query, null, 2 ) + "/" + o2j( body, null, 2 ) );
122
+ next();
123
+ }
173
124
 
174
- // Serve my (tlsd's) own static files
175
- // This is primarily so that GET /rpc/rpc.js can return the client-side code for
176
- // sending WS RPC messages (similar to how socket.io works) and providing the
177
- // browser hot-reload feature (which requires WS RPC)
178
- app.use( serveStatic( __dirname + "/rpc_static" ) );
125
+ // Creates and returns a handler function that intercepts and services
126
+ // POST requests to "/rpc" endpoint.
127
+ function rpc_post( root ) {
128
+ return function( req, res, next ) {
129
+ let { method, url, query, body } = req;
179
130
 
131
+ url = url.split( "?" ).shift();
132
+ if( url != "/rpc" && url != "/rpc/" ) {
133
+ next();
134
+ return
135
+ }
180
136
 
181
- // Serve static files for the domain
182
- app.use( serveStatic( root + "/static" ) );
137
+ // Only allow POSTS in TLS mode.
138
+ // In dev mode, GET is allowed as well with query args.
139
+ let input = body;
140
+ if( method != "POST" ) {
141
+ if( ! ( method == "GET" && dev_mode ) ) {
142
+ W( "GET /rpc is disallowed in TLS mode" );
143
+ res.writeHead( 405 ); // Method Not Allowed
144
+ res.end();
145
+ return;
146
+ }
147
+ input = query;
148
+ }
183
149
 
150
+ // Set up callbacks
151
+ const done = ( error, data ) => {
152
+ res.writeHead( 200, {
153
+ "Content-Type": "application/json",
154
+ "Cache-Control": "no-store",
155
+ });
156
+ res.write( o2j( data ) );
157
+ res.end();
158
+ };
159
+ const okay = ( data ) => { done( null, data ); };
160
+ // XXX wrong
161
+ const fail = ( error, body ) => { done( error, body ); };
162
+
163
+ // Summon the rpc handler for the domain root.
164
+ rpc_handler( root, input, "REST", okay, fail );
165
+ };
166
+ }
184
167
 
185
- // finally, if serveStatic can't service the request,
186
- // look for a 404/ dir and redirect to that if present
187
- app.use( function( req, res, next ) {
188
- // I can't just return a redirect here because if the /404 doesn't exist,
189
- // I will just go into a redirect loop, so I have to test to see if the
190
- // dir exists, and then if so, redirect to it, otherwise, just return 404.
191
- let { url, } = req;
192
- let p = root + "/static/404";
193
- is_dir( p, so => {
194
- if( so ) {
195
- let p404 = "/404/?original_path=" + encodeURIComponent( url );
196
- res.writeHead( 302, { "Location": p404, } );
168
+ // Handler for tlsd's own static files.
169
+ // This is so that "GET /rpc/rpc.js" can return the client-side code for
170
+ // sending WS RPC messages (similar to how socket.io works).
171
+ const rpc_static = serveStatic( __dirname + "/rpc_static" );
172
+
173
+
174
+ // Creates and returns a handler that checks for "/404" at root.
175
+ // If found, it redirects to it, otherwise it responds with 404.
176
+ function not_found( root ) {
177
+ return function( req, res, next ) {
178
+ // Can't just do a redirect here because if /404 doesn't exist, it will
179
+ // just go into a redirect loop. So test to see if the dir exists first.
180
+ let path = root + "/static/404";
181
+ is_dir( path, so => {
182
+ if( so ) {
183
+ let p404 = "/404/?original_path=" + encodeURIComponent( req.url );
184
+ res.writeHead( 302, { "Location": p404, } ); // redirect to path
197
185
  } else {
198
- res.writeHead( 404 );
186
+ res.writeHead( 404 );
199
187
  }
200
- res.end();
201
- } );
202
- } );
188
+ res.end();
189
+ } );
190
+ }
191
+ }
203
192
 
193
+ // Create and return a connect app/function that implements
194
+ // default (basic) functionality.
195
+ function basic_handler( root ) {
196
+ const app = connect();
197
+ app.use( body_parser );
198
+ app.use( compression );
199
+ app.use( cors ); // allow requests from other domains
200
+ app.use( populate_query );
201
+ app.use( logger );
202
+ app.use( rpc_post( root ) );
203
+ app.use( rpc_static );
204
+ app.use( serveStatic( root + "/static" ) ); // static files for domain
205
+ app.use( not_found( root ) );
204
206
  return app;
205
-
206
207
  }
207
208
 
208
209
 
210
+ const cached_basic_handlers = {};
211
+
209
212
  // Handle REST calls (as opposed to websocket messages)
210
- const rest_handler = function( root, req, rsp ) {
213
+ function rest_handler( root, req, rsp ) {
211
214
 
212
215
  I( req.headers[ "host" ] + ": " + req.method + " " + req.url );
213
216
  D( "rest_handler root: " + root );
@@ -231,21 +234,31 @@ const rest_handler = function( root, req, rsp ) {
231
234
  D( "Using custom REST handler loaded from "+root );
232
235
  call( handler );
233
236
  } catch( err ) {
234
- // custom handler load attempt threw an exception
235
- const handler = basic_handler( root );
237
+ // attempt to load custom handler threw an exception
238
+
239
+ let handler = cached_basic_handlers[ root ];
240
+ if( ! handler ) {
241
+ handler = basic_handler( root );
242
+ cached_basic_handlers[ root ] = handler;
243
+ }
244
+
236
245
  D( "Using basic REST handler" );
237
246
  call( handler );
238
247
  }
239
248
  };
240
249
 
241
250
 
251
+ // -----------------------
252
+
253
+
242
254
  // tracked websocket connections
243
255
  const ws_connections = {};
244
256
 
245
257
 
246
- const ws_attach = function( httpServer, msg_handler ) {
258
+ // Associate a websocket message handler (msg_handler) to a webserver instance (server)
259
+ function ws_attach( server, msg_handler ) {
247
260
 
248
- const wsd = new websocket.server( { httpServer, autoAcceptConnections: false, } );
261
+ const wsd = new websocket.server( { httpServer: server, autoAcceptConnections: false, } );
249
262
 
250
263
  wsd.on( "request", function( wsreq ) {
251
264
  V( "WS: connection request from "+wsreq.remoteAddress+" "+wsreq.resource )
@@ -254,7 +267,7 @@ const ws_attach = function( httpServer, msg_handler ) {
254
267
 
255
268
  const socket = wsreq.accept( null, wsreq.origin );
256
269
 
257
- const name = "ws-client-"+next_seq(); // XXX just use the websocket id
270
+ const name = "ws-conn-"+next_seq(); // XXX just use the websocket id
258
271
 
259
272
  // send msg to connected client
260
273
  const send = function( msg , cb ) {
@@ -316,84 +329,6 @@ const ws_attach = function( httpServer, msg_handler ) {
316
329
  };
317
330
 
318
331
 
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
332
  // -----------------------
398
333
 
399
334
  const argv = process.argv;
@@ -415,12 +350,11 @@ if( argv.length == 2 ) {
415
350
  maintainerEmail: MAINTAINER_EMAIL,
416
351
  configDir: "./greenlock.d",
417
352
  cluster: false,
418
- //notify
419
353
  } ).ready( glx => {
420
354
 
421
- var httpd = glx.httpsServer();
355
+ var server = glx.httpsServer();
422
356
 
423
- ws_attach( httpd, ( msg, conn, domain ) => {
357
+ ws_attach( server, ( msg, conn, domain ) => {
424
358
  const root = path.resolve( DOMAINS_ROOT + "/" + domain );
425
359
  ws_msg_handler( root, msg );
426
360
  } );
@@ -454,22 +388,17 @@ if( argv.length == 5 ) {
454
388
  V( "SITE_ROOT: " + SITE_ROOT );
455
389
  V( "PORT: " + PORT );
456
390
 
457
- const httpd = http.createServer( ( req, res ) => {
391
+ const server = http.createServer( ( req, res ) => {
458
392
  rest_handler( SITE_ROOT, req, res );
459
393
  } );
460
394
 
461
- // XXX call ws_attach *before* calling listen() ?
462
- httpd.listen( toInt( PORT ), () => {
463
-
464
- ws_attach( httpd, ( msg, conn, domain ) => {
465
- ws_msg_handler( SITE_ROOT, msg );
466
- } );
395
+ ws_attach( server, ( msg, conn, domain ) => {
396
+ ws_msg_handler( SITE_ROOT, msg );
397
+ } );
467
398
 
468
- // enable the file system watch, which handles hot-reload and auto-build
469
- // enable_fs_watch( SITE_ROOT );
399
+ server.listen( toInt( PORT ), () => {
470
400
 
471
401
  I( "Listening on " + PORT + " & serving from " + SITE_ROOT );
472
-
473
402
  } );
474
403
 
475
404
  } else {