From 6d923174acab477e02319c42f00e0097e274dafc Mon Sep 17 00:00:00 2001 From: unnoq Date: Mon, 29 Sep 2025 10:31:03 +0700 Subject: [PATCH 1/2] fix(server): avoid opaque path in URL for better compatibility --- .../client/src/adapters/message-port/rpc-link.test.ts | 4 ++-- packages/client/src/adapters/message-port/rpc-link.ts | 2 +- .../client/src/adapters/websocket/rpc-link.test.ts | 2 +- packages/client/src/adapters/websocket/rpc-link.ts | 2 +- .../server/src/adapters/bun-ws/rpc-handler.test.ts | 2 +- .../server/src/adapters/crossws/rpc-handler.test.ts | 2 +- .../src/adapters/message-port/rpc-handler.test.ts | 2 +- .../server/src/adapters/websocket/rpc-handler.test.ts | 2 +- packages/server/src/adapters/ws/rpc-handler.test.ts | 2 +- packages/standard-server-peer/src/codec.test.ts | 2 ++ packages/standard-server-peer/src/codec.ts | 11 ++++++++--- 11 files changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/client/src/adapters/message-port/rpc-link.test.ts b/packages/client/src/adapters/message-port/rpc-link.test.ts index 777002369..341160d46 100644 --- a/packages/client/src/adapters/message-port/rpc-link.test.ts +++ b/packages/client/src/adapters/message-port/rpc-link.test.ts @@ -36,7 +36,7 @@ describe('rpcLink', () => { expect(id).toBeTypeOf('string') expect(payload).toEqual({ - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: { json: 'input' }, headers: {}, method: 'POST', @@ -56,7 +56,7 @@ describe('rpcLink', () => { expect(id).toBeTypeOf('string') expect(payload).toEqual({ - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: expect.any(FormData), headers: expect.any(Object), method: 'POST', diff --git a/packages/client/src/adapters/message-port/rpc-link.ts b/packages/client/src/adapters/message-port/rpc-link.ts index 69129341a..796dccd9d 100644 --- a/packages/client/src/adapters/message-port/rpc-link.ts +++ b/packages/client/src/adapters/message-port/rpc-link.ts @@ -16,6 +16,6 @@ export interface RPCLinkOptions export class RPCLink extends StandardRPCLink { constructor(options: RPCLinkOptions) { const linkClient = new LinkMessagePortClient(options) - super(linkClient, { ...options, url: 'orpc:/' }) + super(linkClient, { ...options, url: 'orpc://localhost' }) } } diff --git a/packages/client/src/adapters/websocket/rpc-link.test.ts b/packages/client/src/adapters/websocket/rpc-link.test.ts index 481330938..06b099655 100644 --- a/packages/client/src/adapters/websocket/rpc-link.test.ts +++ b/packages/client/src/adapters/websocket/rpc-link.test.ts @@ -36,7 +36,7 @@ describe('rpcLink', () => { expect(id).toBeTypeOf('string') expect(payload).toEqual({ - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: { json: 'input' }, headers: {}, method: 'POST', diff --git a/packages/client/src/adapters/websocket/rpc-link.ts b/packages/client/src/adapters/websocket/rpc-link.ts index e16a2d06b..b1dbdf40a 100644 --- a/packages/client/src/adapters/websocket/rpc-link.ts +++ b/packages/client/src/adapters/websocket/rpc-link.ts @@ -17,6 +17,6 @@ export class RPCLink extends StandardRPCLink { constructor(options: RPCLinkOptions) { const linkClient = new LinkWebsocketClient(options) - super(linkClient, { ...options, url: 'orpc:/' }) + super(linkClient, { ...options, url: 'orpc://localhost' }) } } diff --git a/packages/server/src/adapters/bun-ws/rpc-handler.test.ts b/packages/server/src/adapters/bun-ws/rpc-handler.test.ts index f52218799..12072f5ef 100644 --- a/packages/server/src/adapters/bun-ws/rpc-handler.test.ts +++ b/packages/server/src/adapters/bun-ws/rpc-handler.test.ts @@ -23,7 +23,7 @@ describe('rpcHandler', async () => { } const string_request_message = await encodeRequestMessage('19', MessageType.REQUEST, { - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: { json: 'input' }, headers: {}, method: 'POST', diff --git a/packages/server/src/adapters/crossws/rpc-handler.test.ts b/packages/server/src/adapters/crossws/rpc-handler.test.ts index bc844958e..916854657 100644 --- a/packages/server/src/adapters/crossws/rpc-handler.test.ts +++ b/packages/server/src/adapters/crossws/rpc-handler.test.ts @@ -24,7 +24,7 @@ describe('rpcHandler', async () => { const ping_request_message = { rawData: await encodeRequestMessage('19', MessageType.REQUEST, { - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: { json: 'input' }, headers: {}, method: 'POST', diff --git a/packages/server/src/adapters/message-port/rpc-handler.test.ts b/packages/server/src/adapters/message-port/rpc-handler.test.ts index a3ae549e3..275e2fc07 100644 --- a/packages/server/src/adapters/message-port/rpc-handler.test.ts +++ b/packages/server/src/adapters/message-port/rpc-handler.test.ts @@ -35,7 +35,7 @@ describe('rpcHandler', async () => { }) const string_request_message = await encodeRequestMessage('19', MessageType.REQUEST, { - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: { json: 'input' }, headers: {}, method: 'POST', diff --git a/packages/server/src/adapters/websocket/rpc-handler.test.ts b/packages/server/src/adapters/websocket/rpc-handler.test.ts index d8caf1685..1027b99ad 100644 --- a/packages/server/src/adapters/websocket/rpc-handler.test.ts +++ b/packages/server/src/adapters/websocket/rpc-handler.test.ts @@ -35,7 +35,7 @@ describe('rpcHandler', async () => { const string_request_message = { data: await encodeRequestMessage('19', MessageType.REQUEST, { - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: { json: 'input' }, headers: {}, method: 'POST', diff --git a/packages/server/src/adapters/ws/rpc-handler.test.ts b/packages/server/src/adapters/ws/rpc-handler.test.ts index 3c4fddde4..f81a3c594 100644 --- a/packages/server/src/adapters/ws/rpc-handler.test.ts +++ b/packages/server/src/adapters/ws/rpc-handler.test.ts @@ -35,7 +35,7 @@ describe('rpcHandler', async () => { const string_request_message = { data: await encodeRequestMessage('19', MessageType.REQUEST, { - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: { json: 'input' }, headers: {}, method: 'POST', diff --git a/packages/standard-server-peer/src/codec.test.ts b/packages/standard-server-peer/src/codec.test.ts index 7c5f5b386..91fb8ba3e 100644 --- a/packages/standard-server-peer/src/codec.test.ts +++ b/packages/standard-server-peer/src/codec.test.ts @@ -49,6 +49,8 @@ describe('encode/decode request message', () => { }) describe.each([ + ['GET', new URL('orpc://localhost/api/v1/users/1?a=1&b=2'), {}], + ['GET', new URL('orpc:///localhost/api/v1/users/1?a=1&b=2'), {}], ['GET', new URL('orpc://example.com/api/v1/users/1?a=1&b=2'), {}], ['GET', new URL('orpc:///example.com/api/v1/users/1?a=1&b=2'), {}], ['GET', new URL('orpc:/api/v1/users/1?a=1&b=2'), {}], diff --git a/packages/standard-server-peer/src/codec.ts b/packages/standard-server-peer/src/codec.ts index af9f1c540..9dcbc95e9 100644 --- a/packages/standard-server-peer/src/codec.ts +++ b/packages/standard-server-peer/src/codec.ts @@ -54,7 +54,7 @@ interface SerializedRequestPayload { /** * The url of the request * - * might be relative path if it starts with `orpc:/` + * might be relative path if origin is `orpc://localhost` */ u: string @@ -120,7 +120,7 @@ export async function encodeRequestMessage( ) const serializedPayload: SerializedRequestPayload = { - u: request.url.toString().replace(/^orpc:\//, '/'), + u: request.url.toString().replace(/^orpc:\/\/localhost\//, '/'), b: processedBody instanceof Blob ? undefined : processedBody, h: Object.keys(processedHeaders).length > 0 ? processedHeaders : undefined, m: request.method === 'POST' ? undefined : request.method, @@ -159,7 +159,12 @@ export async function decodeRequestMessage(raw: EncodedMessage): Promise( From 17333c7ee800bdc9056fab475b1d376f34dc59c1 Mon Sep 17 00:00:00 2001 From: unnoq Date: Mon, 29 Sep 2025 10:40:05 +0700 Subject: [PATCH 2/2] fix and improve --- packages/client/src/adapters/websocket/rpc-link.test.ts | 2 +- packages/standard-server-peer/src/codec.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/client/src/adapters/websocket/rpc-link.test.ts b/packages/client/src/adapters/websocket/rpc-link.test.ts index 06b099655..2d1d3595b 100644 --- a/packages/client/src/adapters/websocket/rpc-link.test.ts +++ b/packages/client/src/adapters/websocket/rpc-link.test.ts @@ -56,7 +56,7 @@ describe('rpcLink', () => { expect(id).toBeTypeOf('string') expect(payload).toEqual({ - url: new URL('orpc:/ping'), + url: new URL('orpc://localhost/ping'), body: { json: 'input' }, headers: {}, method: 'POST', diff --git a/packages/standard-server-peer/src/codec.ts b/packages/standard-server-peer/src/codec.ts index 9dcbc95e9..702da68c3 100644 --- a/packages/standard-server-peer/src/codec.ts +++ b/packages/standard-server-peer/src/codec.ts @@ -3,6 +3,9 @@ import type { EncodedMessage } from './types' import { isAsyncIteratorObject, readAsBuffer, stringifyJSON } from '@orpc/shared' import { flattenHeader, generateContentDisposition, getFilenameFromContentDisposition } from '@orpc/standard-server' +const SHORTABLE_ORIGIN = 'orpc://localhost' +const SHORTABLE_ORIGIN_MATCHER = /^orpc:\/\/localhost\// + export enum MessageType { REQUEST = 1, RESPONSE = 2, @@ -120,7 +123,7 @@ export async function encodeRequestMessage( ) const serializedPayload: SerializedRequestPayload = { - u: request.url.toString().replace(/^orpc:\/\/localhost\//, '/'), + u: request.url.toString().replace(SHORTABLE_ORIGIN_MATCHER, '/'), b: processedBody instanceof Blob ? undefined : processedBody, h: Object.keys(processedHeaders).length > 0 ? processedHeaders : undefined, m: request.method === 'POST' ? undefined : request.method, @@ -160,7 +163,7 @@ export async function decodeRequestMessage(raw: EncodedMessage): Promise