コンテンツにスキップ

TCPソケット

Workersランタイムは、WorkersからのアウトバウンドTCP接続を作成するためのconnect() APIを提供します。

多くのアプリケーション層プロトコルは、トランスミッションコントロールプロトコル(TCP)の上に構築されています。これらのアプリケーション層プロトコルには、SSH、MQTT、SMTP、FTP、IRC、MySQL、PostgreSQL、MongoDBを含むほとんどのデータベースワイヤプロトコルが含まれており、機能するためには基盤となるTCPソケットAPIが必要です。

connect()

connect()関数は、読み取り可能および書き込み可能なデータストリームを持つTCPソケットを返します。これにより、接続がオープンな限り、データを継続的に読み書きできます。

connect()Runtime APIとして提供されており、cloudflare:socketsからconnect関数をインポートすることでアクセスされます。このプロセスは、Node.jsで組み込みモジュールをインポートする方法に似ています。以下のコードブロックは、TCPソケットを作成し、それに書き込み、ソケットの読み取り可能な側をレスポンスとして返す例です:

import { connect } from 'cloudflare:sockets';
export default {
async fetch(req): Promise<Response> {
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("ソケット接続に失敗しました: " + error, { status: 500 });
}
}
} satisfies ExportedHandler;
  • connect(address: SocketAddress | string, options?: optional SocketOptions) : Socket
    • connect()は、接続先のホスト名とポート番号を定義するために、URL文字列またはSocketAddressを受け入れ、オプションの設定オブジェクトSocketOptionsを受け取ります。これはSocketのインスタンスを返します。

SocketAddress

  • hostname string

    • 接続先のホスト名。例: cloudflare.com
  • port number

    • 接続先のポート番号。例: 5432

SocketOptions

  • secureTransport “off” | “on” | “starttls” — デフォルトはoff

    • TCPソケットを作成する際にTLSを使用するかどうかを指定します。
    • off — TLSを使用しない。
    • on — TLSを使用する。
    • starttls — 最初はTLSを使用せず、startTls()を呼び出すことでソケットをTLSを使用するようにアップグレードできる。
  • allowHalfOpen boolean — デフォルトはfalse

    • TCPソケットの書き込み可能な側がファイルの終わり(EOF)で自動的に閉じるかどうかを定義します。falseに設定すると、TCPソケットの書き込み可能な側はEOFで自動的に閉じます。trueに設定すると、TCPソケットの書き込み可能な側はEOFで開いたままになります。
    • このオプションは、Node.jsのnetモジュールが提供するものに似ており、それを利用するコードとの相互運用性を可能にします。

SocketInfo

  • remoteAddress string | null

    • ソケットが接続されているリモートピアのアドレス。常に設定されるわけではありません。
  • localAddress string | null

    • このソケットのローカルネットワークエンドポイントのアドレス。常に設定されるわけではありません。

Socket

  • readable : ReadableStream

    • TCPソケットの読み取り可能な側を返します。
  • writable : WritableStream

    • TCPソケットの書き込み可能な側を返します。
    • 返されるWritableStreamは、Uint8Arrayまたはそのビューのチャンクのみを受け入れます。
  • opened Promise<SocketInfo>

    • このプロミスは、ソケット接続が確立されたときに解決され、ソケットがエラーに遭遇した場合は拒否されます。
  • closed Promise<void>

    • このプロミスは、ソケットが閉じられたときに解決され、ソケットがエラーに遭遇した場合は拒否されます。
  • close() Promise<void>

    • TCPソケットを閉じます。読み取り可能なストリームと書き込み可能なストリームの両方が強制的に閉じられます。
  • startTls() : Socket

    • 不正なソケットをTLSを使用する安全なソケットにアップグレードし、新しいSocketを返します。startTls()を呼び出すには、最初にソケットを作成する際にsecureTransportstarttlsに設定する必要があります。

オポチュニスティックTLS(StartTLS)

多くのTCPベースのシステム、データベースやメールサーバーを含む、は、接続時にクライアントがオポチュニスティックTLS(別名StartTLS)を使用することを要求します。このパターンでは、クライアントは最初にTLSなしで不正なTCPソケットを作成し、その後それをTLSを使用する安全なTCPソケットにアップグレードします。connect() APIは、startTls()メソッドを提供することでこれを簡素化し、TLSを使用する新しいSocketインスタンスを返します:

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()は、初期TCPソケットを作成する際にsecureTransportstarttlsに設定されている場合にのみ呼び出すことができます。
  • startTls()が呼び出されると、初期ソケットは閉じられ、もはや読み書きできなくなります。上記の例では、startTls()が呼び出された後は、常に新しく作成されたsecureSocketを使用します。元のソケットに基づく既存のリーダーとライターはもはや機能しません。新しく作成されたsecureSocketから新しいリーダーとライターを作成する必要があります。
  • startTls()は、既存のソケットで一度だけ呼び出すべきです。

エラーの処理

新しいTCPソケットを作成したり、ソケットから読み取ったり、ソケットに書き込んだりする際のエラーを処理するには、これらの呼び出しをtry..catchブロック内にラップします。以下の例は、Google.comへの接続を開き、HTTPリクエストを開始し、レスポンスを返します。これが失敗して例外をスローした場合、500レスポンスを返します:

import { connect } from 'cloudflare:sockets';
const connectionUrl = { hostname: "google.com", port: 80 };
export interface Env { }
export default {
async fetch(req, env, ctx): 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(`ソケット接続に失敗しました: ${error}`, { status: 500 });
}
}
} satisfies ExportedHandler<Env>;

TCP接続のクローズ

ソケットでclose()を呼び出すことでTCP接続を閉じることができます。これにより、ソケットの読み取り可能な側と書き込み可能な側の両方が閉じられます。

import { connect } from "cloudflare:sockets"
const socket = connect({ hostname: "my-url.com", port: 70 });
const reader = socket.readable.getReader();
socket.close();
// close()が呼び出された後は、ソケットの読み取り可能な側から読み取ることはできません
const reader = socket.readable.getReader(); // これは失敗します

考慮事項

  • Cloudflare IP範囲へのアウトバウンドTCPソケットは一時的にブロックされますが、すぐに再有効化されます。
  • TCPソケットはグローバルスコープで作成できず、リクエスト間で共有することはできません。常にハンドラー内でTCPソケットを作成する必要があります(例: fetch()scheduled()queue())またはalarm()
  • 各オープンTCPソケットは、同時にオープンできる最大のオープン接続数にカウントされます。
  • デフォルトでは、WorkersはSMTPメールサーバーにメールを送信するためにポート25でアウトバウンドTCP接続を作成できません。Cloudflare Email Workersは、メールを処理して転送するためのAPIを提供します。
  • インバウンドTCP接続の処理のサポートは近日中に登場予定です。現在、CONNECT HTTPメソッドを使用してWorkerにインバウンドTCP接続を行うことはできません。

トラブルシューティング

TCPソケットを使用する際に見られる一般的なエラーメッセージの説明を確認し、エラーメッセージの意味とそれを解決する方法を確認してください。

proxy request failed, cannot connect to the specified address

ソケットが接続しようとしているアドレスが許可されていません。許可されていないアドレスの例には、Cloudflare IP、localhost、およびプライベートネットワークIPが含まれます。

ポート80または443のアドレスにHTTPリクエストを行う必要がある場合は、fetchを使用してください。

TCP Loop detected

ソケットがアウトバウンド接続を開始したWorkerに接続しています。言い換えれば、Workerが自分自身に接続しています。これは現在サポートされていません。

Connections to port 25 are prohibited

ソケットがポート25のアドレスに接続しています。これは通常、SMTPメールサーバーで使用されるポートです。Workersはポート25でアウトバウンド接続を作成できません。Cloudflare Email Workersを代わりに使用してください。