0
0
Fork 0
deno_monorepo/bin/gemini.ts

105 lines
2.3 KiB
TypeScript

// Gemini v0.11.0 client
import {
BufReader,
readLines,
} from "https://deno.land/std@0.74.0/io/bufio.ts";
const content_buffer_size = 512;
const gemini_port = "1965";
interface Response {
status: string;
meta: string;
content?: Uint8Array;
}
async function fetchGemini(
url: URL,
redirect_count: number = 5,
): Promise<Response> {
if (url.protocol != "gemini:") {
throw new Error("Unsupported protocol");
}
const conn = await Deno.connectTls({
hostname: url.hostname,
port: parseInt(url.port != "" ? url.port : gemini_port),
});
try {
const request = new TextEncoder().encode(url.href + "\r\n");
if ((await conn.write(request)) != request.length) {
throw new Error("Write failure");
}
const reader = new BufReader(conn);
const header = await reader.readString("\n");
if (header == null) {
throw new Error("Unexpected end of stream");
}
const [_, status, meta] = header.match(/(\d+)\s+(.*)/) ?? ["", "", ""];
if (
status == undefined ||
status.length != 2 ||
(meta != undefined && meta.length > 1024)
) {
throw new Error("Received malformed response");
}
switch (status[0]) {
case "2":
break;
case "3":
if (redirect_count == 0) {
throw new Error("Too many redirects");
}
return fetchGemini(new URL(meta, url), redirect_count - 1);
default:
return {
status,
meta,
};
}
// Read remaining stream
let total = new Uint8Array(0);
let current = new Uint8Array(content_buffer_size);
for (
let count = await reader.read(current);
count != null;
count = await reader.read(current)
) {
let new_total = new Uint8Array(total.length + count);
new_total.set(total, 0);
new_total.set(current.slice(0, count), total.length);
total = new_total;
}
return {
status,
meta,
content: total,
};
} finally {
conn.close();
}
}
if (Deno.args.length != 1) {
console.error("Invalid number of arguments");
Deno.exit(1);
}
const res = await fetchGemini(new URL(Deno.args[0]));
console.log("Status " + res.status + " " + res.meta);
if (res.content != undefined) {
console.log(new TextDecoder().decode(res.content));
}