UNPKG

service-model

Version:

An object oriented web service framework inspired by Windows Communication Foundation.

176 lines (175 loc) 6.2 kB
var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var events_1 = require("events"); var message_1 = require("../message"); var requestHandler_1 = require("./requestHandler"); var httpStatusCode_1 = require("../httpStatusCode"); var nullLogger_1 = require("../nullLogger"); /** * Responsible for dispatching service requests. * * <uml> * hide members * hide circle * DispatcherFactory .> RequestDispatcher * EventEmitter <|.. RequestDispatcher * RequestDispatcher *-- DispatchService : services * RequestDispatcher *- Logger : logger * </uml> * * ### Events * * `closing` - Triggered when [[close]] is called on the RequestDispatcher. This event indicates that the dispatcher is * no longer accepting new requests. * * `closed` - Triggered after the RequestedDispatched closes. This event indicates that all pending requests have * completed or timed out. * * `error` - Trigger if an unhandled exception is raised in an operation. This event is only relevant if an * [[OperationContext]] is created. * */ var RequestDispatcher = (function (_super) { __extends(RequestDispatcher, _super); function RequestDispatcher() { _super.apply(this, arguments); /** * Timeout in milliseconds for close operation to complete. Any requests that have not completed before * the timeout are aborted. */ this.closeTimeout = 30000; /** * List of services in the dispatcher. */ this.services = []; /** * Logger to use that conforms to [bunyan](https://www.npmjs.com/package/bunyan)'s logger interface. */ this.logger = nullLogger_1.NullLogger.instance; /** * @hidden */ this._requestCount = 0; } /** * Dispatches a request. * @param request The request to dispatch. */ RequestDispatcher.prototype.dispatch = function (request) { var _this = this; if (this._closing) { request.reply(message_1.Message.createReply(httpStatusCode_1.HttpStatusCode.ServiceUnavailable, "Service is currently unavailable.")); return; } var endpoint = this._chooseEndpoint(request.message); if (!endpoint) { request.reply(message_1.Message.createReply(httpStatusCode_1.HttpStatusCode.NotFound, "Endpoint not found.")); } else { var handler = new requestHandler_1.RequestHandler(endpoint, request); this._addRequest(handler); handler.process(function () { return _this._removeRequest(handler); }); } }; /** * Validates that the dispatcher is correctly configured. */ RequestDispatcher.prototype.validate = function () { this.services.forEach(function (service) { return service.validate(); }); }; /** * Closes the dispatcher. If any requests do not complete within 'closeTimeout', they are aborted. * @param callback Optional. Called after dispatcher is closed. */ RequestDispatcher.prototype.close = function (callback) { var _this = this; if (callback) { if (this._closed) { callback(); return; } this.on('closed', callback); } if (this._closing) return; this._closing = true; this.emit('closing'); // if there are not any pending requests when close is called then close immediately. if (this._requestCount == 0 && this._closing) { this._closed = true; this.emit('closed'); return; } this._closeTimer = setTimeout(function () { _this.logger.warn("Timeout of %dms exceeded while closing dispatcher.", _this.closeTimeout); var handler = _this._head; while (handler) { handler.abort(); handler = handler.next; } }, this.closeTimeout); this._closeTimer.unref(); }; /** * Chooses the appropriate endpoint for the request. * @param message The request message. * @hidden */ RequestDispatcher.prototype._chooseEndpoint = function (message) { var max = -Infinity, match; for (var i = 0; i < this.services.length; i++) { var service = this.services[i]; for (var j = 0; j < service.endpoints.length; j++) { var endpoint = service.endpoints[j]; if (endpoint.filter.match(message)) { if (endpoint.filterPriority > max) { max = endpoint.filterPriority; match = endpoint; } } } } return match; }; /** * Adds a request to the list of pending requests. * @param handler The request handler. * @hidden */ RequestDispatcher.prototype._addRequest = function (handler) { if (this._head) { handler.prev = this._tail; this._tail = this._tail.next = handler; } else { this._head = this._tail = handler; } this._requestCount++; }; /** * Removes a request from the list of pending requests. * @param handler The request handler. * @hidden */ RequestDispatcher.prototype._removeRequest = function (handler) { if (handler.prev) { handler.prev.next = handler.next; } else { this._head = handler.next; } if (handler.next) { handler.next.prev = handler.prev; } else { this._tail = handler.prev; } this._requestCount--; if (this._requestCount == 0 && this._closing) { clearTimeout(this._closeTimer); this.emit('closed'); } }; return RequestDispatcher; })(events_1.EventEmitter); exports.RequestDispatcher = RequestDispatcher;