Пример #1
0
        private static Memory <char> ToChars(Stream workspace)
        {
            if (workspace.Position == 0)
            {
                return(Memory <char> .Empty);
            }

            var sourceSpan = new Span <byte>(new byte[workspace.Position]);

            workspace.Position = 0;

            if (workspace.Read(sourceSpan) == -1)
            {
                throw NatsException.OpParserError("Error while trying to read from workspace stream.");
            }

            var result     = new Memory <char>(new char[sourceSpan.Length]);
            var resultSpan = result.Span;

            for (var i = 0; i < sourceSpan.Length; i++)
            {
                resultSpan[i] = (char)sourceSpan[i];
            }

            return(result);
        }
Пример #2
0
 private void ThrowIfNotConnected()
 {
     if (!IsConnected)
     {
         throw NatsException.NotConnected();
     }
 }
Пример #3
0
        private static ReadOnlyMemory <byte> ReadPayload(Stream source, int payloadSize)
        {
            if (payloadSize == 0)
            {
                return(ReadOnlyMemory <byte> .Empty);
            }

            var payload = new Memory <byte>(new byte[payloadSize]);

            var consumed = 0;

            while (true)
            {
                var read = source.Read(payload.Slice(consumed).Span);
                if (read < 1)
                {
                    throw NatsException.OpParserOpParsingError(MsgOp.Name, "Could not read payload from stream.");
                }

                consumed += read;

                if (consumed == payloadSize)
                {
                    break;
                }

                if (consumed > payloadSize)
                {
                    throw NatsException.OpParserOpParsingError(MsgOp.Name, "Read to many bytes for the payload.");
                }
            }

            return(payload);
        }
Пример #4
0
        private static ErrOp ParseErrorOp(Stream source, Stream workspace)
        {
            while (true)
            {
                var b = (byte)source.ReadByte();
                if (b == Cr)
                {
                    b = (byte)source.ReadByte();
                    if (b != Lf)
                    {
                        throw NatsException.OpParserOpParsingError(ErrOp.Name, Lf, b);
                    }
                    break;
                }

                workspace.WriteByte(b);
            }

            var buff = new Memory <byte>(new byte[workspace.Position]);

            workspace.Position = 0;
            workspace.Read(buff.Span);

            return(new ErrOp(string.Create(buff.Length, buff, (t, s) =>
            {
                var x = s.Span;
                for (var i = 0; i < t.Length; i++)
                {
                    t[i] = (char)x[i];
                }
            })));
        }
Пример #5
0
        private static PingOp ParsePingOp(Stream source)
        {
            var c = (byte)source.ReadByte();

            if (c != Lf)
            {
                throw NatsException.OpParserOpParsingError(PingOp.Name, Lf, c);
            }

            return(PingOp.Instance);
        }
Пример #6
0
        public Task <MsgOp> RequestAsync(string subject, ReadOnlyMemory <byte> body, CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();

            if (body.Length > _connection.ServerInfo.MaxPayload)
            {
                throw NatsException.ExceededMaxPayload(_connection.ServerInfo.MaxPayload, body.Length);
            }

            return(_connectionInfo.UseInboxRequests
                ? DoRequestUsingInboxAsync(subject, body, cancellationToken)
                : DoRequestAsync(subject, body, cancellationToken));
        }
Пример #7
0
        private async Task <MsgOp> DoRequestAsync(string subject, byte[] body, int?timeoutMs)
        {
            var requestReplyAddress = $"{Guid.NewGuid():N}";
            var pubCmd = PubCmd.Generate(subject, body, requestReplyAddress);

            var taskComp            = new TaskCompletionSource <MsgOp>();
            var requestSubscription = MsgOpStream.Where(msg => msg.Subject == requestReplyAddress).Subscribe(
                msg => taskComp.SetResult(msg),
                ex => taskComp.SetException(ex));

            var subscriptionInfo = new SubscriptionInfo(requestReplyAddress, maxMessages: 1);
            var subCmd           = SubCmd.Generate(subscriptionInfo.Subject, subscriptionInfo.Id);
            var unsubCmd         = UnsubCmd.Generate(subscriptionInfo.Id, subscriptionInfo.MaxMessages);

            await _connection.WithWriteLockAsync(async writer =>
            {
                await writer.WriteAsync(subCmd).ConfigureAwait(false);
                await writer.WriteAsync(unsubCmd).ConfigureAwait(false);
                await writer.FlushAsync().ConfigureAwait(false);
                await writer.WriteAsync(pubCmd).ConfigureAwait(false);
                await writer.FlushAsync().ConfigureAwait(false);
            }).ConfigureAwait(false);

            Task.WaitAny(new[] { Task.Delay(timeoutMs ?? _connectionInfo.RequestTimeoutMs), taskComp.Task }, _cancellation.Token);
            if (!taskComp.Task.IsCompleted)
            {
                taskComp.SetException(NatsException.RequestTimedOut());
            }

            return(await taskComp.Task
                   .ContinueWith(t =>
            {
                requestSubscription?.Dispose();

                if (!t.IsFaulted)
                {
                    return t.Result;
                }

                var ex = t.Exception?.GetBaseException() ?? t.Exception;
                if (ex == null)
                {
                    return t.Result;
                }

                _logger.Error("Exception while performing request.", ex);

                throw ex;
            })
                   .ConfigureAwait(false));
        }
Пример #8
0
        private Subscription CreateSubscription(SubscriptionInfo subscriptionInfo, Func <INatsObservable <MsgOp>, IDisposable> subscriptionFactory)
        {
            var subscription = Subscription.Create(
                subscriptionInfo,
                subscriptionFactory(MsgOpStream.Where(msg => msg.SubscriptionId == subscriptionInfo.Id)),
                DisposeSubscription);

            if (!_subscriptions.TryAdd(subscription.SubscriptionInfo.Id, subscription))
            {
                throw NatsException.CouldNotCreateSubscription(subscription.SubscriptionInfo);
            }

            return(subscription);
        }
Пример #9
0
        private Subscription CreateMsgOpSubscription(SubscriptionInfo subscriptionInfo, Func <INatsObservable <MsgOp>, IDisposable> subscriptionFactory)
        {
            var subscription = Subscription.Create(
                subscriptionInfo,
                subscriptionFactory(MsgOpStream.Where(msg => subscriptionInfo.Matches(msg.Subject))),
                info => Swallow.Everything(() => Unsub(info)));

            if (!_subscriptions.TryAdd(subscription.SubscriptionInfo.Id, subscription))
            {
                throw NatsException.CouldNotCreateSubscription(subscription.SubscriptionInfo);
            }

            return(subscription);
        }
Пример #10
0
        private async Task <MsgOp> DoRequestUsingInboxAsync(string subject, ReadOnlyMemory <byte> body, CancellationToken cancellationToken = default)
        {
            var requestId      = UniqueId.Generate();
            var replyToSubject = $"{_inboxAddress}.{requestId}";
            var taskComp       = new TaskCompletionSource <MsgOp>();

            if (!_outstandingRequests.TryAdd(requestId, taskComp))
            {
                throw NatsException.InitRequestError("Unable to initiate request.");
            }

            using var cts = cancellationToken == default
                ? new CancellationTokenSource(_connectionInfo.RequestTimeoutMs)
                : CancellationTokenSource.CreateLinkedTokenSource(_cancellation.Token, cancellationToken);

            await using var _ = cts.Token.Register(() =>
            {
                if (_outstandingRequests.TryRemove(requestId, out var ts))
                {
                    ts.TrySetCanceled();
                }
            }).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested();

            await _connection.WithWriteLockAsync(async (writer, arg) =>
            {
                var(subjectIn, bodyIn, replyToIn) = arg;

                if (_inboxSubscription == null)
                {
                    SetupInboxSubscription();

                    await SubCmd.WriteAsync(
                        writer,
                        _inboxSubscription.SubscriptionInfo.Subject.AsMemory(),
                        _inboxSubscription.SubscriptionInfo.Id.AsMemory(),
                        _inboxSubscription.SubscriptionInfo.QueueGroup.AsMemory()).ConfigureAwait(false);
                }

                await PubCmd.WriteAsync(writer, subjectIn, replyToIn, bodyIn).ConfigureAwait(false);
                await writer.FlushAsync().ConfigureAwait(false);
            }, Tuple.Create(subject.AsMemory(), body, replyToSubject.AsMemory())).ConfigureAwait(false);

            return(await taskComp.Task.ConfigureAwait(false));
        }
Пример #11
0
        public async Task PubAsync(string subject, ReadOnlyMemory <byte> body, string replyTo = null)
        {
            ThrowIfDisposed();

            if (body.Length > _connection.ServerInfo.MaxPayload)
            {
                throw NatsException.ExceededMaxPayload(_connection.ServerInfo.MaxPayload, body.Length);
            }

            await _connection.WithWriteLockAsync(async (writer, arg) =>
            {
                var(subjectIn, bodyIn, replyToIn) = arg;
                await PubCmd.WriteAsync(writer, subjectIn, replyToIn, bodyIn).ConfigureAwait(false);
                if (ShouldAutoFlush)
                {
                    await writer.FlushAsync().ConfigureAwait(false);
                }
            }, Tuple.Create(subject.AsMemory(), body, replyTo.AsMemory())).ConfigureAwait(false);
        }
Пример #12
0
        public void Pub(string subject, ReadOnlyMemory <byte> body, string replyTo = null)
        {
            ThrowIfDisposed();

            if (body.Length > _connection.ServerInfo.MaxPayload)
            {
                throw NatsException.ExceededMaxPayload(_connection.ServerInfo.MaxPayload, body.Length);
            }

            _connection.WithWriteLock((writer, arg) =>
            {
                var(subjectIn, bodyIn, replyToIn) = arg;

                PubCmd.Write(writer, subjectIn.Span, replyToIn.Span, bodyIn);
                if (ShouldAutoFlush)
                {
                    writer.Flush();
                }
            }, Tuple.Create(subject.AsMemory(), body, replyTo.AsMemory()));
        }
Пример #13
0
        private static InfoOp ParseInfoOp(Stream source, Stream workspace)
        {
            while (true)
            {
                var b = (byte)source.ReadByte();
                if (b == Cr)
                {
                    b = (byte)source.ReadByte();
                    if (b != Lf)
                    {
                        throw NatsException.OpParserOpParsingError(InfoOp.Name, Lf, b);
                    }
                    break;
                }

                workspace.WriteByte(b);
            }

            var m = ToChars(workspace);

            return(new InfoOp(m));
        }
Пример #14
0
        private void Worker()
        {
            bool ShouldDoWork() => !_isDisposed && IsConnected && _cancellation?.IsCancellationRequested == false;

            var lastOpReceivedAt = DateTime.UtcNow;
            var ping             = false;

            while (ShouldDoWork())
            {
                try
                {
                    if (ping)
                    {
                        ping = false;
                        _logger.Debug("Pinging due to silent server.");
                        Ping();
                    }

                    foreach (var op in _connection.ReadOp())
                    {
                        lastOpReceivedAt = DateTime.UtcNow;

                        _opMediator.Emit(op);

                        switch (op)
                        {
                        case PingOp _ when _connectionInfo.AutoRespondToPing && ShouldDoWork():
                            Pong();
                            break;

                        case ErrOp errOp:
                            throw NatsException.ClientReceivedErrOp(errOp);
                        }
                    }
                }
                catch (NatsException nex) when(nex.ExceptionCode == NatsExceptionCodes.OpParserError)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    if (!ShouldDoWork())
                    {
                        break;
                    }

                    _logger.Error("Worker got Exception.", ex);

                    if (ex.InnerException is SocketException socketEx)
                    {
                        // ReSharper disable once HeapView.BoxingAllocation
                        _logger.Error($"Worker task got SocketException with SocketErrorCode='{socketEx.SocketErrorCode}'");

                        if (socketEx.SocketErrorCode == SocketError.Interrupted)
                        {
                            break;
                        }

                        if (socketEx.SocketErrorCode != SocketError.TimedOut)
                        {
                            throw;
                        }
                    }

                    var silenceDeltaMs = DateTime.UtcNow.Subtract(lastOpReceivedAt).TotalMilliseconds;
                    if (silenceDeltaMs >= ConsumerMaxMsSilenceFromServer)
                    {
                        throw NatsException.ConnectionFoundIdling(_connection.ServerInfo.Host, _connection.ServerInfo.Port);
                    }

                    if (silenceDeltaMs >= ConsumerPingAfterMsSilenceFromServer)
                    {
                        ping = true;
                    }
                }
            }
        }
Пример #15
0
        private static MsgOp ParseMsgOp(Stream source, Stream workspace)
        {
            var  sub     = ReadOnlySpan <char> .Empty;
            var  sid     = ReadOnlySpan <char> .Empty;
            var  replyTo = ReadOnlySpan <char> .Empty;
            int  payloadSize;
            byte b;

            while (true)
            {
                b = (byte)source.ReadByte();
                if (b == Cr)
                {
                    payloadSize = int.Parse(ToChars(workspace).Span);

                    b = (byte)source.ReadByte();
                    if (b != Lf)
                    {
                        throw NatsException.OpParserOpParsingError(MsgOp.Name, Lf, b);
                    }
                    break;
                }

                if (!IsDelimMarker(b))
                {
                    workspace.WriteByte(b);
                }
                else
                {
                    if (sub.IsEmpty)
                    {
                        sub = ToChars(workspace).Span;
                        workspace.Position = 0;
                        continue;
                    }

                    if (sid.IsEmpty)
                    {
                        sid = ToChars(workspace).Span;
                        workspace.Position = 0;
                        continue;
                    }

                    if (replyTo.IsEmpty)
                    {
                        replyTo            = ToChars(workspace).Span;
                        workspace.Position = 0;
                        continue;
                    }

                    throw NatsException.OpParserOpParsingError(MsgOp.Name, "Message does not conform to expected protocol format.");
                }
            }

            var payload = ReadPayload(source, payloadSize);

            b = (byte)source.ReadByte();
            if (b != Cr)
            {
                throw NatsException.OpParserOpParsingError(MsgOp.Name, Cr, b);
            }

            b = (byte)source.ReadByte();
            if (b != Lf)
            {
                throw NatsException.OpParserOpParsingError(MsgOp.Name, Lf, b);
            }

            return(new MsgOp(
                       sub,
                       sid,
                       replyTo,
                       payload));
        }
Пример #16
0
        public IOp ReadOneOp()
        {
            IOp op = null;

            using var workspace = new MemoryStream();
            var opMarkerChars = new byte[4];
            var i             = -1;

            while (true)
            {
                var curr = _stream.ReadByte();
                if (curr == -1)
                {
                    return(null);
                }

                var c = (byte)curr;
                if (!IsDelimMarker(c) && c != Cr && c != Lf)
                {
                    opMarkerChars[++i] = c;
                    continue;
                }

                if (i == -1)
                {
                    continue;
                }

                if (opMarkerChars[0] == M)
                {
                    op = ParseMsgOp(_stream, workspace);
                }
                else if (opMarkerChars[0] == P)
                {
                    if (opMarkerChars[1] == I)
                    {
                        op = ParsePingOp(_stream);
                    }
                    else if (opMarkerChars[1] == O)
                    {
                        op = ParsePongOp(_stream);
                    }
                }
                else if (opMarkerChars[0] == I)
                {
                    op = ParseInfoOp(_stream, workspace);
                }
                else if (opMarkerChars[0] == Plus)
                {
                    op = ParseOkOp(_stream);
                }
                else if (opMarkerChars[0] == Minus)
                {
                    op = ParseErrorOp(_stream, workspace);
                }

                if (op == null)
                {
                    var opMarker = string.Create(i + 1, opMarkerChars, (t, v) =>
                    {
                        if (t.Length == 4)
                        {
                            t[3] = (char)v[3];
                        }

                        t[2] = (char)v[2];
                        t[1] = (char)v[1];
                        t[0] = (char)v[0];
                    });

                    throw NatsException.OpParserUnsupportedOp(opMarker);
                }

                i = -1;
                opMarkerChars[0]   = EmptyOpMarker;
                opMarkerChars[1]   = EmptyOpMarker;
                opMarkerChars[2]   = EmptyOpMarker;
                opMarkerChars[3]   = EmptyOpMarker;
                workspace.Position = 0;

                return(op);
            }
        }
Пример #17
0
        private void Worker()
        {
            bool ShouldDoWork() => !_isDisposed && IsConnected && _cancellation?.IsCancellationRequested == false;

            while (ShouldDoWork())
            {
                var lastOpReceivedAt = DateTime.UtcNow;

                try
                {
                    foreach (var op in _connection.ReadOp())
                    {
                        if (!ShouldDoWork())
                        {
                            break;
                        }

                        lastOpReceivedAt = DateTime.UtcNow;

                        _opMediator.Emit(op);

                        if (op is PingOp && _connectionInfo.AutoRespondToPing)
                        {
                            Pong();
                        }

                        if (op is ErrOp errOp)
                        {
                            throw NatsException.ClientReceivedErrOp(errOp);
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (!ShouldDoWork())
                    {
                        break;
                    }

                    _logger.Error("Worker got Exception.", ex);

                    if (ex.InnerException is SocketException socketEx)
                    {
                        _logger.Error($"Worker task got SocketException with SocketErrorCode='{socketEx.SocketErrorCode}'");

                        if (socketEx.SocketErrorCode == SocketError.Interrupted)
                        {
                            break;
                        }

                        if (socketEx.SocketErrorCode != SocketError.TimedOut)
                        {
                            throw;
                        }
                    }

                    var silenceDeltaMs = DateTime.UtcNow.Subtract(lastOpReceivedAt).TotalMilliseconds;
                    if (silenceDeltaMs >= ConsumerMaxMsSilenceFromServer)
                    {
                        throw NatsException.ConnectionFoundIdling(_connection.ServerInfo.Host, _connection.ServerInfo.Port);
                    }

                    if (silenceDeltaMs >= ConsumerPingAfterMsSilenceFromServer)
                    {
                        Ping();
                    }
                }
            }
        }