Repository: microsoft/nni
File: ts/nni_manager/common/command_channel/http.ts (TypeScript)

Code before:
const HttpRequestTimeout = 408;
const HttpGone = 410;

export class HttpChannelServer implements CommandChannelServer {
    private emitter: EventEmitter = new EventEmitter();
    private log: Logger;
    // the server can only send commands when the client requests, so it needs a queue
    private outgoingQueues: Map<string, CommandQueue> = new Map();
    private path: string;
    private serving: boolean = false;

    constructor(name: string, urlPath: string) {
        this.log = getLogger(`HttpChannelManager.${name}`);
        this.path = urlPath;
    }

    public start(): void {
        this.serving = true;
        const channelPath = globals.rest.urlJoin(this.path, ':channel');
        globals.rest.registerSyncHandler('GET', channelPath, this.handleGet.bind(this));
        globals.rest.registerSyncHandler('PUT', channelPath, this.handlePut.bind(this));
    }

    public shutdown(): void {
        this.serving = false;
        this.outgoingQueues.forEach(queue => { queue.clear(); });
    }

    public getChannelUrl(channelId: string): string {
        return globals.rest.getFullUrl('http', this.path, channelId);
    }

    public send(channelId: string, command: Command): void {
        this.getOutgoingQueue(channelId).push(command);
    }

    public onReceive(callback: (channelId: string, command: Command) => void): void {
        this.emitter.on('receive', callback);
    }

    private handleGet(request: Request, response: Response): void {
        const channelId = request.params['channel'];
        const promise = this.getOutgoingQueue(channelId).asyncPop(timeoutMilliseconds);
        promise.then(command => {
            if (command === null) {
                response.sendStatus(this.serving ? HttpRequestTimeout : HttpGone);
            } else {
                response.send(command);
            }
        });
    }

Review comment (suggestion):
It looks not symmetric to `start()`. For example, `start()` has registerHandler, but shudown doesn't have unregister.

Code after:
const HttpRequestTimeout = 408;
const HttpGone = 410;

export class HttpChannelServer implements CommandChannelServer {
    private emitter: EventEmitter = new EventEmitter();
    private log: Logger;
    // the server can only send commands when the client requests, so it needs a queue
    private outgoingQueues: Map<string, CommandQueue> = new Map();
    private path: string;
    private serving: boolean = false;

    constructor(name: string, urlPath: string) {
        this.log = getLogger(`HttpChannelManager.${name}`);
        this.path = urlPath;
    }

    public async start(): Promise<void> {
        this.serving = true;
        const channelPath = globals.rest.urlJoin(this.path, ':channel');
        globals.rest.registerSyncHandler('GET', channelPath, this.handleGet.bind(this));
        globals.rest.registerSyncHandler('PUT', channelPath, this.handlePut.bind(this));
    }

    public async shutdown(): Promise<void> {
        this.serving = false;
        this.outgoingQueues.forEach(queue => { queue.clear(); });
    }

    public getChannelUrl(channelId: string): string {
        return globals.rest.getFullUrl('http', this.path, channelId);
    }

    public send(channelId: string, command: Command): void {
        this.getOutgoingQueue(channelId).push(command);
    }

    public onReceive(callback: (channelId: string, command: Command) => void): void {
        this.emitter.on('receive', callback);
    }

    private handleGet(request: Request, response: Response): void {
        const channelId = request.params['channel'];
        const promise = this.getOutgoingQueue(channelId).asyncPop(timeoutMilliseconds);
        promise.then(command => {
            if (command === null) {
                response.sendStatus(this.serving ? HttpRequestTimeout : HttpGone);
            } else {
                response.send(command);
            }
        });
    }