Last active
July 23, 2023 20:59
-
-
Save guest271314/49b0dfc91ac78fcc57c54485327646a8 to your computer and use it in GitHub Desktop.
Browser <=> Deno fetch(), listenTls() full duplex streaming
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env -S deno run -A --unsafely-ignore-certificate-errors=localhost --unstable --v8-flags="--expose-gc,--jitless" | |
// Deno Native Messaging host | |
// guest271314, 10-5-2022 | |
// Browser <=> Deno fetch(), listenTls() full duplex streaming | |
// 7-22-2023 | |
/* | |
Usage: | |
port.postMessage({url:'https://localhost:8443', method:'post', body: `new ReadableStream({ | |
async start(controller) { | |
await wait(1000); | |
controller.enqueue('This '); | |
await wait(1000); | |
controller.enqueue('is '); | |
await wait(1000); | |
controller.enqueue('a '); | |
await wait(1000); | |
controller.enqueue('slow '); | |
await wait(1000); | |
controller.enqueue('request.'); | |
controller.close(); | |
}, | |
}).pipeThrough(new TextEncoderStream())`, eval: true}) | |
port.postMessage({url:'https://github.com/guest271314/AudioWorkletStream/raw/master/house--64kbs-0-wav', method:'get', body: null}) | |
*/ | |
// https://github.com/denoland/deno/discussions/17236#discussioncomment-4566134 | |
// https://github.com/saghul/txiki.js/blob/master/src/js/core/tjs/eval-stdin.js | |
const encoder = new TextEncoder(); | |
const decoder = new TextDecoder(); | |
const wait = async (ms) => new Promise((r) => setTimeout(r, ms)); | |
const responseInit = { | |
headers: { | |
'Connection': 'close', | |
'Cache-Control': 'no-cache', | |
'Content-Type': 'text/plain; charset=UTF-8', | |
'Cross-Origin-Opener-Policy': 'unsafe-none', | |
'Cross-Origin-Embedder-Policy': 'unsafe-none', | |
'Access-Control-Allow-Origin': '*', | |
'Access-Control-Allow-Private-Network': 'true', | |
'Access-Control-Allow-Headers': 'Access-Control-Request-Private-Network', | |
'Access-Control-Allow-Methods': 'OPTIONS,POST,GET,HEAD,QUERY', | |
}, | |
}; | |
async function readFullAsync(length) { | |
const buffer = new Uint8Array(65536); | |
const data = []; | |
let n = null; | |
while (data.length < length && (n = await Deno.stdin.read(buffer))) { | |
data.push(...buffer.subarray(0, n)); | |
} | |
return new Uint8Array(data); | |
} | |
async function getMessage() { | |
const header = new Uint32Array(1); | |
await Deno.stdin.read(new Uint8Array(header.buffer)); | |
const settings = JSON.parse(decoder.decode(await readFullAsync(header[0]))); | |
return fetch(settings.url, { | |
method: settings.method, | |
headers: { | |
'Content-Type': 'text/plain', | |
'Access-Control-Request-Private-Network': true | |
}, | |
body: settings.eval ? eval(settings.body) : settings.body | |
}) | |
// .then((r) => r.body.pipeThrough(new TextDecoderStream())) | |
.then((r) => r.body.pipeTo(new WritableStream({ | |
async write(value) { | |
await sendMessage(encodeMessage([...value])); | |
gc(); | |
}, | |
async close() { | |
await sendMessage(encodeMessage('Stream closed.')); | |
} | |
}))).catch(async (e) => { | |
await sendMessage(encodeMessage(e.message)); | |
}); | |
} | |
async function sendMessage(message) { | |
const header = new Uint32Array([message.length]); | |
await Deno.stdout.write(new Uint8Array(header.buffer)); | |
await Deno.stdout.write(message); | |
} | |
function encodeMessage(message) { | |
return encoder.encode(JSON.stringify(message)); | |
} | |
async function main() { | |
(async () => { | |
// https://medium.com/deno-the-complete-reference/http-2-in-deno-f825251a5ab2 | |
for await ( | |
const conn of Deno.listenTls({ | |
port: 8443, | |
certFile: './certificate.pem', | |
keyFile: './certificate.key', | |
alpnProtocols: ["h2", "http/1.1"], | |
}) | |
) { | |
for await (const { | |
request, | |
respondWith | |
} | |
of Deno.serveHttp(conn)) { | |
await sendMessage(encodeMessage(`fetch request method: ${request.method}`)); | |
if (request.method === 'OPTIONS' || request.method === 'HEAD') { | |
respondWith(new Response(null, responseInit)); | |
} | |
if (request.method === 'GET') { | |
respondWith(new Response(null, responseInit)); | |
} | |
const stream = request.body; | |
/* | |
.pipeThrough(new TextDecoderStream()) | |
.pipeThrough( | |
new TransformStream({ | |
transform(value, c) { | |
c.enqueue(value.toUpperCase()); | |
}, | |
async flush() { | |
// await sendMessage(encodeMessage('TransformStream flush.')); | |
}, | |
}) | |
) | |
.pipeThrough(new TextEncoderStream()); | |
*/ | |
respondWith(new Response( | |
stream, responseInit)); | |
} | |
} | |
})().catch(async (e) => { | |
await sendMessage(encoder.encode(e.message)); | |
}); | |
while (true) { | |
await getMessage(); | |
gc(); | |
} | |
} | |
try { | |
main(); | |
} catch (e) { | |
Deno.exit(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment