github.com/nsqio/nsq@v1.3.0/nsqd/client_v2.go (about)

     1  package nsqd
     2  
     3  import (
     4  	"bufio"
     5  	"compress/flate"
     6  	"crypto/tls"
     7  	"fmt"
     8  	"net"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/golang/snappy"
    15  	"github.com/nsqio/nsq/internal/auth"
    16  )
    17  
    18  const defaultBufferSize = 16 * 1024
    19  
    20  const (
    21  	stateInit = iota
    22  	stateDisconnected
    23  	stateConnected
    24  	stateSubscribed
    25  	stateClosing
    26  )
    27  
    28  type identifyDataV2 struct {
    29  	ClientID            string `json:"client_id"`
    30  	Hostname            string `json:"hostname"`
    31  	HeartbeatInterval   int    `json:"heartbeat_interval"`
    32  	OutputBufferSize    int    `json:"output_buffer_size"`
    33  	OutputBufferTimeout int    `json:"output_buffer_timeout"`
    34  	FeatureNegotiation  bool   `json:"feature_negotiation"`
    35  	TLSv1               bool   `json:"tls_v1"`
    36  	Deflate             bool   `json:"deflate"`
    37  	DeflateLevel        int    `json:"deflate_level"`
    38  	Snappy              bool   `json:"snappy"`
    39  	SampleRate          int32  `json:"sample_rate"`
    40  	UserAgent           string `json:"user_agent"`
    41  	MsgTimeout          int    `json:"msg_timeout"`
    42  }
    43  
    44  type identifyEvent struct {
    45  	OutputBufferTimeout time.Duration
    46  	HeartbeatInterval   time.Duration
    47  	SampleRate          int32
    48  	MsgTimeout          time.Duration
    49  }
    50  
    51  type PubCount struct {
    52  	Topic string `json:"topic"`
    53  	Count uint64 `json:"count"`
    54  }
    55  
    56  type ClientV2Stats struct {
    57  	ClientID        string `json:"client_id"`
    58  	Hostname        string `json:"hostname"`
    59  	Version         string `json:"version"`
    60  	RemoteAddress   string `json:"remote_address"`
    61  	State           int32  `json:"state"`
    62  	ReadyCount      int64  `json:"ready_count"`
    63  	InFlightCount   int64  `json:"in_flight_count"`
    64  	MessageCount    uint64 `json:"message_count"`
    65  	FinishCount     uint64 `json:"finish_count"`
    66  	RequeueCount    uint64 `json:"requeue_count"`
    67  	ConnectTime     int64  `json:"connect_ts"`
    68  	SampleRate      int32  `json:"sample_rate"`
    69  	Deflate         bool   `json:"deflate"`
    70  	Snappy          bool   `json:"snappy"`
    71  	UserAgent       string `json:"user_agent"`
    72  	Authed          bool   `json:"authed,omitempty"`
    73  	AuthIdentity    string `json:"auth_identity,omitempty"`
    74  	AuthIdentityURL string `json:"auth_identity_url,omitempty"`
    75  
    76  	PubCounts []PubCount `json:"pub_counts,omitempty"`
    77  
    78  	TLS                           bool   `json:"tls"`
    79  	CipherSuite                   string `json:"tls_cipher_suite"`
    80  	TLSVersion                    string `json:"tls_version"`
    81  	TLSNegotiatedProtocol         string `json:"tls_negotiated_protocol"`
    82  	TLSNegotiatedProtocolIsMutual bool   `json:"tls_negotiated_protocol_is_mutual"`
    83  }
    84  
    85  func (s ClientV2Stats) String() string {
    86  	connectTime := time.Unix(s.ConnectTime, 0)
    87  	duration := time.Since(connectTime).Truncate(time.Second)
    88  
    89  	_, port, _ := net.SplitHostPort(s.RemoteAddress)
    90  	id := fmt.Sprintf("%s:%s %s", s.Hostname, port, s.UserAgent)
    91  
    92  	// producer
    93  	if len(s.PubCounts) > 0 {
    94  		var total uint64
    95  		var topicOut []string
    96  		for _, v := range s.PubCounts {
    97  			total += v.Count
    98  			topicOut = append(topicOut, fmt.Sprintf("%s=%d", v.Topic, v.Count))
    99  		}
   100  		return fmt.Sprintf("[%s %-21s] msgs: %-8d topics: %s connected: %s",
   101  			s.Version,
   102  			id,
   103  			total,
   104  			strings.Join(topicOut, ","),
   105  			duration,
   106  		)
   107  	}
   108  
   109  	// consumer
   110  	return fmt.Sprintf("[%s %-21s] state: %d inflt: %-4d rdy: %-4d fin: %-8d re-q: %-8d msgs: %-8d connected: %s",
   111  		s.Version,
   112  		id,
   113  		s.State,
   114  		s.InFlightCount,
   115  		s.ReadyCount,
   116  		s.FinishCount,
   117  		s.RequeueCount,
   118  		s.MessageCount,
   119  		duration,
   120  	)
   121  }
   122  
   123  type clientV2 struct {
   124  	// 64bit atomic vars need to be first for proper alignment on 32bit platforms
   125  	ReadyCount    int64
   126  	InFlightCount int64
   127  	MessageCount  uint64
   128  	FinishCount   uint64
   129  	RequeueCount  uint64
   130  
   131  	pubCounts map[string]uint64
   132  
   133  	writeLock sync.RWMutex
   134  	metaLock  sync.RWMutex
   135  
   136  	ID        int64
   137  	nsqd      *NSQD
   138  	UserAgent string
   139  
   140  	// original connection
   141  	net.Conn
   142  
   143  	// connections based on negotiated features
   144  	tlsConn     *tls.Conn
   145  	flateWriter *flate.Writer
   146  
   147  	// reading/writing interfaces
   148  	Reader *bufio.Reader
   149  	Writer *bufio.Writer
   150  
   151  	OutputBufferSize    int
   152  	OutputBufferTimeout time.Duration
   153  
   154  	HeartbeatInterval time.Duration
   155  
   156  	MsgTimeout time.Duration
   157  
   158  	State          int32
   159  	ConnectTime    time.Time
   160  	Channel        *Channel
   161  	ReadyStateChan chan int
   162  	ExitChan       chan int
   163  
   164  	ClientID string
   165  	Hostname string
   166  
   167  	SampleRate int32
   168  
   169  	IdentifyEventChan chan identifyEvent
   170  	SubEventChan      chan *Channel
   171  
   172  	TLS     int32
   173  	Snappy  int32
   174  	Deflate int32
   175  
   176  	// re-usable buffer for reading the 4-byte lengths off the wire
   177  	lenBuf   [4]byte
   178  	lenSlice []byte
   179  
   180  	AuthSecret string
   181  	AuthState  *auth.State
   182  }
   183  
   184  func newClientV2(id int64, conn net.Conn, nsqd *NSQD) *clientV2 {
   185  	var identifier string
   186  	if conn != nil {
   187  		identifier, _, _ = net.SplitHostPort(conn.RemoteAddr().String())
   188  	}
   189  
   190  	c := &clientV2{
   191  		ID:   id,
   192  		nsqd: nsqd,
   193  
   194  		Conn: conn,
   195  
   196  		Reader: bufio.NewReaderSize(conn, defaultBufferSize),
   197  		Writer: bufio.NewWriterSize(conn, defaultBufferSize),
   198  
   199  		OutputBufferSize:    defaultBufferSize,
   200  		OutputBufferTimeout: nsqd.getOpts().OutputBufferTimeout,
   201  
   202  		MsgTimeout: nsqd.getOpts().MsgTimeout,
   203  
   204  		// ReadyStateChan has a buffer of 1 to guarantee that in the event
   205  		// there is a race the state update is not lost
   206  		ReadyStateChan: make(chan int, 1),
   207  		ExitChan:       make(chan int),
   208  		ConnectTime:    time.Now(),
   209  		State:          stateInit,
   210  
   211  		ClientID: identifier,
   212  		Hostname: identifier,
   213  
   214  		SubEventChan:      make(chan *Channel, 1),
   215  		IdentifyEventChan: make(chan identifyEvent, 1),
   216  
   217  		// heartbeats are client configurable but default to 30s
   218  		HeartbeatInterval: nsqd.getOpts().ClientTimeout / 2,
   219  
   220  		pubCounts: make(map[string]uint64),
   221  	}
   222  	c.lenSlice = c.lenBuf[:]
   223  	return c
   224  }
   225  
   226  func (c *clientV2) String() string {
   227  	return c.RemoteAddr().String()
   228  }
   229  
   230  func (c *clientV2) Type() int {
   231  	c.metaLock.RLock()
   232  	hasPublished := len(c.pubCounts) > 0
   233  	c.metaLock.RUnlock()
   234  	if hasPublished {
   235  		return typeProducer
   236  	}
   237  	return typeConsumer
   238  }
   239  
   240  func (c *clientV2) Identify(data identifyDataV2) error {
   241  	c.nsqd.logf(LOG_INFO, "[%s] IDENTIFY: %+v", c, data)
   242  
   243  	c.metaLock.Lock()
   244  	c.ClientID = data.ClientID
   245  	c.Hostname = data.Hostname
   246  	c.UserAgent = data.UserAgent
   247  	c.metaLock.Unlock()
   248  
   249  	err := c.SetHeartbeatInterval(data.HeartbeatInterval)
   250  	if err != nil {
   251  		return err
   252  	}
   253  
   254  	err = c.SetOutputBuffer(data.OutputBufferSize, data.OutputBufferTimeout)
   255  	if err != nil {
   256  		return err
   257  	}
   258  
   259  	err = c.SetSampleRate(data.SampleRate)
   260  	if err != nil {
   261  		return err
   262  	}
   263  
   264  	err = c.SetMsgTimeout(data.MsgTimeout)
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	ie := identifyEvent{
   270  		OutputBufferTimeout: c.OutputBufferTimeout,
   271  		HeartbeatInterval:   c.HeartbeatInterval,
   272  		SampleRate:          c.SampleRate,
   273  		MsgTimeout:          c.MsgTimeout,
   274  	}
   275  
   276  	// update the client's message pump
   277  	select {
   278  	case c.IdentifyEventChan <- ie:
   279  	default:
   280  	}
   281  
   282  	return nil
   283  }
   284  
   285  func (c *clientV2) Stats(topicName string) ClientStats {
   286  	c.metaLock.RLock()
   287  	clientID := c.ClientID
   288  	hostname := c.Hostname
   289  	userAgent := c.UserAgent
   290  	var identity string
   291  	var identityURL string
   292  	if c.AuthState != nil {
   293  		identity = c.AuthState.Identity
   294  		identityURL = c.AuthState.IdentityURL
   295  	}
   296  	pubCounts := make([]PubCount, 0, len(c.pubCounts))
   297  	for topic, count := range c.pubCounts {
   298  		if len(topicName) > 0 && topic != topicName {
   299  			continue
   300  		}
   301  		pubCounts = append(pubCounts, PubCount{
   302  			Topic: topic,
   303  			Count: count,
   304  		})
   305  		break
   306  	}
   307  	c.metaLock.RUnlock()
   308  	stats := ClientV2Stats{
   309  		Version:         "V2",
   310  		RemoteAddress:   c.RemoteAddr().String(),
   311  		ClientID:        clientID,
   312  		Hostname:        hostname,
   313  		UserAgent:       userAgent,
   314  		State:           atomic.LoadInt32(&c.State),
   315  		ReadyCount:      atomic.LoadInt64(&c.ReadyCount),
   316  		InFlightCount:   atomic.LoadInt64(&c.InFlightCount),
   317  		MessageCount:    atomic.LoadUint64(&c.MessageCount),
   318  		FinishCount:     atomic.LoadUint64(&c.FinishCount),
   319  		RequeueCount:    atomic.LoadUint64(&c.RequeueCount),
   320  		ConnectTime:     c.ConnectTime.Unix(),
   321  		SampleRate:      atomic.LoadInt32(&c.SampleRate),
   322  		TLS:             atomic.LoadInt32(&c.TLS) == 1,
   323  		Deflate:         atomic.LoadInt32(&c.Deflate) == 1,
   324  		Snappy:          atomic.LoadInt32(&c.Snappy) == 1,
   325  		Authed:          c.HasAuthorizations(),
   326  		AuthIdentity:    identity,
   327  		AuthIdentityURL: identityURL,
   328  		PubCounts:       pubCounts,
   329  	}
   330  	if stats.TLS {
   331  		p := prettyConnectionState{c.tlsConn.ConnectionState()}
   332  		stats.CipherSuite = p.GetCipherSuite()
   333  		stats.TLSVersion = p.GetVersion()
   334  		stats.TLSNegotiatedProtocol = p.NegotiatedProtocol
   335  		stats.TLSNegotiatedProtocolIsMutual = p.NegotiatedProtocolIsMutual
   336  	}
   337  	return stats
   338  }
   339  
   340  // struct to convert from integers to the human readable strings
   341  type prettyConnectionState struct {
   342  	tls.ConnectionState
   343  }
   344  
   345  func (p *prettyConnectionState) GetCipherSuite() string {
   346  	switch p.CipherSuite {
   347  	case tls.TLS_RSA_WITH_RC4_128_SHA:
   348  		return "TLS_RSA_WITH_RC4_128_SHA"
   349  	case tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
   350  		return "TLS_RSA_WITH_3DES_EDE_CBC_SHA"
   351  	case tls.TLS_RSA_WITH_AES_128_CBC_SHA:
   352  		return "TLS_RSA_WITH_AES_128_CBC_SHA"
   353  	case tls.TLS_RSA_WITH_AES_256_CBC_SHA:
   354  		return "TLS_RSA_WITH_AES_256_CBC_SHA"
   355  	case tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
   356  		return "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"
   357  	case tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
   358  		return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"
   359  	case tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
   360  		return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
   361  	case tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
   362  		return "TLS_ECDHE_RSA_WITH_RC4_128_SHA"
   363  	case tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
   364  		return "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"
   365  	case tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
   366  		return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
   367  	case tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
   368  		return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
   369  	case tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
   370  		return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
   371  	case tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
   372  		return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
   373  	}
   374  	return fmt.Sprintf("Unknown %d", p.CipherSuite)
   375  }
   376  
   377  func (p *prettyConnectionState) GetVersion() string {
   378  	switch p.Version {
   379  	case tls.VersionTLS10:
   380  		return "TLS1.0"
   381  	case tls.VersionTLS11:
   382  		return "TLS1.1"
   383  	case tls.VersionTLS12:
   384  		return "TLS1.2"
   385  	case tls.VersionTLS13:
   386  		return "TLS1.3"
   387  	default:
   388  		return fmt.Sprintf("Unknown %d", p.Version)
   389  	}
   390  }
   391  
   392  func (c *clientV2) IsReadyForMessages() bool {
   393  	if c.Channel.IsPaused() {
   394  		return false
   395  	}
   396  
   397  	readyCount := atomic.LoadInt64(&c.ReadyCount)
   398  	inFlightCount := atomic.LoadInt64(&c.InFlightCount)
   399  
   400  	c.nsqd.logf(LOG_DEBUG, "[%s] state rdy: %4d inflt: %4d", c, readyCount, inFlightCount)
   401  
   402  	if inFlightCount >= readyCount || readyCount <= 0 {
   403  		return false
   404  	}
   405  
   406  	return true
   407  }
   408  
   409  func (c *clientV2) SetReadyCount(count int64) {
   410  	oldCount := atomic.SwapInt64(&c.ReadyCount, count)
   411  
   412  	if oldCount != count {
   413  		c.tryUpdateReadyState()
   414  	}
   415  }
   416  
   417  func (c *clientV2) tryUpdateReadyState() {
   418  	// you can always *try* to write to ReadyStateChan because in the cases
   419  	// where you cannot the message pump loop would have iterated anyway.
   420  	// the atomic integer operations guarantee correctness of the value.
   421  	select {
   422  	case c.ReadyStateChan <- 1:
   423  	default:
   424  	}
   425  }
   426  
   427  func (c *clientV2) FinishedMessage() {
   428  	atomic.AddUint64(&c.FinishCount, 1)
   429  	atomic.AddInt64(&c.InFlightCount, -1)
   430  	c.tryUpdateReadyState()
   431  }
   432  
   433  func (c *clientV2) Empty() {
   434  	atomic.StoreInt64(&c.InFlightCount, 0)
   435  	c.tryUpdateReadyState()
   436  }
   437  
   438  func (c *clientV2) SendingMessage() {
   439  	atomic.AddInt64(&c.InFlightCount, 1)
   440  	atomic.AddUint64(&c.MessageCount, 1)
   441  }
   442  
   443  func (c *clientV2) PublishedMessage(topic string, count uint64) {
   444  	c.metaLock.Lock()
   445  	c.pubCounts[topic] += count
   446  	c.metaLock.Unlock()
   447  }
   448  
   449  func (c *clientV2) TimedOutMessage() {
   450  	atomic.AddInt64(&c.InFlightCount, -1)
   451  	c.tryUpdateReadyState()
   452  }
   453  
   454  func (c *clientV2) RequeuedMessage() {
   455  	atomic.AddUint64(&c.RequeueCount, 1)
   456  	atomic.AddInt64(&c.InFlightCount, -1)
   457  	c.tryUpdateReadyState()
   458  }
   459  
   460  func (c *clientV2) StartClose() {
   461  	// Force the client into ready 0
   462  	c.SetReadyCount(0)
   463  	// mark this client as closing
   464  	atomic.StoreInt32(&c.State, stateClosing)
   465  }
   466  
   467  func (c *clientV2) Pause() {
   468  	c.tryUpdateReadyState()
   469  }
   470  
   471  func (c *clientV2) UnPause() {
   472  	c.tryUpdateReadyState()
   473  }
   474  
   475  func (c *clientV2) SetHeartbeatInterval(desiredInterval int) error {
   476  	c.writeLock.Lock()
   477  	defer c.writeLock.Unlock()
   478  
   479  	switch {
   480  	case desiredInterval == -1:
   481  		c.HeartbeatInterval = 0
   482  	case desiredInterval == 0:
   483  		// do nothing (use default)
   484  	case desiredInterval >= 1000 &&
   485  		desiredInterval <= int(c.nsqd.getOpts().MaxHeartbeatInterval/time.Millisecond):
   486  		c.HeartbeatInterval = time.Duration(desiredInterval) * time.Millisecond
   487  	default:
   488  		return fmt.Errorf("heartbeat interval (%d) is invalid", desiredInterval)
   489  	}
   490  
   491  	return nil
   492  }
   493  
   494  func (c *clientV2) SetOutputBuffer(desiredSize int, desiredTimeout int) error {
   495  	c.writeLock.Lock()
   496  	defer c.writeLock.Unlock()
   497  
   498  	switch {
   499  	case desiredTimeout == -1:
   500  		c.OutputBufferTimeout = 0
   501  	case desiredTimeout == 0:
   502  		// do nothing (use default)
   503  	case true &&
   504  		desiredTimeout >= int(c.nsqd.getOpts().MinOutputBufferTimeout/time.Millisecond) &&
   505  		desiredTimeout <= int(c.nsqd.getOpts().MaxOutputBufferTimeout/time.Millisecond):
   506  
   507  		c.OutputBufferTimeout = time.Duration(desiredTimeout) * time.Millisecond
   508  	default:
   509  		return fmt.Errorf("output buffer timeout (%d) is invalid", desiredTimeout)
   510  	}
   511  
   512  	switch {
   513  	case desiredSize == -1:
   514  		// effectively no buffer (every write will go directly to the wrapped net.Conn)
   515  		c.OutputBufferSize = 1
   516  		c.OutputBufferTimeout = 0
   517  	case desiredSize == 0:
   518  		// do nothing (use default)
   519  	case desiredSize >= 64 && desiredSize <= int(c.nsqd.getOpts().MaxOutputBufferSize):
   520  		c.OutputBufferSize = desiredSize
   521  	default:
   522  		return fmt.Errorf("output buffer size (%d) is invalid", desiredSize)
   523  	}
   524  
   525  	if desiredSize != 0 {
   526  		err := c.Writer.Flush()
   527  		if err != nil {
   528  			return err
   529  		}
   530  		c.Writer = bufio.NewWriterSize(c.Conn, c.OutputBufferSize)
   531  	}
   532  
   533  	return nil
   534  }
   535  
   536  func (c *clientV2) SetSampleRate(sampleRate int32) error {
   537  	if sampleRate < 0 || sampleRate > 99 {
   538  		return fmt.Errorf("sample rate (%d) is invalid", sampleRate)
   539  	}
   540  	atomic.StoreInt32(&c.SampleRate, sampleRate)
   541  	return nil
   542  }
   543  
   544  func (c *clientV2) SetMsgTimeout(msgTimeout int) error {
   545  	c.writeLock.Lock()
   546  	defer c.writeLock.Unlock()
   547  
   548  	switch {
   549  	case msgTimeout == 0:
   550  		// do nothing (use default)
   551  	case msgTimeout >= 1000 &&
   552  		msgTimeout <= int(c.nsqd.getOpts().MaxMsgTimeout/time.Millisecond):
   553  		c.MsgTimeout = time.Duration(msgTimeout) * time.Millisecond
   554  	default:
   555  		return fmt.Errorf("msg timeout (%d) is invalid", msgTimeout)
   556  	}
   557  
   558  	return nil
   559  }
   560  
   561  func (c *clientV2) UpgradeTLS() error {
   562  	c.writeLock.Lock()
   563  	defer c.writeLock.Unlock()
   564  
   565  	tlsConn := tls.Server(c.Conn, c.nsqd.tlsConfig)
   566  	tlsConn.SetDeadline(time.Now().Add(5 * time.Second))
   567  	err := tlsConn.Handshake()
   568  	if err != nil {
   569  		return err
   570  	}
   571  	c.tlsConn = tlsConn
   572  
   573  	c.Reader = bufio.NewReaderSize(c.tlsConn, defaultBufferSize)
   574  	c.Writer = bufio.NewWriterSize(c.tlsConn, c.OutputBufferSize)
   575  
   576  	atomic.StoreInt32(&c.TLS, 1)
   577  
   578  	return nil
   579  }
   580  
   581  func (c *clientV2) UpgradeDeflate(level int) error {
   582  	c.writeLock.Lock()
   583  	defer c.writeLock.Unlock()
   584  
   585  	conn := c.Conn
   586  	if c.tlsConn != nil {
   587  		conn = c.tlsConn
   588  	}
   589  
   590  	c.Reader = bufio.NewReaderSize(flate.NewReader(conn), defaultBufferSize)
   591  
   592  	fw, _ := flate.NewWriter(conn, level)
   593  	c.flateWriter = fw
   594  	c.Writer = bufio.NewWriterSize(fw, c.OutputBufferSize)
   595  
   596  	atomic.StoreInt32(&c.Deflate, 1)
   597  
   598  	return nil
   599  }
   600  
   601  func (c *clientV2) UpgradeSnappy() error {
   602  	c.writeLock.Lock()
   603  	defer c.writeLock.Unlock()
   604  
   605  	conn := c.Conn
   606  	if c.tlsConn != nil {
   607  		conn = c.tlsConn
   608  	}
   609  
   610  	c.Reader = bufio.NewReaderSize(snappy.NewReader(conn), defaultBufferSize)
   611  	//lint:ignore SA1019 NewWriter is deprecated by NewBufferedWriter, but we're doing our own buffering
   612  	c.Writer = bufio.NewWriterSize(snappy.NewWriter(conn), c.OutputBufferSize)
   613  
   614  	atomic.StoreInt32(&c.Snappy, 1)
   615  
   616  	return nil
   617  }
   618  
   619  func (c *clientV2) Flush() error {
   620  	var zeroTime time.Time
   621  	if c.HeartbeatInterval > 0 {
   622  		c.SetWriteDeadline(time.Now().Add(c.HeartbeatInterval))
   623  	} else {
   624  		c.SetWriteDeadline(zeroTime)
   625  	}
   626  
   627  	err := c.Writer.Flush()
   628  	if err != nil {
   629  		return err
   630  	}
   631  
   632  	if c.flateWriter != nil {
   633  		return c.flateWriter.Flush()
   634  	}
   635  
   636  	return nil
   637  }
   638  
   639  func (c *clientV2) QueryAuthd() error {
   640  	remoteIP := ""
   641  	if c.RemoteAddr().Network() == "tcp" {
   642  		ip, _, err := net.SplitHostPort(c.String())
   643  		if err != nil {
   644  			return err
   645  		}
   646  		remoteIP = ip
   647  	}
   648  
   649  	tlsEnabled := atomic.LoadInt32(&c.TLS) == 1
   650  	commonName := ""
   651  	if tlsEnabled {
   652  		tlsConnState := c.tlsConn.ConnectionState()
   653  		if len(tlsConnState.PeerCertificates) > 0 {
   654  			commonName = tlsConnState.PeerCertificates[0].Subject.CommonName
   655  		}
   656  	}
   657  
   658  	authState, err := auth.QueryAnyAuthd(c.nsqd.getOpts().AuthHTTPAddresses,
   659  		remoteIP, tlsEnabled, commonName, c.AuthSecret,
   660  		c.nsqd.clientTLSConfig,
   661  		c.nsqd.getOpts().HTTPClientConnectTimeout,
   662  		c.nsqd.getOpts().HTTPClientRequestTimeout)
   663  	if err != nil {
   664  		return err
   665  	}
   666  	c.AuthState = authState
   667  	return nil
   668  }
   669  
   670  func (c *clientV2) Auth(secret string) error {
   671  	c.AuthSecret = secret
   672  	return c.QueryAuthd()
   673  }
   674  
   675  func (c *clientV2) IsAuthorized(topic, channel string) (bool, error) {
   676  	if c.AuthState == nil {
   677  		return false, nil
   678  	}
   679  	if c.AuthState.IsExpired() {
   680  		err := c.QueryAuthd()
   681  		if err != nil {
   682  			return false, err
   683  		}
   684  	}
   685  	if c.AuthState.IsAllowed(topic, channel) {
   686  		return true, nil
   687  	}
   688  	return false, nil
   689  }
   690  
   691  func (c *clientV2) HasAuthorizations() bool {
   692  	if c.AuthState != nil {
   693  		return len(c.AuthState.Authorizations) != 0
   694  	}
   695  	return false
   696  }