braid-http 1.3.40 → 1.3.41
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 +40 -40
- package/braid-http-server.js +25 -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,19 @@ 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
|
-
parse_multiplex_stream(r.body.getReader(), (
|
|
872
|
-
|
|
870
|
+
// and send messages to the appropriate requests
|
|
871
|
+
parse_multiplex_stream(r.body.getReader(), (reqest, bytes) => {
|
|
872
|
+
requests.get(reqest)?.(bytes)
|
|
873
873
|
}, e => {
|
|
874
874
|
// the multiplexer stream has died.. let everyone know..
|
|
875
875
|
mux_error = e
|
|
876
|
-
for (var f of
|
|
876
|
+
for (var f of requests.values()) f()
|
|
877
877
|
delete multiplex_fetch.multiplexers[mux_key]
|
|
878
878
|
})
|
|
879
879
|
})()
|
|
@@ -887,27 +887,27 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
887
887
|
if ((await promise_done(mux_promise)) && (await mux_promise) === false && !params.headers.get('multiplex-at'))
|
|
888
888
|
return await normal_fetch(url, params)
|
|
889
889
|
|
|
890
|
-
// make up a new
|
|
891
|
-
var
|
|
890
|
+
// make up a new request id (unless it is being overriden)
|
|
891
|
+
var request = params.headers.get('multiplex-at')?.split('/')[4] ?? Math.random().toString(36).slice(2)
|
|
892
892
|
|
|
893
893
|
// add the Multiplex-At header without affecting the underlying params
|
|
894
894
|
var mux_headers = new Headers(params.headers)
|
|
895
|
-
mux_headers.set('Multiplex-At', `/.well-known/multiplex/${multiplexer}/${
|
|
895
|
+
mux_headers.set('Multiplex-At', `/.well-known/multiplex/${multiplexer}/${request}`)
|
|
896
896
|
mux_headers.set('Multiplex-Version', multiplex_version)
|
|
897
897
|
params = {...params, headers: mux_headers}
|
|
898
898
|
|
|
899
899
|
// setup a way to receive incoming data from the multiplexer
|
|
900
900
|
var buffers = []
|
|
901
901
|
var bytes_available = () => {}
|
|
902
|
-
var
|
|
902
|
+
var request_error = null
|
|
903
903
|
|
|
904
904
|
// this utility calls the callback whenever new data is available to process
|
|
905
905
|
async function process_buffers(cb) {
|
|
906
906
|
while (true) {
|
|
907
907
|
// wait for data if none is available
|
|
908
|
-
if (!mux_error && !
|
|
908
|
+
if (!mux_error && !request_error && !buffers.length)
|
|
909
909
|
await new Promise(done => bytes_available = done)
|
|
910
|
-
if (mux_error ||
|
|
910
|
+
if (mux_error || request_error) throw (mux_error || request_error)
|
|
911
911
|
|
|
912
912
|
// process the data
|
|
913
913
|
let ret = cb()
|
|
@@ -915,20 +915,18 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
915
915
|
}
|
|
916
916
|
}
|
|
917
917
|
|
|
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)
|
|
918
|
+
// tell the multiplexer to send bytes for this request to us
|
|
919
|
+
requests.set(request, bytes => {
|
|
920
|
+
if (!bytes) buffers.push(bytes)
|
|
921
|
+
else if (!mux_error) buffers.push(bytes)
|
|
924
922
|
bytes_available()
|
|
925
923
|
})
|
|
926
924
|
|
|
927
925
|
// prepare a function that we'll call to cleanly tear things down
|
|
928
926
|
var unset = async e => {
|
|
929
927
|
unset = () => {}
|
|
930
|
-
|
|
931
|
-
|
|
928
|
+
requests.delete(request)
|
|
929
|
+
request_error = e
|
|
932
930
|
bytes_available()
|
|
933
931
|
try {
|
|
934
932
|
var r = await braid_fetch(`${origin}${params.headers.get('multiplex-at')}`, {
|
|
@@ -939,7 +937,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
939
937
|
if (!r.ok) throw new Error('status not ok: ' + r.status)
|
|
940
938
|
if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
|
|
941
939
|
} catch (e) {
|
|
942
|
-
e = new Error(`Could not cancel multiplexed
|
|
940
|
+
e = new Error(`Could not cancel multiplexed request: ${e}`)
|
|
943
941
|
console.error('' + e)
|
|
944
942
|
throw e
|
|
945
943
|
}
|
|
@@ -947,9 +945,11 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
947
945
|
|
|
948
946
|
// do the underlying fetch
|
|
949
947
|
try {
|
|
948
|
+
var mux_was_done = await promise_done(mux_promise)
|
|
949
|
+
|
|
950
950
|
var res = await normal_fetch(url, params)
|
|
951
951
|
|
|
952
|
-
if (res.status === 424 && !
|
|
952
|
+
if (res.status === 424 && !mux_was_done) {
|
|
953
953
|
// this error will trigger a retry if the user is using that option
|
|
954
954
|
throw new Error('multiplexer not yet connected')
|
|
955
955
|
}
|
|
@@ -959,29 +959,29 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
959
959
|
// fall back to as if it was a normal fetch
|
|
960
960
|
if (res.ok && res.status !== 293) return res
|
|
961
961
|
|
|
962
|
-
if (res.status !== 293) throw new Error('Could not establish multiplexed
|
|
962
|
+
if (res.status !== 293) throw new Error('Could not establish multiplexed request ' + params.headers.get('multiplex-at') + ', got status: ' + res.status)
|
|
963
963
|
|
|
964
|
-
if (res.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('Could not establish multiplexed
|
|
964
|
+
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
965
|
|
|
966
966
|
// we want to present the illusion that the connection is still open,
|
|
967
967
|
// and therefor closable with "abort",
|
|
968
|
-
// so we handle the abort ourselves to close the multiplexed
|
|
968
|
+
// so we handle the abort ourselves to close the multiplexed request
|
|
969
969
|
params.signal?.addEventListener('abort', () =>
|
|
970
|
-
unset(create_abort_error('
|
|
970
|
+
unset(create_abort_error('request aborted')))
|
|
971
971
|
|
|
972
972
|
// first, we need to listen for the headers..
|
|
973
973
|
var headers_buffer = new Uint8Array()
|
|
974
974
|
var parsed_headers = await process_buffers(() => {
|
|
975
|
-
// check if the
|
|
976
|
-
var
|
|
977
|
-
if (
|
|
975
|
+
// check if the request has been closed
|
|
976
|
+
var request_ended = !buffers[buffers.length - 1]
|
|
977
|
+
if (request_ended) buffers.pop()
|
|
978
978
|
|
|
979
979
|
// aggregate all the new buffers into our big headers_buffer
|
|
980
980
|
headers_buffer = concat_buffers([headers_buffer, ...buffers])
|
|
981
981
|
buffers = []
|
|
982
982
|
|
|
983
|
-
// and if the
|
|
984
|
-
if (
|
|
983
|
+
// and if the request had ended, put that information back
|
|
984
|
+
if (request_ended) buffers.push(null)
|
|
985
985
|
|
|
986
986
|
// try parsing what we got so far as headers..
|
|
987
987
|
var x = parse_headers(headers_buffer)
|
|
@@ -992,7 +992,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
992
992
|
console.log(`headers_buffer: ` + new TextDecoder().decode(headers_buffer))
|
|
993
993
|
throw new Error('error parsing headers')
|
|
994
994
|
} else if (x.result === 'waiting') {
|
|
995
|
-
if (
|
|
995
|
+
if (request_ended) throw new Error('Multiplexed request ended before headers received.')
|
|
996
996
|
} else return x
|
|
997
997
|
})
|
|
998
998
|
|
|
@@ -1006,7 +1006,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
1006
1006
|
|
|
1007
1007
|
// create our own fake response object,
|
|
1008
1008
|
// to mimik fetch's response object,
|
|
1009
|
-
// feeding the user our
|
|
1009
|
+
// feeding the user our request data from the multiplexer
|
|
1010
1010
|
var res = new Response(new ReadableStream({
|
|
1011
1011
|
async start(controller) {
|
|
1012
1012
|
try {
|
|
@@ -1047,7 +1047,7 @@ async function parse_multiplex_stream(reader, cb, on_error) {
|
|
|
1047
1047
|
var buffers = [new Uint8Array(0)]
|
|
1048
1048
|
var buffers_size = 0
|
|
1049
1049
|
var chunk_size = null
|
|
1050
|
-
var
|
|
1050
|
+
var request_id = null
|
|
1051
1051
|
var header_length = 0
|
|
1052
1052
|
var header_started = false
|
|
1053
1053
|
|
|
@@ -1076,13 +1076,13 @@ async function parse_multiplex_stream(reader, cb, on_error) {
|
|
|
1076
1076
|
var headerStr = new TextDecoder().decode(buffers[0].slice(0, header_length))
|
|
1077
1077
|
var m = headerStr.match(/^[\r\n]*((\d+) bytes for|close) request ([A-Za-z0-9_-]+)\r\n$/)
|
|
1078
1078
|
if (!m) throw new Error('invalid multiplex header')
|
|
1079
|
-
|
|
1079
|
+
request_id = m[3]
|
|
1080
1080
|
|
|
1081
1081
|
buffers[0] = buffers[0].slice(header_length)
|
|
1082
1082
|
buffers_size -= header_length
|
|
1083
1083
|
|
|
1084
1084
|
if (m[1] === 'close') {
|
|
1085
|
-
cb(
|
|
1085
|
+
cb(request_id)
|
|
1086
1086
|
break
|
|
1087
1087
|
} else chunk_size = 1 * m[2]
|
|
1088
1088
|
} else break
|
|
@@ -1093,12 +1093,12 @@ async function parse_multiplex_stream(reader, cb, on_error) {
|
|
|
1093
1093
|
buffers[0] = buffers[0].slice(chunk_size)
|
|
1094
1094
|
buffers_size -= chunk_size
|
|
1095
1095
|
|
|
1096
|
-
// console.log(`
|
|
1096
|
+
// console.log(`request_id: ${request_id}, ${new TextDecoder().decode(chunk)}`)
|
|
1097
1097
|
|
|
1098
|
-
cb(
|
|
1098
|
+
cb(request_id, chunk)
|
|
1099
1099
|
|
|
1100
1100
|
chunk_size = null
|
|
1101
|
-
|
|
1101
|
+
request_id = null
|
|
1102
1102
|
header_length = 0
|
|
1103
1103
|
header_started = false
|
|
1104
1104
|
} 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)
|
|
@@ -384,10 +384,10 @@ function braidify (req, res, next) {
|
|
|
384
384
|
|
|
385
385
|
// first we create a kind of fake socket
|
|
386
386
|
class MultiplexedWritable extends require('stream').Writable {
|
|
387
|
-
constructor(multiplexer,
|
|
387
|
+
constructor(multiplexer, request) {
|
|
388
388
|
super()
|
|
389
389
|
this.multiplexer = multiplexer
|
|
390
|
-
this.
|
|
390
|
+
this.request = request
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
_write(chunk, encoding, callback) {
|
|
@@ -395,11 +395,11 @@ function braidify (req, res, next) {
|
|
|
395
395
|
|
|
396
396
|
try {
|
|
397
397
|
var len = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk, encoding)
|
|
398
|
-
this.multiplexer.res.write(`${len} bytes for request ${this.
|
|
398
|
+
this.multiplexer.res.write(`${len} bytes for request ${this.request}\r\n`)
|
|
399
399
|
this.multiplexer.res.write(chunk, encoding, callback)
|
|
400
400
|
|
|
401
401
|
// console.log(`wrote:`)
|
|
402
|
-
// console.log(`${len} bytes for request /${this.
|
|
402
|
+
// console.log(`${len} bytes for request /${this.request}\r\n`)
|
|
403
403
|
// if (Buffer.isBuffer(chunk)) console.log(new TextDecoder().decode(chunk))
|
|
404
404
|
// else console.log('STRING?: ' + chunk)
|
|
405
405
|
|
|
@@ -408,7 +408,7 @@ function braidify (req, res, next) {
|
|
|
408
408
|
}
|
|
409
409
|
}
|
|
410
410
|
}
|
|
411
|
-
var mw = new MultiplexedWritable(m,
|
|
411
|
+
var mw = new MultiplexedWritable(m, request)
|
|
412
412
|
|
|
413
413
|
// then we create a fake server response,
|
|
414
414
|
// that pipes data to our fake socket
|
|
@@ -417,15 +417,15 @@ function braidify (req, res, next) {
|
|
|
417
417
|
res2.assignSocket(mw)
|
|
418
418
|
|
|
419
419
|
// register a handler for when the multiplexer closes,
|
|
420
|
-
// to close our fake response
|
|
421
|
-
m.
|
|
420
|
+
// to close our fake response
|
|
421
|
+
m.requests.set(request, () => {
|
|
422
422
|
og_res_end?.()
|
|
423
423
|
res2.destroy()
|
|
424
424
|
})
|
|
425
425
|
|
|
426
426
|
// when our fake response is done,
|
|
427
427
|
// we want to send a special message to the multiplexer saying so
|
|
428
|
-
res2.on('finish', () => m.res.write(`close
|
|
428
|
+
res2.on('finish', () => m.res.write(`close request ${request}\r\n`))
|
|
429
429
|
|
|
430
430
|
// we want access to "res" to be forwarded to our fake "res2",
|
|
431
431
|
// so that it goes into the multiplexer
|