braid-http 1.3.44 → 1.3.45

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.
@@ -163,7 +163,10 @@ async function braid_fetch (url, params = {}) {
163
163
  params.headers.set('peer', params.peer)
164
164
 
165
165
  if (params.heartbeats)
166
- params.headers.set('heartbeats', typeof params.heartbeats === 'number' ? `${params.heartbeats}s` : params.heartbeats)
166
+ params.headers.set('heartbeats',
167
+ typeof params.heartbeats === 'number'
168
+ ? `${params.heartbeats}s`
169
+ : params.heartbeats)
167
170
 
168
171
  // Prevent browsers from going to disk cache
169
172
  params.cache = 'no-cache'
@@ -262,7 +265,7 @@ async function braid_fetch (url, params = {}) {
262
265
  underlying_aborter?.abort()
263
266
 
264
267
  // see if we should retry..
265
- var retry = params.retry && // only try to reconnect if the user has chosen to
268
+ var retry = params.retry && // only try to reconnect if the user has chosen to
266
269
  e.name !== "AbortError" && // don't retry if the user has chosen to abort
267
270
  !e.startsWith?.('Parse error in headers') && // in this case, the server is spewing garbage, so reconnecting might be bad
268
271
  !e.message?.startsWith?.('Could not establish multiplexed request') && // the server has told us no, or is using a different version of multiplexing
@@ -314,9 +317,8 @@ async function braid_fetch (url, params = {}) {
314
317
  braid_fetch.subscription_counts?.[origin] >
315
318
  (!mux_params ? 1 : (mux_params.after ?? 0))))) {
316
319
  res = await multiplex_fetch(url, params, mux_params?.via === 'POST')
317
- } else {
320
+ } else
318
321
  res = await normal_fetch(url, params)
319
- }
320
322
 
321
323
  // And customize the response with a couple methods for getting
322
324
  // the braid subscription data:
@@ -338,7 +340,7 @@ async function braid_fetch (url, params = {}) {
338
340
  clearTimeout(timeout)
339
341
  let wait_seconds = 1.2 * heartbeats + 3
340
342
  timeout = setTimeout(() => {
341
- on_error(new Error(`heartbeat not seen in ${wait_seconds.toFixed(2)}s`))
343
+ on_error(new Error(`heartbeat seen in ${wait_seconds.toFixed(2)}s`))
342
344
  }, wait_seconds * 1000)
343
345
  }
344
346
  on_heartbeat()
@@ -361,7 +363,8 @@ async function braid_fetch (url, params = {}) {
361
363
  async (result, err) => {
362
364
  if (!err) {
363
365
  // check whether we aborted
364
- if (original_signal?.aborted) throw create_abort_error('already aborted')
366
+ if (original_signal?.aborted)
367
+ throw create_abort_error('already aborted')
365
368
 
366
369
  // Yay! We got a new version! Tell the callback!
367
370
  cb_running = true
@@ -540,7 +543,8 @@ var subscription_parser = (cb) => ({
540
543
 
541
544
  Object.defineProperty(update, 'body_text', {
542
545
  get: function () {
543
- if (this.body != null) return new TextDecoder('utf-8').decode(this.body.buffer)
546
+ if (this.body != null)
547
+ return new TextDecoder('utf-8').decode(this.body.buffer)
544
548
  }
545
549
  })
546
550
 
@@ -634,7 +638,9 @@ function parse_headers (input) {
634
638
 
635
639
  // Extract the header string
636
640
  var headers_source = input.slice(start, end)
637
- headers_source = Array.isArray(headers_source) ? headers_source.map(x => String.fromCharCode(x)).join('') : new TextDecoder().decode(headers_source)
641
+ headers_source = Array.isArray(headers_source)
642
+ ? headers_source.map(x => String.fromCharCode(x)).join('')
643
+ : new TextDecoder().decode(headers_source)
638
644
 
639
645
  // Convert "HTTP 200 OK" to a :status: 200 header
640
646
  headers_source = headers_source.replace(/^HTTP\/?\d*\.?\d* (\d\d\d).*\r?\n/,
@@ -837,215 +843,244 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
837
843
  var mux_key = params.headers.get('multiplex-through')?.split('/')[3] ?? origin
838
844
 
839
845
  // create a new multiplexer if it doesn't exist for this origin
840
- if (!multiplex_fetch.multiplexers) multiplex_fetch.multiplexers = {}
841
- if (!multiplex_fetch.multiplexers[mux_key]) multiplex_fetch.multiplexers[mux_key] = (async () => {
842
- // make up a new multiplexer id (unless it is being overriden)
843
- var multiplexer = params.headers.get('multiplex-through')?.split('/')[3] ?? Math.random().toString(36).slice(2)
844
-
845
- var requests = new Map()
846
- var mux_error = null
847
-
848
- var mux_promise = (async () => {
849
- // attempt to establish a multiplexed connection
850
- try {
851
- if (skip_multiplex_method) throw 'skip multiplex method'
852
- var r = await braid_fetch(`${origin}/${multiplexer}`, {method: 'MULTIPLEX', headers: {'Multiplex-Version': multiplex_version}, retry: true})
853
- if (!r.ok || r.headers.get('Multiplex-Version') !== multiplex_version) throw 'bad'
854
- } catch (e) {
855
- // some servers don't like custom methods,
856
- // so let's try with a custom header
846
+ if (!multiplex_fetch.multiplexers) multiplex_fetch.multiplexers = {}
847
+ if (!multiplex_fetch.multiplexers[mux_key]) multiplex_fetch.multiplexers[mux_key] = (
848
+ async () => {
849
+ // make up a new multiplexer id (unless it is being overriden)
850
+ var multiplexer = params.headers.get('multiplex-through')?.split('/')[3]
851
+ ?? Math.random().toString(36).slice(2)
852
+
853
+ var requests = new Map()
854
+ var mux_error = null
855
+
856
+ var mux_promise = (async () => {
857
+ // attempt to establish a multiplexed connection
857
858
  try {
858
- r = await braid_fetch(`${origin}/.well-known/multiplexer/${multiplexer}`, {method: 'POST', headers: {'Multiplex-Version': multiplex_version}, retry: true})
859
-
860
- if (!r.ok) throw new Error('status not ok: ' + r.status)
861
- if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
859
+ if (skip_multiplex_method) throw 'skip multiplex method'
860
+ var r = await braid_fetch(`${origin}/${multiplexer}`, {
861
+ method: 'MULTIPLEX',
862
+ headers: {'Multiplex-Version': multiplex_version},
863
+ retry: true
864
+ })
865
+ if (!r.ok || r.headers.get('Multiplex-Version') !== multiplex_version)
866
+ throw 'bad'
862
867
  } catch (e) {
863
- // fallback to normal fetch if multiplexed connection fails
864
- console.error(`Could not establish multiplexer.\nGot error: ${e}.\nFalling back to normal connection.`)
865
- return false
868
+ // some servers don't like custom methods,
869
+ // so let's try with a custom header
870
+ try {
871
+ r = await braid_fetch(`${origin}/.well-known/multiplexer/${multiplexer}`,
872
+ {method: 'POST',
873
+ headers: {'Multiplex-Version': multiplex_version},
874
+ retry: true})
875
+
876
+ if (!r.ok) throw new Error('status not ok: ' + r.status)
877
+ if (r.headers.get('Multiplex-Version') !== multiplex_version)
878
+ throw new Error('wrong multiplex version: '
879
+ + r.headers.get('Multiplex-Version')
880
+ + ', expected ' + multiplex_version)
881
+ } catch (e) {
882
+ // fallback to normal fetch if multiplexed connection fails
883
+ console.error(`Could not establish multiplexer.\n`
884
+ + `Got error: ${e}.\nFalling back to normal connection.`)
885
+ return false
886
+ }
866
887
  }
867
- }
868
888
 
869
- // parse the multiplexed stream,
870
- // and send messages to the appropriate requests
871
- var try_deleting = new Set()
872
- parse_multiplex_stream(r.body.getReader(), async (request, bytes) => {
873
- if (requests.has(request)) {
874
- requests.get(request)(bytes)
875
- } else if (!try_deleting.has(request)) {
876
- try_deleting.add(request)
889
+ // parse the multiplexed stream,
890
+ // and send messages to the appropriate requests
891
+ var try_deleting = new Set()
892
+ parse_multiplex_stream(r.body.getReader(), async (request, bytes) => {
893
+ if (requests.has(request)) {
894
+ requests.get(request)(bytes)
895
+ } else if (!try_deleting.has(request)) {
896
+ try_deleting.add(request)
897
+ try {
898
+ await braid_fetch(`${origin}/.well-known/multiplexer/${multiplexer}/${request}`, {
899
+ method: 'DELETE',
900
+ headers: { 'Multiplex-Version': multiplex_version },
901
+ retry: true
902
+ })
903
+ } finally { try_deleting.delete(request) }
904
+ }
905
+ }, e => {
906
+ // the multiplexer stream has died.. let everyone know..
907
+ mux_error = e
908
+ for (var f of requests.values()) f()
909
+ delete multiplex_fetch.multiplexers[mux_key]
910
+ })
911
+ })()
912
+
913
+ // return a "fetch" for this multiplexer
914
+ return async (url, params) => {
915
+
916
+ // if we already know the multiplexer is not working,
917
+ // then fallback to normal fetch
918
+ // (unless the user is specifically asking for multiplexing)
919
+ if ((await promise_done(mux_promise))
920
+ && (await mux_promise) === false
921
+ && !params.headers.get('multiplex-through'))
922
+ return await normal_fetch(url, params)
923
+
924
+ // make up a new request id (unless it is being overriden)
925
+ var request = params.headers.get('multiplex-through')?.split('/')[4]
926
+ ?? Math.random().toString(36).slice(2)
927
+
928
+ // add the Multiplex-Through header without affecting the underlying params
929
+ var mux_headers = new Headers(params.headers)
930
+ mux_headers.set('Multiplex-Through', `/.well-known/multiplexer/${multiplexer}/${request}`)
931
+ mux_headers.set('Multiplex-Version', multiplex_version)
932
+ params = {...params, headers: mux_headers}
933
+
934
+ // setup a way to receive incoming data from the multiplexer
935
+ var buffers = []
936
+ var bytes_available = () => {}
937
+ var request_error = null
938
+
939
+ // this utility calls the callback whenever new data is available to process
940
+ async function process_buffers(cb) {
941
+ while (true) {
942
+ // wait for data if none is available
943
+ if (!mux_error && !request_error && !buffers.length)
944
+ await new Promise(done => bytes_available = done)
945
+ if (mux_error || request_error) throw (mux_error || request_error)
946
+
947
+ // process the data
948
+ let ret = cb()
949
+ if (ret) return ret
950
+ }
951
+ }
952
+
953
+ // tell the multiplexer to send bytes for this request to us
954
+ requests.set(request, bytes => {
955
+ if (!bytes) buffers.push(bytes)
956
+ else if (!mux_error) buffers.push(bytes)
957
+ bytes_available()
958
+ })
959
+
960
+ // prepare a function that we'll call to cleanly tear things down
961
+ var unset = async e => {
962
+ unset = () => {}
963
+ requests.delete(request)
964
+ request_error = e
965
+ bytes_available()
877
966
  try {
878
- await braid_fetch(`${origin}/.well-known/multiplexer/${multiplexer}/${request}`, {
967
+ var r = await braid_fetch(`${origin}${params.headers.get('multiplex-through')}`, {
879
968
  method: 'DELETE',
880
969
  headers: { 'Multiplex-Version': multiplex_version }, retry: true
881
970
  })
882
- } finally { try_deleting.delete(request) }
883
- }
884
- }, e => {
885
- // the multiplexer stream has died.. let everyone know..
886
- mux_error = e
887
- for (var f of requests.values()) f()
888
- delete multiplex_fetch.multiplexers[mux_key]
889
- })
890
- })()
891
971
 
892
- // return a "fetch" for this multiplexer
893
- return async (url, params) => {
894
-
895
- // if we already know the multiplexer is not working,
896
- // then fallback to normal fetch
897
- // (unless the user is specifically asking for multiplexing)
898
- if ((await promise_done(mux_promise)) && (await mux_promise) === false && !params.headers.get('multiplex-through'))
899
- return await normal_fetch(url, params)
900
-
901
- // make up a new request id (unless it is being overriden)
902
- var request = params.headers.get('multiplex-through')?.split('/')[4] ?? Math.random().toString(36).slice(2)
903
-
904
- // add the Multiplex-Through header without affecting the underlying params
905
- var mux_headers = new Headers(params.headers)
906
- mux_headers.set('Multiplex-Through', `/.well-known/multiplexer/${multiplexer}/${request}`)
907
- mux_headers.set('Multiplex-Version', multiplex_version)
908
- params = {...params, headers: mux_headers}
909
-
910
- // setup a way to receive incoming data from the multiplexer
911
- var buffers = []
912
- var bytes_available = () => {}
913
- var request_error = null
914
-
915
- // this utility calls the callback whenever new data is available to process
916
- async function process_buffers(cb) {
917
- while (true) {
918
- // wait for data if none is available
919
- if (!mux_error && !request_error && !buffers.length)
920
- await new Promise(done => bytes_available = done)
921
- if (mux_error || request_error) throw (mux_error || request_error)
922
-
923
- // process the data
924
- let ret = cb()
925
- if (ret) return ret
972
+ if (!r.ok) throw new Error('status not ok: ' + r.status)
973
+ if (r.headers.get('Multiplex-Version') !== multiplex_version)
974
+ throw new Error('wrong multiplex version: '
975
+ + r.headers.get('Multiplex-Version')
976
+ + ', expected ' + multiplex_version)
977
+ } catch (e) {
978
+ e = new Error(`Could not cancel multiplexed request: ${e}`)
979
+ console.error('' + e)
980
+ throw e
981
+ }
926
982
  }
927
- }
928
983
 
929
- // tell the multiplexer to send bytes for this request to us
930
- requests.set(request, bytes => {
931
- if (!bytes) buffers.push(bytes)
932
- else if (!mux_error) buffers.push(bytes)
933
- bytes_available()
934
- })
935
-
936
- // prepare a function that we'll call to cleanly tear things down
937
- var unset = async e => {
938
- unset = () => {}
939
- requests.delete(request)
940
- request_error = e
941
- bytes_available()
984
+ // do the underlying fetch
942
985
  try {
943
- var r = await braid_fetch(`${origin}${params.headers.get('multiplex-through')}`, {
944
- method: 'DELETE',
945
- headers: { 'Multiplex-Version': multiplex_version }, retry: true
946
- })
986
+ var mux_was_done = await promise_done(mux_promise)
947
987
 
948
- if (!r.ok) throw new Error('status not ok: ' + r.status)
949
- if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
950
- } catch (e) {
951
- e = new Error(`Could not cancel multiplexed request: ${e}`)
952
- console.error('' + e)
953
- throw e
954
- }
955
- }
956
-
957
- // do the underlying fetch
958
- try {
959
- var mux_was_done = await promise_done(mux_promise)
960
-
961
- var res = await normal_fetch(url, params)
988
+ var res = await normal_fetch(url, params)
962
989
 
963
- if (res.status === 424 && !mux_was_done) {
964
- // this error will trigger a retry if the user is using that option
965
- throw new Error('multiplexer not yet connected')
966
- }
967
-
968
- // if the response says it's ok,
969
- // but it's is not a multiplexed response,
970
- // fall back to as if it was a normal fetch
971
- if (res.ok && res.status !== 293) return res
972
-
973
- if (res.status !== 293) throw new Error('Could not establish multiplexed request ' + params.headers.get('multiplex-through') + ', got status: ' + res.status)
974
-
975
- if (res.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('Could not establish multiplexed request ' + params.headers.get('multiplex-through') + ', got unknown version: ' + res.headers.get('Multiplex-Version'))
976
-
977
- // we want to present the illusion that the connection is still open,
978
- // and therefor closable with "abort",
979
- // so we handle the abort ourselves to close the multiplexed request
980
- params.signal?.addEventListener('abort', () =>
981
- unset(create_abort_error('request aborted')))
982
-
983
- // first, we need to listen for the headers..
984
- var headers_buffer = new Uint8Array()
985
- var parsed_headers = await process_buffers(() => {
986
- // check if the request has been closed
987
- var request_ended = !buffers[buffers.length - 1]
988
- if (request_ended) buffers.pop()
989
-
990
- // aggregate all the new buffers into our big headers_buffer
991
- headers_buffer = concat_buffers([headers_buffer, ...buffers])
992
- buffers = []
993
-
994
- // and if the request had ended, put that information back
995
- if (request_ended) buffers.push(null)
996
-
997
- // try parsing what we got so far as headers..
998
- var x = parse_headers(headers_buffer)
999
-
1000
- // how did it go?
1001
- if (x.result === 'error') {
1002
- // if we got an error, give up
1003
- console.log(`headers_buffer: ` + new TextDecoder().decode(headers_buffer))
1004
- throw new Error('error parsing headers')
1005
- } else if (x.result === 'waiting') {
1006
- if (request_ended) throw new Error('Multiplexed request ended before headers received.')
1007
- } else return x
1008
- })
1009
-
1010
- // put the bytes left over from the header back
1011
- if (parsed_headers.input.length) buffers.unshift(parsed_headers.input)
990
+ if (res.status === 424 && !mux_was_done) {
991
+ // this error will trigger a retry if the user is using that option
992
+ throw new Error('multiplexer not yet connected')
993
+ }
1012
994
 
1013
- // these headers will also have the status,
1014
- // but we want to present the status in a more usual way below
1015
- var status = parsed_headers.headers[':status']
1016
- delete parsed_headers.headers[':status']
995
+ // if the response says it's ok,
996
+ // but it's is not a multiplexed response,
997
+ // fall back to as if it was a normal fetch
998
+ if (res.ok && res.status !== 293) return res
999
+
1000
+ if (res.status !== 293)
1001
+ throw new Error('Could not establish multiplexed request '
1002
+ + params.headers.get('multiplex-through')
1003
+ + ', got status: ' + res.status)
1004
+
1005
+ if (res.headers.get('Multiplex-Version') !== multiplex_version)
1006
+ throw new Error('Could not establish multiplexed request '
1007
+ + params.headers.get('multiplex-through')
1008
+ + ', got unknown version: '
1009
+ + res.headers.get('Multiplex-Version'))
1010
+
1011
+ // we want to present the illusion that the connection is still open,
1012
+ // and therefor closable with "abort",
1013
+ // so we handle the abort ourselves to close the multiplexed request
1014
+ params.signal?.addEventListener('abort', () =>
1015
+ unset(create_abort_error('request aborted')))
1016
+
1017
+ // first, we need to listen for the headers..
1018
+ var headers_buffer = new Uint8Array()
1019
+ var parsed_headers = await process_buffers(() => {
1020
+ // check if the request has been closed
1021
+ var request_ended = !buffers[buffers.length - 1]
1022
+ if (request_ended) buffers.pop()
1023
+
1024
+ // aggregate all the new buffers into our big headers_buffer
1025
+ headers_buffer = concat_buffers([headers_buffer, ...buffers])
1026
+ buffers = []
1027
+
1028
+ // and if the request had ended, put that information back
1029
+ if (request_ended) buffers.push(null)
1030
+
1031
+ // try parsing what we got so far as headers..
1032
+ var x = parse_headers(headers_buffer)
1033
+
1034
+ // how did it go?
1035
+ if (x.result === 'error') {
1036
+ // if we got an error, give up
1037
+ console.log(`headers_buffer: ` + new TextDecoder().decode(headers_buffer))
1038
+ throw new Error('error parsing headers')
1039
+ } else if (x.result === 'waiting') {
1040
+ if (request_ended)
1041
+ throw new Error('Multiplexed request ended before headers received.')
1042
+ } else return x
1043
+ })
1017
1044
 
1018
- // create our own fake response object,
1019
- // to mimik fetch's response object,
1020
- // feeding the user our request data from the multiplexer
1021
- var res = new Response(new ReadableStream({
1022
- async start(controller) {
1023
- try {
1024
- await process_buffers(() => {
1025
- var b = buffers.shift()
1026
- if (!b) return true
1027
- controller.enqueue(b)
1028
- })
1029
- } finally { controller.close() }
1030
- }
1031
- }), {
1032
- status,
1033
- headers: parsed_headers.headers
1034
- })
1045
+ // put the bytes left over from the header back
1046
+ if (parsed_headers.input.length) buffers.unshift(parsed_headers.input)
1047
+
1048
+ // these headers will also have the status,
1049
+ // but we want to present the status in a more usual way below
1050
+ var status = parsed_headers.headers[':status']
1051
+ delete parsed_headers.headers[':status']
1052
+
1053
+ // create our own fake response object,
1054
+ // to mimik fetch's response object,
1055
+ // feeding the user our request data from the multiplexer
1056
+ var res = new Response(new ReadableStream({
1057
+ async start(controller) {
1058
+ try {
1059
+ await process_buffers(() => {
1060
+ var b = buffers.shift()
1061
+ if (!b) return true
1062
+ controller.enqueue(b)
1063
+ })
1064
+ } finally { controller.close() }
1065
+ }
1066
+ }), {
1067
+ status,
1068
+ headers: parsed_headers.headers
1069
+ })
1035
1070
 
1036
- // add a convenience property for the user to know if
1037
- // this response is being multiplexed
1038
- res.is_multiplexed = true
1071
+ // add a convenience property for the user to know if
1072
+ // this response is being multiplexed
1073
+ res.is_multiplexed = true
1039
1074
 
1040
- // return the fake response object
1041
- return res
1042
- } catch (e) {
1043
- // if we had an error, be sure to unregister ourselves
1044
- unset(e)
1045
- throw e
1075
+ // return the fake response object
1076
+ return res
1077
+ } catch (e) {
1078
+ // if we had an error, be sure to unregister ourselves
1079
+ unset(e)
1080
+ throw e
1081
+ }
1046
1082
  }
1047
- }
1048
- })()
1083
+ })()
1049
1084
 
1050
1085
  // call the special fetch function for the multiplexer
1051
1086
  return await (await multiplex_fetch.multiplexers[mux_key])(url, params)
@@ -269,8 +269,7 @@ function braidify (req, res, next) {
269
269
  res.writeHead(409, 'Conflict', {'Content-Type': 'application/json'})
270
270
  return res.end(JSON.stringify({
271
271
  error: 'Multiplexer already exists',
272
- message: `Cannot create duplicate multiplexer with ID '${multiplexer}'`,
273
- details: 'This multiplexer ID must be unique'
272
+ details: `Cannot create duplicate multiplexer with ID '${multiplexer}'`
274
273
  }))
275
274
  }
276
275
 
@@ -344,9 +343,9 @@ function braidify (req, res, next) {
344
343
  if (m.requests.has(request)) {
345
344
  res.writeHead(409, 'Conflict', {'Content-Type': 'application/json'})
346
345
  return res.end(JSON.stringify({
347
- error: 'Request already exists',
348
- message: `Cannot create duplicate request with ID '${request}'`,
349
- details: 'This request ID must be unique'
346
+ error: 'Request already multiplexed',
347
+ details: `Cannot multiplex request with duplicate ID '`
348
+ + request + `' for multiplexer '` + multiplexer + `'`
350
349
  }))
351
350
  }
352
351
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-http",
3
- "version": "1.3.44",
3
+ "version": "1.3.45",
4
4
  "description": "An implementation of Braid-HTTP for Node.js and Browsers",
5
5
  "scripts": {
6
6
  "test": "node test/server.js"