Skip to content

Commit

Permalink
refactor: add support user defined UA
Browse files Browse the repository at this point in the history
  • Loading branch information
polRk committed Oct 12, 2024
1 parent bbaa1f4 commit 425c9d3
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 9 deletions.
10 changes: 5 additions & 5 deletions packages/connect/src/protocol-grpc/handler-factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ describe("createHandlerFactory()", function () {
const it = res.body![Symbol.asyncIterator]();
await it.next();
const writeError = new Error("write error");
await it.throw?.(writeError).catch(() => {});
await it.throw?.(writeError).catch(() => { });
await expectAsync(catchError).toBeResolvedTo(writeError);
await expectAsync(abortCalled).toBeResolved();
});
Expand All @@ -169,7 +169,7 @@ describe("createHandlerFactory()", function () {
{},
async (req, ctx) => {
handlerContextSignal = ctx.signal;
for (;;) {
for (; ;) {
await new Promise((r) => setTimeout(r, 1));
ctx.signal.throwIfAborted();
}
Expand All @@ -179,7 +179,7 @@ describe("createHandlerFactory()", function () {
httpVersion: "2.0",
method: "POST",
url: `https://example.com/${method.parent.typeName}/${method.name}`,
header: requestHeader(true, timeoutMs, undefined),
header: requestHeader(true, undefined, timeoutMs, undefined),
body: createAsyncIterable([encodeEnvelope(0, new Uint8Array(0))]),
signal: new AbortController().signal,
});
Expand Down Expand Up @@ -268,7 +268,7 @@ describe("createHandlerFactory()", function () {
{},
async (req, ctx) => {
handlerContextSignal = ctx.signal;
for (;;) {
for (; ;) {
await new Promise((r) => setTimeout(r, 1));
ctx.signal.throwIfAborted();
}
Expand All @@ -279,7 +279,7 @@ describe("createHandlerFactory()", function () {
httpVersion: "2.0",
method: "POST",
url: `https://example.com/${method.parent.typeName}/${method.name}`,
header: requestHeader(true, undefined, undefined),
header: requestHeader(true, undefined, undefined, undefined),
body: createAsyncIterable([encodeEnvelope(0, new Uint8Array(0))]),
signal: ac.signal,
});
Expand Down
18 changes: 15 additions & 3 deletions packages/connect/src/protocol-grpc/request-header.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ function listHeaderKeys(header: Headers): string[] {

describe("requestHeader", () => {
it("should create request headers", () => {
const headers = requestHeader(true, undefined, undefined);
const headers = requestHeader(true, undefined, undefined, undefined);
expect(listHeaderKeys(headers)).toEqual([
"content-type",
"te",
"user-agent",
]);
expect(headers.get("Content-Type")).toBe("application/grpc+proto");
expect(headers.get("User-Agent")).toMatch(/^connect-es\/\d+\.\d+\.\d+/);
expect(headers.get("User-Agent")).toMatch(/grpc-js-?\w*\//);
});

it("should create request headers with timeout", () => {
const headers = requestHeader(true, 10, undefined);
const headers = requestHeader(true, undefined, 10, undefined);
expect(listHeaderKeys(headers)).toEqual([
"content-type",
"grpc-timeout",
Expand All @@ -48,6 +48,17 @@ describe("requestHeader", () => {
expect(headers.get("Grpc-Timeout")).toBe("10m");
});

it("should create request headers with userAgent", () => {
const headers = requestHeader(true, "grpc-js-test", 10, undefined);
expect(listHeaderKeys(headers)).toEqual([
"content-type",
"grpc-timeout",
"te",
"user-agent",
]);
expect(headers.get("User-Agent")).toBe("grpc-js-test");
});

it("should create request headers with compression", () => {
const compressionMock: Compression = {
name: "gzip",
Expand All @@ -59,6 +70,7 @@ describe("requestHeader", () => {
true,
undefined,
undefined,
undefined,
[compressionMock],
compressionMock,
);
Expand Down
34 changes: 33 additions & 1 deletion packages/connect/src/protocol-grpc/request-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,39 @@ import {
import { contentTypeJson, contentTypeProto } from "./content-type.js";
import type { Compression } from "../protocol/compression.js";

declare const globalThis: any;

function getPlatfromUserAgent() {
let runtime = ""
let runtimeVersion = "0.0.0"

// Bun
if (globalThis.process?.versions?.bun !== undefined) {
runtime = "-bun";
runtimeVersion = globalThis.process.versions.bun
}
// Deno
else if (globalThis.process?.versions?.deno !== undefined) {
runtime = "-deno";
runtimeVersion = globalThis.process.versions.deno;
}
// Node
else if (globalThis.process?.versions?.node !== undefined) {
runtime = "-node";
runtimeVersion = globalThis.process?.versions?.node
}

return `grpc-js${runtime}/${runtimeVersion} (CONNECT_ES_USER_AGENT)`;
}

/**
* Creates headers for a gRPC request.
*
* @private Internal code, does not follow semantic versioning.
*/
export function requestHeader(
useBinaryFormat: boolean,
userAgent: string | undefined,
timeoutMs: number | undefined,
userProvidedHeaders: HeadersInit | undefined,
): Headers {
Expand All @@ -38,6 +64,11 @@ export function requestHeader(
useBinaryFormat ? contentTypeProto : contentTypeJson,
);

result.set(
headerUserAgent,
userAgent || result.get(headerUserAgent) || getPlatfromUserAgent(),
);

if (timeoutMs !== undefined) {
result.set(headerTimeout, `${timeoutMs}m`);
}
Expand All @@ -55,12 +86,13 @@ export function requestHeader(
*/
export function requestHeaderWithCompression(
useBinaryFormat: boolean,
userAgent: string | undefined,
timeoutMs: number | undefined,
userProvidedHeaders: HeadersInit | undefined,
acceptCompression: Compression[],
sendCompression: Compression | null,
): Headers {
const result = requestHeader(useBinaryFormat, timeoutMs, userProvidedHeaders);
const result = requestHeader(useBinaryFormat, userAgent, timeoutMs, userProvidedHeaders);
if (sendCompression != null) {
result.set(headerEncoding, sendCompression.name);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/connect/src/protocol-grpc/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export function createTransport(opt: CommonTransportOptions): Transport {
url: createMethodUrl(opt.baseUrl, method),
header: requestHeaderWithCompression(
opt.useBinaryFormat,
opt.userAgent,
timeoutMs,
header,
opt.acceptCompression,
Expand Down Expand Up @@ -205,6 +206,7 @@ export function createTransport(opt: CommonTransportOptions): Transport {
url: createMethodUrl(opt.baseUrl, method),
header: requestHeaderWithCompression(
opt.useBinaryFormat,
opt.userAgent,
timeoutMs,
header,
opt.acceptCompression,
Expand Down
9 changes: 9 additions & 0 deletions packages/connect/src/protocol/transport-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ export interface CommonTransportOptions {
*/
interceptors: Interceptor[];

/**
* The user agent to use for all HTTP requests.
*
* https:/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents
*
* Example: `userAgent: "grpc-node-js/22.0.0 (connect-es/1.0.0; typescript/5.0.0)"`
*/
userAgent?: string;

/**
* Options for the JSON format.
* By default, unknown fields are ignored.
Expand Down

0 comments on commit 425c9d3

Please sign in to comment.