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

Upgrade to hapi version 20 #85406

Merged
merged 15 commits into from
Dec 19, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<b>Signature:</b>

```typescript
export interface LegacyElasticsearchError extends Boom
export interface LegacyElasticsearchError extends Boom.Boom
```

## Properties
Expand Down
35 changes: 14 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@
"url": "https://github.com/elastic/kibana.git"
},
"resolutions": {
"**/@hapi/iron": "^5.1.4",
Copy link
Member

Choose a reason for hiding this comment

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

🎉

"**/@types/hapi__boom": "^7.4.1",
"**/@types/hapi__hapi": "^18.2.6",
"**/@types/hapi__mimos": "4.1.0",
"**/@types/node": "14.14.14",
"**/chokidar": "^3.4.3",
"**/cross-fetch/node-fetch": "^2.6.1",
Expand Down Expand Up @@ -115,17 +111,17 @@
"@elastic/request-crypto": "1.1.4",
"@elastic/safer-lodash-set": "link:packages/elastic-safer-lodash-set",
"@elastic/search-ui-app-search-connector": "^1.5.0",
"@hapi/boom": "^7.4.11",
"@hapi/cookie": "^10.1.2",
"@hapi/good-squeeze": "5.2.1",
"@hapi/h2o2": "^8.3.2",
"@hapi/hapi": "^18.4.1",
"@hapi/hoek": "^8.5.1",
"@hapi/inert": "^5.2.2",
"@hapi/podium": "^3.4.3",
"@hapi/statehood": "^6.1.2",
"@hapi/vision": "^5.5.4",
"@hapi/wreck": "^15.0.2",
"@hapi/boom": "^9.1.1",
"@hapi/cookie": "^11.0.2",
"@hapi/good-squeeze": "6.0.0",
"@hapi/h2o2": "^9.0.2",
"@hapi/hapi": "^20.0.3",
"@hapi/hoek": "^9.1.0",
"@hapi/inert": "^6.0.3",
"@hapi/podium": "^4.1.1",
"@hapi/statehood": "^7.0.3",
"@hapi/vision": "^6.0.1",
"@hapi/wreck": "^17.1.0",
"@kbn/ace": "link:packages/kbn-ace",
"@kbn/analytics": "link:packages/kbn-analytics",
"@kbn/apm-config-loader": "link:packages/kbn-apm-config-loader",
Expand Down Expand Up @@ -451,14 +447,11 @@
"@types/graphql": "^0.13.2",
"@types/gulp": "^4.0.6",
"@types/gulp-zip": "^4.0.1",
"@types/hapi__boom": "^7.4.1",
"@types/hapi__cookie": "^10.1.1",
"@types/hapi__h2o2": "8.3.0",
"@types/hapi__hapi": "^18.2.6",
"@types/hapi__hoek": "^6.2.0",
"@types/hapi__inert": "^5.2.1",
"@types/hapi__h2o2": "^8.3.2",
"@types/hapi__hapi": "^20.0.2",
"@types/hapi__inert": "^5.2.2",
"@types/hapi__podium": "^3.4.1",
"@types/hapi__wreck": "^15.0.1",
"@types/has-ansi": "^3.0.0",
"@types/he": "^1.1.1",
"@types/history": "^4.7.3",
Expand Down
4 changes: 2 additions & 2 deletions src/core/server/elasticsearch/legacy/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enum ErrorCode {
* @deprecated. The new elasticsearch client doesn't wrap errors anymore.
* @public
* */
export interface LegacyElasticsearchError extends Boom {
export interface LegacyElasticsearchError extends Boom.Boom {
[code]?: string;
}

Expand Down Expand Up @@ -86,7 +86,7 @@ export class LegacyElasticsearchErrorHelpers {
const decoratedError = decorate(error, ErrorCode.NOT_AUTHORIZED, 401, reason);
const wwwAuthHeader = get(error, 'body.error.header[WWW-Authenticate]') as string;

decoratedError.output.headers['WWW-Authenticate'] =
(decoratedError.output.headers as { [key: string]: string })['WWW-Authenticate'] =
wwwAuthHeader || 'Basic realm="Authorization Required"';

return decoratedError;
Expand Down
4 changes: 2 additions & 2 deletions src/core/server/http/http_server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1214,7 +1214,7 @@ describe('timeout options', () => {
router.get(
{
path: '/',
validate: { body: schema.any() },
validate: { body: schema.maybe(schema.any()) },
},
(context, req, res) => {
return res.ok({
Expand Down Expand Up @@ -1247,7 +1247,7 @@ describe('timeout options', () => {
router.get(
{
path: '/',
validate: { body: schema.any() },
validate: { body: schema.maybe(schema.any()) },
options: { timeout: { idleSocket: 12000 } },
},
(context, req, res) => {
Expand Down
58 changes: 22 additions & 36 deletions src/core/server/http/http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Server } from '@hapi/hapi';
import { Server, ServerRoute } from '@hapi/hapi';
import HapiStaticFiles from '@hapi/inert';
import url from 'url';
import uuid from 'uuid';
Expand Down Expand Up @@ -167,66 +167,52 @@ export class HttpServer {
for (const router of this.registeredRouters) {
for (const route of router.getRoutes()) {
this.log.debug(`registering route handler for [${route.path}]`);
// Hapi does not allow payload validation to be specified for 'head' or 'get' requests
const validate = isSafeMethod(route.method) ? undefined : { payload: true };
const { authRequired, tags, body = {}, timeout } = route.options;
const { accepts: allow, maxBytes, output, parse } = body;

const kibanaRouteOptions: KibanaRouteOptions = {
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
};

// To work around https://github.com/hapijs/hapi/issues/4122 until v20, set the socket
// timeout on the route to a fake timeout only when the payload timeout is specified.
// Within the onPreAuth lifecycle of the route itself, we'll override the timeout with the
// real socket timeout.
const fakeSocketTimeout = timeout?.payload ? timeout.payload + 1 : undefined;

this.server.route({
const routeOpts: ServerRoute = {
handler: route.handler,
method: route.method,
path: route.path,
options: {
auth: this.getAuthOption(authRequired),
app: kibanaRouteOptions,
ext: {
onPreAuth: {
method: (request, h) => {
// At this point, the socket timeout has only been set to work-around the HapiJS bug.
// We need to either set the real per-route timeout or use the default idle socket timeout
if (timeout?.idleSocket) {
request.raw.req.socket.setTimeout(timeout.idleSocket);
} else if (fakeSocketTimeout) {
// NodeJS uses a socket timeout of `0` to denote "no timeout"
request.raw.req.socket.setTimeout(this.config!.socketTimeout ?? 0);
}

return h.continue;
},
},
},
tags: tags ? Array.from(tags) : undefined,
// TODO: This 'validate' section can be removed once the legacy platform is completely removed.
// We are telling Hapi that NP routes can accept any payload, so that it can bypass the default
// validation applied in ./http_tools#getServerOptions
// (All NP routes are already required to specify their own validation in order to access the payload)
validate,
payload: [allow, maxBytes, output, parse, timeout?.payload].some(
(v) => typeof v !== 'undefined'
)
// @ts-expect-error Types are outdated and doesn't allow `payload.multipart` to be `true`
payload: [allow, maxBytes, output, parse, timeout?.payload].some((x) => x !== undefined)
? {
allow,
maxBytes,
output,
parse,
timeout: timeout?.payload,
multipart: true,
}
: undefined,
timeout: {
socket: fakeSocketTimeout,
socket: timeout?.idleSocket ?? this.config!.socketTimeout,
},
},
});
};

// Hapi does not allow payload validation to be specified for 'head' or 'get' requests
if (!isSafeMethod(route.method)) {
// TODO: This 'validate' section can be removed once the legacy platform is completely removed.
// We are telling Hapi that NP routes can accept any payload, so that it can bypass the default
// validation applied in ./http_tools#getServerOptions
// (All NP routes are already required to specify their own validation in order to access the payload)
// TODO: Move the setting of the validate option back up to being set at `routeOpts` creation-time once
// https://github.com/hapijs/hoek/pull/365 is merged and released in @hapi/hoek v9.1.1. At that point I
// imagine the ts-error below will go away as well.
// @ts-expect-error "Property 'validate' does not exist on type 'RouteOptions'" <-- ehh?!? yes it does!
routeOpts.options!.validate = { payload: true };
}

this.server.route(routeOpts);
}
}

Expand Down
12 changes: 8 additions & 4 deletions src/core/server/http/lifecycle/on_pre_response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,11 @@ export function adoptToHapiOnPreResponseFormat(fn: OnPreResponseHandler, log: Lo
if (preResponseResult.isNext(result)) {
if (result.headers) {
if (isBoom(response)) {
findHeadersIntersection(response.output.headers, result.headers, log);
findHeadersIntersection(
response.output.headers as { [key: string]: string },
watson marked this conversation as resolved.
Show resolved Hide resolved
result.headers,
log
);
// hapi wraps all error response in Boom object internally
response.output.headers = {
...response.output.headers,
Expand All @@ -157,7 +161,7 @@ export function adoptToHapiOnPreResponseFormat(fn: OnPreResponseHandler, log: Lo
const overriddenResponse = responseToolkit.response(result.body).code(statusCode);

const originalHeaders = isBoom(response) ? response.output.headers : response.headers;
setHeaders(overriddenResponse, originalHeaders);
setHeaders(overriddenResponse, originalHeaders as { [key: string]: string });
if (result.headers) {
setHeaders(overriddenResponse, result.headers);
}
Expand All @@ -178,8 +182,8 @@ export function adoptToHapiOnPreResponseFormat(fn: OnPreResponseHandler, log: Lo
};
}

function isBoom(response: any): response is Boom {
return response instanceof Boom;
function isBoom(response: any): response is Boom.Boom {
return response instanceof Boom.Boom;
}

function setHeaders(response: ResponseObject, headers: ResponseHeaders) {
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/http/router/error_wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const wrapErrors: RequestHandlerWrapper = (handler) => {
return response.customError({
body: e.output.payload,
statusCode: e.output.statusCode,
headers: e.output.headers,
headers: e.output.headers as { [key: string]: string },
});
}
throw e;
Expand Down
7 changes: 3 additions & 4 deletions src/core/server/http/router/response_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class HapiResponseAdapter {
}

public toInternalError() {
const error = new Boom('', {
const error = new Boom.Boom('', {
statusCode: 500,
});

Expand Down Expand Up @@ -129,7 +129,7 @@ export class HapiResponseAdapter {
}

// we use for BWC with Boom payload for error responses - {error: string, message: string, statusCode: string}
const error = new Boom('', {
const error = new Boom.Boom('', {
statusCode: kibanaResponse.status,
});

Expand All @@ -142,8 +142,7 @@ export class HapiResponseAdapter {

const headers = kibanaResponse.options.headers;
if (headers) {
// Hapi typings for header accept only strings, although string[] is a valid value
error.output.headers = headers as any;
error.output.headers = headers;
}

return error;
Expand Down
5 changes: 4 additions & 1 deletion src/core/server/http/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ interface RouterRoute {
method: RouteMethod;
path: string;
options: RouteConfigOptions<RouteMethod>;
handler: (req: Request, responseToolkit: ResponseToolkit) => Promise<ResponseObject | Boom<any>>;
handler: (
req: Request,
responseToolkit: ResponseToolkit
) => Promise<ResponseObject | Boom.Boom<any>>;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/saved_objects/service/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const CODE_GENERAL_ERROR = 'SavedObjectsClient/generalError';

const code = Symbol('SavedObjectsClientErrorCode');

export interface DecoratedError extends Boom {
export interface DecoratedError extends Boom.Boom {
[code]?: string;
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/server/server.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1541,7 +1541,7 @@ export type LegacyElasticsearchClientConfig = Pick<ConfigOptions, 'keepAlive' |
};

// @public
export interface LegacyElasticsearchError extends Boom {
export interface LegacyElasticsearchError extends Boom.Boom {
// (undocumented)
[code]?: string;
}
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/vis_type_timeseries/server/routes/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const fieldsRoutes = (framework: Framework) => {
return res.customError({
body: err.output.payload,
statusCode: err.output.statusCode,
headers: err.output.headers,
headers: err.output.headers as { [key: string]: string },
});
}

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/apm/server/routes/create_api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export function createApi() {
}

function convertBoomToKibanaResponse(
error: Boom,
error: Boom.Boom,
response: KibanaResponseFactory
) {
const opts = { body: error.message };
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/case/server/routes/api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export function wrapError(error: any): CustomHttpResponseOptions<ResponseError>
const boom = isBoom(error) ? error : boomify(error, options);
return {
body: boom,
headers: boom.output.headers,
headers: boom.output.headers as { [key: string]: string },
statusCode: boom.output.statusCode,
};
}
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/fleet/server/errors/handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ describe('defaultIngestErrorHandler', () => {

describe('Boom', () => {
it('500: constructor - one arg', async () => {
const error = new Boom('bam');
const error = new Boom.Boom('bam');
const response = httpServerMock.createResponseFactory();

await defaultIngestErrorHandler({ error, response });
Expand All @@ -181,7 +181,7 @@ describe('defaultIngestErrorHandler', () => {
});

it('custom: constructor - 2 args', async () => {
const error = new Boom('Problem doing something', {
const error = new Boom.Boom('Problem doing something', {
statusCode: 456,
});
const response = httpServerMock.createResponseFactory();
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/fleet/server/errors/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type IngestErrorHandler = (
params: IngestErrorHandlerParams
) => IKibanaResponse | Promise<IKibanaResponse>;
interface IngestErrorHandlerParams {
error: IngestManagerError | Boom | Error;
error: IngestManagerError | Boom.Boom | Error;
response: KibanaResponseFactory;
request?: KibanaRequest;
context?: RequestHandlerContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const installPreBuiltTemplates = async (paths: string[], callCluster: CallESAsCu
try {
return await Promise.all(templateInstallPromises);
} catch (e) {
throw new Boom(`Error installing prebuilt index templates ${e.message}`, {
throw new Boom.Boom(`Error installing prebuilt index templates ${e.message}`, {
statusCode: 400,
});
}
Expand Down Expand Up @@ -144,7 +144,7 @@ const installPreBuiltComponentTemplates = async (
try {
return await Promise.all(templateInstallPromises);
} catch (e) {
throw new Boom(`Error installing prebuilt component templates ${e.message}`, {
throw new Boom.Boom(`Error installing prebuilt component templates ${e.message}`, {
statusCode: 400,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export async function handleInstallPackageFailure({
callCluster,
}: {
savedObjectsClient: SavedObjectsClientContract;
error: IngestManagerError | Boom | Error;
error: IngestManagerError | Boom.Boom | Error;
pkgName: string;
pkgVersion: string;
installedPkg: SavedObject<Installation> | undefined;
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/lens/server/routes/existing_fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { BASE_API_URL } from '../../common';
import { UI_SETTINGS } from '../../../../../src/plugins/data/server';
import { PluginStartContract } from '../plugin';

export function isBoomError(error: { isBoom?: boolean }): error is Boom {
export function isBoomError(error: { isBoom?: boolean }): error is Boom.Boom {
return error.isBoom === true;
}

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/common/types/data_frame_analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { DATA_FRAME_TASK_STATE } from '../constants/data_frame_analytics';

export interface DeleteDataFrameAnalyticsWithIndexStatus {
success: boolean;
error?: EsErrorBody | Boom;
error?: EsErrorBody | Boom.Boom;
}

export type IndexName = string;
Expand Down
Loading