tlsd 2.8.0 → 2.9.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 CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "tlsd",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
4
  "description": "A server for web app prototyping with HTTPS and Websockets",
5
5
  "main": "tlsd.js",
6
6
  "bin": {
7
7
  "tlsd": "tlsd"
8
8
  },
9
9
  "scripts": {
10
+ "dev": "./tlsd run dev scaffold 12345 5",
10
11
  "test": "echo \"Error: no test specified\" && exit 1"
11
12
  },
12
13
  "author": "Joe Hitchens <joe@sleepless.com>",
@@ -19,9 +20,9 @@
19
20
  "cors": "^2.8.5",
20
21
  "g": "^2.0.1",
21
22
  "greenlock-express": "^4.0.3",
23
+ "sass": "^1.69.5",
22
24
  "serve-static": "^1.15.0",
23
25
  "sleepless": "^5.13.0",
24
- "tlsd": "^2.4.1",
25
26
  "websocket": "^1.0.34"
26
27
  }
27
28
  }
@@ -24,9 +24,11 @@
24
24
  const time = function( dt ) { return Math.round( ( new Date() ).getTime() / 1000 ); }
25
25
 
26
26
 
27
- let DBG = msg => {
27
+ let DBG = function( ...args ) {
28
28
  if( RPC.debug ) {
29
- console.log( "RPC: " + o2j( msg ) );
29
+ args.unshift( "RPC:" );
30
+ console.log.apply( this, args );
31
+ //console.log( "RPC: ", args );
30
32
  }
31
33
  };
32
34
 
@@ -47,11 +49,11 @@
47
49
  delete wraps[id]
48
50
  num -= 1
49
51
  if(num == 0) {
50
- DBG( "clearInterval " + timer );
52
+ //DBG( "clearInterval", timer );
51
53
  clearInterval(timer)
52
54
  timer = null
53
55
  }
54
- DBG("forgetting " + id + " " + o2j( p ));
56
+ //DBG("forgetting", id, p );
55
57
  }
56
58
  return p
57
59
  }
@@ -60,7 +62,7 @@
60
62
  // Put a msg into the list
61
63
  // ttl is in secs and should not be less than 10 (default is 60 if not provided)
62
64
  const ins = self.ins = function(p, id, ttl) {
63
- DBG( "remembering " + id + " " + o2j( p ) );
65
+ //DBG( "remembering", id, p );
64
66
  const w = {
65
67
  expire: time() + (ttl || 60),
66
68
  payload: p,
@@ -77,7 +79,7 @@
77
79
  }
78
80
  }
79
81
  }, 10 * 1000);
80
- DBG( "setInterval " + timer );
82
+ //DBG( "setInterval", timer );
81
83
  }
82
84
  }
83
85
 
@@ -99,7 +101,6 @@
99
101
  const waiting = new WaitList()
100
102
 
101
103
  const send = function(m, cb, fail) {
102
- DBG( "send: " + o2j( m ) );
103
104
  if(m.msg_id === undefined) {
104
105
  m.msg_id = "CMID-" + next_seq(); // every message must have an id
105
106
  }
@@ -109,7 +110,7 @@
109
110
  waiting.ins( { msg: m, cb, fail }, m.msg_id );
110
111
  }
111
112
 
112
- DBG(">>---> server "+o2j( m ));
113
+ DBG(">>--->", m );
113
114
  socket.send( o2j( m ) );
114
115
  }
115
116
 
@@ -117,7 +118,7 @@
117
118
  var socket = new WebSocket( ( loc.protocol == "http:" ? "ws:" : "wss:" ) + "//" + loc.host + path )
118
119
 
119
120
  socket.onerror = function( evt ) {
120
- DBG( "error: " + evt.data );
121
+ DBG( "error:", evt.data );
121
122
  cb_ctrl( "error", evt.data );
122
123
  }
123
124
 
@@ -125,7 +126,7 @@
125
126
  DBG( "close" );
126
127
  cb_ctrl( "disconnect", conn.attempt );
127
128
  setTimeout( () => {
128
- DBG( "reconnect atttempt " + conn.attempt );
129
+ DBG( "reconnect atttempt", conn.attempt );
129
130
  ws_connect( cb_msg, cb_ctrl, path );
130
131
  }, 2 * 1000 );
131
132
  }
@@ -137,7 +138,7 @@
137
138
 
138
139
  // handle incoming messages from server
139
140
  socket.onmessage = function( evt ) {
140
- DBG( "<---<< server " + o2j( evt.data ) )
141
+ //DBG( "<---<<", evt.data )
141
142
 
142
143
  var json = evt.data // raw message is a utf8 string
143
144
  var msg_in = j2o( json )
@@ -146,6 +147,8 @@
146
147
  return;
147
148
  }
148
149
 
150
+ DBG( "<---<<", msg_in )
151
+
149
152
  if(typeof msg_in.msg !== "undefined") {
150
153
  // server initiated msg (not a reply to a client msg)
151
154
 
@@ -257,7 +260,6 @@
257
260
 
258
261
  ws_connect( msg => {
259
262
  // msg initiated by server
260
- //DBG( "Server says: " + msg );
261
263
 
262
264
  // If there's a handler set for this, call that
263
265
  const fn = RPC[ "onmessage" ];
@@ -278,7 +280,7 @@
278
280
 
279
281
  }, ( evt, detail ) => {
280
282
  // event occurred
281
- DBG( "Event: " + evt );
283
+ DBG( "Event:", evt );
282
284
  if( evt === "connect" ) {
283
285
  RPC.connected = true;
284
286
  }
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Page Not Found </title>
5
+ </head>
6
+ <body>
7
+ <h1>Sorry, nothing there, mate!</h1>
8
+ <p>
9
+ <script>
10
+ let url = decodeURIComponent( document.location.search.split( "=" ).pop() );
11
+ document.write( "Requested URL: " + url );
12
+ </script>
13
+ </p>
14
+ </body>
15
+ </html>
16
+
@@ -1,7 +1,10 @@
1
1
  <html>
2
+ <head>
3
+ <link href="/local.sass.css" rel="stylesheet">
4
+ </head>
2
5
  <body>
3
6
 
4
- <h1> Hello </h1>
7
+ <h1>TLSD Test Page</h1>
5
8
  <script>
6
9
  document.write( Date() );
7
10
  </script>
@@ -0,0 +1,11 @@
1
+
2
+ body
3
+ margin: 2em 2vw
4
+ font-family: sans-serif
5
+ text-align: center
6
+
7
+ h1
8
+ font-size: 40px
9
+ a
10
+ font-style: italic
11
+
@@ -0,0 +1,12 @@
1
+ body {
2
+ margin: 2em 2vw;
3
+ font-family: sans-serif;
4
+ text-align: center;
5
+ }
6
+
7
+ h1 {
8
+ font-size: 40px;
9
+ }
10
+ h1 a {
11
+ font-style: italic;
12
+ }
package/tlsd.js CHANGED
@@ -159,25 +159,33 @@ const basic_handler = function( root ) {
159
159
 
160
160
  } );
161
161
 
162
- // Serve my own static files
163
- // this is primarily so that GET /rpc/rpc.js can return the client-side code for
164
- // sending WS and RPC messages (similar to how socket.io works)
165
- app.use( serveStatic( __dirname + "/server_static" ) );
166
162
 
167
- // Serve static files for domain
163
+ // Serve my (tlsd's) own static files
164
+ // This is primarily so that GET /rpc/rpc.js can return the client-side code for
165
+ // sending WS RPC messages (similar to how socket.io works) and providing the
166
+ // browser hot-reload feature (which requires WS RPC)
167
+ app.use( serveStatic( __dirname + "/rpc_static" ) );
168
+
169
+
170
+ // Serve static files for the domain
168
171
  app.use( serveStatic( root + "/static" ) );
169
172
 
173
+
170
174
  // finally, if serveStatic can't service the request,
171
175
  // look for a 404/ dir and redirect to that if present
172
176
  app.use( function( req, res, next ) {
173
177
  // I can't just return a redirect here because if the /404 doesn't exist,
174
178
  // I will just go into a redirect loop, so I have to test to see if the
175
179
  // dir exists, and then if so, redirect to it, otherwise, just return 404.
176
- is_file( root + "/404", so => {
177
- if( so )
178
- res.writeHead( 302, { "Location": "/404/", });
179
- else
180
+ let { url, } = req;
181
+ let p = root + "/static/404";
182
+ is_dir( p, so => {
183
+ if( so ) {
184
+ let p404 = "/404/?original_path=" + encodeURIComponent( url );
185
+ res.writeHead( 302, { "Location": p404, } );
186
+ } else {
180
187
  res.writeHead( 404 );
188
+ }
181
189
  res.end();
182
190
  } );
183
191
  } );
@@ -220,18 +228,22 @@ const rest_handler = function( root, req, rsp ) {
220
228
  };
221
229
 
222
230
 
223
- const ws_attach = function( httpServer, msg_handler, done ) {
231
+ // tracked websocket connections
232
+ const ws_connections = {};
233
+
234
+
235
+ const ws_attach = function( httpServer, msg_handler ) {
224
236
 
225
237
  const wsd = new websocket.server( { httpServer, autoAcceptConnections: false, } );
226
238
 
227
- wsd.on( "request", function( req ) {
228
- V( "WS: connection request from "+req.remoteAddress+" "+req.resource )
239
+ wsd.on( "request", function( wsreq ) {
240
+ V( "WS: connection request from "+wsreq.remoteAddress+" "+wsreq.resource )
229
241
 
230
- const domain = req.httpRequest.headers[ "host" ];
242
+ const domain = wsreq.httpRequest.headers[ "host" ];
231
243
 
232
- const socket = req.accept( null, req.origin );
244
+ const socket = wsreq.accept( null, wsreq.origin );
233
245
 
234
- const name = "ws-client-"+next_seq();
246
+ const name = "ws-client-"+next_seq(); // XXX just use the websocket id
235
247
 
236
248
  // send msg to connected client
237
249
  const send = function( msg , cb ) {
@@ -250,6 +262,7 @@ const ws_attach = function( httpServer, msg_handler, done ) {
250
262
 
251
263
  socket.on("close", function() {
252
264
  D( "WS: disconnect" );
265
+ delete ws_connections[ name ]; // remove from tracked connections
253
266
  });
254
267
 
255
268
  // incoming msgs from client come through here
@@ -281,7 +294,7 @@ const ws_attach = function( httpServer, msg_handler, done ) {
281
294
 
282
295
  D( "WS: connected: "+name );
283
296
 
284
- done( conn );
297
+ ws_connections[ name ] = conn; // add to tracked connections
285
298
 
286
299
  } );
287
300
 
@@ -292,36 +305,81 @@ const ws_attach = function( httpServer, msg_handler, done ) {
292
305
  };
293
306
 
294
307
 
295
- const enable_hot_reload = function( root, conn ) {
296
- D( "Enabling hot-reload" );
297
308
 
298
- let tid = null;
299
- let count = 0;
309
+ const build_sass = function( path ) {
310
+ //D( "_________ auto_build sass " + path );
311
+ const sass = require( "sass" );
312
+ const result = sass.compile( path );
313
+ fs.writeFileSync( path + ".css", result.css, "utf8" );
314
+ };
315
+
316
+ const auto_builders = [
317
+ [ /\.(sass|scss)$/, build_sass ],
318
+ ];
319
+
320
+ // Do auto-build processes (dev mode only)
321
+ const auto_build = function( paths ) {
322
+
323
+ for( let path of paths ) {
324
+ // D( "___ path? " + path );
325
+ for( const bldr of auto_builders ) {
326
+ if( bldr[ 0 ].test( path ) ) {
327
+ bldr[ 1 ]( path );
328
+ }
329
+ }
330
+
331
+ }
332
+
333
+ };
334
+
335
+
336
+ // Engage the filesystem watcher (dev mode only)
337
+ const enable_fs_watch = function( root ) {
338
+
339
+ let paths_noted = {};
300
340
 
301
- function tick() {
302
- count -= 1; // reduce the count by 1
303
- if( count <= 0 ) {
341
+ let hot_reload_tid = null;
342
+ let hot_reload_count = 0;
343
+
344
+ function hot_reload_tick() {
345
+ hot_reload_count -= 1; // reduce count by 1
346
+ if( hot_reload_count <= 0 ) {
304
347
  // counter reached 0
305
- clearInterval( tid ); // turn off the ticker
306
- tid = null;
307
- conn.send( { msg: "RELOAD" } ); // send the RELOAD msg
348
+ clearInterval( hot_reload_tid ); // turn off the ticker
349
+ hot_reload_tid = null;
350
+
351
+ auto_build( Object.keys( paths_noted ) );
352
+ paths_noted = {};
353
+
354
+ // send RELOAD msg to all client that have an active ws connection
355
+ for( let conn of Object.values( ws_connections ) ) {
356
+ conn.send( { msg: "RELOAD" } ); // send RELOAD msg to browser
357
+ }
308
358
  }
309
359
  }
310
360
 
311
- function see( evt, path ) {
312
- // a change of some sort was seen
313
- // hundreds of these can happen very rapidly, so the timer stuff
361
+ function note( evt, path ) {
362
+ // A change of some sort was seen
363
+ // Hundreds of these can happen very rapidly, so the timer stuff
314
364
  // above is used to delay sending the RELOAD command until
315
365
  // a second or so has passed without any more calls to this function
316
- // XXX use regex to only consider certain file extensions?
317
- count = 5;
318
- // If the ticker isn't already running, the start it
319
- if( tid === null ) {
320
- tid = setInterval( tick, 200 );
366
+
367
+ paths_noted[ path ] = evt; // XXX
368
+ //D( "noted fs change: [" + evt + "] " + path );
369
+
370
+ hot_reload_count = 5;
371
+ if( hot_reload_tid === null ) {
372
+ // ticker isn't running, so start it
373
+ hot_reload_tid = setInterval( hot_reload_tick, 200 );
321
374
  }
375
+
322
376
  };
323
377
 
324
- fs.watch( root, { recursive: true, }, see );
378
+ // tell OS to start watching for any changes in root
379
+ fs.watch( root, { recursive: true, }, function( evt, path ) {
380
+ note( evt, root + "/" + path );
381
+ } );
382
+
325
383
  };
326
384
 
327
385
 
@@ -354,8 +412,6 @@ if( argv.length == 2 ) {
354
412
  ws_attach( httpd, ( msg, conn, domain ) => {
355
413
  const root = path.resolve( DOMAINS_ROOT + "/" + domain );
356
414
  ws_msg_handler( root, msg );
357
- }, conn => {
358
- // attachment complete
359
415
  } );
360
416
 
361
417
  glx.serveApp( ( req, res ) => {
@@ -391,16 +447,16 @@ if( argv.length == 5 ) {
391
447
  rest_handler( SITE_ROOT, req, res );
392
448
  } );
393
449
 
450
+ // XXX call ws_attach *before* calling listen() ?
394
451
  httpd.listen( toInt( PORT ), () => {
395
452
 
396
453
  ws_attach( httpd, ( msg, conn, domain ) => {
397
454
  ws_msg_handler( SITE_ROOT, msg );
398
- }, conn => {
399
- // attachment complete
400
- enable_hot_reload( SITE_ROOT, conn );
401
-
402
455
  } );
403
456
 
457
+ // enable the file system watch, which handles hot-reload and auto-build
458
+ enable_fs_watch( SITE_ROOT );
459
+
404
460
  I( "Listening on " + PORT + " & serving from " + SITE_ROOT );
405
461
 
406
462
  } );
@@ -1,11 +0,0 @@
1
- <html>
2
- <body>
3
-
4
- <script src="/rpc/rpc.js"></script>
5
-
6
- <script>
7
- RPC.debug = true;
8
- RPC.connect( function() {
9
- RPC( { action: "ping" }, console.log, alert );
10
- } );
11
- </script>
package/test.html DELETED
@@ -1,4 +0,0 @@
1
- <html>
2
- <head>
3
- #include head.html
4
- </head>