braid-http 1.3.21 → 1.3.23

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.
@@ -236,7 +236,7 @@ async function braid_fetch (url, params = {}) {
236
236
  // then we want to multiplex
237
237
  var subscription_counts_on_close = null
238
238
  if (params.headers.has('subscribe')) {
239
- var origin = url[0] === '/' ? location.origin : new URL(url).origin
239
+ var origin = new URL(url, typeof document !== 'undefined' ? document.baseURI : undefined).origin
240
240
  if (!braid_fetch.subscription_counts)
241
241
  braid_fetch.subscription_counts = {}
242
242
  braid_fetch.subscription_counts[origin] =
@@ -845,30 +845,51 @@ async function multiplex_fetch(url, params) {
845
845
  // make up a new multiplexer id (unless it is being overriden)
846
846
  var multiplexer = params.headers.get('multiplexer')?.split('/')[1] ?? Math.random().toString(36).slice(2)
847
847
 
848
- // attempt to establish a multiplexed connection
849
- try {
850
- var r = await braid_fetch(`${origin}/${multiplexer}`, {method: 'MULTIPLEX', retry: true})
851
- } catch (e) {
852
- // fallback to normal fetch if multiplexed connection fails
853
- console.error(`Could not establish multiplexed connection.\nGot error: ${e}.\nFalling back to normal connection.`)
854
- return (url, params) => normal_fetch(url, params)
855
- }
856
-
857
- // parse the multiplexed stream,
858
- // and send messages to the appropriate streams
859
848
  var streams = new Map()
860
849
  var mux_error = null
861
- parse_multiplex_stream(r.body.getReader(), (stream, bytes) => {
862
- streams.get(stream)?.(bytes)
863
- }, e => {
864
- // the multiplexer stream has died.. let everyone know..
865
- mux_error = e
866
- for (var f of streams.values()) f()
867
- delete multiplex_fetch.multiplexers[mux_key]
868
- })
850
+ var using_multiplex_header = false
851
+
852
+ var mux_promise = (async () => {
853
+ // attempt to establish a multiplexed connection
854
+ try {
855
+ if (params.use_multiplex_header) throw 'skip to trying header'
856
+ var r = await braid_fetch(`${origin}/${multiplexer}`, {method: 'MULTIPLEX', retry: true})
857
+ } catch (e) {
858
+ // some servers don't like custom methods,
859
+ // so let's try with a custom header
860
+ try {
861
+ using_multiplex_header = true
862
+ r = await braid_fetch(`${origin}/${multiplexer}`, {headers: {MULTIPLEX: true}, retry: true})
863
+ } catch (e) {
864
+ // fallback to normal fetch if multiplexed connection fails
865
+ console.error(`Could not establish multiplexed connection.\nGot error: ${e}.\nFalling back to normal connection.`)
866
+ return false
867
+ }
868
+ }
869
+
870
+ // parse the multiplexed stream,
871
+ // and send messages to the appropriate streams
872
+ parse_multiplex_stream(r.body.getReader(), (stream, bytes) => {
873
+ streams.get(stream)?.(bytes)
874
+ }, e => {
875
+ // the multiplexer stream has died.. let everyone know..
876
+ mux_error = e
877
+ for (var f of streams.values()) f()
878
+ delete multiplex_fetch.multiplexers[mux_key]
879
+ })
880
+ })()
869
881
 
870
882
  // return a "fetch" for this multiplexer
871
883
  return async (url, params) => {
884
+ // maybe wait for multiplexer to be connected..
885
+ if (!params.experimental_do_not_wait_for_multiplexer) {
886
+ if ((await mux_promise) === false) {
887
+ // it failed to connect the multiplexer,
888
+ // so fallback to normal fetch
889
+ return await normal_fetch(url, params)
890
+ }
891
+ }
892
+
872
893
  // make up a new stream id (unless it is being overriden)
873
894
  var stream = params.headers.get('multiplexer')?.split('/')[2] ?? Math.random().toString(36).slice(2)
874
895
 
@@ -912,7 +933,7 @@ async function multiplex_fetch(url, params) {
912
933
  stream_error = e
913
934
  bytes_available()
914
935
  try {
915
- await braid_fetch(`${origin}${params.headers.get('multiplexer')}`, {method: 'MULTIPLEX', retry: true})
936
+ await braid_fetch(`${origin}${params.headers.get('multiplexer')}`, {...using_multiplex_header ? {headers: {MULTIPLEX: true}} : {method: 'MULTIPLEX'}, retry: true})
916
937
  } catch (e) {
917
938
  console.error(`Could not cancel multiplexed connection:`, e)
918
939
  throw e
@@ -922,6 +943,14 @@ async function multiplex_fetch(url, params) {
922
943
  // do the underlying fetch
923
944
  try {
924
945
  var res = await normal_fetch(url, params)
946
+
947
+ if (params.experimental_do_not_wait_for_multiplexer &&
948
+ res.status === 422 &&
949
+ !(await promise_done(mux_promise))) {
950
+ // this error will trigger a retry if the user is using that
951
+ throw new Error('multiplexer not yet connected')
952
+ }
953
+
925
954
  if (res.status !== 293) throw new Error('Could not establish multiplexed stream ' + params.headers.get('multiplexer') + ' got status: ' + res.status)
926
955
 
927
956
  // we want to present the illusion that the connection is still open,
@@ -1126,6 +1155,12 @@ function create_abort_error(msg) {
1126
1155
  return e
1127
1156
  }
1128
1157
 
1158
+ async function promise_done(promise) {
1159
+ var pending = {}
1160
+ var ret = await Promise.race([promise, Promise.resolve(pending)])
1161
+ return ret !== pending
1162
+ }
1163
+
1129
1164
  // ****************************
1130
1165
  // Exports
1131
1166
  // ****************************
@@ -243,7 +243,7 @@ function braidify (req, res, next) {
243
243
  req.subscribe = subscribe
244
244
 
245
245
  // Multiplexer stuff
246
- if (braidify.use_multiplexing && req.method === 'MULTIPLEX') {
246
+ if (braidify.use_multiplexing && (req.method === 'MULTIPLEX' || req.headers.multiplex)) {
247
247
  // parse the multiplexer id and stream id from the url
248
248
  var [multiplexer, stream] = req.url.slice(1).split('/')
249
249
 
@@ -304,7 +304,7 @@ function braidify (req, res, next) {
304
304
  var [multiplexer, stream] = req.headers.multiplexer.slice(1).split('/')
305
305
 
306
306
  var end_things = (msg) => {
307
- res.statusCode = 400
307
+ res.statusCode = 422 // Unprocessable Entity (but good syntax!)
308
308
  res.end(msg)
309
309
  }
310
310
 
@@ -334,14 +334,19 @@ function braidify (req, res, next) {
334
334
  }
335
335
 
336
336
  _write(chunk, encoding, callback) {
337
- var len = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk, encoding)
338
- this.multiplexer.res.write(`${len} bytes for stream ${this.stream}\r\n`)
339
- this.multiplexer.res.write(chunk, encoding, callback)
340
-
341
- // console.log(`wrote:`)
342
- // console.log(`${len} bytes for stream /${this.stream}\r\n`)
343
- // if (Buffer.isBuffer(chunk)) console.log(new TextDecoder().decode(chunk))
344
- // else console.log('STRING?: ' + chunk)
337
+ try {
338
+ var len = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk, encoding)
339
+ this.multiplexer.res.write(`${len} bytes for stream ${this.stream}\r\n`)
340
+ this.multiplexer.res.write(chunk, encoding, callback)
341
+
342
+ // console.log(`wrote:`)
343
+ // console.log(`${len} bytes for stream /${this.stream}\r\n`)
344
+ // if (Buffer.isBuffer(chunk)) console.log(new TextDecoder().decode(chunk))
345
+ // else console.log('STRING?: ' + chunk)
346
+
347
+ } catch (e) {
348
+ callback(e)
349
+ }
345
350
  }
346
351
  }
347
352
  var mw = new MultiplexedWritable(m, stream)
@@ -368,7 +373,31 @@ function braidify (req, res, next) {
368
373
  } while (obj = Object.getPrototypeOf(obj))
369
374
  }
370
375
  for (let key of get_props(res)) {
371
- if (key === '_events' || key === 'emit') continue
376
+ // skip keys that break stuff for some reason
377
+ if (
378
+ // just touching these seems to cause issues
379
+ key === '_events' || key === 'emit'
380
+
381
+ // because we called res.end above,
382
+ // in http 1, node is going to wait
383
+ // for the event loop to fire again,
384
+ // and then call these keys:
385
+ // "socket" to set it to null,
386
+ // "detachSocket" to do that,
387
+ // "_closed" to determine if the socket is closed;
388
+ // we're going to override that to say true,
389
+ // we do that below..
390
+ // we do it so we don't need to add "destroyed" here,
391
+ // because if _closed was false,
392
+ // it would try to set destroyed to true
393
+ || key === 'socket'
394
+ || key === 'detachSocket'
395
+ || key === '_closed'
396
+
397
+ // adding these lines gets rid of some deprecation warnings.. keep?
398
+ || key === '_headers'
399
+ || key === '_headerNames') continue
400
+
372
401
  if (res2[key] === undefined) continue
373
402
  var value = res[key]
374
403
  if (typeof value === 'function') {
@@ -383,6 +412,19 @@ function braidify (req, res, next) {
383
412
  }
384
413
  }
385
414
 
415
+ // node http 1 has the issue that when we call res.end,
416
+ // which we do above, not everything happens right away..
417
+ // in the next js event loop tick, it is going to
418
+ // try to tear down the stream,
419
+ // but we have proxied all the properties,
420
+ // so it would tear down our new res2 stream..
421
+ // to prevent that, we sacrafice this property
422
+ // (which the end-user hopefully won't be accessing anyway)
423
+ // to make the http 1 tear down code think its job is complete already,
424
+ // otherwise it would want to set "destroyed",
425
+ // and we would need to not proxy that key as well
426
+ res._closed = true
427
+
386
428
  // this is provided so code can know if the response has been multiplexed
387
429
  res.multiplexer = res2
388
430
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-http",
3
- "version": "1.3.21",
3
+ "version": "1.3.23",
4
4
  "description": "An implementation of Braid-HTTP for Node.js and Browsers",
5
5
  "scripts": {
6
6
  "test": "node test/server.js"