Skip to content
File

Blob: samples/tcp/gopher.js

javascript52 lines
1// Copyright (c) 2022-2023 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5// This is an ESM module implementing a simple Gopher client. Gopher is an old alternative to HTTP,
6// it is also a simple protocol which makes it ideal for demoing TCP sockets in workerd.
7//
8// This module implements both direct and proxied TCP connections. In order to make use of the
9// proxy you'll need to have an HTTP proxy running on port 1234 (this can be changed in config.capnp).
10//
11// Example usage once you have workerd running:
12//
13// - curl localhost:8080/ # Retrieve the main segment in gopher.floodgap.com.
14// - curl localhost:8080/gstats # Retrieve the "gstats" segment.
15// - curl localhost:8080/gstats?use_proxy=1 # Retrieve the "gstats" segment using the configured proxy.
16import { connect } from 'cloudflare:sockets';
17 
18export default {
19 async fetch(req, env) {
20 const gopherAddr = "gopher.floodgap.com:70";
21 
22 const url = new URL(req.url);
23 const useProxy = url.searchParams.has("use_proxy");
24 
25 try {
26 const socket = useProxy ? env.proxy.connect(gopherAddr) : connect(gopherAddr);
27 
28 // Write data to the socket. Specifically the "selector" followed by CR+LF.
29 const writer = socket.writable.getWriter()
30 const encoder = new TextEncoder();
31 const encoded = encoder.encode(url.pathname + "\r\n");
32 await writer.write(encoded);
33 
34 // The Gopher server should now return a response to us and close the connection.
35 // So start reading from the socket.
36 const decoder = new TextDecoder();
37 
38 let gopherResponse = "";
39 for await (const chunk of socket.readable) {
40 gopherResponse += decoder.decode(chunk, { stream: true });
41 }
42 gopherResponse += decoder.decode();
43 
44 console.log("Read ", gopherResponse.length, " from Gopher server");
45 
46 return new Response(gopherResponse, { headers: { "Content-Type": "text/plain" } });
47 } catch (error) {
48 return new Response("Socket connection failed: " + error, { status: 500 });
49 }
50 }
51};