diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d279ee0..95ffdf4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ### unreleased changes -none +Breaking changes: + +- `RpcOptions` takes a `timeout` property now. `deadline` property + has been removed. See #138 for details. ### v2.0.0-alpha.29 diff --git a/MANUAL.md b/MANUAL.md index 65c551e2..af07b6d7 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -1409,10 +1409,9 @@ The options: If a key ends with `-bin`, it should contain binary data in base64 encoding, allowing you to send serialized messages. -- `deadline: Date | number` - - Deadline for the call. Can be a specific date or a - timeout in milliseconds. +- `timeout: Date | number` + Timeout for the call in milliseconds. + If a Date object is given, it is used as a deadline. - `interceptors: RpcInterceptor[]` diff --git a/packages/example-angular-app/src/app/grpcweb-server-streaming/grpcweb-server-streaming.component.html b/packages/example-angular-app/src/app/grpcweb-server-streaming/grpcweb-server-streaming.component.html index fd0213f6..066b174c 100644 --- a/packages/example-angular-app/src/app/grpcweb-server-streaming/grpcweb-server-streaming.component.html +++ b/packages/example-angular-app/src/app/grpcweb-server-streaming/grpcweb-server-streaming.component.html @@ -19,7 +19,7 @@
GrpcWebOptions:
-
diff --git a/packages/example-angular-app/src/app/grpcweb-server-streaming/grpcweb-server-streaming.component.ts b/packages/example-angular-app/src/app/grpcweb-server-streaming/grpcweb-server-streaming.component.ts index f1bec6c2..e4d47c7b 100644 --- a/packages/example-angular-app/src/app/grpcweb-server-streaming/grpcweb-server-streaming.component.ts +++ b/packages/example-angular-app/src/app/grpcweb-server-streaming/grpcweb-server-streaming.component.ts @@ -25,7 +25,7 @@ export class GrpcwebServerStreamingComponent { readonly options: GrpcWebOptions = { baseUrl: 'http://localhost:5080', - deadline: Date.now() + 2000, + timeout: Date.now() + 2000, format: 'binary', // simple example for how to add auth headers to each request diff --git a/packages/example-angular-app/src/app/grpcweb-unary/grpcweb-unary.component.html b/packages/example-angular-app/src/app/grpcweb-unary/grpcweb-unary.component.html index bd50801e..62216c48 100644 --- a/packages/example-angular-app/src/app/grpcweb-unary/grpcweb-unary.component.html +++ b/packages/example-angular-app/src/app/grpcweb-unary/grpcweb-unary.component.html @@ -19,7 +19,7 @@
GrpcWebOptions:
-
diff --git a/packages/example-node-grpcweb-transport-client/client.ts b/packages/example-node-grpcweb-transport-client/client.ts index 5d9401a0..87dd8005 100644 --- a/packages/example-node-grpcweb-transport-client/client.ts +++ b/packages/example-node-grpcweb-transport-client/client.ts @@ -65,7 +65,7 @@ async function callServerStream(client: IExampleServiceClient) { const headers = await call.headers; console.log("got response headers: ", headers) - for await (let response of call.response) { + for await (let response of call.responses) { console.log("got response message: ", response) } diff --git a/packages/grpc-transport/src/grpc-transport.ts b/packages/grpc-transport/src/grpc-transport.ts index 3a1d978d..9ec47ed1 100644 --- a/packages/grpc-transport/src/grpc-transport.ts +++ b/packages/grpc-transport/src/grpc-transport.ts @@ -45,9 +45,13 @@ export class GrpcTransport implements RpcTransport { if (options.callOptions) { return options.callOptions; } - return { - deadline: options.deadline - }; + const co: CallOptions = {}; + if (typeof options.timeout === "number") { + co.deadline = Date.now() + options.timeout; + } else if (options.timeout) { + co.deadline = options.timeout; + } + return co; } unary(method: MethodInfo, input: I, options: GrpcCallOptions): UnaryCall { diff --git a/packages/grpcweb-transport/src/grpc-web-format.ts b/packages/grpcweb-transport/src/grpc-web-format.ts index 84c568cd..f0dfee66 100644 --- a/packages/grpcweb-transport/src/grpc-web-format.ts +++ b/packages/grpcweb-transport/src/grpc-web-format.ts @@ -6,7 +6,7 @@ import {GrpcStatusCode} from "./goog-grpc-status-code"; /** * Create fetch API headers for a grpc-web request. */ -export function createGrpcWebRequestHeader(headers: Headers, format: GrpcWebFormat, deadline: Date | number | undefined, meta?: RpcMetadata, userAgent?: string): Headers { +export function createGrpcWebRequestHeader(headers: Headers, format: GrpcWebFormat, timeout: Date | number | undefined, meta?: RpcMetadata, userAgent?: string): Headers { // add meta as headers if (meta) { for (let [k, v] of Object.entries(meta)) { @@ -28,15 +28,23 @@ export function createGrpcWebRequestHeader(headers: Headers, format: GrpcWebForm headers.set('X-Grpc-Web', "1"); if (userAgent) headers.set("X-User-Agent", userAgent); - if (deadline) { - let ts = typeof deadline == "number" ? deadline : deadline.getTime(); - let timeout = ts - Date.now(); - headers.set('grpc-timeout', timeout + 'm'); + + if (typeof timeout === "number") { + if (timeout <= 0) { + // we raise an error ourselves because header "grpc-timeout" must be a positive integer + throw new RpcError(`timeout ${timeout} ms exceeded`, GrpcStatusCode[GrpcStatusCode.DEADLINE_EXCEEDED]); + } + headers.set('grpc-timeout', `${timeout}m`); + } else if (timeout) { + const deadline = timeout.getTime(); + const now = Date.now(); + if (deadline <= now) { + // we raise an error ourselves because header "grpc-timeout" must be a positive integer + throw new RpcError(`deadline ${timeout} exceeded`, GrpcStatusCode[GrpcStatusCode.DEADLINE_EXCEEDED]); + } + headers.set('grpc-timeout', `${deadline - now}m`); } - // let timeout = typeof deadline == "number" ? deadline : deadline instanceof Date ? (deadline.getTime() - Date.now()) : 0; - // if (timeout > 0) { - // headers.set('grpc-timeout', timeout + 'm'); - // } + return headers; } diff --git a/packages/grpcweb-transport/src/grpc-web-transport.ts b/packages/grpcweb-transport/src/grpc-web-transport.ts index 33390afe..eef328d3 100644 --- a/packages/grpcweb-transport/src/grpc-web-transport.ts +++ b/packages/grpcweb-transport/src/grpc-web-transport.ts @@ -97,7 +97,7 @@ export class GrpcWebFetchTransport implements RpcTransport { globalThis.fetch(url, { ...fetchInit, method: 'POST', - headers: createGrpcWebRequestHeader(new globalThis.Headers(), format, opt.deadline, opt.meta), + headers: createGrpcWebRequestHeader(new globalThis.Headers(), format, opt.timeout, opt.meta), body: createGrpcWebRequestBody(inputBytes, format), signal: options.abort ?? null // node-fetch@3.0.0-beta.9 rejects `undefined` }) @@ -197,7 +197,7 @@ export class GrpcWebFetchTransport implements RpcTransport { globalThis.fetch(url, { ...fetchInit, method: 'POST', - headers: createGrpcWebRequestHeader(new globalThis.Headers(), format, opt.deadline, opt.meta), + headers: createGrpcWebRequestHeader(new globalThis.Headers(), format, opt.timeout, opt.meta), body: createGrpcWebRequestBody(inputBytes, format), signal: options.abort ?? null // node-fetch@3.0.0-beta.9 rejects `undefined` }) diff --git a/packages/runtime-rpc/spec/rpc-options.spec.ts b/packages/runtime-rpc/spec/rpc-options.spec.ts index 495e4fa6..e9f00f43 100644 --- a/packages/runtime-rpc/spec/rpc-options.spec.ts +++ b/packages/runtime-rpc/spec/rpc-options.spec.ts @@ -5,8 +5,8 @@ import type {IMessageType} from "@protobuf-ts/runtime"; describe('mergeRpcOptions()', () => { it('does not require seconds argument', function () { - let opt = mergeRpcOptions({deadline: 123}, undefined); - expect(opt).toEqual({deadline: 123}); + let opt = mergeRpcOptions({timeout: 123}, undefined); + expect(opt).toEqual({timeout: 123}); }); it('merges interceptors', function () { diff --git a/packages/runtime-rpc/src/rpc-options.ts b/packages/runtime-rpc/src/rpc-options.ts index ab5029cd..e36bc149 100644 --- a/packages/runtime-rpc/src/rpc-options.ts +++ b/packages/runtime-rpc/src/rpc-options.ts @@ -23,10 +23,10 @@ export interface RpcOptions { meta?: RpcMetadata; /** - * Deadline for the call. Can be given as a Date object or a - * timestamp in milliseconds. + * Timeout for the call in milliseconds. + * If a Date object is given, it is used as a deadline. */ - deadline?: Date | number; + timeout?: number | Date; /** * Interceptors can be used to manipulate request and response data.