-
Notifications
You must be signed in to change notification settings - Fork 16k
[Workers] Runtime API: TCP Socket API Docs connect()
#8795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
69d5c67
add /runtime-apis/connect
irvinebroque a4f0485
Add note about global scope
irvinebroque b977b02
Add error handling section
irvinebroque fe93dd3
Add section about closing sockets with examples of allowHalfOpen
irvinebroque c5bbd40
Fix example, add note about --experimental-local
irvinebroque 9328883
Apply suggestions from code review
irvinebroque 1239a99
Apply suggestions from code review
irvinebroque 40a6dd1
Remove expectedHostname
irvinebroque 90b5dd0
Clarify connection between connect() and max open connections
irvinebroque bb6c40f
Fix broken link
irvinebroque a01a579
Move StartTlS considerations up to StartTLS section
irvinebroque cf14c87
Fix error handling code block
irvinebroque 695eceb
Formatting, intro sentence to error handling example
irvinebroque b4086f8
Clarify that existing readers/writers cannot be reused after .startTls()
irvinebroque 0f42b61
Rename page from connect to tcp-sockets
irvinebroque ef9cdca
Add /protocols page
irvinebroque e345890
Edits to /protocols
irvinebroque 136e37f
/runtime-apis/connect -> /runtime-apis/tcp-sockets
irvinebroque 6d5a4af
Fix broken URL
irvinebroque c928cfb
workers: Apply suggestions from code review
elithrar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| --- | ||
| pcx_content_type: concept | ||
| title: Protocols | ||
| --- | ||
|
|
||
| # Protocols | ||
|
|
||
| Cloudflare Workers support the following protocols and interfaces: | ||
|
|
||
| | Protocol | Inbound | Outbound | | ||
| |---|---|---| | ||
| | **HTTP / HTTPS** | Handle incoming HTTP requests using the [`fetch()` handler](/workers/runtime-apis/fetch-event/#syntax-module-worker/) | Make HTTP subrequests using the [`fetch()` API](/workers/runtime-apis/fetch/) | | ||
| | **Direct TCP sockets** | Support for handling inbound TCP connections is [coming soon](https://blog.cloudflare.com/introducing-socket-workers/) | Create outbound TCP connections using the [`connect()` API](/workers/runtime-apis/tcp-sockets/) | | ||
| | **WebSockets** | Accept incoming WebSocket connections using the [`WebSocket` API](/workers/runtime-apis/websockets/), or with [MQTT over WebSockets (Pub/Sub)](/pub-sub/learning/websockets-browsers/) | [MQTT over WebSockets (Pub/Sub)](/pub-sub/learning/websockets-browsers/) | | ||
| | **MQTT** | Handle incoming messages to an MQTT broker with [Pub Sub](/pub-sub/learning/integrate-workers/) | Support for publishing MQTT messages to an MQTT topic is [coming soon](/pub-sub/learning/integrate-workers/) | | ||
| | **HTTP/3 (QUIC)** | Accept inbound requests over [HTTP/3](https://www.cloudflare.com/learning/performance/what-is-http3/) by enabling it on your [zone](/fundamentals/get-started/concepts/accounts-and-zones/#zones) in the network tab of the [Cloudflare dashboard](https://dash.cloudflare.com/). | | | ||
| | **SMTP** | Use [Email Workers](/email-routing/email-workers/) to process and forward email, without having to manage TCP connections to SMTP email servers | [Email Workers](/email-routing/email-workers/) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| --- | ||
| pcx_content_type: configuration | ||
| title: TCP sockets | ||
| --- | ||
|
|
||
| # TCP sockets | ||
|
|
||
| The Workers runtime provides the `connect()` API for creating outbound [TCP connections](https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/) 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](/workers/runtime-apis/streams/readablestream/) and [writable](/workers/runtime-apis/streams/writablestream/) 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](/workers/runtime-apis/), 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: | ||
|
|
||
| ```typescript | ||
| 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 }); | ||
| } | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| {{<definitions>}} | ||
|
|
||
| - {{<code>}}connect(address: {{<type-link href="/workers/runtime-apis/tcp-sockets/#socketaddress">}}SocketAddress{{</type-link>}} | string, options?: {{<prop-meta>}}optional{{</prop-meta>}} {{<type-link href="/workers/runtime-apis/tcp-sockets/#socketoptions">}}SocketOptions{{</type-link>}}){{</code>}} : {{<type-link href="/workers/runtime-apis/tcp-sockets/#socket">}}`Socket`{{</type-link>}} | ||
| - `connect()` accepts either a URL string or [`SocketAddress`](/workers/runtime-apis/tcp-sockets/#socketaddress) to define the hostname and port number to connect to, and an optional configuration object, [`SocketOptions`](/workers/runtime-apis/tcp-sockets/#socketoptions). It returns an instance of a [`Socket`](/workers/runtime-apis/tcp-sockets/#socket). | ||
| {{</definitions>}} | ||
|
|
||
| ### `SocketAddress` | ||
|
|
||
| {{<definitions>}} | ||
|
|
||
| - `hostname` {{<type>}}string{{</type>}} | ||
| - The hostname to connect to. Example: `cloudflare.com`. | ||
|
|
||
| - `port` {{<type>}}number{{</type>}} | ||
| - The port number to connect to. Example: `5432`. | ||
|
|
||
| {{</definitions>}} | ||
|
|
||
| ### `SocketOptions` | ||
|
|
||
| {{<definitions>}} | ||
|
|
||
| - `secureTransport` {{<type>}}"off" | "on" | "starttls"{{</type>}} — Defaults to `off` | ||
| - Specifies whether or not to use [TLS](https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/) when creating the TCP socket. | ||
| - `off` — Do not use TLS. | ||
| - `on` — Use TLS. | ||
| - `starttls` — Do not use TLS initially, but allow the socket to be upgraded to use TLS by calling [`startTls()`](/workers/runtime-apis/tcp-sockets/#how-to-implement-the-starttls-pattern). | ||
|
|
||
| - `allowHalfOpen` {{<type>}}boolean{{</type>}} — 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](https://nodejs.org/api/net.html) and allows interoperability with code which utilizes it. | ||
|
|
||
| {{</definitions>}} | ||
|
|
||
| ### `Socket` | ||
|
|
||
| {{<definitions>}} | ||
|
|
||
| - {{<code>}}readable(){{</code>}} : {{<type-link href="/workers/runtime-apis/streams/readablestream/">}}ReadableStream{{</type-link>}} | ||
| - Returns the readable side of the TCP socket. | ||
|
|
||
| - {{<code>}}writeable(){{</code>}} : {{<type-link href="/workers/runtime-apis/streams/writablestream/">}}WriteableStream{{</type-link>}} | ||
| - Returns the writable side of the TCP socket. | ||
|
|
||
| - `closed()` {{<type>}}`Promise<void>`{{</type>}} | ||
| - This promise is resolved when the socket is closed and is rejected if the socket encounters an error. | ||
|
|
||
| - `close()` {{<type>}}`Promise<void>`{{</type>}} | ||
| - Closes the TCP socket. Both the readable and writable streams are forcibly closed. | ||
|
|
||
| - {{<code>}}startTls(){{</code>}} : {{<type-link href="/workers/runtime-apis/tcp-sockets/#socket">}}Socket{{</type-link>}} | ||
| - Upgrades an insecure socket to a secure one that uses TLS, returning a new [Socket](/workers/runtime-apis/tcp-sockets#socket). Note that in order to call `startTls()`, you must set [`secureTransport`](/workers/runtime-apis/tcp-sockets/#socketoptions) to `starttls` when initially calling `connect()` to create the socket. | ||
|
|
||
| {{</definitions>}} | ||
|
|
||
| ## Opportunistic TLS (StartTLS) | ||
|
|
||
| Many TCP-based systems, including databases and email servers, require that clients use opportunistic TLS (otherwise known as [StartTLS](https://en.wikipedia.org/wiki/Opportunistic_TLS)) 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: | ||
|
|
||
| ```typescript | ||
| 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: | ||
|
|
||
| ```typescript | ||
| 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. | ||
|
|
||
| ```typescript | ||
| 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 | ||
|
|
||
| - When developing locally with [Wrangler](/workers/wrangler/), you must pass the [`--experimental-local`](/workers/wrangler/commands/#dev) flag, instead of the `--local` flag, to use `connect()`. | ||
| - TCP sockets must be created within the [`fetch()` handler](/workers/get-started/guide/#3-write-code) of a Worker. TCP sockets cannot be created in global scope and shared across requests. | ||
| - Each open TCP socket counts towards the maximum number of [open connections](/workers/platform/limits/#simultaneous-open-connections) that can be simultaneously open. | ||
| - By default, Workers cannot create outbound TCP connections on port `25` to send email to SMTP mail servers. [Cloudflare Email Workers](/email-routing/email-workers/) provides APIs to process and forward email. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.