Skip to content

Commit 056ec9a

Browse files
committed
feat: add specific error handling for SSL errors with cloud private instances (#54)
* return correct data for requests that don't connect * add some types to local variables in request wrapper
1 parent 2c83eb8 commit 056ec9a

File tree

2 files changed

+69
-13
lines changed

2 files changed

+69
-13
lines changed

lib/requestwrapper.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ export class RequestWrapper {
258258
} catch (e) {
259259
// ignore the error, use the object, and tack on a warning
260260
errorBody = axiosError.data;
261-
errorBody.warning = 'body contains circular reference';
261+
errorBody.warning = 'Body contains circular reference';
262262
}
263263

264264
error.body = errorBody;
@@ -270,14 +270,20 @@ export class RequestWrapper {
270270
if (isAuthenticationError(axiosError)) {
271271
error.message = 'Access is denied due to invalid credentials.';
272272
}
273-
274273
} else if (axiosError.request) {
275274
// The request was made but no response was received
276275
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
277276
// http.ClientRequest in node.js
278-
error.message = 'Response not received. Body of error is HTTP ClientRequest object';
279-
error.body = axiosError.request;
280-
277+
error.message = axiosError.message;
278+
error.statusText = axiosError.code;
279+
error.body = 'Response not received - no connection was made to the service.';
280+
281+
// when a request to a private cloud instance has an ssl problem, it never connects and follows this branch of the error handling
282+
if (isSelfSignedCertificateError(axiosError)) {
283+
error.message = `If you're trying to call a service on ICP or Cloud Pak for Data, you ` +
284+
`may not have a valid SSL certificate. If you need to access the service without setting that up, try using ` +
285+
`the disableSslVerification option in your client configuration and your authentication configuration if applicable.`;
286+
}
281287
} else {
282288
// Something happened in setting up the request that triggered an Error
283289
error.message = axiosError.message;
@@ -311,11 +317,11 @@ function parsePath(path: string, params: Object): string {
311317
*/
312318
function isAuthenticationError(error: any): boolean {
313319
let isAuthErr = false;
314-
const code = error.status;
315-
const body = error.data;
320+
const code: number = error.status || null;
321+
const body: any = error.data || {};
316322

317323
// handle specific error from iam service, should be relevant across platforms
318-
const isIamServiceError = body.context &&
324+
const isIamServiceError: boolean = body.context &&
319325
body.context.url &&
320326
body.context.url.indexOf('iam') > -1;
321327

@@ -326,6 +332,28 @@ function isAuthenticationError(error: any): boolean {
326332
return isAuthErr;
327333
}
328334

335+
/**
336+
* Determine if the error is due to a bad self signed certificate
337+
* @private
338+
* @param {Object} error - error object returned from axios
339+
* @returns {boolean} true if error is due to an SSL error
340+
*/
341+
function isSelfSignedCertificateError(error: any): boolean {
342+
let result = false;
343+
344+
const sslCode = 'DEPTH_ZERO_SELF_SIGNED_CERT';
345+
const sslMessage = 'self signed certificate';
346+
347+
const hasSslCode = error.code === sslCode;
348+
const hasSslMessage = hasStringProperty(error, 'message') && error.message.includes(sslMessage);
349+
350+
if (hasSslCode || hasSslMessage) {
351+
result = true;
352+
}
353+
354+
return result;
355+
}
356+
329357
/**
330358
* Return true if object has a specified property that is a string
331359
* @private

test/unit/requestWrapper.test.js

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -384,10 +384,9 @@ describe('formatError', () => {
384384
'x-global-transaction-id': 'fhd7s8hfudj9ksoo0wpnd78a',
385385
},
386386
},
387-
request: {
388-
message: 'request was made but no response was received',
389-
},
387+
request: 'fake-http-request-object',
390388
message: 'error in building the request',
389+
code: 'SOME_STATUS_KEY',
391390
};
392391

393392
it('should get the message from errors[0].message', () => {
@@ -486,8 +485,37 @@ describe('formatError', () => {
486485
delete basicAxiosError.response;
487486
const error = requestWrapperInstance.formatError(basicAxiosError);
488487
expect(error instanceof Error).toBe(true);
489-
expect(error.message).toBe('Response not received. Body of error is HTTP ClientRequest object');
490-
expect(error.body).toEqual(basicAxiosError.request);
488+
expect(error.message).toBe('error in building the request');
489+
expect(error.statusText).toBe('SOME_STATUS_KEY');
490+
expect(error.body).toBe('Response not received - no connection was made to the service.');
491+
});
492+
493+
it('check the SSL error handler - message condition', () => {
494+
// save the original message
495+
const originalMessage = basicAxiosError.message;
496+
497+
basicAxiosError.message = 'request has self signed certificate';
498+
const error = requestWrapperInstance.formatError(basicAxiosError);
499+
500+
// put the original message back in, before expectations in case they fail
501+
basicAxiosError.message = originalMessage;
502+
503+
expect(error instanceof Error).toBe(true);
504+
expect(error.message).toMatch(/may not have a valid SSL certificate/);
505+
});
506+
507+
it('check the SSL error handler - code condition', () => {
508+
// save the original code
509+
const originalCode = basicAxiosError.code;
510+
511+
basicAxiosError.code = 'DEPTH_ZERO_SELF_SIGNED_CERT';
512+
const error = requestWrapperInstance.formatError(basicAxiosError);
513+
514+
// put the original message back in, before expectations in case they fail
515+
basicAxiosError.code = originalCode;
516+
517+
expect(error instanceof Error).toBe(true);
518+
expect(error.message).toMatch(/may not have a valid SSL certificate/);
491519
});
492520

493521
it('check the message flow', () => {

0 commit comments

Comments
 (0)