UNPKG

ziron-server

Version:
1 lines 11 kB
"use strict";var __awaiter=this&&this.__awaiter||function(e,t,s,i){return new(s||(s=Promise))((function(n,r){function o(e){try{h(i.next(e))}catch(e){r(e)}}function a(e){try{h(i.throw(e))}catch(e){r(e)}}function h(e){var t;e.done?n(e.value):(t=e.value,t instanceof s?t:new s((function(e){e(t)}))).then(o,a)}h((i=i.apply(e,t||[])).next())}))};Object.defineProperty(exports,"__esModule",{value:!0});const ServerOptions_1=require("./ServerOptions"),OriginsChecker_1=require("./OriginsChecker"),AuthEngine_1=require("./AuthEngine"),Socket_1=require("./Socket"),ziron_ws_1=require("ziron-ws"),emitix_1=require("emitix"),ziron_errors_1=require("ziron-errors"),Utils_1=require("./Utils"),MiddlewareUtils_1=require("./MiddlewareUtils"),InternalBroker_1=require("./broker/InternalBroker"),uniqId=require("uniqid"),Constants_1=require("./Constants"),FailedToListenError_1=require("./FailedToListenError"),EnhanceHttpResponse_1=require("./http/EnhanceHttpResponse"),UpgradeRequest_1=require("./http/UpgradeRequest"),ziron_engine_1=require("ziron-engine"),EnhanceHttpRequest_1=require("./http/EnhanceHttpRequest");class Server{get id(){return this.options.id}get port(){return this.options.port}get path(){return this.options.path}get tls(){return!!this.options.tls}constructor(e={}){this.options={id:uniqId(),maxPayloadSize:4194304,maxBackpressure:6291456,socketChannelLimit:1e3,allowClientPublish:!0,publishToPublisher:!0,responseTimeout:7e3,pingInterval:8e3,origins:null,port:3e3,path:"/",auth:{},compression:{},healthEndpoint:!0,tls:null,binaryContentPacketTimeout:1e4,streamsPerPackageLimit:20,chunksCanContainStreams:!1},this._compressionOptions={active:!0,compressor:2,alwaysCompressBatches:!1,minBytes:104857,minLength:2e4},this.emitter=new emitix_1.default,this.once=this.emitter.once.bind(this.emitter),this.on=this.emitter.on.bind(this.emitter),this.off=this.emitter.off.bind(this.emitter),this._emit=this.emitter.emit.bind(this.emitter),this.clientCount=0,this.clients={},this.wsMessageCount=0,this.invokeMessageCount=0,this.transmitMessageCount=0,this.httpMessageCount=0,this.socketConstructorExtension=Constants_1.EMPTY_FUNCTION,this.connectionHandler=Constants_1.EMPTY_FUNCTION,this.healthCheck=()=>!0,this.refuseConnections=!1,this.ignoreFurtherTransmits=!1,this.ignoreFurtherInvokes=!1,this._shouldCompress=()=>!1,Object.assign(this.options,e),this.options.path=(0,Utils_1.preprocessPath)(this.options.path),Object.assign(this._compressionOptions,e.compression),this.transportOptions=this._createTransportOptions(),this.lowSendBackpressureMark=Math.trunc(.5*this.options.maxBackpressure),this._loadCompressionOptions(),this.auth=new AuthEngine_1.default(this.options.auth),this.originsChecker=(0,OriginsChecker_1.createOriginsChecker)(this.options.origins),this.internalBroker=new InternalBroker_1.default(this),this._internalBroker=this.internalBroker,this.channels=this.internalBroker.exchange,this._setUpSocketChLimit(),this._app=this._setUpApp(),this._groupTransport=this._createGroupTransport(),this._startPingInterval(),this._startAuthExpireCheck(),this._setupHttpRequestHandling()}_createGroupTransport(){return new ziron_engine_1.DynamicGroupTransport({send:(e,t,s,i)=>{this._app.publish("G"+e,t,s,this._shouldCompress(t,s,i))},isConnected:()=>!0},ziron_engine_1.DynamicGroupTransport.buildOptions(Object.assign({freeBufferMaxPoolSize:200},this.transportOptions)))}_createTransportOptions(){return ziron_engine_1.Transport.buildOptions({maxBufferSize:Math.trunc(.7*this.options.maxBackpressure),limitBatchBinarySize:Math.max(Math.ceil(.7*this.options.maxPayloadSize),200),limitBatchStringLength:Math.max(Math.ceil(this.options.maxPayloadSize/4*.7),2e3),responseTimeout:this.options.responseTimeout,binaryContentPacketTimeout:this.options.binaryContentPacketTimeout,streamsPerPackageLimit:this.options.streamsPerPackageLimit,chunksCanContainStreams:this.options.chunksCanContainStreams,streamsEnabled:!0})}_loadCompressionOptions(){if(!this._compressionOptions.active)return;const e=this._compressionOptions.minBytes,t=this._compressionOptions.minLength;this._shouldCompress=this._compressionOptions.alwaysCompressBatches?(s,i,n)=>n||(i?s.byteLength>=e:s.length>=t):(s,i)=>i?s.byteLength>=e:s.length>=t}_setUpSocketChLimit(){if(null!=this.options.socketChannelLimit){const e=this.options.socketChannelLimit;this._checkSocketChLimitReached=t=>t>=e}else this._checkSocketChLimitReached=()=>!1}_setUpApp(){const e=this.options.tls;return(e?(0,ziron_ws_1.SSLApp)({key_file_name:e.keyFile,cert_file_name:e.certFile,ca_file_name:e.caFile,passphrase:e.passphrase,dh_params_file_name:e.dhParamsFile,ssl_prefer_low_memory_usage:e.releaseBuffersMode}):(0,ziron_ws_1.App)()).ws("/*",Object.assign(Object.assign({compression:this._compressionOptions.active?ServerOptions_1.COMPRESSOR_TO_INTERNAL_COMPRESSOR[this._compressionOptions.compressor]:ziron_ws_1.DISABLED,maxPayloadLength:this.options.maxPayloadSize,maxBackpressure:this.options.maxBackpressure},{closeOnBackpressureLimit:1}),{idleTimeout:Math.max(8,Math.min(Math.ceil(this.options.pingInterval/1e3*2),960)),upgrade:this._handleUpgrade.bind(this),open:this._handleWsOpen.bind(this),message:this._handleWsMessage.bind(this),drain:Server._handleWsDrain,close:Server._handleWsClose,sendPingsAutomatically:!1}))}_startPingInterval(){this._pingTicker=setInterval(()=>{this._app.publish("broadcast",ziron_engine_1.PING,!0)},this.options.pingInterval)}_startAuthExpireCheck(){var e;this._authTokenExpireCheckerTicker=setInterval(()=>{for(const e in this.clients)this.clients[e]._checkAuthTokenExpire()},null!==(e=this.options.auth.expireCheckInterval)&&void 0!==e?e:12e3)}static _abortUpgrade(e,t,s){e.end(`HTTP/1.1 ${t} ${s}\r\n\r\n`,!0)}_handleUpgrade(e,t,s){if(this.httpMessageCount++,this.refuseConnections)return Server._abortUpgrade(e,403,"Client verification failed");let i=!1;e.onAborted(()=>i=!0);const n=t.getUrl().split("?")[0].split("#")[0];if(n!==this.options.path&&n!==this.options.path+"/")return Server._abortUpgrade(e,400,"URL not supported");const r=t.getHeader("origin");if(!this.originsChecker(r)){const t=new ziron_errors_1.ServerProtocolError("Failed to authorize socket handshake - Invalid origin: "+r);return this._emit("warning",t),Server._abortUpgrade(e,403,t.message)}const o=new UpgradeRequest_1.default(t);if("ziron"!==o.headers.secWebSocketProtocol)return Server._abortUpgrade(e,4800,"Unsupported protocol");const{secWebSocketKey:a,secWebSocketProtocol:h,secWebSocketExtensions:c}=o.headers;this.upgradeMiddleware?(()=>{__awaiter(this,void 0,void 0,(function*(){try{yield this.upgradeMiddleware(o),i||e.upgrade({req:o},a,h,c,s)}catch(t){t instanceof MiddlewareUtils_1.Block?i||Server._abortUpgrade(e,t.code,t.message||"Handshake was blocked by the handshake middleware"):(this._emit("error",(0,Utils_1.ensureError)(t)),i||Server._abortUpgrade(e,"number"==typeof(null==t?void 0:t.code)?t.code:403,"Handshake was blocked by the handshake middleware"))}}))})():e.upgrade({req:o},a,h,c,s)}_handleWsOpen(e){return __awaiter(this,void 0,void 0,(function*(){let t;try{t=new Socket_1.default(this,e)}catch(t){return e.end(1011,"Unknown connection error")}e.zSocket=t,e.subscribe("broadcast"),this.clientCount++,this.clients[t.id]=t;try{const e=t.upgradeRequest.signedToken;if(this.socketMiddleware)try{yield this.socketMiddleware(t)}catch(e){return void(e instanceof MiddlewareUtils_1.Block?t.disconnect(e.code,e.message||"Connection was blocked by the socket middleware"):(this._emit("error",(0,Utils_1.ensureError)(e)),t.disconnect("number"==typeof(null==e?void 0:e.code)?e.code:4403,"Connection was blocked by the socket middleware")))}let s=-1;if(e)try{yield t._processAuthToken(e),s=0}catch(e){s=e&&e.badAuthToken?2:1}const i=yield this.connectionHandler(t),n=[this.options.pingInterval,this.options.maxPayloadSize,s];void 0!==i&&n.push(i),t.transmit("#",n)}catch(e){this._emit("error",(0,Utils_1.ensureError)(e)),t.disconnect("number"==typeof(null==e?void 0:e.code)?e.code:1011,"Unknown connection error")}}))}_handleWsMessage(e,t,s){this.wsMessageCount++;const i=e.zSocket;i&&i._emitMessage(s?t:Buffer.from(t).toString())}static _handleWsDrain(e){const t=e.zSocket;t&&t._emitDrain()}static _handleWsClose(e,t,s){const i=e.zSocket;i&&i._emitClose(t,Buffer.from(s).toString())}_setupHttpRequestHandling(){const e=this.options.path+"/health";this._app.any("/*",(t,s)=>__awaiter(this,void 0,void 0,(function*(){this.httpMessageCount++;const i=(0,EnhanceHttpRequest_1.default)(s),n=(0,EnhanceHttpResponse_1.default)(t),r=i.getHeader("origin");if(this.originsChecker(r)){if(n.headers["Access-Control-Allow-Origin"]=r,n.headers["Access-Control-Allow-Methods"]="*",n.headers["Access-Control-Allow-Headers"]="X-Requested-With,contenttype",n.headers["Access-Control-Allow-Credentials"]="true",this.options.healthEndpoint&&"get"===i.getMethod()&&i.getPath()===e)return this._processHttpHealthCheckRequest(n);this.httpRequestHandler&&(yield this.httpRequestHandler(i,n)),n.available&&n.cork(()=>{n.writeStatus("426 Upgrade Required"),n.end()})}else n.cork(()=>{n.writeStatus("401 Unauthorized"),n.headers["Content-Type"]="text/plain",n.writeHeaders(),n.end("Failed - Invalid origin: "+r)})})))}_processHttpHealthCheckRequest(e){return __awaiter(this,void 0,void 0,(function*(){let t=!1;try{t=yield this.healthCheck()}catch(e){this._emit("error",(0,Utils_1.ensureError)(e))}e.available&&e.cork(()=>{e.writeStatus(t?"200 OK":"500 Internal Server Error"),e.headers["Content-Type"]="text/html",e.writeHeaders(),e.end(t?"Healthy":"Unhealthy")})}))}transmitToGroup(e,t,s,i){const n=null==i?void 0:i.skipMember;n?(0,ziron_engine_1.sendPackage)(ziron_engine_1.Transport.prepareMultiTransmit(t,s,i),(t,s,i)=>{n._socket.publish("G"+e,t,s,this._shouldCompress(t,s,i))}):this._groupTransport.transmit(e,t,s,i)}getGroupMemberCount(e){return this._app.numSubscribers("G"+e)}listen(){return __awaiter(this,void 0,void 0,(function*(){return null!=this._listenToken?Promise.resolve():this._startListenPromise?void 0:this._startListenPromise=new Promise((e,t)=>{const s=this.options.port;this._app.listen(s,1,i=>{i?(this._listenToken=i,e()):t(new FailedToListenError_1.FailedToListenError(s)),this._startListenPromise=null})})}))}_removeSocket(e){this.clientCount--,delete this.clients[e.id]}getInternalSubscriptions(){return this.internalBroker.getSubscriptions()}resetWsMessageCount(){this.wsMessageCount=0}resetInvokeMessageCount(){this.invokeMessageCount=0}resetTransmitMessageCount(){this.transmitMessageCount=0}resetHttpMessageCount(){this.httpMessageCount=0}resetCounts(){this.wsMessageCount=0,this.invokeMessageCount=0,this.transmitMessageCount=0,this.httpMessageCount=0}stopListen(){this._listenToken&&((0,ziron_ws_1.us_listen_socket_close)(this._listenToken),this._listenToken=null)}isListening(){return!!this._listenToken}terminate(){this.stopListen(),Object.values(this.clients).forEach(e=>e._terminate()),this.clients={},this.clientCount=0,this.internalBroker.terminate()}}exports.default=Server;