braid-http 1.3.39 → 1.3.40
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 +23 -21
- package/braid-http-server.js +26 -14
- package/package.json +1 -1
package/braid-http-client.js
CHANGED
|
@@ -307,9 +307,9 @@ async function braid_fetch (url, params = {}) {
|
|
|
307
307
|
// Now we run the original fetch....
|
|
308
308
|
|
|
309
309
|
// try multiplexing if the multiplex flag is set, and conditions are met
|
|
310
|
-
var mux_params = params.multiplex ?? braid_fetch.
|
|
310
|
+
var mux_params = params.multiplex ?? braid_fetch.enable_multiplex
|
|
311
311
|
if (mux_params !== false &&
|
|
312
|
-
(params.headers.has('
|
|
312
|
+
(params.headers.has('multiplex-at') ||
|
|
313
313
|
(params.headers.has('subscribe') &&
|
|
314
314
|
braid_fetch.subscription_counts?.[origin] >
|
|
315
315
|
(!mux_params ? 1 : (mux_params.after ?? 0))))) {
|
|
@@ -828,17 +828,19 @@ function parse_body (state) {
|
|
|
828
828
|
// multiplex_fetch provides a fetch-like experience for HTTP requests
|
|
829
829
|
// where the result is actually being sent over a separate multiplexed connection.
|
|
830
830
|
async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
831
|
+
var multiplex_version = '1.0'
|
|
832
|
+
|
|
831
833
|
var origin = new URL(url, typeof document !== 'undefined' ? document.baseURI : undefined).origin
|
|
832
834
|
|
|
833
835
|
// the mux_key is the same as the origin, unless it is being overriden
|
|
834
836
|
// (the overriding is done by the tests)
|
|
835
|
-
var mux_key = params.headers.get('
|
|
837
|
+
var mux_key = params.headers.get('multiplex-at')?.split('/')[3] ?? origin
|
|
836
838
|
|
|
837
839
|
// create a new multiplexer if it doesn't exist for this origin
|
|
838
840
|
if (!multiplex_fetch.multiplexers) multiplex_fetch.multiplexers = {}
|
|
839
841
|
if (!multiplex_fetch.multiplexers[mux_key]) multiplex_fetch.multiplexers[mux_key] = (async () => {
|
|
840
842
|
// make up a new multiplexer id (unless it is being overriden)
|
|
841
|
-
var multiplexer = params.headers.get('
|
|
843
|
+
var multiplexer = params.headers.get('multiplex-at')?.split('/')[3] ?? Math.random().toString(36).slice(2)
|
|
842
844
|
|
|
843
845
|
var streams = new Map()
|
|
844
846
|
var mux_error = null
|
|
@@ -847,16 +849,16 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
847
849
|
// attempt to establish a multiplexed connection
|
|
848
850
|
try {
|
|
849
851
|
if (skip_multiplex_method) throw 'skip multiplex method'
|
|
850
|
-
var r = await braid_fetch(`${origin}/${multiplexer}`, {method: 'MULTIPLEX', headers: {'Multiplex-Version':
|
|
851
|
-
if (!r.ok || r.headers.get('Multiplex-Version') !==
|
|
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'
|
|
852
854
|
} catch (e) {
|
|
853
855
|
// some servers don't like custom methods,
|
|
854
856
|
// so let's try with a custom header
|
|
855
857
|
try {
|
|
856
|
-
r = await braid_fetch(`${origin}/.well-known/multiplex/${multiplexer}`, {method: 'POST', headers: {'Multiplex-Version':
|
|
858
|
+
r = await braid_fetch(`${origin}/.well-known/multiplex/${multiplexer}`, {method: 'POST', headers: {'Multiplex-Version': multiplex_version}, retry: true})
|
|
857
859
|
|
|
858
860
|
if (!r.ok) throw new Error('status not ok: ' + r.status)
|
|
859
|
-
if (r.headers.get('Multiplex-Version') !==
|
|
861
|
+
if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
|
|
860
862
|
} catch (e) {
|
|
861
863
|
// fallback to normal fetch if multiplexed connection fails
|
|
862
864
|
console.error(`Could not establish multiplexed connection.\nGot error: ${e}.\nFalling back to normal connection.`)
|
|
@@ -882,16 +884,16 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
882
884
|
// if we already know the multiplexer is not working,
|
|
883
885
|
// then fallback to normal fetch
|
|
884
886
|
// (unless the user is specifically asking for multiplexing)
|
|
885
|
-
if ((await promise_done(mux_promise)) && (await mux_promise) === false && !params.headers.get('
|
|
887
|
+
if ((await promise_done(mux_promise)) && (await mux_promise) === false && !params.headers.get('multiplex-at'))
|
|
886
888
|
return await normal_fetch(url, params)
|
|
887
889
|
|
|
888
890
|
// make up a new stream id (unless it is being overriden)
|
|
889
|
-
var stream = params.headers.get('
|
|
891
|
+
var stream = params.headers.get('multiplex-at')?.split('/')[4] ?? Math.random().toString(36).slice(2)
|
|
890
892
|
|
|
891
|
-
// add the
|
|
893
|
+
// add the Multiplex-At header without affecting the underlying params
|
|
892
894
|
var mux_headers = new Headers(params.headers)
|
|
893
|
-
mux_headers.set('
|
|
894
|
-
mux_headers.set('Multiplex-Version',
|
|
895
|
+
mux_headers.set('Multiplex-At', `/.well-known/multiplex/${multiplexer}/${stream}`)
|
|
896
|
+
mux_headers.set('Multiplex-Version', multiplex_version)
|
|
895
897
|
params = {...params, headers: mux_headers}
|
|
896
898
|
|
|
897
899
|
// setup a way to receive incoming data from the multiplexer
|
|
@@ -929,13 +931,13 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
929
931
|
stream_error = e
|
|
930
932
|
bytes_available()
|
|
931
933
|
try {
|
|
932
|
-
var r = await braid_fetch(`${origin}${params.headers.get('
|
|
934
|
+
var r = await braid_fetch(`${origin}${params.headers.get('multiplex-at')}`, {
|
|
933
935
|
method: 'DELETE',
|
|
934
|
-
headers: { 'Multiplex-Version':
|
|
936
|
+
headers: { 'Multiplex-Version': multiplex_version }, retry: true
|
|
935
937
|
})
|
|
936
938
|
|
|
937
939
|
if (!r.ok) throw new Error('status not ok: ' + r.status)
|
|
938
|
-
if (r.headers.get('Multiplex-Version') !==
|
|
940
|
+
if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
|
|
939
941
|
} catch (e) {
|
|
940
942
|
e = new Error(`Could not cancel multiplexed connection: ${e}`)
|
|
941
943
|
console.error('' + e)
|
|
@@ -957,9 +959,9 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
957
959
|
// fall back to as if it was a normal fetch
|
|
958
960
|
if (res.ok && res.status !== 293) return res
|
|
959
961
|
|
|
960
|
-
if (res.status !== 293) throw new Error('Could not establish multiplexed stream ' + params.headers.get('
|
|
962
|
+
if (res.status !== 293) throw new Error('Could not establish multiplexed stream ' + params.headers.get('multiplex-at') + ', got status: ' + res.status)
|
|
961
963
|
|
|
962
|
-
if (res.headers.get('Multiplex-Version') !==
|
|
964
|
+
if (res.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('Could not establish multiplexed stream ' + params.headers.get('multiplex-at') + ', got unknown version: ' + res.headers.get('Multiplex-Version'))
|
|
963
965
|
|
|
964
966
|
// we want to present the illusion that the connection is still open,
|
|
965
967
|
// and therefor closable with "abort",
|
|
@@ -1022,7 +1024,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
1022
1024
|
|
|
1023
1025
|
// add a convenience property for the user to know if
|
|
1024
1026
|
// this response is being multiplexed
|
|
1025
|
-
res.
|
|
1027
|
+
res.is_multiplexed = true
|
|
1026
1028
|
|
|
1027
1029
|
// return the fake response object
|
|
1028
1030
|
return res
|
|
@@ -1038,7 +1040,7 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
1038
1040
|
return await (await multiplex_fetch.multiplexers[mux_key])(url, params)
|
|
1039
1041
|
}
|
|
1040
1042
|
|
|
1041
|
-
// waits on reader for chunks like: 123 bytes for
|
|
1043
|
+
// waits on reader for chunks like: 123 bytes for request ABC\r\n..123 bytes..
|
|
1042
1044
|
// which would trigger cb("ABC", bytes)
|
|
1043
1045
|
async function parse_multiplex_stream(reader, cb, on_error) {
|
|
1044
1046
|
try {
|
|
@@ -1072,7 +1074,7 @@ async function parse_multiplex_stream(reader, cb, on_error) {
|
|
|
1072
1074
|
}
|
|
1073
1075
|
if (headerComplete) {
|
|
1074
1076
|
var headerStr = new TextDecoder().decode(buffers[0].slice(0, header_length))
|
|
1075
|
-
var m = headerStr.match(/^[\r\n]*((\d+) bytes for|close)
|
|
1077
|
+
var m = headerStr.match(/^[\r\n]*((\d+) bytes for|close) request ([A-Za-z0-9_-]+)\r\n$/)
|
|
1076
1078
|
if (!m) throw new Error('invalid multiplex header')
|
|
1077
1079
|
stream_id = m[3]
|
|
1078
1080
|
|
package/braid-http-server.js
CHANGED
|
@@ -243,9 +243,10 @@ function braidify (req, res, next) {
|
|
|
243
243
|
req.subscribe = subscribe
|
|
244
244
|
|
|
245
245
|
// Multiplexer stuff
|
|
246
|
-
|
|
246
|
+
var multiplex_version = '1.0'
|
|
247
|
+
if ((braidify.enable_multiplex ?? true) &&
|
|
247
248
|
(req.method === 'MULTIPLEX' || req.url.startsWith('/.well-known/multiplex/')) &&
|
|
248
|
-
req.headers['multiplex-version'] ===
|
|
249
|
+
req.headers['multiplex-version'] === multiplex_version) {
|
|
249
250
|
|
|
250
251
|
// let the caller know we're handling things
|
|
251
252
|
req.is_multiplexer = res.is_multiplexer = true
|
|
@@ -262,6 +263,17 @@ function braidify (req, res, next) {
|
|
|
262
263
|
if (!stream) {
|
|
263
264
|
// maintain a Map of all the multiplexers
|
|
264
265
|
if (!braidify.multiplexers) braidify.multiplexers = new Map()
|
|
266
|
+
|
|
267
|
+
// if this multiplexer already exists, respond with an error
|
|
268
|
+
if (braidify.multiplexers.has(multiplexer)) {
|
|
269
|
+
res.writeHead(409, 'Conflict', {'Content-Type': 'application/json'})
|
|
270
|
+
return res.end(JSON.stringify({
|
|
271
|
+
error: 'Multiplexer already exists',
|
|
272
|
+
message: `Cannot create duplicate multiplexer with ID '${multiplexer}'`,
|
|
273
|
+
details: 'This multiplexer ID must be unique'
|
|
274
|
+
}))
|
|
275
|
+
}
|
|
276
|
+
|
|
265
277
|
braidify.multiplexers.set(multiplexer, {streams: new Map(), res})
|
|
266
278
|
|
|
267
279
|
// when the response closes,
|
|
@@ -274,7 +286,7 @@ function braidify (req, res, next) {
|
|
|
274
286
|
// keep the connection open,
|
|
275
287
|
// so people can send multiplexed data to it
|
|
276
288
|
res.writeHead(200, 'OK', {
|
|
277
|
-
'Multiplex-Version':
|
|
289
|
+
'Multiplex-Version': multiplex_version,
|
|
278
290
|
'Incremental': '?1',
|
|
279
291
|
'Cache-Control': 'no-cache',
|
|
280
292
|
'X-Accel-Buffering': 'no',
|
|
@@ -306,7 +318,7 @@ function braidify (req, res, next) {
|
|
|
306
318
|
s()
|
|
307
319
|
|
|
308
320
|
// let the requester know we succeeded
|
|
309
|
-
res.writeHead(200, 'OK', { 'Multiplex-Version':
|
|
321
|
+
res.writeHead(200, 'OK', { 'Multiplex-Version': multiplex_version })
|
|
310
322
|
return res.end(``)
|
|
311
323
|
}
|
|
312
324
|
}
|
|
@@ -314,12 +326,12 @@ function braidify (req, res, next) {
|
|
|
314
326
|
// a multiplexer header means the user wants to send the
|
|
315
327
|
// results of this request to the provided multiplexer,
|
|
316
328
|
// tagged with the given stream id
|
|
317
|
-
if (braidify.
|
|
318
|
-
req.headers
|
|
319
|
-
req.headers['multiplex-version'] ===
|
|
329
|
+
if ((braidify.enable_multiplex ?? true) &&
|
|
330
|
+
req.headers['multiplex-at'] &&
|
|
331
|
+
req.headers['multiplex-version'] === multiplex_version) {
|
|
320
332
|
|
|
321
333
|
// parse the multiplexer id and stream id from the header
|
|
322
|
-
var [multiplexer, stream] = req.headers.
|
|
334
|
+
var [multiplexer, stream] = req.headers['multiplex-at'].split('/').slice(3)
|
|
323
335
|
|
|
324
336
|
// find the multiplexer object (contains a response object)
|
|
325
337
|
var m = braidify.multiplexers?.get(multiplexer)
|
|
@@ -349,16 +361,16 @@ function braidify (req, res, next) {
|
|
|
349
361
|
if (og_stream) {
|
|
350
362
|
og_stream.respond({
|
|
351
363
|
':status': 293,
|
|
352
|
-
|
|
353
|
-
'Multiplex-Version':
|
|
364
|
+
'Multiplex-At': req.headers['multiplex-at'],
|
|
365
|
+
'Multiplex-Version': multiplex_version,
|
|
354
366
|
...Object.fromEntries(cors_headers)
|
|
355
367
|
})
|
|
356
368
|
og_stream.write('Ok.')
|
|
357
369
|
og_stream.end()
|
|
358
370
|
} else {
|
|
359
371
|
og_socket.write('HTTP/1.1 293 Responded via multiplexer\r\n')
|
|
360
|
-
og_socket.write(`
|
|
361
|
-
og_socket.write(`Multiplex-Version:
|
|
372
|
+
og_socket.write(`Multiplex-At: ${req.headers['multiplex-at']}\r\n`)
|
|
373
|
+
og_socket.write(`Multiplex-Version: ${multiplex_version}\r\n`)
|
|
362
374
|
cors_headers.forEach(([key, value]) =>
|
|
363
375
|
og_socket.write(`${key}: ${value}\r\n`))
|
|
364
376
|
og_socket.write('\r\n')
|
|
@@ -383,11 +395,11 @@ function braidify (req, res, next) {
|
|
|
383
395
|
|
|
384
396
|
try {
|
|
385
397
|
var len = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk, encoding)
|
|
386
|
-
this.multiplexer.res.write(`${len} bytes for
|
|
398
|
+
this.multiplexer.res.write(`${len} bytes for request ${this.stream}\r\n`)
|
|
387
399
|
this.multiplexer.res.write(chunk, encoding, callback)
|
|
388
400
|
|
|
389
401
|
// console.log(`wrote:`)
|
|
390
|
-
// console.log(`${len} bytes for
|
|
402
|
+
// console.log(`${len} bytes for request /${this.stream}\r\n`)
|
|
391
403
|
// if (Buffer.isBuffer(chunk)) console.log(new TextDecoder().decode(chunk))
|
|
392
404
|
// else console.log('STRING?: ' + chunk)
|
|
393
405
|
|