From 024ab16a74d101985f767f478a751f817edafa57 Mon Sep 17 00:00:00 2001 From: Ryan Willis Date: Mon, 6 Jan 2025 12:03:00 -0700 Subject: [PATCH 1/7] allow deselecting proto file --- .../grpc-method-dropdown.tsx | 1 + .../ui/components/modals/proto-files-modal.tsx | 15 +++++++-------- .../ui/components/panes/grpc-request-pane.tsx | 16 +++++++++------- .../ui/components/proto-file/proto-file-list.tsx | 16 +++++++++++++++- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/packages/insomnia/src/ui/components/dropdowns/grpc-method-dropdown/grpc-method-dropdown.tsx b/packages/insomnia/src/ui/components/dropdowns/grpc-method-dropdown/grpc-method-dropdown.tsx index 309c4b0cbcc..baa7ef367cc 100644 --- a/packages/insomnia/src/ui/components/dropdowns/grpc-method-dropdown/grpc-method-dropdown.tsx +++ b/packages/insomnia/src/ui/components/dropdowns/grpc-method-dropdown/grpc-method-dropdown.tsx @@ -70,6 +70,7 @@ export const GrpcMethodDropdown: FunctionComponent = ({ })); const selectedPath = selectedMethod?.fullPath; + console.log(methods, disabled); return ( Date: Thu, 9 Jan 2025 07:14:37 -0700 Subject: [PATCH 3/7] fix parsing to match previous tests --- .../grpc/__tests__/parse-grpc-url.test.ts | 4 ++++ .../insomnia/src/network/grpc/parse-grpc-url.ts | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/insomnia/src/network/grpc/__tests__/parse-grpc-url.test.ts b/packages/insomnia/src/network/grpc/__tests__/parse-grpc-url.test.ts index 47d08ef9aa7..55f6edf7340 100644 --- a/packages/insomnia/src/network/grpc/__tests__/parse-grpc-url.test.ts +++ b/packages/insomnia/src/network/grpc/__tests__/parse-grpc-url.test.ts @@ -11,6 +11,7 @@ describe('parseGrpcUrl', () => { expect(parseGrpcUrl(input)).toStrictEqual({ url: expected, enableTls: false, + path: '', }); }); @@ -22,6 +23,7 @@ describe('parseGrpcUrl', () => { expect(parseGrpcUrl(input)).toStrictEqual({ url: expected, enableTls: true, + path: '', }); }); @@ -33,6 +35,7 @@ describe('parseGrpcUrl', () => { expect(parseGrpcUrl(input)).toStrictEqual({ url: expected, enableTls: false, + path: '', }); }); @@ -40,6 +43,7 @@ describe('parseGrpcUrl', () => { expect(parseGrpcUrl(input)).toStrictEqual({ url: '', enableTls: false, + path: '', }); }); }); diff --git a/packages/insomnia/src/network/grpc/parse-grpc-url.ts b/packages/insomnia/src/network/grpc/parse-grpc-url.ts index c40c3156c15..b4aef0df5b2 100644 --- a/packages/insomnia/src/network/grpc/parse-grpc-url.ts +++ b/packages/insomnia/src/network/grpc/parse-grpc-url.ts @@ -2,13 +2,24 @@ export const parseGrpcUrl = (grpcUrl: string): { url: string; enableTls: boolean if (!grpcUrl) { return { url: '', enableTls: false, path: '' }; } - const url = new URL(grpcUrl); + const lcUrl = grpcUrl.toLowerCase(); + let url = { + protocol: 'grpc:', + hostname: lcUrl, + pathname: '', + port: undefined, + } as unknown as URL; + if (lcUrl.includes('://')) { + try { + url = new URL(grpcUrl.toLowerCase()); + } catch (e) { } + } const result = { - url: url.host, + url: `${url.hostname}` + (url.port ? `:${url.port}` : ''), enableTls: false, path: url.pathname, }; - if (url.protocol === 'grpcs:') { + if (url.protocol.toLowerCase() === 'grpcs:') { result.enableTls = true; } if (result.path === '/') { From 7120e6b94d344a9f20c8fae490cd5bab60f28921 Mon Sep 17 00:00:00 2001 From: Ryan Willis Date: Fri, 17 Jan 2025 13:16:46 -0700 Subject: [PATCH 4/7] reflection working --- package-lock.json | 4 ++-- packages/insomnia/package.json | 2 +- packages/insomnia/src/main/ipc/grpc.ts | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e2007c93932..699ce356d3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13059,7 +13059,7 @@ }, "node_modules/grpc-reflection-js": { "version": "0.3.0", - "resolved": "git+ssh://git@github.com/jackkav/grpc-reflection-js.git#e78663356c362d44e629cfa119d12b63ba615bc0", + "resolved": "git+ssh://git@github.com/ryanexus/grpc-reflection-js.git#1d769dc539635cac4b46ae7aab57ff4c4b935e90", "license": "MIT", "dependencies": { "@types/google-protobuf": "^3.7.2", @@ -23095,7 +23095,7 @@ "fastq": "^1.17.1", "graphql": "^16.8.1", "graphql-ws": "^5.16.0", - "grpc-reflection-js": "jackkav/grpc-reflection-js#remove-lodash-set", + "grpc-reflection-js": "ryanexus/grpc-reflection-js#path-prefix", "hawk": "9.0.2", "hkdf": "^0.0.2", "hosted-git-info": "5.2.1", diff --git a/packages/insomnia/package.json b/packages/insomnia/package.json index 2b5c1b95a60..ed320c71156 100644 --- a/packages/insomnia/package.json +++ b/packages/insomnia/package.json @@ -57,7 +57,7 @@ "fastq": "^1.17.1", "graphql": "^16.8.1", "graphql-ws": "^5.16.0", - "grpc-reflection-js": "jackkav/grpc-reflection-js#remove-lodash-set", + "grpc-reflection-js": "ryanexus/grpc-reflection-js#path-prefix", "hawk": "9.0.2", "hkdf": "^0.0.2", "hosted-git-info": "5.2.1", diff --git a/packages/insomnia/src/main/ipc/grpc.ts b/packages/insomnia/src/main/ipc/grpc.ts index ddcbcb6e85f..a23e92706f7 100644 --- a/packages/insomnia/src/main/ipc/grpc.ts +++ b/packages/insomnia/src/main/ipc/grpc.ts @@ -213,12 +213,13 @@ const getMethodsFromReflection = async ( if (reflectionApi.enabled) { return getMethodsFromReflectionServer(reflectionApi); } - const { url } = parseGrpcUrl(host); + const { url, path } = parseGrpcUrl(host); const client = new grpcReflection.Client( url, getChannelCredentials({ url: host, caCertificate, clientCert, clientKey, rejectUnauthorized }), grpcOptions, - filterDisabledMetaData(metadata) + filterDisabledMetaData(metadata), + path ); const services = await client.listServices(); const methodsPromises = services.map(async service => { From 2c352df76b7ce42aa76ffe8e361e7e6451ab2d0a Mon Sep 17 00:00:00 2001 From: Ryan Willis Date: Fri, 17 Jan 2025 13:33:21 -0700 Subject: [PATCH 5/7] fix auto-reflecting onMount when certificates are required --- .../ui/components/panes/grpc-request-pane.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/insomnia/src/ui/components/panes/grpc-request-pane.tsx b/packages/insomnia/src/ui/components/panes/grpc-request-pane.tsx index 08ff0c843ad..d424e9c7f4d 100644 --- a/packages/insomnia/src/ui/components/panes/grpc-request-pane.tsx +++ b/packages/insomnia/src/ui/components/panes/grpc-request-pane.tsx @@ -81,7 +81,24 @@ export const GrpcRequestPane: FunctionComponent = ({ reflectionApi: activeRequest.reflectionApi, }, }); - const methods = await window.main.grpc.loadMethodsFromReflection(rendered); + + const workspaceClientCertificates = await models.clientCertificate.findByParentId(workspaceId); + const clientCertificate = workspaceClientCertificates.find(c => !c.disabled && urlMatchesCertHost(setDefaultProtocol(c.host, 'grpc:'), rendered.url, false)); + const caCertificate = (await models.caCertificate.findByParentId(workspaceId)); + const caCertificatePath = caCertificate && !caCertificate.disabled ? caCertificate.path : undefined; + const clientCert = clientCertificate?.cert ? await readFile(clientCertificate?.cert, 'utf8') : undefined; + const clientKey = clientCertificate?.key ? await readFile(clientCertificate?.key, 'utf8') : undefined; + + const renderedWithCertificates = { + ...rendered, + rejectUnauthorized: settings.validateSSL, + ...(activeRequest.url.toLowerCase().startsWith('grpcs:') ? { + clientCert, + clientKey, + caCertificate: caCertificatePath ? await readFile(caCertificatePath, 'utf8') : undefined, + } : {}), + }; + const methods = await window.main.grpc.loadMethodsFromReflection(renderedWithCertificates); setGrpcState({ ...grpcState, methods }); } }); From a77d5d06f9350f0c9aeb61c8bfe76fbe2224106b Mon Sep 17 00:00:00 2001 From: Ryan Willis Date: Fri, 17 Jan 2025 13:49:04 -0700 Subject: [PATCH 6/7] improve messaging on reflection and method selection --- packages/insomnia/src/utils/grpc.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/insomnia/src/utils/grpc.ts b/packages/insomnia/src/utils/grpc.ts index da265b5f1ce..2c757ce278f 100644 --- a/packages/insomnia/src/utils/grpc.ts +++ b/packages/insomnia/src/utils/grpc.ts @@ -4,6 +4,9 @@ const GRPC_CONNECTION_ERROR_STRINGS = { SERVER_CANCELED: 'CANCELLED', TLS_NOT_SUPPORTED: 'WRONG_VERSION_NUMBER', BAD_LOCAL_ROOT_CERT: 'unable to get local issuer certificate', + UNIMPLEMENTED: 'UNIMPLEMENTED', + // this next one will break if we rename the call made from the pane + CANNOT_REFLECT: "'grpc.loadMethodsFromReflection': Error: 12", }; export function isGrpcConnectionError(error: Error) { @@ -34,6 +37,12 @@ export function getGrpcConnectionErrorDetails(error: Error) { } else if (error.message.includes(GRPC_CONNECTION_ERROR_STRINGS.BAD_LOCAL_ROOT_CERT)) { title = 'Local Root Certificate Error'; message = 'The local root certificate enabled for the host is not valid.\nEither disable the root certificate, or update it with a valid one.'; + } else if (error.message.includes(GRPC_CONNECTION_ERROR_STRINGS.CANNOT_REFLECT)) { + title = 'Reflection Not Supported'; + message = 'The server has indicated that it does not support reflection. You may need to manually enable it server-side.'; + } else if (error.message.includes(GRPC_CONNECTION_ERROR_STRINGS.UNIMPLEMENTED)) { + title = 'Unimplemented Method'; + message = 'The server does not support the requested method. Is the .proto file correct?'; } return { From ba449e36a932440b456c992178628a585c456943 Mon Sep 17 00:00:00 2001 From: Ryan Willis Date: Tue, 21 Jan 2025 08:22:25 -0700 Subject: [PATCH 7/7] switch to internal package, clean up url parsing --- package-lock.json | 4 +-- packages/insomnia/package.json | 2 +- .../src/network/grpc/parse-grpc-url.ts | 30 +++++-------------- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 699ce356d3e..885a2f7c23f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13059,7 +13059,7 @@ }, "node_modules/grpc-reflection-js": { "version": "0.3.0", - "resolved": "git+ssh://git@github.com/ryanexus/grpc-reflection-js.git#1d769dc539635cac4b46ae7aab57ff4c4b935e90", + "resolved": "git+ssh://git@github.com/Kong/grpc-reflection-js.git#f806b5a0dc2092b7a6fb54dfb66c38fb58231774", "license": "MIT", "dependencies": { "@types/google-protobuf": "^3.7.2", @@ -23095,7 +23095,7 @@ "fastq": "^1.17.1", "graphql": "^16.8.1", "graphql-ws": "^5.16.0", - "grpc-reflection-js": "ryanexus/grpc-reflection-js#path-prefix", + "grpc-reflection-js": "Kong/grpc-reflection-js#master", "hawk": "9.0.2", "hkdf": "^0.0.2", "hosted-git-info": "5.2.1", diff --git a/packages/insomnia/package.json b/packages/insomnia/package.json index ed320c71156..d83a5c6b7f9 100644 --- a/packages/insomnia/package.json +++ b/packages/insomnia/package.json @@ -57,7 +57,7 @@ "fastq": "^1.17.1", "graphql": "^16.8.1", "graphql-ws": "^5.16.0", - "grpc-reflection-js": "ryanexus/grpc-reflection-js#path-prefix", + "grpc-reflection-js": "Kong/grpc-reflection-js#master", "hawk": "9.0.2", "hkdf": "^0.0.2", "hosted-git-info": "5.2.1", diff --git a/packages/insomnia/src/network/grpc/parse-grpc-url.ts b/packages/insomnia/src/network/grpc/parse-grpc-url.ts index b4aef0df5b2..00ef812cdd2 100644 --- a/packages/insomnia/src/network/grpc/parse-grpc-url.ts +++ b/packages/insomnia/src/network/grpc/parse-grpc-url.ts @@ -2,28 +2,12 @@ export const parseGrpcUrl = (grpcUrl: string): { url: string; enableTls: boolean if (!grpcUrl) { return { url: '', enableTls: false, path: '' }; } - const lcUrl = grpcUrl.toLowerCase(); - let url = { - protocol: 'grpc:', - hostname: lcUrl, - pathname: '', - port: undefined, - } as unknown as URL; - if (lcUrl.includes('://')) { - try { - url = new URL(grpcUrl.toLowerCase()); - } catch (e) { } - } - const result = { - url: `${url.hostname}` + (url.port ? `:${url.port}` : ''), - enableTls: false, - path: url.pathname, + const url = new URL((grpcUrl.includes('://') ? '' : 'grpc://') + grpcUrl.toLowerCase()); + return { + url: url.host, + enableTls: url.protocol === 'grpcs:', + // remove trailing slashes from pathname; the full request + // path is a concatenation of this parsed path + method path + path: url.pathname.endsWith('/') ? url.pathname.slice(0, -1) : url.pathname, }; - if (url.protocol.toLowerCase() === 'grpcs:') { - result.enableTls = true; - } - if (result.path === '/') { - result.path = ''; - } - return result; };