File
Blob: src/workerd/api/tests/connect-handler-test.js
| 1 | // Copyright (c) 2026 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 | import { connect } from 'cloudflare:sockets'; |
| 5 | import { strictEqual } from 'assert'; |
| 6 | |
| 7 | export const connectHandler = { |
| 8 | async test() { |
| 9 | // Check that the connect handler can send a message through a socket |
| 10 | const socket = connect('localhost:8081'); |
| 11 | await socket.opened; |
| 12 | const dec = new TextDecoder(); |
| 13 | let result = ''; |
| 14 | for await (const chunk of socket.readable) { |
| 15 | result += dec.decode(chunk, { stream: true }); |
| 16 | } |
| 17 | result += dec.decode(); |
| 18 | strictEqual(result, 'hello'); |
| 19 | await socket.closed; |
| 20 | }, |
| 21 | }; |
| 22 | |
| 23 | export const connectHandlerProxy = { |
| 24 | async test() { |
| 25 | // Check that we can get a message proxied through a connect handler. This call connects us with |
| 26 | // an instance of Server, which serves as a proxy for an instance of OtherServer, as defined in |
| 27 | // connect-handler-test-proxy.js. |
| 28 | const socket = connect('localhost:8082'); |
| 29 | await socket.opened; |
| 30 | const dec = new TextDecoder(); |
| 31 | let result = ''; |
| 32 | for await (const chunk of socket.readable) { |
| 33 | result += dec.decode(chunk, { stream: true }); |
| 34 | } |
| 35 | result += dec.decode(); |
| 36 | strictEqual(result, 'hello-from-endpoint'); |
| 37 | await socket.closed; |
| 38 | }, |
| 39 | }; |
| 40 | |
| 41 | // Exercises the service-binding path: fetcher.connect("host:port") -> WorkerEntrypoint::connect on |
| 42 | // the target worker. Unlike the TCP listener path (which presents the listener's bound address as |
| 43 | // the authority), the service-binding path forwards the caller's authority string verbatim, so we |
| 44 | // can assert strict equality between what the client passed and what the server observes as |
| 45 | // socket.opened.localAddress. |
| 46 | export const localAddressViaServiceBinding = { |
| 47 | async test(ctrl, env) { |
| 48 | const AUTHORITY = 'example.com:1234'; |
| 49 | const socket = env.TARGET.connect(AUTHORITY); |
| 50 | await socket.opened; |
| 51 | const dec = new TextDecoder(); |
| 52 | let result = ''; |
| 53 | for await (const chunk of socket.readable) { |
| 54 | result += dec.decode(chunk, { stream: true }); |
| 55 | } |
| 56 | result += dec.decode(); |
| 57 | strictEqual(result, `OK:${AUTHORITY}`); |
| 58 | await socket.closed; |
| 59 | }, |
| 60 | }; |
| 61 | |
| 62 | export default { |
| 63 | async connect(socket) { |
| 64 | const enc = new TextEncoder(); |
| 65 | let writer = socket.writable.getWriter(); |
| 66 | await writer.write(enc.encode('hello')); |
| 67 | await writer.close(); |
| 68 | }, |
| 69 | }; |