braid-http 1.3.48 → 1.3.50

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.
@@ -315,7 +315,7 @@ async function braid_fetch (url, params = {}) {
315
315
  (params.headers.has('subscribe') &&
316
316
  braid_fetch.subscription_counts?.[origin] >
317
317
  (!mux_params ? 1 : (mux_params.after ?? 0))))) {
318
- res = await multiplex_fetch(url, params, mux_params)
318
+ res = await multiplex_fetch(url, params, mux_params, underlying_aborter)
319
319
  } else
320
320
  res = await normal_fetch(url, params)
321
321
 
@@ -830,9 +830,72 @@ function parse_body (state) {
830
830
  }
831
831
  }
832
832
 
833
+
834
+ // The "extra_headers" field is returned to the client on any *update* or
835
+ // *patch* to include any headers that we've received, but don't have braid
836
+ // semantics for.
837
+ //
838
+ // This function creates that hash from a headers object, by filtering out all
839
+ // known headers.
840
+ function extra_headers (headers) {
841
+ // Clone headers
842
+ var result = Object.assign({}, headers)
843
+
844
+ // Remove the non-extra parts
845
+ var known_headers = ['version', 'parents', 'patches',
846
+ 'content-length', 'content-range', ':status']
847
+ for (var i = 0; i < known_headers.length; i++)
848
+ delete result[known_headers[i]]
849
+
850
+ // Return undefined if we deleted them all
851
+ if (Object.keys(result).length === 0)
852
+ return undefined
853
+
854
+ return result
855
+ }
856
+
857
+ function get_binary_length(x) {
858
+ return x instanceof ArrayBuffer ? x.byteLength :
859
+ x instanceof Uint8Array ? x.length :
860
+ x instanceof Blob ? x.size : undefined
861
+ }
862
+
863
+ function deep_copy(x) {
864
+ if (x === null || typeof x !== 'object') return x
865
+ if (Array.isArray(x)) return x.map(x => deep_copy(x))
866
+ if (Object.prototype.toString.call(x) === '[object Object]')
867
+ return Object.fromEntries(Object.entries(x).map(([k, x]) => [k, deep_copy(x)]))
868
+ return x
869
+ }
870
+
871
+ function ascii_ify(s) {
872
+ return s.replace(/[^\x20-\x7E]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'))
873
+ }
874
+
875
+ function create_abort_error(msg) {
876
+ var e = new Error(msg)
877
+ e.name = 'AbortError'
878
+ return e
879
+ }
880
+
881
+ async function promise_done(promise) {
882
+ var pending = {}
883
+ var ret = await Promise.race([promise, Promise.resolve(pending)])
884
+ return ret !== pending
885
+ }
886
+
887
+ function random_base64url(n) {
888
+ return [...crypto.getRandomValues(new Uint8Array(n))].map(x => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'[x % 64]).join('')
889
+ }
890
+
891
+
892
+ // ****************************
893
+ // Multiplexing
894
+ // ****************************
895
+
833
896
  // multiplex_fetch provides a fetch-like experience for HTTP requests
834
897
  // where the result is actually being sent over a separate multiplexed connection.
835
- async function multiplex_fetch(url, params, mux_params) {
898
+ async function multiplex_fetch(url, params, mux_params, aborter) {
836
899
  var multiplex_version = '1.0'
837
900
 
838
901
  var origin = new URL(url, typeof document !== 'undefined' ? document.baseURI : undefined).origin
@@ -894,7 +957,8 @@ async function multiplex_fetch(url, params, mux_params) {
894
957
  })
895
958
  if (r.status === 409) {
896
959
  var e = await r.json()
897
- if (e.error === 'Multiplexer already exists') return cleanup(new Error(e.error))
960
+ if (e.error === 'Multiplexer already exists')
961
+ return cleanup(new Error(e.error))
898
962
  }
899
963
  if (!r.ok || r.headers.get('Multiplex-Version') !== multiplex_version)
900
964
  throw 'bad'
@@ -908,7 +972,8 @@ async function multiplex_fetch(url, params, mux_params) {
908
972
  retry: true})
909
973
  if (r.status === 409) {
910
974
  var e = await r.json()
911
- if (e.error === 'Multiplexer already exists') return cleanup(new Error(e.error))
975
+ if (e.error === 'Multiplexer already exists')
976
+ return cleanup(new Error(e.error))
912
977
  }
913
978
  if (!r.ok) throw new Error('status not ok: ' + r.status)
914
979
  if (r.headers.get('Multiplex-Version') !== multiplex_version)
@@ -919,13 +984,13 @@ async function multiplex_fetch(url, params, mux_params) {
919
984
  // fallback to normal fetch if multiplexed connection fails
920
985
  console.error(`Could not establish multiplexer.\n`
921
986
  + `Got error: ${e}.\nFalling back to normal connection.`)
922
- return cleanup(e, true)
987
+ cleanup(e, true)
988
+ return false
923
989
  }
924
990
  }
925
991
 
926
992
  // parse the multiplexed stream,
927
993
  // and send messages to the appropriate requests
928
- var try_deleting = new Set()
929
994
  parse_multiplex_stream(r.body.getReader(), async (request, bytes) => {
930
995
  if (requests.has(request)) requests.get(request)(bytes)
931
996
  else try_deleting_request(request)
@@ -935,6 +1000,14 @@ async function multiplex_fetch(url, params, mux_params) {
935
1000
  // return a "fetch" for this multiplexer
936
1001
  return async (url, params) => {
937
1002
 
1003
+ // if we already know the multiplexer is not working,
1004
+ // then fallback to normal fetch
1005
+ // (unless the user is specifically asking for multiplexing)
1006
+ if ((await promise_done(mux_promise))
1007
+ && (await mux_promise) === false
1008
+ && !params.headers.get('multiplex-through'))
1009
+ return await normal_fetch(url, params)
1010
+
938
1011
  // make up a new request id (unless it is being overriden)
939
1012
  var request = params.headers.get('multiplex-through')?.split('/')[4]
940
1013
  ?? random_base64url(Math.ceil((mux_params?.id_bits ?? 72) / 6))
@@ -966,8 +1039,10 @@ async function multiplex_fetch(url, params, mux_params) {
966
1039
 
967
1040
  // tell the multiplexer to send bytes for this request to us
968
1041
  requests.set(request, bytes => {
969
- if (!bytes) buffers.push(bytes)
970
- else if (!mux_error) buffers.push(bytes)
1042
+ if (!bytes) {
1043
+ buffers.push(bytes)
1044
+ if (mux_error || request_error) aborter.abort()
1045
+ } else if (!mux_error) buffers.push(bytes)
971
1046
  bytes_available()
972
1047
  })
973
1048
 
@@ -982,8 +1057,6 @@ async function multiplex_fetch(url, params, mux_params) {
982
1057
 
983
1058
  // do the underlying fetch
984
1059
  try {
985
- var mux_was_done = await promise_done(mux_promise)
986
-
987
1060
  var res = await normal_fetch(url, params)
988
1061
 
989
1062
  if (res.status === 409) {
@@ -991,7 +1064,7 @@ async function multiplex_fetch(url, params, mux_params) {
991
1064
  if (e.error === 'Request already multiplexed') throw new Error(e.error)
992
1065
  }
993
1066
 
994
- if (res.status === 424 && !mux_was_done) {
1067
+ if (res.status === 424) {
995
1068
  // this error will trigger a retry if the user is using that option
996
1069
  throw new Error('multiplexer not yet connected')
997
1070
  }
@@ -1062,7 +1135,10 @@ async function multiplex_fetch(url, params, mux_params) {
1062
1135
  try {
1063
1136
  await process_buffers(() => {
1064
1137
  var b = buffers.shift()
1065
- if (!b) return true
1138
+ if (!b) {
1139
+ if (mux_error || request_error) controller.error(mux_error || request_error)
1140
+ return true
1141
+ }
1066
1142
  controller.enqueue(b)
1067
1143
  })
1068
1144
  } finally { controller.close() }
@@ -1081,7 +1157,7 @@ async function multiplex_fetch(url, params, mux_params) {
1081
1157
  } catch (e) {
1082
1158
  // if we had an error, be sure to unregister ourselves
1083
1159
  unset(e)
1084
- throw e
1160
+ throw mux_error || e
1085
1161
  }
1086
1162
  }
1087
1163
  })()
@@ -1169,62 +1245,6 @@ function concat_buffers(buffers) {
1169
1245
  return x
1170
1246
  }
1171
1247
 
1172
- // The "extra_headers" field is returned to the client on any *update* or
1173
- // *patch* to include any headers that we've received, but don't have braid
1174
- // semantics for.
1175
- //
1176
- // This function creates that hash from a headers object, by filtering out all
1177
- // known headers.
1178
- function extra_headers (headers) {
1179
- // Clone headers
1180
- var result = Object.assign({}, headers)
1181
-
1182
- // Remove the non-extra parts
1183
- var known_headers = ['version', 'parents', 'patches',
1184
- 'content-length', 'content-range', ':status']
1185
- for (var i = 0; i < known_headers.length; i++)
1186
- delete result[known_headers[i]]
1187
-
1188
- // Return undefined if we deleted them all
1189
- if (Object.keys(result).length === 0)
1190
- return undefined
1191
-
1192
- return result
1193
- }
1194
-
1195
- function get_binary_length(x) {
1196
- return x instanceof ArrayBuffer ? x.byteLength :
1197
- x instanceof Uint8Array ? x.length :
1198
- x instanceof Blob ? x.size : undefined
1199
- }
1200
-
1201
- function deep_copy(x) {
1202
- if (x === null || typeof x !== 'object') return x
1203
- if (Array.isArray(x)) return x.map(x => deep_copy(x))
1204
- if (Object.prototype.toString.call(x) === '[object Object]')
1205
- return Object.fromEntries(Object.entries(x).map(([k, x]) => [k, deep_copy(x)]))
1206
- return x
1207
- }
1208
-
1209
- function ascii_ify(s) {
1210
- return s.replace(/[^\x20-\x7E]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'))
1211
- }
1212
-
1213
- function create_abort_error(msg) {
1214
- var e = new Error(msg)
1215
- e.name = 'AbortError'
1216
- return e
1217
- }
1218
-
1219
- async function promise_done(promise) {
1220
- var pending = {}
1221
- var ret = await Promise.race([promise, Promise.resolve(pending)])
1222
- return ret !== pending
1223
- }
1224
-
1225
- function random_base64url(n) {
1226
- return [...crypto.getRandomValues(new Uint8Array(n))].map(x => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'[x % 64]).join('')
1227
- }
1228
1248
 
1229
1249
  // ****************************
1230
1250
  // Exports
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-http",
3
- "version": "1.3.48",
3
+ "version": "1.3.50",
4
4
  "description": "An implementation of Braid-HTTP for Node.js and Browsers",
5
5
  "scripts": {
6
6
  "test": "node test/server.js"