/// <summary> /// Simple handler for the NATS protocol state-machine /// </summary> /// <param name="segment"></param> /// <param name="op"></param> private void NotifyOperationHandler( ArraySegment <byte> segment, int op ) { switch (this._workerStage) { case StageWaitInfo: if (op == Parser.INFO_ARG) { this._info = this.ProcessInfo(segment, false); // This will check to see if the connection should be // secure. This can be dictated from either end and should // only be called after the INIT protocol has been received. // Check to see if we need to engage TLS // Check for mismatch in setups bool currentIsSecured = this._srvPool.CurrentServer.EndPoint.Secured; if (currentIsSecured && this._info.TlsRequired == false) { throw new NATSSecureConnWantedException(); } else if (this._info.TlsRequired && currentIsSecured == false) { throw new NATSSecureConnRequiredException(); } // Need to rewrap with bufio if (currentIsSecured) { // makeSecureConn will wrap an existing Conn using TLS this._tcp.MakeTLS(); } } break; case StageSendConnectVerbose: if (op == Parser.OP_PLUS_OK) { this._workerStage = StageSendConnectNormal; } break; case StageSendConnectNormal: if (op == Parser.OP_PONG) { this._workerStage = StageLogicallyConnected; } break; case StageLogicallyConnected: if (op == Parser.OP_PING) { this.ProcessPing(); } else if (op == Parser.OP_PONG) { this.ProcessPong(); } break; default: throw new NotSupportedException("worker-stage not supported: " + this._workerStage); } }
/// <summary> /// The "inner" worker /// </summary> /// <remarks> /// This method can raise any kind of exception, which will be caught by the <see cref="OuterWorker(object)"/>. /// </remarks> /// <param name="ctx"></param> private void InnerWorker( WorkerContext ctx ) { this._info = null; /** * Initial connection to a server **/ try { this._tcp = this._srvPool.ConnectToAServer( this._workerStage == StageServerInitialConnection, ctx.CancToken ); if (this._tcp == null) { throw new Exception(); } } catch (OperationCanceledException ex) { throw ex; } catch (Exception) { throw new NATSNoServersException("Unable to connect to a server."); } /** * Waiting for the "INFO" data issued by the server **/ this._workerStage = StageWaitInfo; this.ReadThenParseSegment(null, null, ReadTimeout, ctx.CancToken); if (this._info == null) { throw new NATSConnectionException("Protocol exception, INFO not received"); } /** * Send "CONNECT" to the server **/ this._workerStage = this._options.Verbose ? StageSendConnectVerbose : StageSendConnectNormal; try { this._tcp.SocketSendTimeout = 0; this._tcp.WriteString(this.ConnectProto()); this.SendPing(); } catch (Exception ex) { throw new NATSException("Error sending connect protocol message", ex); } this.ReadThenParseSegment( ex => throw new NATSConnectionException("Connect read error", ex), null, ReadTimeout, ctx.CancToken ); if (this._workerStage != StageLogicallyConnected) { throw new NATSConnectionException("Connect read protocol error"); } /** * Main processing loop **/ this._tcp.SocketReceiveTimeout = ReadTimeout; //for safety this.SetStatus(ConnState.CONNECTED); this.PingStart(); this._owner.RequestReplyManager.Start(); int readCount = 0; while (ctx.CancToken.IsCancellationRequested == false) { if (this._writePending) { //something to send is pending lock (this._writeLocker) { //is PING, maybe? if (this._pingSendPending) { this.SendPing(); } //are one or more SUB messages, maybe? if (this._subPending) { for (int i = 0, sublen = this._subQueue.Count; i < sublen; i++) { this._tcp.WriteString(this._subQueue[i]); } this._subQueue.Clear(); this._tcp.WriteFlush(); this._subPending = false; } this._writePending = false; } } else if (this._isLogicalStarted) { if (readCount == 0) { //no operation was perfomed: no data in, no data out. //the cycle would be too tight, then. //give the OS a breathe of fresh air... Thread.Sleep(1); } } else { //the initial NATS handshake with the server is complete: //if the start was async, then consider its task as terminated this._isLogicalStarted = true; ctx.TaskCompletion?.SetResult(null); } //listen to incoming data readCount = this.ReadThenParseSegment( this.ProcessOpError, this.ProcessOpError, 0, ctx.CancToken ); } }