internal void manualAck(StanMsg m)
        {
            if (m == null)
            {
                return;
            }

            rwLock.EnterReadLock();

            string     localAckSubject = ackInbox;
            bool       localManualAck  = options.manualAcks;
            Connection sc = this.sc;

            rwLock.ExitReadLock();

            if (localManualAck == false)
            {
                throw new StanManualAckException();
            }

            if (sc == null)
            {
                throw new StanBadSubscriptionException();
            }

            byte[] b = ProtocolSerializer.createAck(m.proto);
            sc.NATSConnection.Publish(localAckSubject, b);
        }
        internal void processMsg(object sender, MsgHandlerEventArgs args)
        {
            bool isClosed         = false;
            AsyncSubscription sub = null;
            Msg raw = null;

            MsgProto mp = new MsgProto();

            ProtocolSerializer.unmarshal(args.Message.Data, mp);

            raw = args.Message;

            lock (mu)
            {
                isClosed = (nc == null);
                subMap.TryGetValue(raw.Subject, out sub);
            }

            if (isClosed || sub == null)
            {
                return;
            }

            sub.processMsg(mp);
        }
        internal void unsubscribe(string subject, string ackInbox)
        {
            IConnection lnc;

            lock (mu)
            {
                lnc = this.nc;
                subMap.Remove(ackInbox);
            }

            UnsubscribeRequest usr = new UnsubscribeRequest();

            usr.ClientID = clientID;
            usr.Subject  = subject;
            usr.Inbox    = ackInbox;
            byte[] b = ProtocolSerializer.marshal(usr);

            var r = lnc.Request(unsubRequests, b, 2000);

            SubscriptionResponse sr = new SubscriptionResponse();

            ProtocolSerializer.unmarshal(r.Data, sr);
            if (!string.IsNullOrEmpty(sr.Error))
            {
                throw new StanException(sr.Error);
            }
        }
        private void processPingResponse(object sender, MsgHandlerEventArgs e)
        {
            // No data means OK (no need to unmarshall)
            var data = e.Message.Data;

            if (data?.Length > 0)
            {
                var pingResp = new PingResponse();
                try
                {
                    ProtocolSerializer.unmarshal(data, pingResp);
                }
                catch
                {
                    // Ignore this as an invalid protocol message.
                    return;
                }

                string err = pingResp.Error;
                if (err?.Length > 0)
                {
                    closeDueToPing(new StanException(err));
                }
            }

            lock (pingLock)
            {
                pingOut = 0;
            }
        }
        public void Close()
        {
            Msg reply = null;

            lock (mu)
            {
                cleanupOnClose(null);

                if (nc == null || nc.IsClosed())
                {
                    nc = null;
                    return;
                }

                CloseRequest req = new CloseRequest
                {
                    ClientID = clientID
                };

                try
                {
                    if (closeRequests != null)
                    {
                        reply = nc.Request(closeRequests, ProtocolSerializer.marshal(req));
                    }
                }
                catch (StanBadSubscriptionException)
                {
                    // it's possible we never actually connected.
                    return;
                }

                if (reply != null)
                {
                    CloseResponse resp = new CloseResponse();
                    try
                    {
                        ProtocolSerializer.unmarshal(reply.Data, resp);
                    }
                    catch (Exception e)
                    {
                        throw new StanCloseRequestException(e);
                    }

                    if (!string.IsNullOrEmpty(resp.Error))
                    {
                        throw new StanCloseRequestException(resp.Error);
                    }
                }

                if (ncOwned)
                {
                    nc.Close();
                }

                nc = null;
            }
        }
        internal PublishAck publish(string subject, byte[] data, EventHandler <StanAckHandlerArgs> handler)
        {
            string localAckSubject = null;
            long   localAckTimeout = 0;

            string subj      = this.pubPrefix + "." + subject;
            string guidValue = newGUID();

            byte[] b = ProtocolSerializer.createPubMsg(clientID, guidValue, subject, data);

            PublishAck a = new PublishAck(this, guidValue, handler, opts.PubAckWait);

            lock (mu)
            {
                if (nc == null)
                {
                    throw new StanConnectionClosedException();
                }

                if (pubAckMap.isAtCapacity())
                {
                    var bd = pubAckMap;

                    Monitor.Exit(mu);
                    // Wait for space outside of the lock so
                    // acks can be removed.
                    bd.waitForSpace();
                    Monitor.Enter(mu);

                    if (nc == null)
                    {
                        throw new StanConnectionClosedException();
                    }
                }

                pubAckMap.Add(guidValue, a);
                localAckSubject = ackSubject;
                localAckTimeout = opts.ackTimeout;
            }

            try
            {
                nc.Publish(subj, localAckSubject, b);
            }
            catch (Exception e)
            {
                removeAck(guidValue);
                throw e;
            }

            return(a);
        }
        internal void unsubscribe(string subject, string inbox, string ackInbox, bool close)
        {
            IConnection lnc;

            lock (mu)
            {
                lnc = nc;
                if (lnc == null)
                {
                    throw new StanConnectionClosedException();
                }
                subMap.Remove(inbox);
            }

            string requestSubject = unsubRequests;

            if (close)
            {
                requestSubject = subCloseRequests;
                if (string.IsNullOrEmpty(requestSubject))
                {
                    throw new StanNoServerSupport();
                }
            }

            UnsubscribeRequest usr = new UnsubscribeRequest
            {
                ClientID = clientID,
                Subject  = subject,
                Inbox    = ackInbox
            };

            byte[] b = ProtocolSerializer.marshal(usr);

            var r = lnc.Request(requestSubject, b, 2000);
            SubscriptionResponse sr = new SubscriptionResponse();

            ProtocolSerializer.unmarshal(r.Data, sr);
            if (!string.IsNullOrEmpty(sr.Error))
            {
                throw new StanException(sr.Error);
            }
        }
        private void processAck(object sender, MsgHandlerEventArgs args)
        {
            PubAck pa = new PubAck();

            try
            {
                ProtocolSerializer.unmarshal(args.Message.Data, pa);
            }
            catch (Exception)
            {
                // TODO:  (cls) handle this...
                return;
            }

            PublishAck a = removeAck(pa.Guid);

            if (a != null)
            {
                a.InvokeHandler(pa.Guid, pa.Error);
            }
        }
        internal void processMsg(MsgProto mp)
        {
            rwLock.EnterReadLock();

            EventHandler <StanMsgHandlerArgs> cb = handler;
            bool            isManualAck          = options.manualAcks;
            string          localAckSubject      = ackInbox;
            IStanConnection subsSc  = sc;
            IConnection     localNc = null;

            if (subsSc != null)
            {
                localNc = sc.NATSConnection;
            }

            rwLock.ExitReadLock();

            if (cb != null && subsSc != null)
            {
                StanMsgHandlerArgs args = new StanMsgHandlerArgs(new StanMsg(mp, this));
                cb(this, args);
            }

            if (!isManualAck && localNc != null)
            {
                byte[] b = ProtocolSerializer.createAck(mp);
                try
                {
                    localNc.Publish(localAckSubject, b);
                }
                catch (Exception)
                {
                    /*
                     * Ignore - subscriber could have closed the connection
                     * or there's been a connection error.  The server will
                     * resend the unacknowledged messages.
                     */
                }
            }
        }
Example #10
0
        private void processPingResponse(object sender, MsgHandlerEventArgs args)
        {
            if (IsNoResponseMsg(args.Message))
            {
                return;
            }

            // No data can be a valid message
            var data = args.Message.Data;

            if (data?.Length > 0)
            {
                var pingResp = new PingResponse();
                try
                {
                    ProtocolSerializer.unmarshal(data, pingResp);
                }
                catch
                {
                    // Ignore this as an invalid protocol message.
                    return;
                }

                string err = pingResp.Error;
                if (err?.Length > 0)
                {
                    closeDueToPing(new StanException(err));
                    return;
                }
            }

            lock (pingLock)
            {
                pingOut = 0;
            }
        }
Example #11
0
        internal PublishAck publish(string subject, byte[] data, EventHandler <StanAckHandlerArgs> handler)
        {
            string localAckSubject = null;
            long   localAckTimeout = 0;

            string subj      = this.pubPrefix + "." + subject;
            string guidValue = newGUID();

            byte[]     b = ProtocolSerializer.createPubMsg(clientID, guidValue, subject, data, connID);
            PublishAck a = null;

            lock (mu)
            {
                if (nc == null)
                {
                    throw new StanConnectionClosedException();
                }

                if (nc.IsReconnecting())
                {
                    throw new StanConnectionException("The NATS connection is reconnecting");
                }

                a = new PublishAck(this, guidValue, handler, opts.PubAckWait);
                while (!pubAckMap.TryAdd(guidValue, a))
                {
                    var bd = pubAckMap;

                    Monitor.Exit(mu);
                    // Wait for space outside of the lock so
                    // acks can be removed.
                    bd.waitForSpace();
                    Monitor.Enter(mu);

                    if (nc == null)
                    {
                        throw new StanConnectionClosedException();
                    }
                }

                localAckSubject = ackSubject;
                localAckTimeout = opts.ackTimeout;
            }

            try
            {
                nc.Publish(subj, localAckSubject, b);

                // Flush to reduce latency.
                //
                // TODO:  Add a soft (non-ping) flush to NATS.net
                // for this type of situation.  Only flush in
                // blocking publish calls.
                if (handler == null)
                {
                    nc.Flush();
                }
            }
            catch
            {
                removeAck(guidValue);
                throw;
            }

            return(a);
        }
Example #12
0
        public void Close()
        {
            Msg reply = null;

            lock (mu)
            {
                cleanupOnClose(null);

                if (nc == null || nc.IsClosed())
                {
                    nc = null;
                    return;
                }

                CloseRequest req = new CloseRequest
                {
                    ClientID = clientID
                };

                try
                {
                    if (closeRequests != null)
                    {
                        reply = nc.Request(closeRequests, ProtocolSerializer.marshal(req));
                    }
                }
                catch (StanBadSubscriptionException)
                {
                    // it's possible we never actually connected.
                    return;
                }
                catch (NATSReconnectBufferException)
                {
                    // In order to maintain backward compatibility, we need to avoid throwing
                    // this exception.  The reply will be null, so we'll fall through
                    // and gracefully close the streaming connection.  The streaming server
                    // will cleanup this client on its own.
                }

                if (reply != null)
                {
                    CloseResponse resp = new CloseResponse();
                    try
                    {
                        ProtocolSerializer.unmarshal(reply.Data, resp);
                    }
                    catch (Exception e)
                    {
                        throw new StanCloseRequestException(e);
                    }

                    if (!string.IsNullOrEmpty(resp.Error))
                    {
                        throw new StanCloseRequestException(resp.Error);
                    }
                }

                if (ncOwned)
                {
                    nc.Close();
                }

                nc = null;
            }
        }
Example #13
0
        internal Connection(string stanClusterID, string clientID, StanOptions options)
        {
            this.clientID = clientID;

            if (options != null)
            {
                opts = new StanOptions(options);
            }
            else
            {
                opts = new StanOptions();
            }

            if (opts.natsConn == null)
            {
                ncOwned = true;
                try
                {
                    nc = new ConnectionFactory().CreateConnection(opts.NatsURL);
                }
                catch (Exception ex)
                {
                    throw new StanConnectionException(ex);
                }
            }
            else
            {
                nc      = opts.natsConn;
                ncOwned = false;
            }

            // create a heartbeat inbox
            string hbInbox = newInbox();

            hbSubscription = nc.SubscribeAsync(hbInbox, processHeartBeat);

            string discoverSubject = opts.discoverPrefix + "." + stanClusterID;

            ConnectRequest req = new ConnectRequest();

            req.ClientID       = this.clientID;
            req.HeartbeatInbox = hbInbox;

            Msg cr;

            try
            {
                cr = nc.Request(discoverSubject,
                                ProtocolSerializer.marshal(req),
                                opts.ConnectTimeout);
            }
            catch (NATSTimeoutException)
            {
                throw new StanConnectRequestTimeoutException();
            }

            ConnectResponse response = new ConnectResponse();

            try
            {
                ProtocolSerializer.unmarshal(cr.Data, response);
            }
            catch (Exception e)
            {
                throw new StanConnectRequestException(e);
            }

            if (!string.IsNullOrEmpty(response.Error))
            {
                throw new StanConnectRequestException(response.Error);
            }

            // capture cluster configuration endpoints to publish and subscribe/unsubscribe
            pubPrefix        = response.PubPrefix;
            subRequests      = response.SubRequests;
            unsubRequests    = response.UnsubRequests;
            subCloseRequests = response.SubCloseRequests;
            closeRequests    = response.CloseRequests;

            // setup the Ack subscription
            ackSubject      = StanConsts.DefaultACKPrefix + "." + newGUID();
            ackSubscription = nc.SubscribeAsync(ackSubject, processAck);

            // TODO:  hardcode or options?
            ackSubscription.SetPendingLimits(1024 * 1024, 32 * 1024 * 1024);

            pubAckMap = new BlockingDictionary <string, PublishAck>(opts.maxPubAcksInflight);
        }
Example #14
0
        public void Close()
        {
            Msg reply = null;

            lock (mu)
            {
                IConnection lnc = nc;
                nc = null;

                if (lnc == null)
                {
                    return;
                }

                if (lnc.IsClosed())
                {
                    return;
                }

                if (ackSubscription != null)
                {
                    ackSubscription.Unsubscribe();
                    ackSubscription = null;
                }

                if (hbSubscription != null)
                {
                    hbSubscription.Unsubscribe();
                    hbSubscription = null;
                }

                CloseRequest req = new CloseRequest();
                req.ClientID = this.clientID;

                try
                {
                    if (this.closeRequests != null)
                    {
                        reply = lnc.Request(closeRequests, ProtocolSerializer.marshal(req));
                    }
                }
                catch (StanBadSubscriptionException)
                {
                    // it's possible we never actually connected.
                    return;
                }

                if (reply != null)
                {
                    CloseResponse resp = new CloseResponse();
                    try
                    {
                        ProtocolSerializer.unmarshal(reply.Data, resp);
                    }
                    catch (Exception e)
                    {
                        throw new StanCloseRequestException(e);
                    }

                    if (!string.IsNullOrEmpty(resp.Error))
                    {
                        throw new StanCloseRequestException(resp.Error);
                    }
                }

                if (ncOwned && lnc != null)
                {
                    lnc.Close();
                }
            }
        }
Example #15
0
        internal PublishAck publish(string subject, byte[] data, EventHandler <StanAckHandlerArgs> handler)
        {
            string localAckSubject = null;

            string subj      = this.pubPrefix + "." + subject;
            string guidValue = newGUID();

            byte[]     b = ProtocolSerializer.createPubMsg(clientID, guidValue, subject, data, connID);
            PublishAck a = null;

            lock (mu)
            {
                if (nc == null || nc.IsClosed())
                {
                    throw new StanConnectionClosedException();
                }

                if (nc.IsReconnecting())
                {
                    throw new StanConnectionException("The NATS connection is reconnecting");
                }

                a = new PublishAck(this, guidValue, handler, opts.PubAckWait);

                int pingInterval = opts.PingInterval;
                while (!pubAckMap.TryAdd(guidValue, a))
                {
                    Monitor.Exit(mu);
                    // Wait for space outside of the lock so
                    // acks can be removed and other executive
                    // functions can continue
                    pubAckMap.TryWaitForSpace(pingInterval);
                    Monitor.Enter(mu);

                    if (nc == null || nc.IsClosed())
                    {
                        throw new StanConnectionClosedException();
                    }

                    if (nc.IsReconnecting())
                    {
                        throw new StanConnectionException("The NATS connection is reconnecting");
                    }
                }

                localAckSubject = ackSubject;
            }

            try
            {
                nc.Publish(subj, localAckSubject, b);

                // Flush to reduce latency.
                if (handler == null)
                {
                    nc.FlushBuffer();
                }
            }
            catch
            {
                removeAck(guidValue);
                throw;
            }

            return(a);
        }
Example #16
0
        internal Connection(string stanClusterID, string clientID, StanOptions options)
        {
            this.clientID = clientID;
            connID        = Google.Protobuf.ByteString.CopyFrom(System.Text.Encoding.UTF8.GetBytes(pubNUID.Next));

            opts = (options != null) ? new StanOptions(options) : new StanOptions();

            if (opts.natsConn == null)
            {
                ncOwned = true;
                try
                {
                    nc = new ConnectionFactory().CreateConnection(opts.NatsURL);
                    nc.Opts.MaxReconnect = Options.ReconnectForever;
                    // TODO:  disable buffering.
                }
                catch (Exception ex)
                {
                    throw new StanConnectionException(ex);
                }
            }
            else
            {
                nc      = opts.natsConn;
                ncOwned = false;
            }

            // Prepare a subscription on ping responses, even if we are not
            // going to need it, so that if that fails, it fails before initiating
            // a connection.
            pingSubscription = nc.SubscribeAsync(newInbox(), processPingResponse);

            // create a heartbeat inbox
            string hbInbox = newInbox();

            hbSubscription = nc.SubscribeAsync(hbInbox, processHeartBeat);

            string discoverSubject = opts.discoverPrefix + "." + stanClusterID;

            // The streaming server expects seconds, but can handle millis
            // millis are denoted by negative numbers.
            int pi;

            if (opts.PingInterval < 1000)
            {
                pi = opts.pingInterval * -1;
            }
            else
            {
                pi = opts.pingInterval / 1000;
            }

            ConnectRequest req = new ConnectRequest
            {
                ClientID       = clientID,
                HeartbeatInbox = hbInbox,
                ConnID         = (Google.Protobuf.ByteString)connID,
                Protocol       = StanConsts.protocolOne,
                PingMaxOut     = opts.PingMaxOutstanding,
                PingInterval   = pi
            };

            Msg cr;

            try
            {
                cr = nc.Request(discoverSubject,
                                ProtocolSerializer.marshal(req),
                                opts.ConnectTimeout);
            }
            catch (NATSTimeoutException)
            {
                protoUnsubscribe();
                throw new StanConnectRequestTimeoutException(
                          string.Format("No response from a streaming server with a cluster ID of '{0}'", stanClusterID));
            }

            ConnectResponse response = new ConnectResponse();

            try
            {
                ProtocolSerializer.unmarshal(cr.Data, response);
            }
            catch (Exception e)
            {
                protoUnsubscribe();
                throw new StanConnectRequestException(e);
            }

            if (!string.IsNullOrEmpty(response.Error))
            {
                protoUnsubscribe();
                throw new StanConnectRequestException(response.Error);
            }

            // capture cluster configuration endpoints to publish and subscribe/unsubscribe
            pubPrefix        = response.PubPrefix;
            subRequests      = response.SubRequests;
            unsubRequests    = response.UnsubRequests;
            subCloseRequests = response.SubCloseRequests;
            closeRequests    = response.CloseRequests;

            // setup the Ack subscription
            ackSubject      = StanConsts.DefaultACKPrefix + "." + newGUID();
            ackSubscription = nc.SubscribeAsync(ackSubject, processAck);

            // TODO:  hardcode or options?
            ackSubscription.SetPendingLimits(1024 * 1024, 32 * 1024 * 1024);

            pubAckMap = new BlockingDictionary <string, PublishAck>(opts.maxPubAcksInflight);

            // TODO - check out sub map and chans

            bool unsubPing = true;

            // Do this with servers which are at least at protcolOne.
            if (response.Protocol >= StanConsts.protocolOne)
            {
                // Note that in the future server may override client ping
                // interval value sent in ConnectRequest, so use the
                // value in ConnectResponse to decide if we send PINGs
                // and at what interval.
                // In tests, the interval could be negative to indicate
                // milliseconds.
                if (response.PingInterval != 0)
                {
                    unsubPing = false;

                    // These will be immutable
                    pingRequests = response.PingRequests;
                    pingInbox    = pingSubscription.Subject;

                    // negative values returned from the server are ms
                    if (response.PingInterval < 0)
                    {
                        pingInterval = response.PingInterval * -1;
                    }
                    else
                    {
                        // if positive, the result is in seconds, but
                        // in the .NET clients we always use millis.
                        pingInterval = response.PingInterval * 1000;
                    }

                    pingMaxOut = response.PingMaxOut;
                    pingBytes  = ProtocolSerializer.createPing(connID);

                    pingTimer = new Timer(pingServer, null, pingInterval, Timeout.Infinite);
                }
            }
            if (unsubPing)
            {
                pingSubscription.Unsubscribe();
                pingSubscription = null;
            }
        }
Example #17
0
        // in STAN, much of this code is in the connection module.
        internal void subscribe(string subRequestSubject, string subject, string qgroup, EventHandler <StanMsgHandlerArgs> handler)
        {
            rwLock.EnterWriteLock();
            try
            {
                this.handler += handler;
                this.subject  = subject;

                if (sc == null)
                {
                    throw new StanConnectionClosedException();
                }

                // Listen for actual messages.
                inboxSub = sc.NATSConnection.SubscribeAsync(inbox, sc.processMsg);

                SubscriptionRequest sr = new SubscriptionRequest();
                sr.ClientID      = sc.ClientID;
                sr.Subject       = subject;
                sr.QGroup        = (qgroup == null ? "" : qgroup);
                sr.Inbox         = inbox;
                sr.MaxInFlight   = options.MaxInflight;
                sr.AckWaitInSecs = options.AckWait / 1000;
                sr.StartPosition = options.startAt;
                sr.DurableName   = (options.DurableName == null ? "" : options.DurableName);

                // Conditionals
                switch (sr.StartPosition)
                {
                case StartPosition.TimeDeltaStart:
                    sr.StartTimeDelta = convertTimeSpan(
                        options.useStartTimeDelta ?
                        options.startTimeDelta :
                        (DateTime.UtcNow - options.startTime));
                    break;

                case StartPosition.SequenceStart:
                    sr.StartSequence = options.startSequence;
                    break;
                }

                byte[] b = ProtocolSerializer.marshal(sr);

                Msg m = sc.NATSConnection.Request(subRequestSubject, b, sc.opts.ConnectTimeout);

                SubscriptionResponse r = new SubscriptionResponse();
                ProtocolSerializer.unmarshal(m.Data, r);

                if (string.IsNullOrWhiteSpace(r.Error) == false)
                {
                    throw new StanException(r.Error);
                }

                ackInbox = r.AckInbox;
            }
            catch
            {
                if (inboxSub != null)
                {
                    try
                    {
                        inboxSub.Unsubscribe();
                    }
                    catch (NATSTimeoutException)
                    {
                        // NOOP - this is unrecoverable.
                    }
                }
                throw;
            }
            finally
            {
                rwLock.ExitWriteLock();
            }
        }