tlsd 2.8.0 → 2.9.1

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.1",
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,15 +118,16 @@
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
- cb_ctrl( "error", evt.data );
121
+ evt.preventDefault();
122
+ DBG( "error:", evt );
123
+ cb_ctrl( "error", evt );
122
124
  }
123
125
 
124
126
  socket.onclose = function() {
125
127
  DBG( "close" );
126
128
  cb_ctrl( "disconnect", conn.attempt );
127
129
  setTimeout( () => {
128
- DBG( "reconnect atttempt " + conn.attempt );
130
+ DBG( "reconnect atttempt", conn.attempt );
129
131
  ws_connect( cb_msg, cb_ctrl, path );
130
132
  }, 2 * 1000 );
131
133
  }
@@ -137,7 +139,7 @@
137
139
 
138
140
  // handle incoming messages from server
139
141
  socket.onmessage = function( evt ) {
140
- DBG( "<---<< server " + o2j( evt.data ) )
142
+ //DBG( "<---<<", evt.data )
141
143
 
142
144
  var json = evt.data // raw message is a utf8 string
143
145
  var msg_in = j2o( json )
@@ -146,6 +148,8 @@
146
148
  return;
147
149
  }
148
150
 
151
+ DBG( "<---<<", msg_in )
152
+
149
153
  if(typeof msg_in.msg !== "undefined") {
150
154
  // server initiated msg (not a reply to a client msg)
151
155
 
@@ -257,7 +261,6 @@
257
261
 
258
262
  ws_connect( msg => {
259
263
  // msg initiated by server
260
- //DBG( "Server says: " + msg );
261
264
 
262
265
  // If there's a handler set for this, call that
263
266
  const fn = RPC[ "onmessage" ];
@@ -278,7 +281,7 @@
278
281
 
279
282
  }, ( evt, detail ) => {
280
283
  // event occurred
281
- DBG( "Event: " + evt );
284
+ DBG( "Event:", evt );
282
285
  if( evt === "connect" ) {
283
286
  RPC.connected = true;
284
287
  }
@@ -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,11 +1,16 @@
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>
8
11
 
12
+ <div id=connect_status>?</div>
13
+
9
14
  <script src="/rpc/rpc.js"></script>
10
15
 
11
16
  <script>
@@ -29,9 +34,17 @@
29
34
 
30
35
  RPC.on_connect = function( evt ) {
31
36
  // RPC is ready to be used
32
- RPC( { action: "hello" }, console.log, alert )
37
+ RPC( { action: "hello" }, msg => {
38
+ document.getElementById( "connect_status" ).innerText = msg;
39
+ } );
33
40
  }
34
41
 
42
+ RPC.on_disconnect = function( evt ) {
43
+ // The websocket connection ws lost for some reason
44
+ // The RPC code will reconnect automatically
45
+ document.getElementById( "connect_status" ).innerText = "[ DISCONNECTED ]";
46
+ }
47
+
35
48
  </script>
36
49
 
37
50
  </body>
@@ -0,0 +1,14 @@
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
+
12
+ #connect_status
13
+ margin: 2em 0
14
+ font-size: 200%
@@ -0,0 +1,17 @@
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
+ }
13
+
14
+ #connect_status {
15
+ margin: 2em 0;
16
+ font-size: 200%;
17
+ }
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>