File
Blob: samples/tcp/gopher.js
| 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. |
| 16 | import { connect } from 'cloudflare:sockets'; |
| 17 | |
| 18 | export 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 | }; |