RFC1436 Gopher Client for Deno, with basic support for Gopher+.
// Import the client.
import {GopherClient} from 'https://deno.land/x/gopher/mod.ts';
// Create a new GopherClient, optionally specifying the protocol version to use.
const client = new GopherClient({
protocolVersion: GopherProtocol.RFC1436,
});
try {
// Download the Gopher server's menu.
const menuBytes = await client.downloadItem({
hostname: 'gopher.example.com',
tls: TlsSupport.PreferTls,
// Optional: port (default 70)
// Optional: selector (e.g. '/foo')
});
// To display the menu items.
const menu = client.handler.parseMenu(menuBytes);
for (const menuItem of menu.Items) {
console.log(menuItem.toString());
}
// To download a single menu item - the response contains the body and header
// (if it was Gopher+ response) as well as some timing information:
const gopherResponse = client.downloadItem(menuItem);
/** Output timing info for the server response, e.g.
* ```
* Timing info for Gopher Request:
* Waiting for connection: 94ms
* Waiting for first byte: 92ms
* Receiving time: 1ms
* Total request duration: 187ms
* ```
*/
console.log(`Timing info for Gopher Request:
Waiting for connection: ${gopherResponse.timing.waitingDurationMillis}ms
Waiting for first byte: ${gopherResponse.timing.waitingForFirstByteDurationMillis}ms
Receiving time: ${gopherResponse.timing.recievingDuratrionMillis}ms
Total request duration: ${gopherResponse.timing.totalDurationMillis}ms`);
// If it was a text entry (use the MenuItem.Type field to check) then you can
// easily convert to a string.
const text = new TextDecoder().decode(gopherResponse.body);
} catch (error) {
console.error(`Error downloading from Gopher: ${error}`);
}
// Having previously obtained the menu item (see basic example), attributes can
// be retrieved like so:
client.populateAttributes(myMenuItem)
console.log(myMenuItem);
Results:
MenuItem {
Type: '0',
Name: 'A file',
Selector: '/a file.txt',
Hostname: 'gopher.example.com',
Port: 70,
Original: '0A file\t/a file.txt\tgopher.example.com\t70\t+',
Attributes: Map {
'ADMIN' => Map {
'Admin' => 'Foo Bar <foobar@example.com>',
'Mod-Date' => 'Sun Feb 21 20:19:18 2021 <20210221201918>'
},
'VIEWS' => Map { 'text/plain' => '<1k>' }
}
}