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

feat: add authority header setup capability #50

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export type ColoredSelectOption = {
id: string;
label: string;
url: string;
authority: string;
color: string;
};
25 changes: 14 additions & 11 deletions src/app/hooks/protocols/grpc/prepare-request.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import {
GrpcClientRequestOptions,
GrpcMethodType,
GrpcOptions,
GrpcTlsConfig,
GrpcTlsType,
} from '@core/types';
import { Collection, CollectionType, GrpcTab, TlsPreset } from '@storage';
import { GrpcClientRequestOptions, GrpcMethodType, GrpcOptions, GrpcTlsConfig, GrpcTlsType } from "@core/types";
import { Collection, CollectionType, GrpcTab, TlsPreset } from "@storage";

function getRequestAddress(tab: GrpcTab<GrpcMethodType>): string {
if (tab.data.url && tab.data.url.length > 0) {
return tab.data.url;
}

throw new Error('Address is empty.');
throw new Error("Address is empty.");
}

function getRequestAuthority(tab: GrpcTab<GrpcMethodType>): string | undefined {
if (tab.data.authority && tab.data.authority.length > 0) {
return tab.data.authority;
}

return undefined;
}

export function getTlsOptions(presets: TlsPreset[], id?: string): GrpcTlsConfig<GrpcTlsType> {
Expand Down Expand Up @@ -43,6 +45,7 @@ export function getOptions(
serviceName: service.name,
methodName: method.name,
address: getRequestAddress(tab),
authorityOverride: getRequestAuthority(tab),
tls,
},
];
Expand All @@ -53,7 +56,7 @@ export function getOptions(

export function parseRequest(tab: GrpcTab<GrpcMethodType>): Record<string, unknown> {
try {
const request = JSON.parse(tab.data.requestTabs.request.value?.trim() || '{}');
const request = JSON.parse(tab.data.requestTabs.request.value?.trim() || "{}");

return request;
} catch (error) {
Expand All @@ -63,7 +66,7 @@ export function parseRequest(tab: GrpcTab<GrpcMethodType>): Record<string, unkno

export function parseMetadata(tab: GrpcTab<GrpcMethodType>): Record<string, unknown> {
try {
const metadata = JSON.parse(tab.data.requestTabs.metadata.value?.trim() || '{}');
const metadata = JSON.parse(tab.data.requestTabs.metadata.value?.trim() || "{}");

return metadata;
} catch (error) {
Expand Down
2 changes: 2 additions & 0 deletions src/app/pages/shortcuts/hooks/use-environment-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function useEnvironmentActions() {
name: environment.label,
keywords: environment.label,
subtitle: environment.url,
authority: environment.authority,
parent: 'environment',
icon: (
<Container
Expand All @@ -34,6 +35,7 @@ export function useEnvironmentActions() {
updateGrpcTabData(activeTabId, {
environmentId: environment.id,
url: environment.url,
authority: environment.authority,
});
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface EnvironmentFormProps {
}

export const EnvironmentForm: React.FC<EnvironmentFormProps> = ({
onSubmit = () => {},
onSubmit = () => { },
id,
defaultValues,
}) => {
Expand Down Expand Up @@ -68,6 +68,17 @@ export const EnvironmentForm: React.FC<EnvironmentFormProps> = ({
color={errors.url ? 'error' : 'default'}
{...register('url', { required: true })}
/>
<Spacer />
<Input
bordered
borderWeight="light"
size="sm"
animated={false}
label="Authority"
clearable
color={errors.authority ? 'error' : 'default'}
{...register('authority', { required: false })}
/>
</Container>
</form>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const SendHeader: React.FC<PropsWithChildren<SendHeaderProps<GrpcMethodTy
updateGrpcTabData(tab.id, {
environmentId: environment?.id,
url: environment?.url,
authority: environment?.authority,
});
};

Expand All @@ -60,10 +61,26 @@ export const SendHeader: React.FC<PropsWithChildren<SendHeaderProps<GrpcMethodTy
};

const handleUrlChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
updateGrpcTabData(tab.id, {
environmentId: undefined,
url: e.target.value,
});
if (tab.data.authority) {
updateGrpcTabData(tab.id, {
environmentId: undefined,
authority: e.target.value,
});
} else {
const urlParts = e.target.value.split("/");
if (urlParts.length > 1 && urlParts[1].trim().length > 0) {
updateGrpcTabData(tab.id, {
environmentId: undefined,
url: urlParts[0].trim(),
authority: urlParts[1].trim(),
});
} else {
updateGrpcTabData(tab.id, {
environmentId: undefined,
url: e.target.value,
});
}
}
};

const handleCreateEnvironmentModalSubmit = (environment: Environment) => {
Expand Down Expand Up @@ -123,8 +140,9 @@ export const SendHeader: React.FC<PropsWithChildren<SendHeaderProps<GrpcMethodTy
animated={false}
clearable
placeholder="0.0.0.0:3000"
helperText={tab.data.authority ? `URL: ${tab.data.url} / Authority: ${tab.data.authority}` : undefined}
css={{ flex: 6, '& input': { paddingLeft: 0, marginLeft: '5px !important' } }}
value={tab.data.url || ''}
value={tab.data.authority ? tab.data.authority : (tab.data.url || "")}
onChange={handleUrlChange}
contentLeftStyling={false}
contentLeft={
Expand Down Expand Up @@ -200,6 +218,7 @@ export const SendHeader: React.FC<PropsWithChildren<SendHeaderProps<GrpcMethodTy
open={createEnvironmentModalVisible}
defaultValues={{
url: tab.data.url || '',
authority: tab.data.authority || '',
}}
onCreate={handleCreateEnvironmentModalSubmit}
onClose={handleCreateEnvironmentModalClose}
Expand Down
1 change: 1 addition & 0 deletions src/app/storage/interfaces/environments.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface Environment {
id: string;
label: string;
url: string;
authority: string;
color: string;
}

Expand Down
21 changes: 11 additions & 10 deletions src/app/storage/interfaces/tabs/grpc-tab.interface.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { GrpcMethodType } from '@core/types';
import { GrpcMethodType } from "@core/types";

export enum GrpcProtocol {
GRPC = 'grpc',
GRPC_WEB = 'grpc-web',
GRPC = "grpc",
GRPC_WEB = "grpc-web",
}

export interface GrpcRequest {
Expand All @@ -23,13 +23,13 @@ export interface GrpcUnaryResponse {
}

export enum GrpcStreamMessageType {
STARTED = 'started',
CLIENT_MESSAGE = 'client-message',
SERVER_MESSAGE = 'server-message',
ERROR = 'error',
CLIENT_STREAMING_ENDED = 'client-streaming-ended',
SERVER_STREAMING_ENDED = 'server-streaming-ended',
CANCELED = 'canceled',
STARTED = "started",
CLIENT_MESSAGE = "client-message",
SERVER_MESSAGE = "server-message",
ERROR = "error",
CLIENT_STREAMING_ENDED = "client-streaming-ended",
SERVER_STREAMING_ENDED = "server-streaming-ended",
CANCELED = "canceled",
}

export interface GrpcStreamMessage {
Expand Down Expand Up @@ -59,6 +59,7 @@ export interface GrpcTabData<T extends GrpcMethodType> {
protocol: GrpcProtocol;
environmentId?: string;
url?: string;
authority?: string;
tlsId?: string;

requestTabs: {
Expand Down
47 changes: 24 additions & 23 deletions src/core/clients/grpc/grpc-client/grpc-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import type {
MetadataValue,
ServerErrorResponse,
ServiceClientConstructor,
} from '@grpc/grpc-js';
import * as grpc from '@grpc/grpc-js';
import type { PackageDefinition } from '@grpc/proto-loader';
import * as fs from 'fs';
import * as _ from 'lodash';
import { performance } from 'perf_hooks';
} from "@grpc/grpc-js";
import * as grpc from "@grpc/grpc-js";
import type { PackageDefinition } from "@grpc/proto-loader";
import * as fs from "fs";
import * as _ from "lodash";
import { performance } from "perf_hooks";

import {
GrpcChannelOptions,
Expand All @@ -23,11 +23,11 @@ import {
GrpcTlsType,
isInsecureTlsConfig,
isMutualTlsConfig,
} from '../interfaces';
import { MetadataParser } from './metadata-parser';
} from "../interfaces";
import { MetadataParser } from "./metadata-parser";

function instanceOfServiceClientConstructor(object: any): object is ServiceClientConstructor {
return 'serviceName' in object;
return "serviceName" in object;
}

export class GrpcClient {
Expand All @@ -54,37 +54,38 @@ export class GrpcClient {
const channelOptions: ChannelOptions = {};

if (options?.sslTargetNameOverride) {
channelOptions['grpc.ssl_target_name_override'] = options.sslTargetNameOverride;
channelOptions["grpc.ssl_target_name_override"] = options.sslTargetNameOverride;
}
if (options?.authorityOverride) {
channelOptions["grpc.ssl_target_name_override"] = options.authorityOverride;
channelOptions["grpc.default_authority"] = options.authorityOverride;
}

return channelOptions;
}

private static loadClient(
packageDefinition: PackageDefinition,
requestOptions: GrpcClientRequestOptions
) {
private static loadClient(packageDefinition: PackageDefinition, requestOptions: GrpcClientRequestOptions) {
const ast = grpc.loadPackageDefinition(packageDefinition);
const ServiceClient = _.get(ast, requestOptions.serviceName);

if (ServiceClient && instanceOfServiceClientConstructor(ServiceClient)) {
const client = new ServiceClient(
requestOptions.address,
this.getChannelCredentials(requestOptions.tls),
this.getChannelOptions(requestOptions.tls.channelOptions)
this.getChannelOptions({
...requestOptions.tls.channelOptions,
authorityOverride: requestOptions.authorityOverride,
})
);

if (
client[requestOptions.methodName] &&
typeof client[requestOptions.methodName] === 'function'
) {
if (client[requestOptions.methodName] && typeof client[requestOptions.methodName] === "function") {
return client;
}

throw new Error('No method definition');
throw new Error("No method definition");
}

throw new Error('No service definition');
throw new Error("No service definition");
}

static async invokeUnaryRequest(
Expand Down Expand Up @@ -147,14 +148,14 @@ export class GrpcClient {
metadata ? MetadataParser.parse(metadata) : new grpc.Metadata(),
(error: ServerErrorResponse, response: Record<string, unknown>) => {
if (error) {
return call.emit('error', {
return call.emit("error", {
code: error.code,
details: error.details,
metadata: error.metadata?.toJSON(),
});
}

return call.emit('data', response);
return call.emit("data", response);
}
);

Expand Down
18 changes: 9 additions & 9 deletions src/core/clients/grpc/interfaces/client.interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export enum GrpcTlsType {
INSECURE = 'insecure',
SERVER_SIDE = 'server-side',
MUTUAL = 'mutual',
INSECURE = "insecure",
SERVER_SIDE = "server-side",
MUTUAL = "mutual",
}

export type GrpcTlsConfig<T extends GrpcTlsType = GrpcTlsType> = T extends GrpcTlsType.MUTUAL
Expand All @@ -25,6 +25,8 @@ export interface GrpcChannelOptions {
* If this parameter is specified and the underlying is not an SSL channel, it will just be ignored.
*/
sslTargetNameOverride?: string;

authorityOverride?: string;
}

export interface GrpcServerSideTlsConfig {
Expand Down Expand Up @@ -60,17 +62,15 @@ export interface GrpcClientRequestOptions {

address: string;

authorityOverride?: string;

tls: GrpcTlsConfig<GrpcTlsType>;
}

export function isInsecureTlsConfig(
config: GrpcTlsConfig<GrpcTlsType>
): config is GrpcTlsConfig<GrpcTlsType.INSECURE> {
export function isInsecureTlsConfig(config: GrpcTlsConfig<GrpcTlsType>): config is GrpcTlsConfig<GrpcTlsType.INSECURE> {
return config.type === GrpcTlsType.INSECURE;
}

export function isMutualTlsConfig(
config: GrpcTlsConfig<GrpcTlsType>
): config is GrpcTlsConfig<GrpcTlsType.MUTUAL> {
export function isMutualTlsConfig(config: GrpcTlsConfig<GrpcTlsType>): config is GrpcTlsConfig<GrpcTlsType.MUTUAL> {
return config.type === GrpcTlsType.MUTUAL;
}