Add foreground service, some cleanup, and start websocket server
This commit is contained in:
@@ -107,7 +107,7 @@ export class Request extends ScopedContainer implements DataContainer {
|
||||
protected clientRequest: IncomingMessage,
|
||||
|
||||
/** The native Node.js response. */
|
||||
protected serverResponse: ServerResponse,
|
||||
protected serverResponse?: ServerResponse,
|
||||
) {
|
||||
super(Container.getContainer())
|
||||
this.registerSingletonInstance(Request, this)
|
||||
|
||||
@@ -66,7 +66,7 @@ export class Response {
|
||||
public readonly request: Request,
|
||||
|
||||
/** The native Node.js ServerResponse. */
|
||||
protected readonly serverResponse: ServerResponse,
|
||||
protected readonly serverResponse?: ServerResponse,
|
||||
) { }
|
||||
|
||||
protected get logging(): Logging {
|
||||
@@ -173,6 +173,11 @@ export class Response {
|
||||
*/
|
||||
public sendHeaders(): this {
|
||||
this.logging.verbose(`Sending headers...`)
|
||||
if ( !this.serverResponse ) {
|
||||
throw new ErrorWithContext('Unable to send headers: Response has no underlying connection.', {
|
||||
suggestion: 'This usually means the Request was created by an alternative server, like WebsocketServer. You should use that server to handle the request.',
|
||||
})
|
||||
}
|
||||
const headers = {} as any
|
||||
|
||||
const setCookieHeaders = this.cookies.getSetCookieHeaders()
|
||||
@@ -220,8 +225,14 @@ export class Response {
|
||||
* @param data
|
||||
*/
|
||||
public async write(data: string | Buffer | Uint8Array | Readable): Promise<void> {
|
||||
this.logging.verbose(`Writing headers & data to response... (destroyed? ${this.serverResponse.destroyed})`)
|
||||
this.logging.verbose(`Writing headers & data to response... (destroyed? ${!this.serverResponse || this.serverResponse.destroyed})`)
|
||||
return new Promise<void>((res, rej) => {
|
||||
if ( !this.serverResponse ) {
|
||||
throw new ErrorWithContext('Unable to write response: Response has no underlying connection.', {
|
||||
suggestion: 'This usually means the Request was created by an alternative server, like WebsocketServer. You should use that server to handle the request.',
|
||||
})
|
||||
}
|
||||
|
||||
if ( this.responseEnded || this.serverResponse.destroyed ) {
|
||||
throw new ErrorWithContext('Tried to write to Response after lifecycle ended.')
|
||||
}
|
||||
@@ -274,7 +285,7 @@ export class Response {
|
||||
* or the connection has been destroyed.
|
||||
*/
|
||||
public canSend(): boolean {
|
||||
return !(this.responseEnded || this.serverResponse.destroyed)
|
||||
return !(this.responseEnded || !this.serverResponse || this.serverResponse.destroyed)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,7 +297,7 @@ export class Response {
|
||||
}
|
||||
|
||||
this.sentHeaders = true
|
||||
this.serverResponse.end()
|
||||
this.serverResponse?.end()
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,10 @@ ${Object.keys(context).map(key => ` - ${key} : ${JSON.stringify(context[key])
|
||||
return 'It looks like this route relies on the security framework. Is the route you are accessing inside a middleware (e.g. SessionAuthMiddleware)?'
|
||||
} else if ( this.thrownError.message.startsWith('Unable to resolve schema for validator') ) {
|
||||
return 'Make sure the directory in which the interface file is located is listed in extollo.cc.zodify in package.json, and that it ends with the proper .type.ts suffix.'
|
||||
} else if ( this.thrownError instanceof ErrorWithContext ) {
|
||||
if ( typeof this.thrownError.context.suggestion === 'string' ) {
|
||||
return this.thrownError.context.suggestion
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
|
||||
@@ -136,6 +136,14 @@ export class Route<TReturn extends ResponseObject, THandlerParams extends unknow
|
||||
return new Route(method, endpoint)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WebSocket route on the given endpoint.
|
||||
* @param endpoint
|
||||
*/
|
||||
public static socket(endpoint: string): Route<void, [void]> {
|
||||
return new Route<void, [void]>('ws', endpoint)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new GET route on the given endpoint.
|
||||
*/
|
||||
@@ -188,10 +196,14 @@ export class Route<TReturn extends ResponseObject, THandlerParams extends unknow
|
||||
protected displays: Collection<{stage: 'pre'|'post'|'handler', display: string}> = new Collection()
|
||||
|
||||
constructor(
|
||||
protected method: HTTPMethod | HTTPMethod[],
|
||||
protected method: 'ws' | HTTPMethod | HTTPMethod[],
|
||||
protected route: string,
|
||||
) {}
|
||||
|
||||
public isForWebSocket(): boolean {
|
||||
return this.method === 'ws'
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a programmatic name for this route.
|
||||
* @param name
|
||||
@@ -212,6 +224,10 @@ export class Route<TReturn extends ResponseObject, THandlerParams extends unknow
|
||||
* Get the string-form methods supported by the route.
|
||||
*/
|
||||
public getMethods(): HTTPMethod[] {
|
||||
if ( this.method === 'ws' ) {
|
||||
return []
|
||||
}
|
||||
|
||||
if ( !Array.isArray(this.method) ) {
|
||||
return [this.method]
|
||||
}
|
||||
@@ -250,10 +266,14 @@ export class Route<TReturn extends ResponseObject, THandlerParams extends unknow
|
||||
* @param method
|
||||
* @param potential
|
||||
*/
|
||||
public match(method: HTTPMethod, potential: string): boolean {
|
||||
if ( Array.isArray(this.method) && !this.method.includes(method) ) {
|
||||
public match(method: 'ws' | HTTPMethod, potential: string): boolean {
|
||||
if ( method === 'ws' && !this.isForWebSocket() ) {
|
||||
return false
|
||||
} else if ( !Array.isArray(this.method) && this.method !== method ) {
|
||||
} else if ( method !== 'ws' && this.isForWebSocket() ) {
|
||||
return false
|
||||
} else if ( method !== 'ws' && Array.isArray(this.method) && !this.method.includes(method) ) {
|
||||
return false
|
||||
} else if ( method !== 'ws' && !Array.isArray(this.method) && this.method !== method ) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user