From 1a076fc4cb40017250e265412d0b16c24086779d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20My=C5=9Bliwiec?= Date: Fri, 15 Nov 2024 11:23:16 +0100 Subject: [PATCH] feat(core): expose listening stream from http adapter host --- packages/core/helpers/http-adapter-host.ts | 30 +++++++++++++++++++ packages/core/nest-application.ts | 8 ++++- .../test/helpers/application-ref-host.spec.ts | 18 ++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/core/helpers/http-adapter-host.ts b/packages/core/helpers/http-adapter-host.ts index 1909ed9e3e5..0064dabca16 100644 --- a/packages/core/helpers/http-adapter-host.ts +++ b/packages/core/helpers/http-adapter-host.ts @@ -1,3 +1,4 @@ +import { Observable, Subject } from 'rxjs'; import { AbstractHttpAdapter } from '../adapters/http-adapter'; /** @@ -16,6 +17,8 @@ export class HttpAdapterHost< T extends AbstractHttpAdapter = AbstractHttpAdapter, > { private _httpAdapter?: T; + private _listen$ = new Subject(); + private isListening = false; /** * Accessor for the underlying `HttpAdapter` @@ -35,4 +38,31 @@ export class HttpAdapterHost< get httpAdapter(): T { return this._httpAdapter; } + + /** + * Observable that allows to subscribe to the `listen` event. + * This event is emitted when the HTTP application is listening for incoming requests. + */ + get listen$(): Observable { + return this._listen$.asObservable(); + } + + /** + * Sets the listening state of the application. + */ + set listening(listening: boolean) { + this.isListening = listening; + + if (listening) { + this._listen$.next(); + this._listen$.complete(); + } + } + + /** + * Returns a boolean indicating whether the application is listening for incoming requests. + */ + get listening(): boolean { + return this.isListening; + } } diff --git a/packages/core/nest-application.ts b/packages/core/nest-application.ts index ef123cde060..a3c6201f529 100644 --- a/packages/core/nest-application.ts +++ b/packages/core/nest-application.ts @@ -294,8 +294,12 @@ export class NestApplication public async listen(port: number | string, hostname: string): Promise; public async listen(port: number | string, ...args: any[]): Promise { this.assertNotInPreviewMode('listen'); - !this.isInitialized && (await this.init()); + if (!this.isInitialized) { + await this.init(); + } + + const httpAdapterHost = this.container.getHttpAdapterHostRef(); return new Promise((resolve, reject) => { const errorHandler = (e: any) => { this.logger.error(e?.toString?.()); @@ -323,6 +327,8 @@ export class NestApplication if (address) { this.httpServer.removeListener('error', errorHandler); this.isListening = true; + + httpAdapterHost.listening = true; resolve(this.httpServer); } if (isCallbackInOriginalArgs) { diff --git a/packages/core/test/helpers/application-ref-host.spec.ts b/packages/core/test/helpers/application-ref-host.spec.ts index 21bc1b78ed6..78704327e41 100644 --- a/packages/core/test/helpers/application-ref-host.spec.ts +++ b/packages/core/test/helpers/application-ref-host.spec.ts @@ -2,11 +2,27 @@ import { expect } from 'chai'; import { HttpAdapterHost } from '../../helpers/http-adapter-host'; describe('HttpAdapterHost', () => { - const applicationRefHost = new HttpAdapterHost(); + let applicationRefHost: HttpAdapterHost; + beforeEach(() => { + applicationRefHost = new HttpAdapterHost(); + }); + it('should wrap application reference', () => { const ref = {}; applicationRefHost.httpAdapter = ref as any; expect(applicationRefHost.httpAdapter).to.be.eql(ref); }); + + it('should emit listen event when listening is set to true', done => { + applicationRefHost.listen$.subscribe(() => { + expect(applicationRefHost.listening).to.be.true; + done(); + }); + applicationRefHost.listening = true; + }); + + it('listening should return false if the application isnt listening yet', () => { + expect(applicationRefHost.listening).to.be.false; + }); });