braid-http 1.3.44 → 1.3.46
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 +230 -198
- package/braid-http-server.js +4 -5
- package/package.json +1 -1
package/braid-http-client.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// var peer = Math.random().toString(36).substr(2)
|
|
2
1
|
|
|
3
2
|
// ***************************
|
|
4
3
|
// http
|
|
@@ -163,7 +162,10 @@ async function braid_fetch (url, params = {}) {
|
|
|
163
162
|
params.headers.set('peer', params.peer)
|
|
164
163
|
|
|
165
164
|
if (params.heartbeats)
|
|
166
|
-
params.headers.set('heartbeats',
|
|
165
|
+
params.headers.set('heartbeats',
|
|
166
|
+
typeof params.heartbeats === 'number'
|
|
167
|
+
? `${params.heartbeats}s`
|
|
168
|
+
: params.heartbeats)
|
|
167
169
|
|
|
168
170
|
// Prevent browsers from going to disk cache
|
|
169
171
|
params.cache = 'no-cache'
|
|
@@ -262,7 +264,7 @@ async function braid_fetch (url, params = {}) {
|
|
|
262
264
|
underlying_aborter?.abort()
|
|
263
265
|
|
|
264
266
|
// see if we should retry..
|
|
265
|
-
var retry = params.retry &&
|
|
267
|
+
var retry = params.retry && // only try to reconnect if the user has chosen to
|
|
266
268
|
e.name !== "AbortError" && // don't retry if the user has chosen to abort
|
|
267
269
|
!e.startsWith?.('Parse error in headers') && // in this case, the server is spewing garbage, so reconnecting might be bad
|
|
268
270
|
!e.message?.startsWith?.('Could not establish multiplexed request') && // the server has told us no, or is using a different version of multiplexing
|
|
@@ -313,10 +315,9 @@ async function braid_fetch (url, params = {}) {
|
|
|
313
315
|
(params.headers.has('subscribe') &&
|
|
314
316
|
braid_fetch.subscription_counts?.[origin] >
|
|
315
317
|
(!mux_params ? 1 : (mux_params.after ?? 0))))) {
|
|
316
|
-
res = await multiplex_fetch(url, params, mux_params
|
|
317
|
-
} else
|
|
318
|
+
res = await multiplex_fetch(url, params, mux_params)
|
|
319
|
+
} else
|
|
318
320
|
res = await normal_fetch(url, params)
|
|
319
|
-
}
|
|
320
321
|
|
|
321
322
|
// And customize the response with a couple methods for getting
|
|
322
323
|
// the braid subscription data:
|
|
@@ -338,7 +339,7 @@ async function braid_fetch (url, params = {}) {
|
|
|
338
339
|
clearTimeout(timeout)
|
|
339
340
|
let wait_seconds = 1.2 * heartbeats + 3
|
|
340
341
|
timeout = setTimeout(() => {
|
|
341
|
-
on_error(new Error(`heartbeat
|
|
342
|
+
on_error(new Error(`heartbeat seen in ${wait_seconds.toFixed(2)}s`))
|
|
342
343
|
}, wait_seconds * 1000)
|
|
343
344
|
}
|
|
344
345
|
on_heartbeat()
|
|
@@ -361,7 +362,8 @@ async function braid_fetch (url, params = {}) {
|
|
|
361
362
|
async (result, err) => {
|
|
362
363
|
if (!err) {
|
|
363
364
|
// check whether we aborted
|
|
364
|
-
if (original_signal?.aborted)
|
|
365
|
+
if (original_signal?.aborted)
|
|
366
|
+
throw create_abort_error('already aborted')
|
|
365
367
|
|
|
366
368
|
// Yay! We got a new version! Tell the callback!
|
|
367
369
|
cb_running = true
|
|
@@ -540,7 +542,8 @@ var subscription_parser = (cb) => ({
|
|
|
540
542
|
|
|
541
543
|
Object.defineProperty(update, 'body_text', {
|
|
542
544
|
get: function () {
|
|
543
|
-
if (this.body != null)
|
|
545
|
+
if (this.body != null)
|
|
546
|
+
return new TextDecoder('utf-8').decode(this.body.buffer)
|
|
544
547
|
}
|
|
545
548
|
})
|
|
546
549
|
|
|
@@ -634,7 +637,9 @@ function parse_headers (input) {
|
|
|
634
637
|
|
|
635
638
|
// Extract the header string
|
|
636
639
|
var headers_source = input.slice(start, end)
|
|
637
|
-
headers_source = Array.isArray(headers_source)
|
|
640
|
+
headers_source = Array.isArray(headers_source)
|
|
641
|
+
? headers_source.map(x => String.fromCharCode(x)).join('')
|
|
642
|
+
: new TextDecoder().decode(headers_source)
|
|
638
643
|
|
|
639
644
|
// Convert "HTTP 200 OK" to a :status: 200 header
|
|
640
645
|
headers_source = headers_source.replace(/^HTTP\/?\d*\.?\d* (\d\d\d).*\r?\n/,
|
|
@@ -827,7 +832,7 @@ function parse_body (state) {
|
|
|
827
832
|
|
|
828
833
|
// multiplex_fetch provides a fetch-like experience for HTTP requests
|
|
829
834
|
// where the result is actually being sent over a separate multiplexed connection.
|
|
830
|
-
async function multiplex_fetch(url, params,
|
|
835
|
+
async function multiplex_fetch(url, params, mux_params) {
|
|
831
836
|
var multiplex_version = '1.0'
|
|
832
837
|
|
|
833
838
|
var origin = new URL(url, typeof document !== 'undefined' ? document.baseURI : undefined).origin
|
|
@@ -837,215 +842,238 @@ async function multiplex_fetch(url, params, skip_multiplex_method) {
|
|
|
837
842
|
var mux_key = params.headers.get('multiplex-through')?.split('/')[3] ?? origin
|
|
838
843
|
|
|
839
844
|
// create a new multiplexer if it doesn't exist for this origin
|
|
840
|
-
if (!multiplex_fetch.multiplexers)
|
|
841
|
-
if (!multiplex_fetch.multiplexers[mux_key]) multiplex_fetch.multiplexers[mux_key] = (
|
|
842
|
-
|
|
843
|
-
|
|
845
|
+
if (!multiplex_fetch.multiplexers) multiplex_fetch.multiplexers = {}
|
|
846
|
+
if (!multiplex_fetch.multiplexers[mux_key]) multiplex_fetch.multiplexers[mux_key] = (
|
|
847
|
+
async () => {
|
|
848
|
+
// make up a new multiplexer id (unless it is being overriden)
|
|
849
|
+
var multiplexer = params.headers.get('multiplex-through')?.split('/')[3]
|
|
850
|
+
?? random_base64url(Math.ceil((mux_params?.id_bits ?? 72) / 6))
|
|
844
851
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
var mux_promise = (async () => {
|
|
849
|
-
// attempt to establish a multiplexed connection
|
|
850
|
-
try {
|
|
851
|
-
if (skip_multiplex_method) throw 'skip multiplex method'
|
|
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'
|
|
854
|
-
} catch (e) {
|
|
855
|
-
// some servers don't like custom methods,
|
|
856
|
-
// so let's try with a custom header
|
|
857
|
-
try {
|
|
858
|
-
r = await braid_fetch(`${origin}/.well-known/multiplexer/${multiplexer}`, {method: 'POST', headers: {'Multiplex-Version': multiplex_version}, retry: true})
|
|
852
|
+
var requests = new Map()
|
|
853
|
+
var mux_error = null
|
|
859
854
|
|
|
860
|
-
|
|
861
|
-
if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
|
|
862
|
-
} catch (e) {
|
|
863
|
-
// fallback to normal fetch if multiplexed connection fails
|
|
864
|
-
console.error(`Could not establish multiplexer.\nGot error: ${e}.\nFalling back to normal connection.`)
|
|
865
|
-
return false
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
// parse the multiplexed stream,
|
|
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/multiplexer/${multiplexer}/${request}`, {
|
|
879
|
-
method: 'DELETE',
|
|
880
|
-
headers: { 'Multiplex-Version': multiplex_version }, retry: true
|
|
881
|
-
})
|
|
882
|
-
} finally { try_deleting.delete(request) }
|
|
883
|
-
}
|
|
884
|
-
}, e => {
|
|
855
|
+
function cleanup(e) {
|
|
885
856
|
// the multiplexer stream has died.. let everyone know..
|
|
886
857
|
mux_error = e
|
|
887
|
-
for (var f of requests.values()) f()
|
|
888
858
|
delete multiplex_fetch.multiplexers[mux_key]
|
|
889
|
-
|
|
890
|
-
})()
|
|
891
|
-
|
|
892
|
-
// return a "fetch" for this multiplexer
|
|
893
|
-
return async (url, params) => {
|
|
894
|
-
|
|
895
|
-
// if we already know the multiplexer is not working,
|
|
896
|
-
// then fallback to normal fetch
|
|
897
|
-
// (unless the user is specifically asking for multiplexing)
|
|
898
|
-
if ((await promise_done(mux_promise)) && (await mux_promise) === false && !params.headers.get('multiplex-through'))
|
|
899
|
-
return await normal_fetch(url, params)
|
|
900
|
-
|
|
901
|
-
// make up a new request id (unless it is being overriden)
|
|
902
|
-
var request = params.headers.get('multiplex-through')?.split('/')[4] ?? Math.random().toString(36).slice(2)
|
|
903
|
-
|
|
904
|
-
// add the Multiplex-Through header without affecting the underlying params
|
|
905
|
-
var mux_headers = new Headers(params.headers)
|
|
906
|
-
mux_headers.set('Multiplex-Through', `/.well-known/multiplexer/${multiplexer}/${request}`)
|
|
907
|
-
mux_headers.set('Multiplex-Version', multiplex_version)
|
|
908
|
-
params = {...params, headers: mux_headers}
|
|
909
|
-
|
|
910
|
-
// setup a way to receive incoming data from the multiplexer
|
|
911
|
-
var buffers = []
|
|
912
|
-
var bytes_available = () => {}
|
|
913
|
-
var request_error = null
|
|
914
|
-
|
|
915
|
-
// this utility calls the callback whenever new data is available to process
|
|
916
|
-
async function process_buffers(cb) {
|
|
917
|
-
while (true) {
|
|
918
|
-
// wait for data if none is available
|
|
919
|
-
if (!mux_error && !request_error && !buffers.length)
|
|
920
|
-
await new Promise(done => bytes_available = done)
|
|
921
|
-
if (mux_error || request_error) throw (mux_error || request_error)
|
|
922
|
-
|
|
923
|
-
// process the data
|
|
924
|
-
let ret = cb()
|
|
925
|
-
if (ret) return ret
|
|
926
|
-
}
|
|
859
|
+
for (var f of requests.values()) f()
|
|
927
860
|
}
|
|
928
861
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
if (!bytes) buffers.push(bytes)
|
|
932
|
-
else if (!mux_error) buffers.push(bytes)
|
|
933
|
-
bytes_available()
|
|
934
|
-
})
|
|
935
|
-
|
|
936
|
-
// prepare a function that we'll call to cleanly tear things down
|
|
937
|
-
var unset = async e => {
|
|
938
|
-
unset = () => {}
|
|
939
|
-
requests.delete(request)
|
|
940
|
-
request_error = e
|
|
941
|
-
bytes_available()
|
|
862
|
+
var mux_promise = (async () => {
|
|
863
|
+
// attempt to establish a multiplexed connection
|
|
942
864
|
try {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
865
|
+
if (mux_params?.via === 'POST') throw 'skip multiplex method'
|
|
866
|
+
var r = await braid_fetch(`${origin}/${multiplexer}`, {
|
|
867
|
+
method: 'MULTIPLEX',
|
|
868
|
+
headers: {'Multiplex-Version': multiplex_version},
|
|
869
|
+
retry: true
|
|
946
870
|
})
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
if (r.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('wrong multiplex version: ' + r.headers.get('Multiplex-Version') + ', expected ' + multiplex_version)
|
|
871
|
+
if (!r.ok || r.headers.get('Multiplex-Version') !== multiplex_version)
|
|
872
|
+
throw 'bad'
|
|
950
873
|
} catch (e) {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
874
|
+
// some servers don't like custom methods,
|
|
875
|
+
// so let's try with a custom header
|
|
876
|
+
try {
|
|
877
|
+
r = await braid_fetch(`${origin}/.well-known/multiplexer/${multiplexer}`,
|
|
878
|
+
{method: 'POST',
|
|
879
|
+
headers: {'Multiplex-Version': multiplex_version},
|
|
880
|
+
retry: true})
|
|
881
|
+
|
|
882
|
+
if (!r.ok) throw new Error('status not ok: ' + r.status)
|
|
883
|
+
if (r.headers.get('Multiplex-Version') !== multiplex_version)
|
|
884
|
+
throw new Error('wrong multiplex version: '
|
|
885
|
+
+ r.headers.get('Multiplex-Version')
|
|
886
|
+
+ ', expected ' + multiplex_version)
|
|
887
|
+
} catch (e) {
|
|
888
|
+
// fallback to normal fetch if multiplexed connection fails
|
|
889
|
+
console.error(`Could not establish multiplexer.\n`
|
|
890
|
+
+ `Got error: ${e}.\nFalling back to normal connection.`)
|
|
891
|
+
return false
|
|
892
|
+
}
|
|
954
893
|
}
|
|
955
|
-
}
|
|
956
894
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
var
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
895
|
+
// parse the multiplexed stream,
|
|
896
|
+
// and send messages to the appropriate requests
|
|
897
|
+
var try_deleting = new Set()
|
|
898
|
+
parse_multiplex_stream(r.body.getReader(), async (request, bytes) => {
|
|
899
|
+
if (requests.has(request)) {
|
|
900
|
+
requests.get(request)(bytes)
|
|
901
|
+
} else if (!try_deleting.has(request)) {
|
|
902
|
+
try_deleting.add(request)
|
|
903
|
+
try {
|
|
904
|
+
await braid_fetch(`${origin}/.well-known/multiplexer/${multiplexer}/${request}`, {
|
|
905
|
+
method: 'DELETE',
|
|
906
|
+
headers: { 'Multiplex-Version': multiplex_version },
|
|
907
|
+
retry: true
|
|
908
|
+
})
|
|
909
|
+
} finally { try_deleting.delete(request) }
|
|
910
|
+
}
|
|
911
|
+
}, cleanup)
|
|
912
|
+
})()
|
|
913
|
+
|
|
914
|
+
// return a "fetch" for this multiplexer
|
|
915
|
+
return async (url, params) => {
|
|
916
|
+
|
|
917
|
+
// make up a new request id (unless it is being overriden)
|
|
918
|
+
var request = params.headers.get('multiplex-through')?.split('/')[4]
|
|
919
|
+
?? random_base64url(Math.ceil((mux_params?.id_bits ?? 72) / 6))
|
|
920
|
+
|
|
921
|
+
// add the Multiplex-Through header without affecting the underlying params
|
|
922
|
+
var mux_headers = new Headers(params.headers)
|
|
923
|
+
mux_headers.set('Multiplex-Through', `/.well-known/multiplexer/${multiplexer}/${request}`)
|
|
924
|
+
mux_headers.set('Multiplex-Version', multiplex_version)
|
|
925
|
+
params = {...params, headers: mux_headers}
|
|
926
|
+
|
|
927
|
+
// setup a way to receive incoming data from the multiplexer
|
|
928
|
+
var buffers = []
|
|
929
|
+
var bytes_available = () => {}
|
|
930
|
+
var request_error = null
|
|
931
|
+
|
|
932
|
+
// this utility calls the callback whenever new data is available to process
|
|
933
|
+
async function process_buffers(cb) {
|
|
934
|
+
while (true) {
|
|
935
|
+
// wait for data if none is available
|
|
936
|
+
if (!mux_error && !request_error && !buffers.length)
|
|
937
|
+
await new Promise(done => bytes_available = done)
|
|
938
|
+
if (mux_error || request_error) throw (mux_error || request_error)
|
|
939
|
+
|
|
940
|
+
// process the data
|
|
941
|
+
let ret = cb()
|
|
942
|
+
if (ret) return ret
|
|
943
|
+
}
|
|
966
944
|
}
|
|
967
945
|
|
|
968
|
-
//
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
if (res.status !== 293) throw new Error('Could not establish multiplexed request ' + params.headers.get('multiplex-through') + ', got status: ' + res.status)
|
|
974
|
-
|
|
975
|
-
if (res.headers.get('Multiplex-Version') !== multiplex_version) throw new Error('Could not establish multiplexed request ' + params.headers.get('multiplex-through') + ', got unknown version: ' + res.headers.get('Multiplex-Version'))
|
|
976
|
-
|
|
977
|
-
// we want to present the illusion that the connection is still open,
|
|
978
|
-
// and therefor closable with "abort",
|
|
979
|
-
// so we handle the abort ourselves to close the multiplexed request
|
|
980
|
-
params.signal?.addEventListener('abort', () =>
|
|
981
|
-
unset(create_abort_error('request aborted')))
|
|
982
|
-
|
|
983
|
-
// first, we need to listen for the headers..
|
|
984
|
-
var headers_buffer = new Uint8Array()
|
|
985
|
-
var parsed_headers = await process_buffers(() => {
|
|
986
|
-
// check if the request has been closed
|
|
987
|
-
var request_ended = !buffers[buffers.length - 1]
|
|
988
|
-
if (request_ended) buffers.pop()
|
|
989
|
-
|
|
990
|
-
// aggregate all the new buffers into our big headers_buffer
|
|
991
|
-
headers_buffer = concat_buffers([headers_buffer, ...buffers])
|
|
992
|
-
buffers = []
|
|
993
|
-
|
|
994
|
-
// and if the request had ended, put that information back
|
|
995
|
-
if (request_ended) buffers.push(null)
|
|
996
|
-
|
|
997
|
-
// try parsing what we got so far as headers..
|
|
998
|
-
var x = parse_headers(headers_buffer)
|
|
999
|
-
|
|
1000
|
-
// how did it go?
|
|
1001
|
-
if (x.result === 'error') {
|
|
1002
|
-
// if we got an error, give up
|
|
1003
|
-
console.log(`headers_buffer: ` + new TextDecoder().decode(headers_buffer))
|
|
1004
|
-
throw new Error('error parsing headers')
|
|
1005
|
-
} else if (x.result === 'waiting') {
|
|
1006
|
-
if (request_ended) throw new Error('Multiplexed request ended before headers received.')
|
|
1007
|
-
} else return x
|
|
946
|
+
// tell the multiplexer to send bytes for this request to us
|
|
947
|
+
requests.set(request, bytes => {
|
|
948
|
+
if (!bytes) buffers.push(bytes)
|
|
949
|
+
else if (!mux_error) buffers.push(bytes)
|
|
950
|
+
bytes_available()
|
|
1008
951
|
})
|
|
1009
952
|
|
|
1010
|
-
//
|
|
1011
|
-
|
|
953
|
+
// prepare a function that we'll call to cleanly tear things down
|
|
954
|
+
var unset = async e => {
|
|
955
|
+
unset = () => {}
|
|
956
|
+
requests.delete(request)
|
|
957
|
+
request_error = e
|
|
958
|
+
bytes_available()
|
|
959
|
+
try {
|
|
960
|
+
var r = await braid_fetch(`${origin}${params.headers.get('multiplex-through')}`, {
|
|
961
|
+
method: 'DELETE',
|
|
962
|
+
headers: { 'Multiplex-Version': multiplex_version }, retry: true
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
if (!r.ok) throw new Error('status not ok: ' + r.status)
|
|
966
|
+
if (r.headers.get('Multiplex-Version') !== multiplex_version)
|
|
967
|
+
throw new Error('wrong multiplex version: '
|
|
968
|
+
+ r.headers.get('Multiplex-Version')
|
|
969
|
+
+ ', expected ' + multiplex_version)
|
|
970
|
+
} catch (e) {
|
|
971
|
+
e = new Error(`Could not cancel multiplexed request: ${e}`)
|
|
972
|
+
console.error('' + e)
|
|
973
|
+
throw e
|
|
974
|
+
}
|
|
975
|
+
}
|
|
1012
976
|
|
|
1013
|
-
//
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
delete parsed_headers.headers[':status']
|
|
977
|
+
// do the underlying fetch
|
|
978
|
+
try {
|
|
979
|
+
var mux_was_done = await promise_done(mux_promise)
|
|
1017
980
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
try {
|
|
1024
|
-
await process_buffers(() => {
|
|
1025
|
-
var b = buffers.shift()
|
|
1026
|
-
if (!b) return true
|
|
1027
|
-
controller.enqueue(b)
|
|
1028
|
-
})
|
|
1029
|
-
} finally { controller.close() }
|
|
981
|
+
var res = await normal_fetch(url, params)
|
|
982
|
+
|
|
983
|
+
if (res.status === 424 && !mux_was_done) {
|
|
984
|
+
// this error will trigger a retry if the user is using that option
|
|
985
|
+
throw new Error('multiplexer not yet connected')
|
|
1030
986
|
}
|
|
1031
|
-
}), {
|
|
1032
|
-
status,
|
|
1033
|
-
headers: parsed_headers.headers
|
|
1034
|
-
})
|
|
1035
987
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
988
|
+
// if the response says it's ok,
|
|
989
|
+
// but it's is not a multiplexed response,
|
|
990
|
+
// fall back to as if it was a normal fetch
|
|
991
|
+
if (res.ok && res.status !== 293) return res
|
|
992
|
+
|
|
993
|
+
if (res.status !== 293)
|
|
994
|
+
throw new Error('Could not establish multiplexed request '
|
|
995
|
+
+ params.headers.get('multiplex-through')
|
|
996
|
+
+ ', got status: ' + res.status)
|
|
997
|
+
|
|
998
|
+
if (res.headers.get('Multiplex-Version') !== multiplex_version)
|
|
999
|
+
throw new Error('Could not establish multiplexed request '
|
|
1000
|
+
+ params.headers.get('multiplex-through')
|
|
1001
|
+
+ ', got unknown version: '
|
|
1002
|
+
+ res.headers.get('Multiplex-Version'))
|
|
1003
|
+
|
|
1004
|
+
// we want to present the illusion that the connection is still open,
|
|
1005
|
+
// and therefor closable with "abort",
|
|
1006
|
+
// so we handle the abort ourselves to close the multiplexed request
|
|
1007
|
+
params.signal?.addEventListener('abort', () =>
|
|
1008
|
+
unset(create_abort_error('request aborted')))
|
|
1009
|
+
|
|
1010
|
+
// first, we need to listen for the headers..
|
|
1011
|
+
var headers_buffer = new Uint8Array()
|
|
1012
|
+
var parsed_headers = await process_buffers(() => {
|
|
1013
|
+
// check if the request has been closed
|
|
1014
|
+
var request_ended = !buffers[buffers.length - 1]
|
|
1015
|
+
if (request_ended) buffers.pop()
|
|
1016
|
+
|
|
1017
|
+
// aggregate all the new buffers into our big headers_buffer
|
|
1018
|
+
headers_buffer = concat_buffers([headers_buffer, ...buffers])
|
|
1019
|
+
buffers = []
|
|
1020
|
+
|
|
1021
|
+
// and if the request had ended, put that information back
|
|
1022
|
+
if (request_ended) buffers.push(null)
|
|
1023
|
+
|
|
1024
|
+
// try parsing what we got so far as headers..
|
|
1025
|
+
var x = parse_headers(headers_buffer)
|
|
1026
|
+
|
|
1027
|
+
// how did it go?
|
|
1028
|
+
if (x.result === 'error') {
|
|
1029
|
+
// if we got an error, give up
|
|
1030
|
+
console.log(`headers_buffer: ` + new TextDecoder().decode(headers_buffer))
|
|
1031
|
+
throw new Error('error parsing headers')
|
|
1032
|
+
} else if (x.result === 'waiting') {
|
|
1033
|
+
if (request_ended)
|
|
1034
|
+
throw new Error('Multiplexed request ended before headers received.')
|
|
1035
|
+
} else return x
|
|
1036
|
+
})
|
|
1039
1037
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1038
|
+
// put the bytes left over from the header back
|
|
1039
|
+
if (parsed_headers.input.length) buffers.unshift(parsed_headers.input)
|
|
1040
|
+
|
|
1041
|
+
// these headers will also have the status,
|
|
1042
|
+
// but we want to present the status in a more usual way below
|
|
1043
|
+
var status = parsed_headers.headers[':status']
|
|
1044
|
+
delete parsed_headers.headers[':status']
|
|
1045
|
+
|
|
1046
|
+
// create our own fake response object,
|
|
1047
|
+
// to mimik fetch's response object,
|
|
1048
|
+
// feeding the user our request data from the multiplexer
|
|
1049
|
+
var res = new Response(new ReadableStream({
|
|
1050
|
+
async start(controller) {
|
|
1051
|
+
try {
|
|
1052
|
+
await process_buffers(() => {
|
|
1053
|
+
var b = buffers.shift()
|
|
1054
|
+
if (!b) return true
|
|
1055
|
+
controller.enqueue(b)
|
|
1056
|
+
})
|
|
1057
|
+
} finally { controller.close() }
|
|
1058
|
+
}
|
|
1059
|
+
}), {
|
|
1060
|
+
status,
|
|
1061
|
+
headers: parsed_headers.headers
|
|
1062
|
+
})
|
|
1063
|
+
|
|
1064
|
+
// add a convenience property for the user to know if
|
|
1065
|
+
// this response is being multiplexed
|
|
1066
|
+
res.is_multiplexed = true
|
|
1067
|
+
|
|
1068
|
+
// return the fake response object
|
|
1069
|
+
return res
|
|
1070
|
+
} catch (e) {
|
|
1071
|
+
// if we had an error, be sure to unregister ourselves
|
|
1072
|
+
unset(e)
|
|
1073
|
+
throw e
|
|
1074
|
+
}
|
|
1046
1075
|
}
|
|
1047
|
-
}
|
|
1048
|
-
})()
|
|
1076
|
+
})()
|
|
1049
1077
|
|
|
1050
1078
|
// call the special fetch function for the multiplexer
|
|
1051
1079
|
return await (await multiplex_fetch.multiplexers[mux_key])(url, params)
|
|
@@ -1183,6 +1211,10 @@ async function promise_done(promise) {
|
|
|
1183
1211
|
return ret !== pending
|
|
1184
1212
|
}
|
|
1185
1213
|
|
|
1214
|
+
function random_base64url(n) {
|
|
1215
|
+
return [...crypto.getRandomValues(new Uint8Array(n))].map(x => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'[x % 64]).join('')
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1186
1218
|
// ****************************
|
|
1187
1219
|
// Exports
|
|
1188
1220
|
// ****************************
|
package/braid-http-server.js
CHANGED
|
@@ -269,8 +269,7 @@ function braidify (req, res, next) {
|
|
|
269
269
|
res.writeHead(409, 'Conflict', {'Content-Type': 'application/json'})
|
|
270
270
|
return res.end(JSON.stringify({
|
|
271
271
|
error: 'Multiplexer already exists',
|
|
272
|
-
|
|
273
|
-
details: 'This multiplexer ID must be unique'
|
|
272
|
+
details: `Cannot create duplicate multiplexer with ID '${multiplexer}'`
|
|
274
273
|
}))
|
|
275
274
|
}
|
|
276
275
|
|
|
@@ -344,9 +343,9 @@ function braidify (req, res, next) {
|
|
|
344
343
|
if (m.requests.has(request)) {
|
|
345
344
|
res.writeHead(409, 'Conflict', {'Content-Type': 'application/json'})
|
|
346
345
|
return res.end(JSON.stringify({
|
|
347
|
-
error: 'Request already
|
|
348
|
-
|
|
349
|
-
|
|
346
|
+
error: 'Request already multiplexed',
|
|
347
|
+
details: `Cannot multiplex request with duplicate ID '`
|
|
348
|
+
+ request + `' for multiplexer '` + multiplexer + `'`
|
|
350
349
|
}))
|
|
351
350
|
}
|
|
352
351
|
|