/// <summary> /// Returns the next <see cref="MsgOut"/> available to a subscriber, /// or block up to a given timeout until the next one is available. /// </summary> /// <param name="timeout">The amount of time to wait for the next message.</param> /// <param name="token">Propagates notification that operations should be canceled.</param> /// <returns>The next <see cref="MsgOut"/> available to a subscriber.</returns> public MsgOut NextMessage(TimeSpan timeout, CancellationToken token) { if (this._disposed != 0) { throw new ObjectDisposedException(nameof(PassiveSubscription)); } MsgOut message = null; try { while ( this._disposed == 0 && this.TryDequeueSinglePendingMessage(out message) == false && this._mainSem.Wait(timeout, token) ) { //do nothing } ; } finally { //check for disposal in progress if (this._disposed != 0) { throw new NATSBadSubscriptionException(); } } return(message); }
/// <summary> /// Tells this instance the request-reply process has been completed with success, /// and feeds the <see cref="MsgOut"/> received. /// </summary> /// <param name="message"></param> internal void Resolve(MsgOut message) { int sts = Interlocked.Exchange(ref this._status, StateComplete); if (sts != StateComplete) { this._resolveValue = message; this._evt.Release(1); } }
/// <summary> /// Processes a valid message just parsed /// </summary> /// <param name="message"></param> private void NotifyMessageHandler( MsgOut message ) { //update stats this.AddIncomingStats(1, message.PayloadLength); //yield the subscription pool to go up this._owner.SubPool.ProcessMessage(message); }
/// <summary> /// Handler of the received <see cref="MsgOut"/> message from the subscription. /// The message is quicky dispatched to the proper <see cref="InFlightRequest"/>. /// </summary> /// <param name="message"></param> /// <param name="reqId"></param> void IRequestReplayFeedback.ProcessMessage(MsgOut message, long reqId) { InFlightRequest request; lock (this._reqlocker) { this._requestRegister.TryGetValue(reqId, out request); } if (request != null) { request.Resolve(message); } }
/// <summary> /// Processes the <see cref="MsgOut"/> as they are given from the <see cref="Parser"/> /// </summary> /// <param name="message"></param> internal void ProcessMessage( MsgOut message ) { SubscriptionBase sub; lock (this._sublocker) { //retrieves the subscription proxy to dispatch the message to this._subRegister.TryGetValue(message.SubId, out sub); } if (sub != null) { //dispatches the message to the proxy message.ArrivalSubcription = sub; sub.ProcessMessage(message); } }
/// <summary> /// Parser for a message header /// </summary> /// <remarks> /// see: https://nats.io/documentation/internals/nats-protocol/ /// </remarks> private void ParseHeader() { //initial byte-array to UTF-8 char-array conversion int charCount = Encoding.UTF8.GetChars(this._xbuffer, 0, this._xbuflen, this._cbuffer, 0); //the idea is to avoid splitting a string into other strings, //so simply mark the useful points of a character array int numseps = 0; for (int i = 0; i < charCount; i++) { if (this._cbuffer[i] == ' ') { this._sepPos[numseps++] = i; } } string replyTo = null; if (numseps == 3) { //read the 'reply' argument replyTo = new string( this._cbuffer, this._sepPos[1] + 1, this._sepPos[2] - this._sepPos[1] - 1 ); } this._message = new MsgOut( subject: new string(this._cbuffer, 0, this._sepPos[0]), replyTo: replyTo, subId: Converters.StringToInt64(this._cbuffer, this._sepPos[0] + 1, this._sepPos[1] - this._sepPos[0] - 1) ) { PayloadLength = (int)Converters.StringToInt64(this._cbuffer, this._sepPos[numseps - 1] + 1, this._xbuflen - this._sepPos[numseps - 1] - 1), }; }
/// <summary> /// Processe the dispatched message /// </summary> /// <param name="message"></param> protected override void ProcessOutgoingMessage(MsgOut message) { this.Owner.ReactiveSubscriptionSignal(this); }
/// <summary> /// Entry-point to feed the parser with data incoming from the NATS server /// </summary> /// <param name="segment"></param> internal void Parse( ArraySegment <byte> segment ) { byte[] buffer = segment.Array; int bstart = 0; int bcount = 0; #if DEBUG //Print(buffer, 0, segment.Count); #endif for (int i = 0, len = segment.Count; i < len; i++) { char b = (char)buffer[i]; switch (_state) { case OP_START: switch (b) { case 'M': case 'm': _state = OP_M; break; case 'C': case 'c': _state = OP_C; break; case 'P': case 'p': _state = OP_P; break; case '+': _state = OP_PLUS; break; case '-': _state = OP_MINUS; break; case 'i': case 'I': _state = OP_I; break; default: this.ParseError(buffer, i, len); break; } break; case OP_M: switch (b) { case 'S': case 's': _state = OP_MS; break; default: this.ParseError(buffer, i, len); break; } break; case OP_MS: switch (b) { case 'G': case 'g': _state = OP_MSG; break; default: this.ParseError(buffer, i, len); break; } break; case OP_MSG: switch (b) { case ' ': case '\t': _state = OP_MSG_SPC; break; default: this.ParseError(buffer, i, len); break; } break; case OP_MSG_SPC: switch (b) { case ' ': break; case '\t': break; default: this._xbuflen = 0; bcount = 0; bstart = i; _state = MSG_ARG; i--; break; } break; case MSG_ARG: switch (b) { case '\r': break; case '\n': if (bcount != 0) { AddSegment(); bcount = 0; } this.ParseHeader(); this._xbuflen = 0; _state = MSG_PAYLOAD; break; default: bcount++; break; } break; case MSG_PAYLOAD: int msgSize = this._message.PayloadLength; if (msgSize == 0) { this.NotifyMessage(this._message); this._message = null; _state = MSG_END; } else { bcount = len - i; if ((this._xbuflen + bcount) > msgSize) { bcount = msgSize - this._xbuflen; } if (bcount != 0) { bstart = i; AddSegment(); i += bcount - 1; bcount = 0; } if (this._xbuflen >= msgSize) { this._message.SetPayload(this._xbuffer); this.NotifyMessage(this._message); this._message = null; this._xbuflen = 0; _state = MSG_END; } } break; case MSG_END: switch (b) { case '\n': _state = OP_START; break; default: continue; } break; case OP_PLUS: switch (b) { case 'O': case 'o': _state = OP_PLUS_O; break; default: this.ParseError(buffer, i, len); break; } break; case OP_PLUS_O: switch (b) { case 'K': case 'k': _state = OP_PLUS_OK; break; default: this.ParseError(buffer, i, len); break; } break; case OP_PLUS_OK: switch (b) { case '\n': this.NotifyOperation( default(ArraySegment <byte>), this._state ); _state = OP_START; break; } break; case OP_MINUS: switch (b) { case 'E': case 'e': _state = OP_MINUS_E; break; default: this.ParseError(buffer, i, len); break; } break; case OP_MINUS_E: switch (b) { case 'R': case 'r': _state = OP_MINUS_ER; break; default: this.ParseError(buffer, i, len); break; } break; case OP_MINUS_ER: switch (b) { case 'R': case 'r': _state = OP_MINUS_ERR; break; default: this.ParseError(buffer, i, len); break; } break; case OP_MINUS_ERR: switch (b) { case ' ': case '\t': _state = OP_MINUS_ERR_SPC; break; default: this.ParseError(buffer, i, len); break; } break; case OP_MINUS_ERR_SPC: switch (b) { case ' ': case '\t': _state = OP_MINUS_ERR_SPC; break; default: this._xbuflen = 0; bcount = 0; bstart = i; _state = MINUS_ERR_ARG; i--; break; } break; case MINUS_ERR_ARG: switch (b) { case '\r': break; case '\n': if (bcount != 0) { AddSegment(); bcount = 0; } this.NotifyError( new ArraySegment <byte>(this._xbuffer, 0, this._xbuflen) ); this._xbuflen = 0; _state = OP_START; break; default: bcount++; break; } break; case OP_P: switch (b) { case 'I': case 'i': _state = OP_PI; break; case 'O': case 'o': _state = OP_PO; break; default: this.ParseError(buffer, i, len); break; } break; case OP_PO: switch (b) { case 'N': case 'n': _state = OP_PON; break; default: this.ParseError(buffer, i, len); break; } break; case OP_PON: switch (b) { case 'G': case 'g': _state = OP_PONG; break; default: this.ParseError(buffer, i, len); break; } break; case OP_PONG: switch (b) { case '\r': break; case '\n': this.NotifyOperation( default(ArraySegment <byte>), this._state ); _state = OP_START; break; } break; case OP_PI: switch (b) { case 'N': case 'n': _state = OP_PIN; break; default: this.ParseError(buffer, i, len); break; } break; case OP_PIN: switch (b) { case 'G': case 'g': _state = OP_PING; break; default: this.ParseError(buffer, i, len); break; } break; case OP_PING: switch (b) { case '\r': break; case '\n': this.NotifyOperation( default(ArraySegment <byte>), this._state ); _state = OP_START; break; default: this.ParseError(buffer, i, len); break; } break; case OP_I: switch (b) { case 'N': case 'n': _state = OP_IN; break; default: this.ParseError(buffer, i, len); break; } break; case OP_IN: switch (b) { case 'F': case 'f': _state = OP_INF; break; default: this.ParseError(buffer, i, len); break; } break; case OP_INF: switch (b) { case 'O': case 'o': _state = OP_INFO; break; default: this.ParseError(buffer, i, len); break; } break; case OP_INFO: switch (b) { case ' ': case '\t': _state = OP_INFO_SPC; break; default: this.ParseError(buffer, i, len); break; } break; case OP_INFO_SPC: switch (b) { case ' ': case '\t': break; default: this._xbuflen = 0; bcount = 0; bstart = i; this._state = INFO_ARG; i--; break; } break; case INFO_ARG: switch (b) { case '\r': break; case '\n': if (bcount != 0) { AddSegment(); bcount = 0; } this.NotifyOperation( new ArraySegment <byte>(this._xbuffer, 0, this._xbuflen), this._state ); this._xbuflen = 0; this._state = OP_START; break; default: bcount++; break; } break; default: throw new NATSException("Unable to parse."); } // switch(state) } // for if (bcount != 0) { AddSegment(); } //inner data accumulator //appends a block of fresh bytes to the local cache void AddSegment() { int newlen = this._xbuflen + bcount; if (this._xbuffer.Length < newlen) { Array.Resize( ref this._xbuffer, this._xbuffer.Length + Defaults.defaultBufSize ); } VectorizedCopyExtension.VectorizedCopy( buffer, bstart, this._xbuffer, this._xbuflen, bcount ); this._xbuflen = newlen; } }
/// <summary> /// Processes the dispatched message /// </summary> /// <param name="message"></param> protected override void ProcessOutgoingMessage(MsgOut message) { //release the semaphore this._mainSem.Release(); }