braid-http 1.3.40 → 1.3.42
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 +55 -43
- package/braid-http-server.js +27 -25
- package/package.json +1 -1
package/braid-http-client.js
CHANGED
|
@@ -265,7 +265,7 @@ async function braid_fetch (url, params = {}) {
|
|
|
265
265
|
var retry = params.retry && // only try to reconnect if the user has chosen to
|
|
266
266
|
e.name !== "AbortError" && // don't retry if the user has chosen to abort
|
|
267
267
|
!e.startsWith?.('Parse error in headers') && // in this case, the server is spewing garbage, so reconnecting might be bad
|
|
268
|
-
!e.message?.startsWith?.('Could not establish multiplexed
|
|
268
|
+
!e.message?.startsWith?.('Could not establish multiplexed request') && // the server has told us no, or is using a different version of multiplexing
|
|
269
269
|
!cb_running // if an error is thrown in the callback, then it may not be good to reconnect, and generate more errors
|
|
270
270
|
|
|
271
271
|
if (retry && !original_signal?.aborted) {
|
|
@@ -842,7 +842,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
842
842
|
// make up a new multiplexer id (unless it is being overriden)
|
|
843
843
|
var multiplexer = params.headers.get('multiplex-at')?.split('/')[3] ?? Math.random().toString(36).slice(2)
|
|
844
844
|
|
|
845
|
-
var
|
|
845
|
+
var requests = new Map()
|
|
846
846
|
var mux_error = null
|
|
847
847
|
|
|
848
848
|
var mux_promise = (async () => {
|
|
@@ -861,19 +861,30 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
861
861
|
if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
|
|
862
862
|
} catch (e) {
|
|
863
863
|
// fallback to normal fetch if multiplexed connection fails
|
|
864
|
-
console.error(`Could not establish
|
|
864
|
+
console.error(`Could not establish multiplexer.\nGot error: ${e}.\nFalling back to normal connection.`)
|
|
865
865
|
return false
|
|
866
866
|
}
|
|
867
867
|
}
|
|
868
868
|
|
|
869
869
|
// parse the multiplexed stream,
|
|
870
|
-
// and send messages to the appropriate
|
|
871
|
-
|
|
872
|
-
|
|
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)
|
|
877
|
+
try {
|
|
878
|
+
await braid_fetch(`${origin}/.well-known/multiplex/${multiplexer}/${request}`, {
|
|
879
|
+
method: 'DELETE',
|
|
880
|
+
headers: { 'Multiplex-Version': multiplex_version }, retry: true
|
|
881
|
+
})
|
|
882
|
+
} finally { try_deleting.delete(request) }
|
|
883
|
+
}
|
|
873
884
|
}, e => {
|
|
874
885
|
// the multiplexer stream has died.. let everyone know..
|
|
875
886
|
mux_error = e
|
|
876
|
-
for (var f of
|
|
887
|
+
for (var f of requests.values()) f()
|
|
877
888
|
delete multiplex_fetch.multiplexers[mux_key]
|
|
878
889
|
})
|
|
879
890
|
})()
|
|
@@ -887,27 +898,27 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
887
898
|
if ((await promise_done(mux_promise)) && (await mux_promise) === false && !params.headers.get('multiplex-at'))
|
|
888
899
|
return await normal_fetch(url, params)
|
|
889
900
|
|
|
890
|
-
// make up a new
|
|
891
|
-
var
|
|
901
|
+
// make up a new request id (unless it is being overriden)
|
|
902
|
+
var request = params.headers.get('multiplex-at')?.split('/')[4] ?? Math.random().toString(36).slice(2)
|
|
892
903
|
|
|
893
904
|
// add the Multiplex-At header without affecting the underlying params
|
|
894
905
|
var mux_headers = new Headers(params.headers)
|
|
895
|
-
mux_headers.set('Multiplex-At', `/.well-known/multiplex/${multiplexer}/${
|
|
906
|
+
mux_headers.set('Multiplex-At', `/.well-known/multiplex/${multiplexer}/${request}`)
|
|
896
907
|
mux_headers.set('Multiplex-Version', multiplex_version)
|
|
897
908
|
params = {...params, headers: mux_headers}
|
|
898
909
|
|
|
899
910
|
// setup a way to receive incoming data from the multiplexer
|
|
900
911
|
var buffers = []
|
|
901
912
|
var bytes_available = () => {}
|
|
902
|
-
var
|
|
913
|
+
var request_error = null
|
|
903
914
|
|
|
904
915
|
// this utility calls the callback whenever new data is available to process
|
|
905
916
|
async function process_buffers(cb) {
|
|
906
917
|
while (true) {
|
|
907
918
|
// wait for data if none is available
|
|
908
|
-
if (!mux_error && !
|
|
919
|
+
if (!mux_error && !request_error && !buffers.length)
|
|
909
920
|
await new Promise(done => bytes_available = done)
|
|
910
|
-
if (mux_error ||
|
|
921
|
+
if (mux_error || request_error) throw (mux_error || request_error)
|
|
911
922
|
|
|
912
923
|
// process the data
|
|
913
924
|
let ret = cb()
|
|
@@ -915,20 +926,18 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
915
926
|
}
|
|
916
927
|
}
|
|
917
928
|
|
|
918
|
-
// tell the multiplexer to send bytes for this
|
|
919
|
-
|
|
920
|
-
if (!bytes)
|
|
921
|
-
|
|
922
|
-
buffers.push(bytes)
|
|
923
|
-
} else if (!mux_error) buffers.push(bytes)
|
|
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)
|
|
924
933
|
bytes_available()
|
|
925
934
|
})
|
|
926
935
|
|
|
927
936
|
// prepare a function that we'll call to cleanly tear things down
|
|
928
937
|
var unset = async e => {
|
|
929
938
|
unset = () => {}
|
|
930
|
-
|
|
931
|
-
|
|
939
|
+
requests.delete(request)
|
|
940
|
+
request_error = e
|
|
932
941
|
bytes_available()
|
|
933
942
|
try {
|
|
934
943
|
var r = await braid_fetch(`${origin}${params.headers.get('multiplex-at')}`, {
|
|
@@ -939,7 +948,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
939
948
|
if (!r.ok) throw new Error('status not ok: ' + r.status)
|
|
940
949
|
if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
|
|
941
950
|
} catch (e) {
|
|
942
|
-
e = new Error(`Could not cancel multiplexed
|
|
951
|
+
e = new Error(`Could not cancel multiplexed request: ${e}`)
|
|
943
952
|
console.error('' + e)
|
|
944
953
|
throw e
|
|
945
954
|
}
|
|
@@ -947,9 +956,11 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
947
956
|
|
|
948
957
|
// do the underlying fetch
|
|
949
958
|
try {
|
|
959
|
+
var mux_was_done = await promise_done(mux_promise)
|
|
960
|
+
|
|
950
961
|
var res = await normal_fetch(url, params)
|
|
951
962
|
|
|
952
|
-
if (res.status === 424 && !
|
|
963
|
+
if (res.status === 424 && !mux_was_done) {
|
|
953
964
|
// this error will trigger a retry if the user is using that option
|
|
954
965
|
throw new Error('multiplexer not yet connected')
|
|
955
966
|
}
|
|
@@ -959,29 +970,29 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
959
970
|
// fall back to as if it was a normal fetch
|
|
960
971
|
if (res.ok && res.status !== 293) return res
|
|
961
972
|
|
|
962
|
-
if (res.status !== 293) throw new Error('Could not establish multiplexed
|
|
973
|
+
if (res.status !== 293) throw new Error('Could not establish multiplexed request ' + params.headers.get('multiplex-at') + ', got status: ' + res.status)
|
|
963
974
|
|
|
964
|
-
if (res.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('Could not establish multiplexed
|
|
975
|
+
if (res.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('Could not establish multiplexed request ' + params.headers.get('multiplex-at') + ', got unknown version: ' + res.headers.get('Multiplex-Version'))
|
|
965
976
|
|
|
966
977
|
// we want to present the illusion that the connection is still open,
|
|
967
978
|
// and therefor closable with "abort",
|
|
968
|
-
// so we handle the abort ourselves to close the multiplexed
|
|
979
|
+
// so we handle the abort ourselves to close the multiplexed request
|
|
969
980
|
params.signal?.addEventListener('abort', () =>
|
|
970
|
-
unset(create_abort_error('
|
|
981
|
+
unset(create_abort_error('request aborted')))
|
|
971
982
|
|
|
972
983
|
// first, we need to listen for the headers..
|
|
973
984
|
var headers_buffer = new Uint8Array()
|
|
974
985
|
var parsed_headers = await process_buffers(() => {
|
|
975
|
-
// check if the
|
|
976
|
-
var
|
|
977
|
-
if (
|
|
986
|
+
// check if the request has been closed
|
|
987
|
+
var request_ended = !buffers[buffers.length - 1]
|
|
988
|
+
if (request_ended) buffers.pop()
|
|
978
989
|
|
|
979
990
|
// aggregate all the new buffers into our big headers_buffer
|
|
980
991
|
headers_buffer = concat_buffers([headers_buffer, ...buffers])
|
|
981
992
|
buffers = []
|
|
982
993
|
|
|
983
|
-
// and if the
|
|
984
|
-
if (
|
|
994
|
+
// and if the request had ended, put that information back
|
|
995
|
+
if (request_ended) buffers.push(null)
|
|
985
996
|
|
|
986
997
|
// try parsing what we got so far as headers..
|
|
987
998
|
var x = parse_headers(headers_buffer)
|
|
@@ -992,7 +1003,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
992
1003
|
console.log(`headers_buffer: ` + new TextDecoder().decode(headers_buffer))
|
|
993
1004
|
throw new Error('error parsing headers')
|
|
994
1005
|
} else if (x.result === 'waiting') {
|
|
995
|
-
if (
|
|
1006
|
+
if (request_ended) throw new Error('Multiplexed request ended before headers received.')
|
|
996
1007
|
} else return x
|
|
997
1008
|
})
|
|
998
1009
|
|
|
@@ -1006,7 +1017,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
1006
1017
|
|
|
1007
1018
|
// create our own fake response object,
|
|
1008
1019
|
// to mimik fetch's response object,
|
|
1009
|
-
// feeding the user our
|
|
1020
|
+
// feeding the user our request data from the multiplexer
|
|
1010
1021
|
var res = new Response(new ReadableStream({
|
|
1011
1022
|
async start(controller) {
|
|
1012
1023
|
try {
|
|
@@ -1047,7 +1058,7 @@ async function parse_multiplex_stream(reader, cb, on_error) {
|
|
|
1047
1058
|
var buffers = [new Uint8Array(0)]
|
|
1048
1059
|
var buffers_size = 0
|
|
1049
1060
|
var chunk_size = null
|
|
1050
|
-
var
|
|
1061
|
+
var request_id = null
|
|
1051
1062
|
var header_length = 0
|
|
1052
1063
|
var header_started = false
|
|
1053
1064
|
|
|
@@ -1074,16 +1085,18 @@ async function parse_multiplex_stream(reader, cb, on_error) {
|
|
|
1074
1085
|
}
|
|
1075
1086
|
if (headerComplete) {
|
|
1076
1087
|
var headerStr = new TextDecoder().decode(buffers[0].slice(0, header_length))
|
|
1077
|
-
var m = headerStr.match(/^[\r\n]*((\d+) bytes for|close) request ([A-Za-z0-9_-]+)\r\n$/)
|
|
1088
|
+
var m = headerStr.match(/^[\r\n]*((\d+) bytes for|close|start) request ([A-Za-z0-9_-]+)\r\n$/)
|
|
1089
|
+
|
|
1078
1090
|
if (!m) throw new Error('invalid multiplex header')
|
|
1079
|
-
|
|
1091
|
+
request_id = m[3]
|
|
1080
1092
|
|
|
1081
1093
|
buffers[0] = buffers[0].slice(header_length)
|
|
1082
1094
|
buffers_size -= header_length
|
|
1083
1095
|
|
|
1084
|
-
if (m[1] === 'close') {
|
|
1085
|
-
cb(
|
|
1086
|
-
|
|
1096
|
+
if (m[1] === 'close' || m[1] === 'start') {
|
|
1097
|
+
cb(request_id, m[1] === 'start' ? new Uint8Array() : undefined)
|
|
1098
|
+
header_length = 0
|
|
1099
|
+
header_started = false
|
|
1087
1100
|
} else chunk_size = 1 * m[2]
|
|
1088
1101
|
} else break
|
|
1089
1102
|
} else if (chunk_size !== null && buffers_size >= chunk_size) {
|
|
@@ -1093,12 +1106,11 @@ async function parse_multiplex_stream(reader, cb, on_error) {
|
|
|
1093
1106
|
buffers[0] = buffers[0].slice(chunk_size)
|
|
1094
1107
|
buffers_size -= chunk_size
|
|
1095
1108
|
|
|
1096
|
-
// console.log(`
|
|
1109
|
+
// console.log(`request_id: ${request_id}, ${new TextDecoder().decode(chunk)}`)
|
|
1097
1110
|
|
|
1098
|
-
cb(
|
|
1111
|
+
cb(request_id, chunk)
|
|
1099
1112
|
|
|
1100
1113
|
chunk_size = null
|
|
1101
|
-
stream_id = null
|
|
1102
1114
|
header_length = 0
|
|
1103
1115
|
header_started = false
|
|
1104
1116
|
} else break
|
package/braid-http-server.js
CHANGED
|
@@ -256,11 +256,11 @@ function braidify (req, res, next) {
|
|
|
256
256
|
res.setHeader("Access-Control-Allow-Methods", "*")
|
|
257
257
|
res.setHeader("Access-Control-Allow-Headers", "*")
|
|
258
258
|
|
|
259
|
-
// parse the multiplexer id and
|
|
260
|
-
var [multiplexer,
|
|
259
|
+
// parse the multiplexer id and request id from the url
|
|
260
|
+
var [multiplexer, request] = req.url.split('/').slice(req.method === 'MULTIPLEX' ? 1 : 3)
|
|
261
261
|
|
|
262
262
|
// if there's just a multiplexer, then we're creating a multiplexer..
|
|
263
|
-
if (!
|
|
263
|
+
if (!request) {
|
|
264
264
|
// maintain a Map of all the multiplexers
|
|
265
265
|
if (!braidify.multiplexers) braidify.multiplexers = new Map()
|
|
266
266
|
|
|
@@ -274,12 +274,12 @@ function braidify (req, res, next) {
|
|
|
274
274
|
}))
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
braidify.multiplexers.set(multiplexer, {
|
|
277
|
+
braidify.multiplexers.set(multiplexer, {requests: new Map(), res})
|
|
278
278
|
|
|
279
279
|
// when the response closes,
|
|
280
280
|
// let everyone know the multiplexer has died
|
|
281
281
|
res.on('close', () => {
|
|
282
|
-
for (var f of braidify.multiplexers.get(multiplexer).
|
|
282
|
+
for (var f of braidify.multiplexers.get(multiplexer).requests.values()) f()
|
|
283
283
|
braidify.multiplexers.delete(multiplexer)
|
|
284
284
|
})
|
|
285
285
|
|
|
@@ -293,11 +293,11 @@ function braidify (req, res, next) {
|
|
|
293
293
|
...req.httpVersion !== '2.0' && {'Connection': 'keep-alive'}
|
|
294
294
|
})
|
|
295
295
|
|
|
296
|
-
// but write something.. won't interfere with
|
|
296
|
+
// but write something.. won't interfere with multiplexer,
|
|
297
297
|
// and helps flush the headers
|
|
298
298
|
return res.write(`\r\n`)
|
|
299
299
|
} else {
|
|
300
|
-
// in this case, we're closing the given
|
|
300
|
+
// in this case, we're closing the given request
|
|
301
301
|
|
|
302
302
|
// if the multiplexer doesn't exist, send an error
|
|
303
303
|
var m = braidify.multiplexers?.get(multiplexer)
|
|
@@ -306,15 +306,15 @@ function braidify (req, res, next) {
|
|
|
306
306
|
return res.end(`multiplexer ${multiplexer} does not exist`)
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
// if the
|
|
310
|
-
let s = m.
|
|
309
|
+
// if the request doesn't exist, send an error
|
|
310
|
+
let s = m.requests.get(request)
|
|
311
311
|
if (!s) {
|
|
312
|
-
res.writeHead(404, '
|
|
313
|
-
return res.end(`
|
|
312
|
+
res.writeHead(404, 'Multiplexed request not found', {'Bad-Request': request})
|
|
313
|
+
return res.end(`request ${request} does not exist`)
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
// remove this
|
|
317
|
-
m.
|
|
316
|
+
// remove this request, and notify it
|
|
317
|
+
m.requests.delete(request)
|
|
318
318
|
s()
|
|
319
319
|
|
|
320
320
|
// let the requester know we succeeded
|
|
@@ -323,15 +323,15 @@ function braidify (req, res, next) {
|
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
// a
|
|
326
|
+
// a Multiplex-At header means the user wants to send the
|
|
327
327
|
// results of this request to the provided multiplexer,
|
|
328
|
-
// tagged with the given
|
|
328
|
+
// tagged with the given request id
|
|
329
329
|
if ((braidify.enable_multiplex ?? true) &&
|
|
330
330
|
req.headers['multiplex-at'] &&
|
|
331
331
|
req.headers['multiplex-version'] === multiplex_version) {
|
|
332
332
|
|
|
333
|
-
// parse the multiplexer id and
|
|
334
|
-
var [multiplexer,
|
|
333
|
+
// parse the multiplexer id and request id from the header
|
|
334
|
+
var [multiplexer, request] = req.headers['multiplex-at'].split('/').slice(3)
|
|
335
335
|
|
|
336
336
|
// find the multiplexer object (contains a response object)
|
|
337
337
|
var m = braidify.multiplexers?.get(multiplexer)
|
|
@@ -340,6 +340,8 @@ function braidify (req, res, next) {
|
|
|
340
340
|
return res.end(`multiplexer ${multiplexer} does not exist`)
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
m.res.write(`start request ${request}\r\n`)
|
|
344
|
+
|
|
343
345
|
// let the requester know we've multiplexed their response
|
|
344
346
|
var og_stream = res.stream
|
|
345
347
|
var og_socket = res.socket
|
|
@@ -384,10 +386,10 @@ function braidify (req, res, next) {
|
|
|
384
386
|
|
|
385
387
|
// first we create a kind of fake socket
|
|
386
388
|
class MultiplexedWritable extends require('stream').Writable {
|
|
387
|
-
constructor(multiplexer,
|
|
389
|
+
constructor(multiplexer, request) {
|
|
388
390
|
super()
|
|
389
391
|
this.multiplexer = multiplexer
|
|
390
|
-
this.
|
|
392
|
+
this.request = request
|
|
391
393
|
}
|
|
392
394
|
|
|
393
395
|
_write(chunk, encoding, callback) {
|
|
@@ -395,11 +397,11 @@ function braidify (req, res, next) {
|
|
|
395
397
|
|
|
396
398
|
try {
|
|
397
399
|
var len = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk, encoding)
|
|
398
|
-
this.multiplexer.res.write(`${len} bytes for request ${this.
|
|
400
|
+
this.multiplexer.res.write(`${len} bytes for request ${this.request}\r\n`)
|
|
399
401
|
this.multiplexer.res.write(chunk, encoding, callback)
|
|
400
402
|
|
|
401
403
|
// console.log(`wrote:`)
|
|
402
|
-
// console.log(`${len} bytes for request /${this.
|
|
404
|
+
// console.log(`${len} bytes for request /${this.request}\r\n`)
|
|
403
405
|
// if (Buffer.isBuffer(chunk)) console.log(new TextDecoder().decode(chunk))
|
|
404
406
|
// else console.log('STRING?: ' + chunk)
|
|
405
407
|
|
|
@@ -408,7 +410,7 @@ function braidify (req, res, next) {
|
|
|
408
410
|
}
|
|
409
411
|
}
|
|
410
412
|
}
|
|
411
|
-
var mw = new MultiplexedWritable(m,
|
|
413
|
+
var mw = new MultiplexedWritable(m, request)
|
|
412
414
|
|
|
413
415
|
// then we create a fake server response,
|
|
414
416
|
// that pipes data to our fake socket
|
|
@@ -417,15 +419,15 @@ function braidify (req, res, next) {
|
|
|
417
419
|
res2.assignSocket(mw)
|
|
418
420
|
|
|
419
421
|
// register a handler for when the multiplexer closes,
|
|
420
|
-
// to close our fake response
|
|
421
|
-
m.
|
|
422
|
+
// to close our fake response
|
|
423
|
+
m.requests.set(request, () => {
|
|
422
424
|
og_res_end?.()
|
|
423
425
|
res2.destroy()
|
|
424
426
|
})
|
|
425
427
|
|
|
426
428
|
// when our fake response is done,
|
|
427
429
|
// we want to send a special message to the multiplexer saying so
|
|
428
|
-
res2.on('finish', () => m.res.write(`close
|
|
430
|
+
res2.on('finish', () => m.res.write(`close request ${request}\r\n`))
|
|
429
431
|
|
|
430
432
|
// we want access to "res" to be forwarded to our fake "res2",
|
|
431
433
|
// so that it goes into the multiplexer
|