diff --git a/packages/core/http/core-http-router-server-internal/src/request.test.ts b/packages/core/http/core-http-router-server-internal/src/request.test.ts index 72d81e9ce48e6..f895cb0e2fde7 100644 --- a/packages/core/http/core-http-router-server-internal/src/request.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/request.test.ts @@ -201,6 +201,36 @@ describe('CoreKibanaRequest', () => { }); }); + describe('route.httpVersion property', () => { + it('returns the version from the raw request', () => { + const request = hapiMocks.createRequest({ + raw: { + req: { + httpVersion: '7.4', + }, + }, + }); + const kibanaRequest = CoreKibanaRequest.from(request); + + expect(kibanaRequest.httpVersion).toEqual('7.4'); + }); + }); + + describe('route.protocol property', () => { + it('return a static value for now as only http1 is supported', () => { + const request = hapiMocks.createRequest({ + raw: { + req: { + httpVersion: '2.0', + }, + }, + }); + const kibanaRequest = CoreKibanaRequest.from(request); + + expect(kibanaRequest.protocol).toEqual('http1'); + }); + }); + describe('route.options.authRequired property', () => { it('handles required auth: undefined', () => { const auth: RouteOptions['auth'] = undefined; @@ -370,6 +400,17 @@ describe('CoreKibanaRequest', () => { }); }); + describe('httpVersion', () => { + it('should be 1.0', () => { + const request: FakeRawRequest = { + headers: {}, + path: '/', + }; + const kibanaRequest = CoreKibanaRequest.from(request); + expect(kibanaRequest.httpVersion).toEqual('1.0'); + }); + }); + describe('headers', () => { it('returns the correct headers', () => { const request: FakeRawRequest = { diff --git a/packages/core/http/core-http-router-server-internal/src/request.ts b/packages/core/http/core-http-router-server-internal/src/request.ts index 450294fb09c0f..d3274b0a2a1fe 100644 --- a/packages/core/http/core-http-router-server-internal/src/request.ts +++ b/packages/core/http/core-http-router-server-internal/src/request.ts @@ -29,6 +29,7 @@ import { KibanaRequestRouteOptions, RawRequest, FakeRawRequest, + HttpProtocol, } from '@kbn/core-http-server'; import { ELASTIC_INTERNAL_ORIGIN_QUERY_PARAM, @@ -131,6 +132,10 @@ export class CoreKibanaRequest< public readonly isInternalApiRequest: boolean; /** {@inheritDoc KibanaRequest.rewrittenUrl} */ public readonly rewrittenUrl?: URL; + /** {@inheritDoc KibanaRequest.httpVersion} */ + public readonly httpVersion: string; + /** {@inheritDoc KibanaRequest.protocol} */ + public readonly protocol: HttpProtocol; /** @internal */ protected readonly [requestSymbol]!: Request; @@ -167,6 +172,10 @@ export class CoreKibanaRequest< enumerable: false, }); + this.httpVersion = isRealReq ? request.raw.req.httpVersion : '1.0'; + // hardcoded for now as only supporting http1 + this.protocol = 'http1'; + this.route = deepFreeze(this.getRouteInfo(request)); this.socket = isRealReq ? new KibanaSocket(request.raw.req.socket) diff --git a/packages/core/http/core-http-server/index.ts b/packages/core/http/core-http-server/index.ts index 859e6ff8efbd5..2b288be521d3a 100644 --- a/packages/core/http/core-http-server/index.ts +++ b/packages/core/http/core-http-server/index.ts @@ -141,6 +141,7 @@ export type { HttpServicePreboot, HttpServiceSetup, HttpServiceStart, + HttpProtocol, } from './src/http_contract'; export type { diff --git a/packages/core/http/core-http-server/src/http_contract.ts b/packages/core/http/core-http-server/src/http_contract.ts index 09250abf8adae..308ba2dd48785 100644 --- a/packages/core/http/core-http-server/src/http_contract.ts +++ b/packages/core/http/core-http-server/src/http_contract.ts @@ -402,3 +402,11 @@ export interface HttpServerInfo { /** The protocol used by the server */ protocol: 'http' | 'https' | 'socket'; } + +/** + * Defines an http protocol. + * (Only supporting http1 for now) + * + * - http1: regroups all http/1.x protocols + */ +export type HttpProtocol = 'http1'; diff --git a/packages/core/http/core-http-server/src/router/request.ts b/packages/core/http/core-http-server/src/router/request.ts index e1242dee7eb67..a58c97ccee762 100644 --- a/packages/core/http/core-http-server/src/router/request.ts +++ b/packages/core/http/core-http-server/src/router/request.ts @@ -10,6 +10,7 @@ import type { URL } from 'url'; import type { RequestApplicationState, RouteOptionsApp } from '@hapi/hapi'; import type { Observable } from 'rxjs'; import type { RecursiveReadonly } from '@kbn/utility-types'; +import type { HttpProtocol } from '../http_contract'; import type { IKibanaSocket } from './socket'; import type { RouteMethod, RouteConfigOptions } from './route'; import type { Headers } from './headers'; @@ -141,6 +142,16 @@ export interface KibanaRequest< */ readonly isInternalApiRequest: boolean; + /** + * The HTTP version sent by the client. + */ + readonly httpVersion: string; + + /** + * The protocol used by the client, inferred from the httpVersion. + */ + readonly protocol: HttpProtocol; + /** * The socket associated with this request. * See {@link IKibanaSocket}. diff --git a/packages/kbn-hapi-mocks/src/request.ts b/packages/kbn-hapi-mocks/src/request.ts index 511e580071954..4379fcb9aeef0 100644 --- a/packages/kbn-hapi-mocks/src/request.ts +++ b/packages/kbn-hapi-mocks/src/request.ts @@ -39,6 +39,7 @@ export const createRequestMock = (customization: DeepPartial = {}): Req req: { url: path, socket: {}, + httpVersion: '1.1', }, res: { addListener: jest.fn(), diff --git a/src/core/server/integration_tests/http/request.test.ts b/src/core/server/integration_tests/http/request.test.ts index 9e90c1364a902..e68bc9013ddfa 100644 --- a/src/core/server/integration_tests/http/request.test.ts +++ b/src/core/server/integration_tests/http/request.test.ts @@ -427,6 +427,7 @@ describe('KibanaRequest', () => { expect(resp3.body).toEqual({ requestId: 'gamma' }); }); }); + describe('request uuid', () => { it('generates a UUID', async () => { const { server: innerServer, createRouter } = await server.setup(setupDeps); @@ -442,4 +443,23 @@ describe('KibanaRequest', () => { expect(resp1.body.requestUuid).toBe('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'); }); }); + + describe('httpVersion and protocol', () => { + it('returns the correct values', async () => { + const { server: innerServer, createRouter } = await server.setup(setupDeps); + const router = createRouter('/'); + router.get({ path: '/', validate: false }, async (context, req, res) => { + return res.ok({ body: { httpVersion: req.httpVersion, protocol: req.protocol } }); + }); + await server.start(); + + const st = supertest(innerServer.listener); + + const resp1 = await st.get('/').expect(200); + expect(resp1.body).toEqual({ + httpVersion: '1.1', + protocol: 'http1', + }); + }); + }); });