Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow JSON in the gRPC-web transport of @bufbuild/connect-web #334

Merged
merged 1 commit into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions packages/connect-node-test/src/crosstest/ping_pong.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@ describe("ping_pong", () => {
[
// This test does full duplex, receiving before it closes the input.
// In this case, your connect fetch transport never issues the request.
"connect fetch transport (binary) against Node.js (http)",
"connect fetch transport (JSON) against Node.js (http)",
"gRPC-web fetch transport (binary) against Node.js (http)",
"@bufbuild/connect-web (Connect, binary) against connect-go (h1)",
"@bufbuild/connect-web (Connect, binary) against connect-go (h2)",
"@bufbuild/connect-web (Connect, binary) against @bufbuild/connect-node (h1)",
Comment on lines +30 to +32
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list of transports to test is growing. Renamed the keys for better readability.

"@bufbuild/connect-web (Connect, JSON) against @bufbuild/connect-node (h1)",
"@bufbuild/connect-web (Connect, JSON) against connect-go (h1)",
"@bufbuild/connect-web (Connect, JSON) against connect-go (h2)",
"@bufbuild/connect-web (gRPC-web, binary) against @bufbuild/connect-node (h1)",
"@bufbuild/connect-web (gRPC-web, binary) against connect-go (h1)",
"@bufbuild/connect-web (gRPC-web, binary) against connect-go (h2)",
"@bufbuild/connect-web (gRPC-web, JSON) against @bufbuild/connect-node (h1)",
"@bufbuild/connect-web (gRPC-web, JSON) against connect-go (h1)",
"@bufbuild/connect-web (gRPC-web, JSON) against connect-go (h2)",
],
(transport) => {
it("with promise client", async function () {
Expand Down
182 changes: 131 additions & 51 deletions packages/connect-node-test/src/helpers/testserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,105 +47,170 @@ export function createTestServers() {
return address.port;
}

// Node's undici fetch does not support HTTP1.1 (as of Nov 2022), so we cannot
// run it against the H2C server
// The following servers are available through crosstests:
// Source: // https://github.com/bufbuild/connect-web/pull/87
const crosstestConnectGoH1Url = "https://127.0.0.1:8080";
const crosstestConnectGoH2Url = "https://127.0.0.1:8081";
// const crosstestGrpcGoUrl = "https://127.0.0.1:8083";

const transports = {
// TODO add gRPC-web transport once implemented
// TODO add gRPC transport once implemented
// TODO add http1.1 transports once implemented
// The following servers are available through crosstests:
//
// | server | port |
// | ------------- | --- |
// | connect-go h1 | 8080 |
// | connect-go h2 | 8081 |
// | grpc-go | 8083 |
//
// Source: // https://github.com/bufbuild/connect-web/pull/87
"connect Node.js http2 transport (binary) against crosstest (connect-go h1)":
"@bufbuild/connect-node (Connect, binary, http2) against connect-go (h1)": (
options?: Record<string, unknown>
) =>
createConnectHttp2Transport({
...options,
baseUrl: crosstestConnectGoH1Url,
useBinaryFormat: true,
}),
"@bufbuild/connect-node (Connect, JSON, http2) against connect-go (h1)": (
options?: Record<string, unknown>
) =>
createConnectHttp2Transport({
...options,
baseUrl: crosstestConnectGoH1Url,
useBinaryFormat: false,
}),
"@bufbuild/connect-node (Connect, binary, http2) against @bufbuild/connect-node (h2c)":
(options?: Record<string, unknown>) =>
createConnectHttp2Transport({
...options,
baseUrl: `https://127.0.0.1:8080`,
baseUrl: `http://localhost:${getPort(nodeH2cServer)}`,
useBinaryFormat: true,
}),
"connect Node.js http2 transport (JSON) against crosstest (connect-go h1)":
"@bufbuild/connect-node (Connect, JSON, http2) against @bufbuild/connect-node (h2c)":
(options?: Record<string, unknown>) =>
createConnectHttp2Transport({
...options,
baseUrl: `https://127.0.0.1:8080`,
baseUrl: `http://localhost:${getPort(nodeH2cServer)}`,
useBinaryFormat: false,
}),
"connect Node.js http2 transport (binary) against Node.js (H2C)": (
"@bufbuild/connect-node (gRPC-web, binary, http2) against connect-go (h1)":
(options?: Record<string, unknown>) =>
createGrpcWebHttp2Transport({
...options,
baseUrl: crosstestConnectGoH1Url,
useBinaryFormat: true,
}),
"@bufbuild/connect-node (gRPC-web, JSON, http2) against connect-go (h1)": (
options?: Record<string, unknown>
) =>
createConnectHttp2Transport({
createGrpcWebHttp2Transport({
...options,
baseUrl: crosstestConnectGoH1Url,
useBinaryFormat: false,
}),
"@bufbuild/connect-node (gRPC-web, binary, http2) against @bufbuild/connect-node (h2c)":
(options?: Record<string, unknown>) =>
createGrpcWebHttp2Transport({
...options,
baseUrl: `http://localhost:${getPort(nodeH2cServer)}`,
useBinaryFormat: true,
}),
"@bufbuild/connect-node (gRPC-web, JSON, http2) against @bufbuild/connect-node (h2c)":
(options?: Record<string, unknown>) =>
createGrpcWebHttp2Transport({
...options,
baseUrl: `http://localhost:${getPort(nodeH2cServer)}`,
useBinaryFormat: false,
}),
// The following transports from @bufbuild/connect-web use the fetch API.
// Node's undici fetch implementation only supports HTTP1.1 (as of Nov 2022),
// so we cannot run it against servers which do not provide a fallback to
// HTTP1.1.
"@bufbuild/connect-web (gRPC-web, binary) against connect-go (h1)": (
options?: Record<string, unknown>
) =>
createGrpcWebTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeH2cServer)}`,
baseUrl: crosstestConnectGoH1Url,
useBinaryFormat: true,
}),
"connect Node.js http2 transport (JSON) against Node.js (H2C)": (
"@bufbuild/connect-web (gRPC-web, binary) against connect-go (h2)": (
options?: Record<string, unknown>
) =>
createConnectHttp2Transport({
createGrpcWebTransport({
...options,
baseUrl: crosstestConnectGoH2Url,
useBinaryFormat: true,
}),
"@bufbuild/connect-web (gRPC-web, JSON) against connect-go (h1)": (
options?: Record<string, unknown>
) =>
createGrpcWebTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeH2cServer)}`,
baseUrl: crosstestConnectGoH1Url,
useBinaryFormat: false,
}),
"gRPC-web fetch transport (binary) against Node.js (http)": (
"@bufbuild/connect-web (gRPC-web, JSON) against connect-go (h2)": (
options?: Record<string, unknown>
) =>
createGrpcWebTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeHttpServer)}`,
// useBinaryFormat: true,
baseUrl: crosstestConnectGoH2Url,
useBinaryFormat: false,
}),
"connect fetch transport (binary) against Node.js (http)": (
"@bufbuild/connect-web (Connect, binary) against connect-go (h1)": (
options?: Record<string, unknown>
) =>
createConnectTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeHttpServer)}`,
baseUrl: crosstestConnectGoH1Url,
useBinaryFormat: true,
}),
"connect fetch transport (JSON) against Node.js (http)": (
"@bufbuild/connect-web (Connect, binary) against connect-go (h2)": (
options?: Record<string, unknown>
) =>
createConnectTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeHttpServer)}`,
useBinaryFormat: false,
baseUrl: crosstestConnectGoH2Url,
useBinaryFormat: true,
}),
"grpc-web Node.js http2 transport (binary) against crosstest (connect-go h1)":
(options?: Record<string, unknown>) =>
createGrpcWebHttp2Transport({
...options,
baseUrl: `https://127.0.0.1:8080`,
useBinaryFormat: true,
}),
"grpc-web Node.js http2 transport (JSON) against crosstest (connect-go h1)":
(options?: Record<string, unknown>) =>
createGrpcWebHttp2Transport({
...options,
baseUrl: `https://127.0.0.1:8080`,
useBinaryFormat: false,
}),
"grpc-web Node.js http2 transport (binary) against Node.js (H2C)": (
"@bufbuild/connect-web (Connect, JSON) against connect-go (h1)": (
options?: Record<string, unknown>
) =>
createGrpcWebHttp2Transport({
createConnectTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeH2cServer)}`,
useBinaryFormat: true,
baseUrl: crosstestConnectGoH1Url,
useBinaryFormat: false,
}),
"grpc-web Node.js http2 transport (JSON) against Node.js (H2C)": (
"@bufbuild/connect-web (Connect, JSON) against connect-go (h2)": (
options?: Record<string, unknown>
) =>
createGrpcWebHttp2Transport({
createConnectTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeH2cServer)}`,
baseUrl: crosstestConnectGoH2Url,
useBinaryFormat: false,
}),
"@bufbuild/connect-web (gRPC-web, binary) against @bufbuild/connect-node (h1)":
(options?: Record<string, unknown>) =>
createGrpcWebTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeHttpServer)}`,
useBinaryFormat: true,
}),
"@bufbuild/connect-web (gRPC-web, JSON) against @bufbuild/connect-node (h1)":
(options?: Record<string, unknown>) =>
createGrpcWebTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeHttpServer)}`,
useBinaryFormat: false,
}),
"@bufbuild/connect-web (Connect, binary) against @bufbuild/connect-node (h1)":
(options?: Record<string, unknown>) =>
createConnectTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeHttpServer)}`,
useBinaryFormat: true,
}),
"@bufbuild/connect-web (Connect, JSON) against @bufbuild/connect-node (h1)":
(options?: Record<string, unknown>) =>
createConnectTransport({
...options,
baseUrl: `http://localhost:${getPort(nodeHttpServer)}`,
useBinaryFormat: false,
}),
} as const;

return {
Expand Down Expand Up @@ -178,6 +243,21 @@ export function createTestServers() {
});
}
},
describeTransportsOnly(
only: Array<keyof typeof transports>,
specDefinitions: (
transport: () => Transport,
transportName: keyof typeof transports
) => void
) {
for (const [name, transportFactory] of Object.entries(transports)) {
if (only.includes(name as keyof typeof transports)) {
describe(name, () => {
specDefinitions(transportFactory, name as keyof typeof transports);
});
}
}
},
start(): Promise<void> {
return Promise.all([
new Promise<void>((resolve) => {
Expand Down
16 changes: 13 additions & 3 deletions packages/connect-node/src/grpc-web-http2-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ import { connectErrorFromNodeReason } from "./private/connect-error-from-node.js
import { responseHeadersPromise } from "./private/response-headers-promise.js";

const trailerFlag = 0b10000000;
const messageFlag = 0b00000000;

/**
* Options used to configure the gRPC-web transport.
*
* See createGrpcWebHttp2Transport().
*/
export interface GrpcWebHttp2TransportOptions {
/**
* Base URI for all HTTP requests.
Expand All @@ -65,7 +71,8 @@ export interface GrpcWebHttp2TransportOptions {
baseUrl: string;

/**
* By default, connect-node clients use the binary format.
* By default, clients use the binary format for gRPC-web, because
* not all gRPC-web implementations support JSON.
*/
useBinaryFormat?: boolean;

Expand Down Expand Up @@ -155,7 +162,10 @@ export function createGrpcWebHttp2Transport(
);

const headersPromise = responseHeadersPromise(stream);
const envelope = encodeEnvelope(0b00000000, serialize(req.message));
const envelope = encodeEnvelope(
messageFlag,
serialize(req.message)
);
await write(stream, envelope);
await end(stream);

Expand Down Expand Up @@ -281,7 +291,7 @@ export function createGrpcWebHttp2Transport(
);
}
const enveloped = encodeEnvelope(
0b00000000,
messageFlag,
serialize(normalize(message))
);
await write(stream, enveloped);
Expand Down
Loading