braid-http 1.3.49 → 1.3.51
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/braid-http-client.js +118 -93
- package/package.json +1 -1
package/braid-http-client.js
CHANGED
|
@@ -266,9 +266,13 @@ async function braid_fetch (url, params = {}) {
|
|
|
266
266
|
// see if we should retry..
|
|
267
267
|
var retry = params.retry && // only try to reconnect if the user has chosen to
|
|
268
268
|
e.name !== "AbortError" && // don't retry if the user has chosen to abort
|
|
269
|
-
!e.
|
|
270
|
-
!
|
|
271
|
-
|
|
269
|
+
!e.dont_retry && // some errors are unlikely to be fixed by retrying
|
|
270
|
+
!cb_running // if an error is thrown in the callback,
|
|
271
|
+
// then it may not be good to reconnect, and generate more errors
|
|
272
|
+
|
|
273
|
+
// some errors can be fixed by changing something, and retrying right away,
|
|
274
|
+
// for instance, for a duplicate multiplexer request id
|
|
275
|
+
if (e.dont_wait) waitTime = 0
|
|
272
276
|
|
|
273
277
|
if (retry && !original_signal?.aborted) {
|
|
274
278
|
// retry after some time..
|
|
@@ -278,7 +282,7 @@ async function braid_fetch (url, params = {}) {
|
|
|
278
282
|
} else {
|
|
279
283
|
// if we would have retried except that original_signal?.aborted,
|
|
280
284
|
// then we want to return that as the error..
|
|
281
|
-
if (retry && original_signal?.aborted) e =
|
|
285
|
+
if (retry && original_signal?.aborted) e = create_error('already aborted', {name: 'AbortError'})
|
|
282
286
|
|
|
283
287
|
// let people know things are shutting down..
|
|
284
288
|
subscription_counts_on_close?.()
|
|
@@ -288,7 +292,7 @@ async function braid_fetch (url, params = {}) {
|
|
|
288
292
|
}
|
|
289
293
|
|
|
290
294
|
try {
|
|
291
|
-
if (original_signal?.aborted) throw
|
|
295
|
+
if (original_signal?.aborted) throw create_error('already aborted', {name: 'AbortError'})
|
|
292
296
|
|
|
293
297
|
// We need a fresh underlying abort controller each time we connect
|
|
294
298
|
underlying_aborter = new AbortController()
|
|
@@ -304,7 +308,7 @@ async function braid_fetch (url, params = {}) {
|
|
|
304
308
|
// undocumented feature used by braid-chrome
|
|
305
309
|
// to see the fetch args as they are right before it is actually called,
|
|
306
310
|
// to display them for the user in the dev panel
|
|
307
|
-
params.onFetch?.(url, params)
|
|
311
|
+
params.onFetch?.(url, params, underlying_aborter)
|
|
308
312
|
|
|
309
313
|
// Now we run the original fetch....
|
|
310
314
|
|
|
@@ -315,7 +319,7 @@ async function braid_fetch (url, params = {}) {
|
|
|
315
319
|
(params.headers.has('subscribe') &&
|
|
316
320
|
braid_fetch.subscription_counts?.[origin] >
|
|
317
321
|
(!mux_params ? 1 : (mux_params.after ?? 0))))) {
|
|
318
|
-
res = await multiplex_fetch(url, params, mux_params)
|
|
322
|
+
res = await multiplex_fetch(url, params, mux_params, underlying_aborter)
|
|
319
323
|
} else
|
|
320
324
|
res = await normal_fetch(url, params)
|
|
321
325
|
|
|
@@ -363,7 +367,7 @@ async function braid_fetch (url, params = {}) {
|
|
|
363
367
|
if (!err) {
|
|
364
368
|
// check whether we aborted
|
|
365
369
|
if (original_signal?.aborted)
|
|
366
|
-
throw
|
|
370
|
+
throw create_error('already aborted', {name: 'AbortError'})
|
|
367
371
|
|
|
368
372
|
// Yay! We got a new version! Tell the callback!
|
|
369
373
|
cb_running = true
|
|
@@ -442,19 +446,16 @@ async function braid_fetch (url, params = {}) {
|
|
|
442
446
|
give_up = false
|
|
443
447
|
}
|
|
444
448
|
if (give_up) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
return fail(e)
|
|
448
|
-
}
|
|
449
|
-
if (!res.ok) throw new Error(`status not ok: ${res.status}`)
|
|
449
|
+
if (subscription_cb) subscription_error?.(new Error(`giving up because of http status: ${res.status}${(res.status === 401 || res.status === 403) ? ` (access denied)` : ''}`))
|
|
450
|
+
} else if (!res.ok) throw new Error(`status not ok: ${res.status}`)
|
|
450
451
|
}
|
|
451
452
|
|
|
452
|
-
if (subscription_cb) start_subscription(subscription_cb, subscription_error)
|
|
453
|
-
|
|
454
|
-
done(res)
|
|
453
|
+
if (subscription_cb && res.ok) start_subscription(subscription_cb, subscription_error)
|
|
455
454
|
|
|
456
455
|
params?.retry?.onRes?.(res)
|
|
457
456
|
waitTime = 1
|
|
457
|
+
|
|
458
|
+
done(res)
|
|
458
459
|
} catch (e) { on_error(e) }
|
|
459
460
|
}
|
|
460
461
|
})
|
|
@@ -566,7 +567,7 @@ var subscription_parser = (cb) => ({
|
|
|
566
567
|
|
|
567
568
|
// Or maybe there's an error to report upstream
|
|
568
569
|
else if (this.state.result === 'error') {
|
|
569
|
-
await this.cb(null, this.state.message)
|
|
570
|
+
await this.cb(null, create_error(this.state.message, {dont_retry: true}))
|
|
570
571
|
return
|
|
571
572
|
}
|
|
572
573
|
|
|
@@ -830,9 +831,72 @@ function parse_body (state) {
|
|
|
830
831
|
}
|
|
831
832
|
}
|
|
832
833
|
|
|
834
|
+
|
|
835
|
+
// The "extra_headers" field is returned to the client on any *update* or
|
|
836
|
+
// *patch* to include any headers that we've received, but don't have braid
|
|
837
|
+
// semantics for.
|
|
838
|
+
//
|
|
839
|
+
// This function creates that hash from a headers object, by filtering out all
|
|
840
|
+
// known headers.
|
|
841
|
+
function extra_headers (headers) {
|
|
842
|
+
// Clone headers
|
|
843
|
+
var result = Object.assign({}, headers)
|
|
844
|
+
|
|
845
|
+
// Remove the non-extra parts
|
|
846
|
+
var known_headers = ['version', 'parents', 'patches',
|
|
847
|
+
'content-length', 'content-range', ':status']
|
|
848
|
+
for (var i = 0; i < known_headers.length; i++)
|
|
849
|
+
delete result[known_headers[i]]
|
|
850
|
+
|
|
851
|
+
// Return undefined if we deleted them all
|
|
852
|
+
if (Object.keys(result).length === 0)
|
|
853
|
+
return undefined
|
|
854
|
+
|
|
855
|
+
return result
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function get_binary_length(x) {
|
|
859
|
+
return x instanceof ArrayBuffer ? x.byteLength :
|
|
860
|
+
x instanceof Uint8Array ? x.length :
|
|
861
|
+
x instanceof Blob ? x.size : undefined
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function deep_copy(x) {
|
|
865
|
+
if (x === null || typeof x !== 'object') return x
|
|
866
|
+
if (Array.isArray(x)) return x.map(x => deep_copy(x))
|
|
867
|
+
if (Object.prototype.toString.call(x) === '[object Object]')
|
|
868
|
+
return Object.fromEntries(Object.entries(x).map(([k, x]) => [k, deep_copy(x)]))
|
|
869
|
+
return x
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function ascii_ify(s) {
|
|
873
|
+
return s.replace(/[^\x20-\x7E]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'))
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function create_error(msg, override) {
|
|
877
|
+
var e = new Error(msg)
|
|
878
|
+
if (override) Object.assign(e, override)
|
|
879
|
+
return e
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
async function promise_done(promise) {
|
|
883
|
+
var pending = {}
|
|
884
|
+
var ret = await Promise.race([promise, Promise.resolve(pending)])
|
|
885
|
+
return ret !== pending
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function random_base64url(n) {
|
|
889
|
+
return [...crypto.getRandomValues(new Uint8Array(n))].map(x => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'[x % 64]).join('')
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
// ****************************
|
|
894
|
+
// Multiplexing
|
|
895
|
+
// ****************************
|
|
896
|
+
|
|
833
897
|
// multiplex_fetch provides a fetch-like experience for HTTP requests
|
|
834
898
|
// where the result is actually being sent over a separate multiplexed connection.
|
|
835
|
-
async function multiplex_fetch(url, params, mux_params) {
|
|
899
|
+
async function multiplex_fetch(url, params, mux_params, aborter) {
|
|
836
900
|
var multiplex_version = '1.0'
|
|
837
901
|
|
|
838
902
|
var origin = new URL(url, typeof document !== 'undefined' ? document.baseURI : undefined).origin
|
|
@@ -894,7 +958,8 @@ async function multiplex_fetch(url, params, mux_params) {
|
|
|
894
958
|
})
|
|
895
959
|
if (r.status === 409) {
|
|
896
960
|
var e = await r.json()
|
|
897
|
-
if (e.error === 'Multiplexer already exists')
|
|
961
|
+
if (e.error === 'Multiplexer already exists')
|
|
962
|
+
return cleanup(create_error(e.error, {dont_wait: true}))
|
|
898
963
|
}
|
|
899
964
|
if (!r.ok || r.headers.get('Multiplex-Version') !== multiplex_version)
|
|
900
965
|
throw 'bad'
|
|
@@ -908,7 +973,8 @@ async function multiplex_fetch(url, params, mux_params) {
|
|
|
908
973
|
retry: true})
|
|
909
974
|
if (r.status === 409) {
|
|
910
975
|
var e = await r.json()
|
|
911
|
-
if (e.error === 'Multiplexer already exists')
|
|
976
|
+
if (e.error === 'Multiplexer already exists')
|
|
977
|
+
return cleanup(create_error(e.error, {dont_wait: true}))
|
|
912
978
|
}
|
|
913
979
|
if (!r.ok) throw new Error('status not ok: ' + r.status)
|
|
914
980
|
if (r.headers.get('Multiplex-Version') !== multiplex_version)
|
|
@@ -938,7 +1004,9 @@ async function multiplex_fetch(url, params, mux_params) {
|
|
|
938
1004
|
// if we already know the multiplexer is not working,
|
|
939
1005
|
// then fallback to normal fetch
|
|
940
1006
|
// (unless the user is specifically asking for multiplexing)
|
|
941
|
-
if ((await promise_done(mux_promise))
|
|
1007
|
+
if ((await promise_done(mux_promise))
|
|
1008
|
+
&& (await mux_promise) === false
|
|
1009
|
+
&& !params.headers.get('multiplex-through'))
|
|
942
1010
|
return await normal_fetch(url, params)
|
|
943
1011
|
|
|
944
1012
|
// make up a new request id (unless it is being overriden)
|
|
@@ -972,8 +1040,10 @@ async function multiplex_fetch(url, params, mux_params) {
|
|
|
972
1040
|
|
|
973
1041
|
// tell the multiplexer to send bytes for this request to us
|
|
974
1042
|
requests.set(request, bytes => {
|
|
975
|
-
if (!bytes)
|
|
976
|
-
|
|
1043
|
+
if (!bytes) {
|
|
1044
|
+
buffers.push(bytes)
|
|
1045
|
+
if (mux_error || request_error) aborter.abort()
|
|
1046
|
+
} else if (!mux_error) buffers.push(bytes)
|
|
977
1047
|
bytes_available()
|
|
978
1048
|
})
|
|
979
1049
|
|
|
@@ -991,13 +1061,20 @@ async function multiplex_fetch(url, params, mux_params) {
|
|
|
991
1061
|
var res = await normal_fetch(url, params)
|
|
992
1062
|
|
|
993
1063
|
if (res.status === 409) {
|
|
994
|
-
var e = await
|
|
995
|
-
if (e.error === 'Request already multiplexed')
|
|
1064
|
+
var e = await res.json()
|
|
1065
|
+
if (e.error === 'Request already multiplexed') {
|
|
1066
|
+
// the id is already in use,
|
|
1067
|
+
// so we want to retry right away with a different id
|
|
1068
|
+
throw create_error(e.error, {dont_wait: true})
|
|
1069
|
+
}
|
|
996
1070
|
}
|
|
997
1071
|
|
|
998
1072
|
if (res.status === 424) {
|
|
999
|
-
//
|
|
1000
|
-
|
|
1073
|
+
// the multiplexer isn't there,
|
|
1074
|
+
// could be we arrived before the multiplexer,
|
|
1075
|
+
// or after it was shutdown;
|
|
1076
|
+
// in either case we want to retry right away
|
|
1077
|
+
throw create_error('multiplexer not connected', {dont_wait: true})
|
|
1001
1078
|
}
|
|
1002
1079
|
|
|
1003
1080
|
// if the response says it's ok,
|
|
@@ -1006,21 +1083,23 @@ async function multiplex_fetch(url, params, mux_params) {
|
|
|
1006
1083
|
if (res.ok && res.status !== 293) return res
|
|
1007
1084
|
|
|
1008
1085
|
if (res.status !== 293)
|
|
1009
|
-
throw
|
|
1010
|
-
|
|
1011
|
-
|
|
1086
|
+
throw create_error('Could not establish multiplexed request '
|
|
1087
|
+
+ params.headers.get('multiplex-through')
|
|
1088
|
+
+ ', got status: ' + res.status,
|
|
1089
|
+
{ dont_retry: true })
|
|
1012
1090
|
|
|
1013
1091
|
if (res.headers.get('Multiplex-Version') !== multiplex_version)
|
|
1014
|
-
throw
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1092
|
+
throw create_error('Could not establish multiplexed request '
|
|
1093
|
+
+ params.headers.get('multiplex-through')
|
|
1094
|
+
+ ', got unknown version: '
|
|
1095
|
+
+ res.headers.get('Multiplex-Version'),
|
|
1096
|
+
{ dont_retry: true })
|
|
1018
1097
|
|
|
1019
1098
|
// we want to present the illusion that the connection is still open,
|
|
1020
1099
|
// and therefor closable with "abort",
|
|
1021
1100
|
// so we handle the abort ourselves to close the multiplexed request
|
|
1022
|
-
params.signal
|
|
1023
|
-
unset(
|
|
1101
|
+
params.signal.addEventListener('abort', () =>
|
|
1102
|
+
unset(create_error('request aborted', {name: 'AbortError'})))
|
|
1024
1103
|
|
|
1025
1104
|
// first, we need to listen for the headers..
|
|
1026
1105
|
var headers_buffer = new Uint8Array()
|
|
@@ -1069,6 +1148,8 @@ async function multiplex_fetch(url, params, mux_params) {
|
|
|
1069
1148
|
if (!b) return true
|
|
1070
1149
|
controller.enqueue(b)
|
|
1071
1150
|
})
|
|
1151
|
+
} catch (e) {
|
|
1152
|
+
controller.error(e)
|
|
1072
1153
|
} finally { controller.close() }
|
|
1073
1154
|
}
|
|
1074
1155
|
}), {
|
|
@@ -1085,7 +1166,7 @@ async function multiplex_fetch(url, params, mux_params) {
|
|
|
1085
1166
|
} catch (e) {
|
|
1086
1167
|
// if we had an error, be sure to unregister ourselves
|
|
1087
1168
|
unset(e)
|
|
1088
|
-
throw e
|
|
1169
|
+
throw mux_error || e
|
|
1089
1170
|
}
|
|
1090
1171
|
}
|
|
1091
1172
|
})()
|
|
@@ -1173,62 +1254,6 @@ function concat_buffers(buffers) {
|
|
|
1173
1254
|
return x
|
|
1174
1255
|
}
|
|
1175
1256
|
|
|
1176
|
-
// The "extra_headers" field is returned to the client on any *update* or
|
|
1177
|
-
// *patch* to include any headers that we've received, but don't have braid
|
|
1178
|
-
// semantics for.
|
|
1179
|
-
//
|
|
1180
|
-
// This function creates that hash from a headers object, by filtering out all
|
|
1181
|
-
// known headers.
|
|
1182
|
-
function extra_headers (headers) {
|
|
1183
|
-
// Clone headers
|
|
1184
|
-
var result = Object.assign({}, headers)
|
|
1185
|
-
|
|
1186
|
-
// Remove the non-extra parts
|
|
1187
|
-
var known_headers = ['version', 'parents', 'patches',
|
|
1188
|
-
'content-length', 'content-range', ':status']
|
|
1189
|
-
for (var i = 0; i < known_headers.length; i++)
|
|
1190
|
-
delete result[known_headers[i]]
|
|
1191
|
-
|
|
1192
|
-
// Return undefined if we deleted them all
|
|
1193
|
-
if (Object.keys(result).length === 0)
|
|
1194
|
-
return undefined
|
|
1195
|
-
|
|
1196
|
-
return result
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
function get_binary_length(x) {
|
|
1200
|
-
return x instanceof ArrayBuffer ? x.byteLength :
|
|
1201
|
-
x instanceof Uint8Array ? x.length :
|
|
1202
|
-
x instanceof Blob ? x.size : undefined
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
function deep_copy(x) {
|
|
1206
|
-
if (x === null || typeof x !== 'object') return x
|
|
1207
|
-
if (Array.isArray(x)) return x.map(x => deep_copy(x))
|
|
1208
|
-
if (Object.prototype.toString.call(x) === '[object Object]')
|
|
1209
|
-
return Object.fromEntries(Object.entries(x).map(([k, x]) => [k, deep_copy(x)]))
|
|
1210
|
-
return x
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
function ascii_ify(s) {
|
|
1214
|
-
return s.replace(/[^\x20-\x7E]/g, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'))
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
function create_abort_error(msg) {
|
|
1218
|
-
var e = new Error(msg)
|
|
1219
|
-
e.name = 'AbortError'
|
|
1220
|
-
return e
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
async function promise_done(promise) {
|
|
1224
|
-
var pending = {}
|
|
1225
|
-
var ret = await Promise.race([promise, Promise.resolve(pending)])
|
|
1226
|
-
return ret !== pending
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
function random_base64url(n) {
|
|
1230
|
-
return [...crypto.getRandomValues(new Uint8Array(n))].map(x => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'[x % 64]).join('')
|
|
1231
|
-
}
|
|
1232
1257
|
|
|
1233
1258
|
// ****************************
|
|
1234
1259
|
// Exports
|