Cloudflare Docs
Workers
Visit Workers on GitHub
Set theme to dark (⇧+D)

TCP sockets

The Workers runtime provides the connect() API for creating outbound TCP connections from Workers.

Many application-layer protocols are built on top of the Transmission Control Protocol (TCP). These application-layer protocols, including SSH, MQTT, SMTP, FTP, IRC, and most database wire protocols including MySQL, PostgreSQL, MongoDB, require an underlying TCP socket API in order to work.

​​ connect()

The connect() function returns a TCP socket, with both a readable and writable stream of data. This allows you to read and write data on an ongoing basis, as long as the connection remains open.

connect() is provided as a Runtime API, and is accessed by importing the connect function from cloudflare:sockets. This process is similar to how one imports built-in modules in Node.js. Refer to the following codeblock for an example of creating a TCP socket, writing to it, and returning the readable side of the socket as a response:

import { connect } from 'cloudflare:sockets';
export default {
async fetch(req: Request) {
const gopherAddr = { hostname: "gopher.floodgap.com", port: 70 };
const url = new URL(req.url);
try {
const socket = connect(gopherAddr);
const writer = socket.writable.getWriter()
const encoder = new TextEncoder();
const encoded = encoder.encode(url.pathname + "\r\n");
await writer.write(encoded);
return new Response(socket.readable, { headers: { "Content-Type": "text/plain" } });
} catch (error) {
return new Response("Socket connection failed: " + error, { status: 500 });
}
}
};

​​ SocketAddress

  • hostname string

    • The hostname to connect to. Example: cloudflare.com.
  • port number

    • The port number to connect to. Example: 5432.

​​ SocketOptions

  • secureTransport “off” | “on” | “starttls” — Defaults to off

  • allowHalfOpen boolean — Defaults to false

    • Defines whether the writable side of the TCP socket will automatically close on end-of-file (EOF). When set to false, the writable side of the TCP socket will automatically close on EOF. When set to true, the writable side of the TCP socket will remain open on EOF.
    • This option is similar to that offered by the Node.js net module and allows interoperability with code which utilizes it.

​​ Socket

  • readable : ReadableStream

    • Returns the readable side of the TCP socket.
  • writeable : WriteableStream

    • Returns the writable side of the TCP socket.
  • closed Promise<void>

    • This promise is resolved when the socket is closed and is rejected if the socket encounters an error.
  • close() Promise<void>

    • Closes the TCP socket. Both the readable and writable streams are forcibly closed.
  • startTls() : Socket

    • Upgrades an insecure socket to a secure one that uses TLS, returning a new Socket. Note that in order to call startTls(), you must set secureTransport to starttls when initially calling connect() to create the socket.

​​ Opportunistic TLS (StartTLS)

Many TCP-based systems, including databases and email servers, require that clients use opportunistic TLS (otherwise known as StartTLS) when connecting. In this pattern, the client first creates an insecure TCP socket, without TLS, and then upgrades it to a secure TCP socket, that uses TLS. The connect() API simplifies this by providing a method, startTls(), which returns a new Socket instance that uses TLS:

import { connect } from "cloudflare:sockets"
const address = {
hostname: "example-postgres-db.com",
port: 5432
};
const socket = connect(address, { secureTransport: "starttls" });
const secureSocket = socket.startTls();
  • startTls() can only be called if secureTransport is set to starttls when creating the initial TCP socket.
  • Once startTls() is called, the initial socket is closed and can no longer be read from or written to. In the example above, anytime after startTls() is called, you would use the newly created secureSocket. Any existing readers and writers based off the original socket will no longer work. You must create new readers and writers from the newly created secureSocket.
  • startTls() should only be called once on an existing socket.

​​ Handle errors

To handle errors when creating a new TCP socket, reading from a socket, or writing to a socket, wrap these calls inside try..catch blocks. The following example opens a connection to Google.com, initiates a HTTP request, and returns the response. If any of this fails and throws an exception, it returns a 500 response:

import { connect } from 'cloudflare:sockets';
const connectionUrl = { hostname: "google.com", port: 80 };
export interface Env { }
export default {
async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
try {
const socket = connect(connectionUrl);
const writer = socket.writable.getWriter();
const encoder = new TextEncoder();
const encoded = encoder.encode("GET / HTTP/1.0\r\n\r\n");
await writer.write(encoded);
return new Response(socket.readable, { headers: { "Content-Type": "text/plain" } });
} catch (error) {
return new Response(`Socket connection failed: ${error}`, { status: 500 });
}
}
};

​​ Close TCP connections

You can close a TCP connection by calling close() on the socket. This will close both the readable and writeable sides of the socket.

import { connect } from "cloudflare:sockets"
const socket = connect({ hostname: "my-url.com", port: 70 });
const reader = socket.readable.getReader();
socket.close();
// After close() is called, you can no longer read from the readable side of the socket
const reader = socket.readable.getReader(); // This fails

​​ Considerations