get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/monitor.go (about)

     1  // Copyright 2013-2023 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package server
    15  
    16  import (
    17  	"bytes"
    18  	"crypto/sha256"
    19  	"crypto/tls"
    20  	"crypto/x509"
    21  	"encoding/hex"
    22  	"encoding/json"
    23  	"fmt"
    24  	"net"
    25  	"net/http"
    26  	"net/url"
    27  	"os"
    28  	"path/filepath"
    29  	"runtime"
    30  	"runtime/pprof"
    31  	"sort"
    32  	"strconv"
    33  	"strings"
    34  	"sync/atomic"
    35  	"time"
    36  
    37  	"get.pme.sh/pnats/server/pse"
    38  	"github.com/nats-io/jwt/v2"
    39  )
    40  
    41  // Connz represents detailed information on current client connections.
    42  type Connz struct {
    43  	ID       string      `json:"server_id"`
    44  	Now      time.Time   `json:"now"`
    45  	NumConns int         `json:"num_connections"`
    46  	Total    int         `json:"total"`
    47  	Offset   int         `json:"offset"`
    48  	Limit    int         `json:"limit"`
    49  	Conns    []*ConnInfo `json:"connections"`
    50  }
    51  
    52  // ConnzOptions are the options passed to Connz()
    53  type ConnzOptions struct {
    54  	// Sort indicates how the results will be sorted. Check SortOpt for possible values.
    55  	// Only the sort by connection ID (ByCid) is ascending, all others are descending.
    56  	Sort SortOpt `json:"sort"`
    57  
    58  	// Username indicates if user names should be included in the results.
    59  	Username bool `json:"auth"`
    60  
    61  	// Subscriptions indicates if subscriptions should be included in the results.
    62  	Subscriptions bool `json:"subscriptions"`
    63  
    64  	// SubscriptionsDetail indicates if subscription details should be included in the results
    65  	SubscriptionsDetail bool `json:"subscriptions_detail"`
    66  
    67  	// Offset is used for pagination. Connz() only returns connections starting at this
    68  	// offset from the global results.
    69  	Offset int `json:"offset"`
    70  
    71  	// Limit is the maximum number of connections that should be returned by Connz().
    72  	Limit int `json:"limit"`
    73  
    74  	// Filter for this explicit client connection.
    75  	CID uint64 `json:"cid"`
    76  
    77  	// Filter for this explicit client connection based on the MQTT client ID
    78  	MQTTClient string `json:"mqtt_client"`
    79  
    80  	// Filter by connection state.
    81  	State ConnState `json:"state"`
    82  
    83  	// The below options only apply if auth is true.
    84  
    85  	// Filter by username.
    86  	User string `json:"user"`
    87  
    88  	// Filter by account.
    89  	Account string `json:"acc"`
    90  
    91  	// Filter by subject interest
    92  	FilterSubject string `json:"filter_subject"`
    93  }
    94  
    95  // ConnState is for filtering states of connections. We will only have two, open and closed.
    96  type ConnState int
    97  
    98  const (
    99  	// ConnOpen filters on open clients.
   100  	ConnOpen = ConnState(iota)
   101  	// ConnClosed filters on closed clients.
   102  	ConnClosed
   103  	// ConnAll returns all clients.
   104  	ConnAll
   105  )
   106  
   107  // ConnInfo has detailed information on a per connection basis.
   108  type ConnInfo struct {
   109  	Cid            uint64         `json:"cid"`
   110  	Kind           string         `json:"kind,omitempty"`
   111  	Type           string         `json:"type,omitempty"`
   112  	IP             string         `json:"ip"`
   113  	Port           int            `json:"port"`
   114  	Start          time.Time      `json:"start"`
   115  	LastActivity   time.Time      `json:"last_activity"`
   116  	Stop           *time.Time     `json:"stop,omitempty"`
   117  	Reason         string         `json:"reason,omitempty"`
   118  	RTT            string         `json:"rtt,omitempty"`
   119  	Uptime         string         `json:"uptime"`
   120  	Idle           string         `json:"idle"`
   121  	Pending        int            `json:"pending_bytes"`
   122  	InMsgs         int64          `json:"in_msgs"`
   123  	OutMsgs        int64          `json:"out_msgs"`
   124  	InBytes        int64          `json:"in_bytes"`
   125  	OutBytes       int64          `json:"out_bytes"`
   126  	NumSubs        uint32         `json:"subscriptions"`
   127  	Name           string         `json:"name,omitempty"`
   128  	Lang           string         `json:"lang,omitempty"`
   129  	Version        string         `json:"version,omitempty"`
   130  	TLSVersion     string         `json:"tls_version,omitempty"`
   131  	TLSCipher      string         `json:"tls_cipher_suite,omitempty"`
   132  	TLSPeerCerts   []*TLSPeerCert `json:"tls_peer_certs,omitempty"`
   133  	TLSFirst       bool           `json:"tls_first,omitempty"`
   134  	AuthorizedUser string         `json:"authorized_user,omitempty"`
   135  	Account        string         `json:"account,omitempty"`
   136  	Subs           []string       `json:"subscriptions_list,omitempty"`
   137  	SubsDetail     []SubDetail    `json:"subscriptions_list_detail,omitempty"`
   138  	JWT            string         `json:"jwt,omitempty"`
   139  	IssuerKey      string         `json:"issuer_key,omitempty"`
   140  	NameTag        string         `json:"name_tag,omitempty"`
   141  	Tags           jwt.TagList    `json:"tags,omitempty"`
   142  	MQTTClient     string         `json:"mqtt_client,omitempty"` // This is the MQTT client id
   143  
   144  	// Internal
   145  	rtt int64 // For fast sorting
   146  }
   147  
   148  // TLSPeerCert contains basic information about a TLS peer certificate
   149  type TLSPeerCert struct {
   150  	Subject          string `json:"subject,omitempty"`
   151  	SubjectPKISha256 string `json:"spki_sha256,omitempty"`
   152  	CertSha256       string `json:"cert_sha256,omitempty"`
   153  }
   154  
   155  // DefaultConnListSize is the default size of the connection list.
   156  const DefaultConnListSize = 1024
   157  
   158  // DefaultSubListSize is the default size of the subscriptions list.
   159  const DefaultSubListSize = 1024
   160  
   161  const defaultStackBufSize = 10000
   162  
   163  func newSubsDetailList(client *client) []SubDetail {
   164  	subsDetail := make([]SubDetail, 0, len(client.subs))
   165  	for _, sub := range client.subs {
   166  		subsDetail = append(subsDetail, newClientSubDetail(sub))
   167  	}
   168  	return subsDetail
   169  }
   170  
   171  func newSubsList(client *client) []string {
   172  	subs := make([]string, 0, len(client.subs))
   173  	for _, sub := range client.subs {
   174  		subs = append(subs, string(sub.subject))
   175  	}
   176  	return subs
   177  }
   178  
   179  // Connz returns a Connz struct containing information about connections.
   180  func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) {
   181  	var (
   182  		sortOpt = ByCid
   183  		auth    bool
   184  		subs    bool
   185  		subsDet bool
   186  		offset  int
   187  		limit   = DefaultConnListSize
   188  		cid     = uint64(0)
   189  		state   = ConnOpen
   190  		user    string
   191  		acc     string
   192  		a       *Account
   193  		filter  string
   194  		mqttCID string
   195  	)
   196  
   197  	if opts != nil {
   198  		// If no sort option given or sort is by uptime, then sort by cid
   199  		if opts.Sort != _EMPTY_ {
   200  			sortOpt = opts.Sort
   201  			if !sortOpt.IsValid() {
   202  				return nil, fmt.Errorf("invalid sorting option: %s", sortOpt)
   203  			}
   204  		}
   205  
   206  		// Auth specifics.
   207  		auth = opts.Username
   208  		user = opts.User
   209  		acc = opts.Account
   210  		mqttCID = opts.MQTTClient
   211  
   212  		subs = opts.Subscriptions
   213  		subsDet = opts.SubscriptionsDetail
   214  		offset = opts.Offset
   215  		if offset < 0 {
   216  			offset = 0
   217  		}
   218  		limit = opts.Limit
   219  		if limit <= 0 {
   220  			limit = DefaultConnListSize
   221  		}
   222  		// state
   223  		state = opts.State
   224  
   225  		// ByStop only makes sense on closed connections
   226  		if sortOpt == ByStop && state != ConnClosed {
   227  			return nil, fmt.Errorf("sort by stop only valid on closed connections")
   228  		}
   229  		// ByReason is the same.
   230  		if sortOpt == ByReason && state != ConnClosed {
   231  			return nil, fmt.Errorf("sort by reason only valid on closed connections")
   232  		}
   233  		// If searching by CID
   234  		if opts.CID > 0 {
   235  			cid = opts.CID
   236  			limit = 1
   237  		}
   238  		// If filtering by subject.
   239  		if opts.FilterSubject != _EMPTY_ && opts.FilterSubject != fwcs {
   240  			if acc == _EMPTY_ {
   241  				return nil, fmt.Errorf("filter by subject only valid with account filtering")
   242  			}
   243  			filter = opts.FilterSubject
   244  		}
   245  	}
   246  
   247  	c := &Connz{
   248  		Offset: offset,
   249  		Limit:  limit,
   250  		Now:    time.Now().UTC(),
   251  	}
   252  
   253  	// Open clients
   254  	var openClients []*client
   255  	// Hold for closed clients if requested.
   256  	var closedClients []*closedClient
   257  
   258  	var clist map[uint64]*client
   259  
   260  	if acc != _EMPTY_ {
   261  		var err error
   262  		a, err = s.lookupAccount(acc)
   263  		if err != nil {
   264  			return c, nil
   265  		}
   266  		a.mu.RLock()
   267  		clist = make(map[uint64]*client, a.numLocalConnections())
   268  		for c := range a.clients {
   269  			if c.kind == CLIENT || c.kind == LEAF {
   270  				clist[c.cid] = c
   271  			}
   272  		}
   273  		a.mu.RUnlock()
   274  	}
   275  
   276  	// Walk the open client list with server lock held.
   277  	s.mu.RLock()
   278  	// Default to all client unless filled in above.
   279  	if clist == nil {
   280  		clist = s.clients
   281  	}
   282  
   283  	// copy the server id for monitoring
   284  	c.ID = s.info.ID
   285  
   286  	// Number of total clients. The resulting ConnInfo array
   287  	// may be smaller if pagination is used.
   288  	switch state {
   289  	case ConnOpen:
   290  		c.Total = len(clist)
   291  	case ConnClosed:
   292  		closedClients = s.closed.closedClients()
   293  		c.Total = len(closedClients)
   294  	case ConnAll:
   295  		c.Total = len(clist)
   296  		closedClients = s.closed.closedClients()
   297  		c.Total += len(closedClients)
   298  	}
   299  
   300  	// We may need to filter these connections.
   301  	if acc != _EMPTY_ && len(closedClients) > 0 {
   302  		var ccc []*closedClient
   303  		for _, cc := range closedClients {
   304  			if cc.acc != acc {
   305  				continue
   306  			}
   307  			ccc = append(ccc, cc)
   308  		}
   309  		c.Total -= (len(closedClients) - len(ccc))
   310  		closedClients = ccc
   311  	}
   312  
   313  	totalClients := c.Total
   314  	if cid > 0 { // Meaning we only want 1.
   315  		totalClients = 1
   316  	}
   317  	if state == ConnOpen || state == ConnAll {
   318  		openClients = make([]*client, 0, totalClients)
   319  	}
   320  
   321  	// Data structures for results.
   322  	var conns []ConnInfo // Limits allocs for actual ConnInfos.
   323  	var pconns ConnInfos
   324  
   325  	switch state {
   326  	case ConnOpen:
   327  		conns = make([]ConnInfo, totalClients)
   328  		pconns = make(ConnInfos, totalClients)
   329  	case ConnClosed:
   330  		pconns = make(ConnInfos, totalClients)
   331  	case ConnAll:
   332  		conns = make([]ConnInfo, cap(openClients))
   333  		pconns = make(ConnInfos, totalClients)
   334  	}
   335  
   336  	// Search by individual CID.
   337  	if cid > 0 {
   338  		if state == ConnClosed || state == ConnAll {
   339  			copyClosed := closedClients
   340  			closedClients = nil
   341  			for _, cc := range copyClosed {
   342  				if cc.Cid == cid {
   343  					closedClients = []*closedClient{cc}
   344  					break
   345  				}
   346  			}
   347  		} else if state == ConnOpen || state == ConnAll {
   348  			client := s.clients[cid]
   349  			if client != nil {
   350  				openClients = append(openClients, client)
   351  			}
   352  		}
   353  	} else {
   354  		// Gather all open clients.
   355  		if state == ConnOpen || state == ConnAll {
   356  			for _, client := range clist {
   357  				// If we have an account specified we need to filter.
   358  				if acc != _EMPTY_ && (client.acc == nil || client.acc.Name != acc) {
   359  					continue
   360  				}
   361  				// Do user filtering second
   362  				if user != _EMPTY_ && client.getRawAuthUserLock() != user {
   363  					continue
   364  				}
   365  				// Do mqtt client ID filtering next
   366  				if mqttCID != _EMPTY_ && client.getMQTTClientID() != mqttCID {
   367  					continue
   368  				}
   369  				openClients = append(openClients, client)
   370  			}
   371  		}
   372  	}
   373  	s.mu.RUnlock()
   374  
   375  	// Filter by subject now if needed. We do this outside of server lock.
   376  	if filter != _EMPTY_ {
   377  		var oc []*client
   378  		for _, c := range openClients {
   379  			c.mu.Lock()
   380  			for _, sub := range c.subs {
   381  				if SubjectsCollide(filter, string(sub.subject)) {
   382  					oc = append(oc, c)
   383  					break
   384  				}
   385  			}
   386  			c.mu.Unlock()
   387  			openClients = oc
   388  		}
   389  	}
   390  
   391  	// Just return with empty array if nothing here.
   392  	if len(openClients) == 0 && len(closedClients) == 0 {
   393  		c.Conns = ConnInfos{}
   394  		return c, nil
   395  	}
   396  
   397  	// Now whip through and generate ConnInfo entries
   398  	// Open Clients
   399  	i := 0
   400  	for _, client := range openClients {
   401  		client.mu.Lock()
   402  		ci := &conns[i]
   403  		ci.fill(client, client.nc, c.Now, auth)
   404  		// Fill in subscription data if requested.
   405  		if len(client.subs) > 0 {
   406  			if subsDet {
   407  				ci.SubsDetail = newSubsDetailList(client)
   408  			} else if subs {
   409  				ci.Subs = newSubsList(client)
   410  			}
   411  		}
   412  		// Fill in user if auth requested.
   413  		if auth {
   414  			ci.AuthorizedUser = client.getRawAuthUser()
   415  			// Add in account iff not the global account.
   416  			if client.acc != nil && (client.acc.Name != globalAccountName) {
   417  				ci.Account = client.acc.Name
   418  			}
   419  			ci.JWT = client.opts.JWT
   420  			ci.IssuerKey = issuerForClient(client)
   421  			ci.Tags = client.tags
   422  			ci.NameTag = client.nameTag
   423  		}
   424  		client.mu.Unlock()
   425  		pconns[i] = ci
   426  		i++
   427  	}
   428  	// Closed Clients
   429  	var needCopy bool
   430  	if subs || auth {
   431  		needCopy = true
   432  	}
   433  	for _, cc := range closedClients {
   434  		// If we have an account specified we need to filter.
   435  		if acc != _EMPTY_ && cc.acc != acc {
   436  			continue
   437  		}
   438  		// Do user filtering second
   439  		if user != _EMPTY_ && cc.user != user {
   440  			continue
   441  		}
   442  		// Do mqtt client ID filtering next
   443  		if mqttCID != _EMPTY_ && cc.MQTTClient != mqttCID {
   444  			continue
   445  		}
   446  		// Copy if needed for any changes to the ConnInfo
   447  		if needCopy {
   448  			cx := *cc
   449  			cc = &cx
   450  		}
   451  		// Fill in subscription data if requested.
   452  		if len(cc.subs) > 0 {
   453  			if subsDet {
   454  				cc.SubsDetail = cc.subs
   455  			} else if subs {
   456  				cc.Subs = make([]string, 0, len(cc.subs))
   457  				for _, sub := range cc.subs {
   458  					cc.Subs = append(cc.Subs, sub.Subject)
   459  				}
   460  			}
   461  		}
   462  		// Fill in user if auth requested.
   463  		if auth {
   464  			cc.AuthorizedUser = cc.user
   465  			// Add in account iff not the global account.
   466  			if cc.acc != _EMPTY_ && (cc.acc != globalAccountName) {
   467  				cc.Account = cc.acc
   468  			}
   469  		}
   470  		pconns[i] = &cc.ConnInfo
   471  		i++
   472  	}
   473  
   474  	// This will trip if we have filtered out client connections.
   475  	if len(pconns) != i {
   476  		pconns = pconns[:i]
   477  		totalClients = i
   478  	}
   479  
   480  	switch sortOpt {
   481  	case ByCid, ByStart:
   482  		sort.Sort(byCid{pconns})
   483  	case BySubs:
   484  		sort.Sort(sort.Reverse(bySubs{pconns}))
   485  	case ByPending:
   486  		sort.Sort(sort.Reverse(byPending{pconns}))
   487  	case ByOutMsgs:
   488  		sort.Sort(sort.Reverse(byOutMsgs{pconns}))
   489  	case ByInMsgs:
   490  		sort.Sort(sort.Reverse(byInMsgs{pconns}))
   491  	case ByOutBytes:
   492  		sort.Sort(sort.Reverse(byOutBytes{pconns}))
   493  	case ByInBytes:
   494  		sort.Sort(sort.Reverse(byInBytes{pconns}))
   495  	case ByLast:
   496  		sort.Sort(sort.Reverse(byLast{pconns}))
   497  	case ByIdle:
   498  		sort.Sort(sort.Reverse(byIdle{pconns, c.Now}))
   499  	case ByUptime:
   500  		sort.Sort(byUptime{pconns, time.Now()})
   501  	case ByStop:
   502  		sort.Sort(sort.Reverse(byStop{pconns}))
   503  	case ByReason:
   504  		sort.Sort(byReason{pconns})
   505  	case ByRTT:
   506  		sort.Sort(sort.Reverse(byRTT{pconns}))
   507  	}
   508  
   509  	minoff := c.Offset
   510  	maxoff := c.Offset + c.Limit
   511  
   512  	maxIndex := totalClients
   513  
   514  	// Make sure these are sane.
   515  	if minoff > maxIndex {
   516  		minoff = maxIndex
   517  	}
   518  	if maxoff > maxIndex {
   519  		maxoff = maxIndex
   520  	}
   521  
   522  	// Now pare down to the requested size.
   523  	// TODO(dlc) - for very large number of connections we
   524  	// could save the whole list in a hash, send hash on first
   525  	// request and allow users to use has for subsequent pages.
   526  	// Low TTL, say < 1sec.
   527  	c.Conns = pconns[minoff:maxoff]
   528  	c.NumConns = len(c.Conns)
   529  
   530  	return c, nil
   531  }
   532  
   533  // Fills in the ConnInfo from the client.
   534  // client should be locked.
   535  func (ci *ConnInfo) fill(client *client, nc net.Conn, now time.Time, auth bool) {
   536  	// For fast sort if required.
   537  	rtt := client.getRTT()
   538  	ci.rtt = int64(rtt)
   539  
   540  	ci.Cid = client.cid
   541  	ci.MQTTClient = client.getMQTTClientID()
   542  	ci.Kind = client.kindString()
   543  	ci.Type = client.clientTypeString()
   544  	ci.Start = client.start
   545  	ci.LastActivity = client.last
   546  	ci.Uptime = myUptime(now.Sub(client.start))
   547  	ci.Idle = myUptime(now.Sub(client.last))
   548  	ci.RTT = rtt.String()
   549  	ci.OutMsgs = client.outMsgs
   550  	ci.OutBytes = client.outBytes
   551  	ci.NumSubs = uint32(len(client.subs))
   552  	ci.Pending = int(client.out.pb)
   553  	ci.Name = client.opts.Name
   554  	ci.Lang = client.opts.Lang
   555  	ci.Version = client.opts.Version
   556  	// inMsgs and inBytes are updated outside of the client's lock, so
   557  	// we need to use atomic here.
   558  	ci.InMsgs = atomic.LoadInt64(&client.inMsgs)
   559  	ci.InBytes = atomic.LoadInt64(&client.inBytes)
   560  
   561  	// If the connection is gone, too bad, we won't set TLSVersion and TLSCipher.
   562  	// Exclude clients that are still doing handshake so we don't block in
   563  	// ConnectionState().
   564  	if client.flags.isSet(handshakeComplete) && nc != nil {
   565  		if conn, ok := nc.(*tls.Conn); ok {
   566  			cs := conn.ConnectionState()
   567  			ci.TLSVersion = tlsVersion(cs.Version)
   568  			ci.TLSCipher = tlsCipher(cs.CipherSuite)
   569  			if auth && len(cs.PeerCertificates) > 0 {
   570  				ci.TLSPeerCerts = makePeerCerts(cs.PeerCertificates)
   571  			}
   572  			ci.TLSFirst = client.flags.isSet(didTLSFirst)
   573  		}
   574  	}
   575  
   576  	if client.port != 0 {
   577  		ci.Port = int(client.port)
   578  		ci.IP = client.host
   579  	}
   580  }
   581  
   582  func makePeerCerts(pc []*x509.Certificate) []*TLSPeerCert {
   583  	res := make([]*TLSPeerCert, len(pc))
   584  	for i, c := range pc {
   585  		tmp := sha256.Sum256(c.RawSubjectPublicKeyInfo)
   586  		ssha := hex.EncodeToString(tmp[:])
   587  		tmp = sha256.Sum256(c.Raw)
   588  		csha := hex.EncodeToString(tmp[:])
   589  		res[i] = &TLSPeerCert{Subject: c.Subject.String(), SubjectPKISha256: ssha, CertSha256: csha}
   590  	}
   591  	return res
   592  }
   593  
   594  // Assume lock is held
   595  func (c *client) getRTT() time.Duration {
   596  	if c.rtt == 0 {
   597  		// If a real client, go ahead and send ping now to get a value
   598  		// for RTT. For tests and telnet, or if client is closing, etc skip.
   599  		if c.opts.Lang != _EMPTY_ {
   600  			c.sendRTTPingLocked()
   601  		}
   602  		return 0
   603  	}
   604  	var rtt time.Duration
   605  	if c.rtt > time.Microsecond && c.rtt < time.Millisecond {
   606  		rtt = c.rtt.Truncate(time.Microsecond)
   607  	} else {
   608  		rtt = c.rtt.Truncate(time.Nanosecond)
   609  	}
   610  	return rtt
   611  }
   612  
   613  func decodeBool(w http.ResponseWriter, r *http.Request, param string) (bool, error) {
   614  	str := r.URL.Query().Get(param)
   615  	if str == _EMPTY_ {
   616  		return false, nil
   617  	}
   618  	val, err := strconv.ParseBool(str)
   619  	if err != nil {
   620  		w.WriteHeader(http.StatusBadRequest)
   621  		w.Write([]byte(fmt.Sprintf("Error decoding boolean for '%s': %v", param, err)))
   622  		return false, err
   623  	}
   624  	return val, nil
   625  }
   626  
   627  func decodeUint64(w http.ResponseWriter, r *http.Request, param string) (uint64, error) {
   628  	str := r.URL.Query().Get(param)
   629  	if str == _EMPTY_ {
   630  		return 0, nil
   631  	}
   632  	val, err := strconv.ParseUint(str, 10, 64)
   633  	if err != nil {
   634  		w.WriteHeader(http.StatusBadRequest)
   635  		w.Write([]byte(fmt.Sprintf("Error decoding uint64 for '%s': %v", param, err)))
   636  		return 0, err
   637  	}
   638  	return val, nil
   639  }
   640  
   641  func decodeInt(w http.ResponseWriter, r *http.Request, param string) (int, error) {
   642  	str := r.URL.Query().Get(param)
   643  	if str == _EMPTY_ {
   644  		return 0, nil
   645  	}
   646  	val, err := strconv.Atoi(str)
   647  	if err != nil {
   648  		w.WriteHeader(http.StatusBadRequest)
   649  		w.Write([]byte(fmt.Sprintf("Error decoding int for '%s': %v", param, err)))
   650  		return 0, err
   651  	}
   652  	return val, nil
   653  }
   654  
   655  func decodeState(w http.ResponseWriter, r *http.Request) (ConnState, error) {
   656  	str := r.URL.Query().Get("state")
   657  	if str == _EMPTY_ {
   658  		return ConnOpen, nil
   659  	}
   660  	switch strings.ToLower(str) {
   661  	case "open":
   662  		return ConnOpen, nil
   663  	case "closed":
   664  		return ConnClosed, nil
   665  	case "any", "all":
   666  		return ConnAll, nil
   667  	}
   668  	// We do not understand intended state here.
   669  	w.WriteHeader(http.StatusBadRequest)
   670  	err := fmt.Errorf("Error decoding state for %s", str)
   671  	w.Write([]byte(err.Error()))
   672  	return 0, err
   673  }
   674  
   675  func decodeSubs(w http.ResponseWriter, r *http.Request) (subs bool, subsDet bool, err error) {
   676  	subsDet = strings.ToLower(r.URL.Query().Get("subs")) == "detail"
   677  	if !subsDet {
   678  		subs, err = decodeBool(w, r, "subs")
   679  	}
   680  	return
   681  }
   682  
   683  // HandleConnz process HTTP requests for connection information.
   684  func (s *Server) HandleConnz(w http.ResponseWriter, r *http.Request) {
   685  	sortOpt := SortOpt(r.URL.Query().Get("sort"))
   686  	auth, err := decodeBool(w, r, "auth")
   687  	if err != nil {
   688  		return
   689  	}
   690  	subs, subsDet, err := decodeSubs(w, r)
   691  	if err != nil {
   692  		return
   693  	}
   694  	offset, err := decodeInt(w, r, "offset")
   695  	if err != nil {
   696  		return
   697  	}
   698  	limit, err := decodeInt(w, r, "limit")
   699  	if err != nil {
   700  		return
   701  	}
   702  	cid, err := decodeUint64(w, r, "cid")
   703  	if err != nil {
   704  		return
   705  	}
   706  	state, err := decodeState(w, r)
   707  	if err != nil {
   708  		return
   709  	}
   710  
   711  	user := r.URL.Query().Get("user")
   712  	acc := r.URL.Query().Get("acc")
   713  	mqttCID := r.URL.Query().Get("mqtt_client")
   714  
   715  	connzOpts := &ConnzOptions{
   716  		Sort:                sortOpt,
   717  		Username:            auth,
   718  		Subscriptions:       subs,
   719  		SubscriptionsDetail: subsDet,
   720  		Offset:              offset,
   721  		Limit:               limit,
   722  		CID:                 cid,
   723  		MQTTClient:          mqttCID,
   724  		State:               state,
   725  		User:                user,
   726  		Account:             acc,
   727  	}
   728  
   729  	s.mu.Lock()
   730  	s.httpReqStats[ConnzPath]++
   731  	s.mu.Unlock()
   732  
   733  	c, err := s.Connz(connzOpts)
   734  	if err != nil {
   735  		w.WriteHeader(http.StatusBadRequest)
   736  		w.Write([]byte(err.Error()))
   737  		return
   738  	}
   739  	b, err := json.MarshalIndent(c, "", "  ")
   740  	if err != nil {
   741  		s.Errorf("Error marshaling response to /connz request: %v", err)
   742  	}
   743  
   744  	// Handle response
   745  	ResponseHandler(w, r, b)
   746  }
   747  
   748  // Routez represents detailed information on current client connections.
   749  type Routez struct {
   750  	ID        string             `json:"server_id"`
   751  	Name      string             `json:"server_name"`
   752  	Now       time.Time          `json:"now"`
   753  	Import    *SubjectPermission `json:"import,omitempty"`
   754  	Export    *SubjectPermission `json:"export,omitempty"`
   755  	NumRoutes int                `json:"num_routes"`
   756  	Routes    []*RouteInfo       `json:"routes"`
   757  }
   758  
   759  // RoutezOptions are options passed to Routez
   760  type RoutezOptions struct {
   761  	// Subscriptions indicates that Routez will return a route's subscriptions
   762  	Subscriptions bool `json:"subscriptions"`
   763  	// SubscriptionsDetail indicates if subscription details should be included in the results
   764  	SubscriptionsDetail bool `json:"subscriptions_detail"`
   765  }
   766  
   767  // RouteInfo has detailed information on a per connection basis.
   768  type RouteInfo struct {
   769  	Rid          uint64             `json:"rid"`
   770  	RemoteID     string             `json:"remote_id"`
   771  	RemoteName   string             `json:"remote_name"`
   772  	DidSolicit   bool               `json:"did_solicit"`
   773  	IsConfigured bool               `json:"is_configured"`
   774  	IP           string             `json:"ip"`
   775  	Port         int                `json:"port"`
   776  	Start        time.Time          `json:"start"`
   777  	LastActivity time.Time          `json:"last_activity"`
   778  	RTT          string             `json:"rtt,omitempty"`
   779  	Uptime       string             `json:"uptime"`
   780  	Idle         string             `json:"idle"`
   781  	Import       *SubjectPermission `json:"import,omitempty"`
   782  	Export       *SubjectPermission `json:"export,omitempty"`
   783  	Pending      int                `json:"pending_size"`
   784  	InMsgs       int64              `json:"in_msgs"`
   785  	OutMsgs      int64              `json:"out_msgs"`
   786  	InBytes      int64              `json:"in_bytes"`
   787  	OutBytes     int64              `json:"out_bytes"`
   788  	NumSubs      uint32             `json:"subscriptions"`
   789  	Subs         []string           `json:"subscriptions_list,omitempty"`
   790  	SubsDetail   []SubDetail        `json:"subscriptions_list_detail,omitempty"`
   791  	Account      string             `json:"account,omitempty"`
   792  	Compression  string             `json:"compression,omitempty"`
   793  }
   794  
   795  // Routez returns a Routez struct containing information about routes.
   796  func (s *Server) Routez(routezOpts *RoutezOptions) (*Routez, error) {
   797  	rs := &Routez{Routes: []*RouteInfo{}}
   798  	rs.Now = time.Now().UTC()
   799  
   800  	if routezOpts == nil {
   801  		routezOpts = &RoutezOptions{}
   802  	}
   803  
   804  	s.mu.Lock()
   805  	rs.NumRoutes = s.numRoutes()
   806  
   807  	// copy the server id for monitoring
   808  	rs.ID = s.info.ID
   809  
   810  	// Check for defined permissions for all connected routes.
   811  	if perms := s.getOpts().Cluster.Permissions; perms != nil {
   812  		rs.Import = perms.Import
   813  		rs.Export = perms.Export
   814  	}
   815  	rs.Name = s.getOpts().ServerName
   816  
   817  	addRoute := func(r *client) {
   818  		r.mu.Lock()
   819  		ri := &RouteInfo{
   820  			Rid:          r.cid,
   821  			RemoteID:     r.route.remoteID,
   822  			RemoteName:   r.route.remoteName,
   823  			DidSolicit:   r.route.didSolicit,
   824  			IsConfigured: r.route.routeType == Explicit,
   825  			InMsgs:       atomic.LoadInt64(&r.inMsgs),
   826  			OutMsgs:      r.outMsgs,
   827  			InBytes:      atomic.LoadInt64(&r.inBytes),
   828  			OutBytes:     r.outBytes,
   829  			NumSubs:      uint32(len(r.subs)),
   830  			Import:       r.opts.Import,
   831  			Export:       r.opts.Export,
   832  			RTT:          r.getRTT().String(),
   833  			Start:        r.start,
   834  			LastActivity: r.last,
   835  			Uptime:       myUptime(rs.Now.Sub(r.start)),
   836  			Idle:         myUptime(rs.Now.Sub(r.last)),
   837  			Account:      string(r.route.accName),
   838  			Compression:  r.route.compression,
   839  		}
   840  
   841  		if len(r.subs) > 0 {
   842  			if routezOpts.SubscriptionsDetail {
   843  				ri.SubsDetail = newSubsDetailList(r)
   844  			} else if routezOpts.Subscriptions {
   845  				ri.Subs = newSubsList(r)
   846  			}
   847  		}
   848  
   849  		switch conn := r.nc.(type) {
   850  		case *net.TCPConn, *tls.Conn:
   851  			addr := conn.RemoteAddr().(*net.TCPAddr)
   852  			ri.Port = addr.Port
   853  			ri.IP = addr.IP.String()
   854  		}
   855  		r.mu.Unlock()
   856  		rs.Routes = append(rs.Routes, ri)
   857  	}
   858  
   859  	// Walk the list
   860  	s.forEachRoute(func(r *client) {
   861  		addRoute(r)
   862  	})
   863  	s.mu.Unlock()
   864  	return rs, nil
   865  }
   866  
   867  // HandleRoutez process HTTP requests for route information.
   868  func (s *Server) HandleRoutez(w http.ResponseWriter, r *http.Request) {
   869  	subs, subsDetail, err := decodeSubs(w, r)
   870  	if err != nil {
   871  		return
   872  	}
   873  
   874  	opts := RoutezOptions{Subscriptions: subs, SubscriptionsDetail: subsDetail}
   875  
   876  	s.mu.Lock()
   877  	s.httpReqStats[RoutezPath]++
   878  	s.mu.Unlock()
   879  
   880  	// As of now, no error is ever returned.
   881  	rs, _ := s.Routez(&opts)
   882  	b, err := json.MarshalIndent(rs, "", "  ")
   883  	if err != nil {
   884  		s.Errorf("Error marshaling response to /routez request: %v", err)
   885  	}
   886  
   887  	// Handle response
   888  	ResponseHandler(w, r, b)
   889  }
   890  
   891  // Subsz represents detail information on current connections.
   892  type Subsz struct {
   893  	ID  string    `json:"server_id"`
   894  	Now time.Time `json:"now"`
   895  	*SublistStats
   896  	Total  int         `json:"total"`
   897  	Offset int         `json:"offset"`
   898  	Limit  int         `json:"limit"`
   899  	Subs   []SubDetail `json:"subscriptions_list,omitempty"`
   900  }
   901  
   902  // SubszOptions are the options passed to Subsz.
   903  // As of now, there are no options defined.
   904  type SubszOptions struct {
   905  	// Offset is used for pagination. Subsz() only returns connections starting at this
   906  	// offset from the global results.
   907  	Offset int `json:"offset"`
   908  
   909  	// Limit is the maximum number of subscriptions that should be returned by Subsz().
   910  	Limit int `json:"limit"`
   911  
   912  	// Subscriptions indicates if subscription details should be included in the results.
   913  	Subscriptions bool `json:"subscriptions"`
   914  
   915  	// Filter based on this account name.
   916  	Account string `json:"account,omitempty"`
   917  
   918  	// Test the list against this subject. Needs to be literal since it signifies a publish subject.
   919  	// We will only return subscriptions that would match if a message was sent to this subject.
   920  	Test string `json:"test,omitempty"`
   921  }
   922  
   923  // SubDetail is for verbose information for subscriptions.
   924  type SubDetail struct {
   925  	Account string `json:"account,omitempty"`
   926  	Subject string `json:"subject"`
   927  	Queue   string `json:"qgroup,omitempty"`
   928  	Sid     string `json:"sid"`
   929  	Msgs    int64  `json:"msgs"`
   930  	Max     int64  `json:"max,omitempty"`
   931  	Cid     uint64 `json:"cid"`
   932  }
   933  
   934  // Subscription client should be locked and guaranteed to be present.
   935  func newSubDetail(sub *subscription) SubDetail {
   936  	sd := newClientSubDetail(sub)
   937  	if sub.client.acc != nil {
   938  		sd.Account = sub.client.acc.Name
   939  	}
   940  	return sd
   941  }
   942  
   943  // For subs details under clients.
   944  func newClientSubDetail(sub *subscription) SubDetail {
   945  	return SubDetail{
   946  		Subject: string(sub.subject),
   947  		Queue:   string(sub.queue),
   948  		Sid:     string(sub.sid),
   949  		Msgs:    sub.nm,
   950  		Max:     sub.max,
   951  		Cid:     sub.client.cid,
   952  	}
   953  }
   954  
   955  // Subsz returns a Subsz struct containing subjects statistics
   956  func (s *Server) Subsz(opts *SubszOptions) (*Subsz, error) {
   957  	var (
   958  		subdetail bool
   959  		test      bool
   960  		offset    int
   961  		testSub   string
   962  		filterAcc string
   963  		limit     = DefaultSubListSize
   964  	)
   965  
   966  	if opts != nil {
   967  		subdetail = opts.Subscriptions
   968  		offset = opts.Offset
   969  		if offset < 0 {
   970  			offset = 0
   971  		}
   972  		limit = opts.Limit
   973  		if limit <= 0 {
   974  			limit = DefaultSubListSize
   975  		}
   976  		if opts.Test != _EMPTY_ {
   977  			testSub = opts.Test
   978  			test = true
   979  			if !IsValidLiteralSubject(testSub) {
   980  				return nil, fmt.Errorf("invalid test subject, must be valid publish subject: %s", testSub)
   981  			}
   982  		}
   983  		if opts.Account != _EMPTY_ {
   984  			filterAcc = opts.Account
   985  		}
   986  	}
   987  
   988  	slStats := &SublistStats{}
   989  
   990  	// FIXME(dlc) - Make account aware.
   991  	sz := &Subsz{s.info.ID, time.Now().UTC(), slStats, 0, offset, limit, nil}
   992  
   993  	if subdetail {
   994  		var raw [4096]*subscription
   995  		subs := raw[:0]
   996  		s.accounts.Range(func(k, v interface{}) bool {
   997  			acc := v.(*Account)
   998  			if filterAcc != _EMPTY_ && acc.GetName() != filterAcc {
   999  				return true
  1000  			}
  1001  			slStats.add(acc.sl.Stats())
  1002  			acc.sl.localSubs(&subs, false)
  1003  			return true
  1004  		})
  1005  
  1006  		details := make([]SubDetail, len(subs))
  1007  		i := 0
  1008  		// TODO(dlc) - may be inefficient and could just do normal match when total subs is large and filtering.
  1009  		for _, sub := range subs {
  1010  			// Check for filter
  1011  			if test && !matchLiteral(testSub, string(sub.subject)) {
  1012  				continue
  1013  			}
  1014  			if sub.client == nil {
  1015  				continue
  1016  			}
  1017  			sub.client.mu.Lock()
  1018  			details[i] = newSubDetail(sub)
  1019  			sub.client.mu.Unlock()
  1020  			i++
  1021  		}
  1022  		minoff := sz.Offset
  1023  		maxoff := sz.Offset + sz.Limit
  1024  
  1025  		maxIndex := i
  1026  
  1027  		// Make sure these are sane.
  1028  		if minoff > maxIndex {
  1029  			minoff = maxIndex
  1030  		}
  1031  		if maxoff > maxIndex {
  1032  			maxoff = maxIndex
  1033  		}
  1034  		sz.Subs = details[minoff:maxoff]
  1035  		sz.Total = len(sz.Subs)
  1036  	} else {
  1037  		s.accounts.Range(func(k, v interface{}) bool {
  1038  			acc := v.(*Account)
  1039  			if filterAcc != _EMPTY_ && acc.GetName() != filterAcc {
  1040  				return true
  1041  			}
  1042  			slStats.add(acc.sl.Stats())
  1043  			return true
  1044  		})
  1045  	}
  1046  
  1047  	return sz, nil
  1048  }
  1049  
  1050  // HandleSubsz processes HTTP requests for subjects stats.
  1051  func (s *Server) HandleSubsz(w http.ResponseWriter, r *http.Request) {
  1052  	s.mu.Lock()
  1053  	s.httpReqStats[SubszPath]++
  1054  	s.mu.Unlock()
  1055  
  1056  	subs, err := decodeBool(w, r, "subs")
  1057  	if err != nil {
  1058  		return
  1059  	}
  1060  	offset, err := decodeInt(w, r, "offset")
  1061  	if err != nil {
  1062  		return
  1063  	}
  1064  	limit, err := decodeInt(w, r, "limit")
  1065  	if err != nil {
  1066  		return
  1067  	}
  1068  	testSub := r.URL.Query().Get("test")
  1069  	// Filtered account.
  1070  	filterAcc := r.URL.Query().Get("acc")
  1071  
  1072  	subszOpts := &SubszOptions{
  1073  		Subscriptions: subs,
  1074  		Offset:        offset,
  1075  		Limit:         limit,
  1076  		Account:       filterAcc,
  1077  		Test:          testSub,
  1078  	}
  1079  
  1080  	st, err := s.Subsz(subszOpts)
  1081  	if err != nil {
  1082  		w.WriteHeader(http.StatusBadRequest)
  1083  		w.Write([]byte(err.Error()))
  1084  		return
  1085  	}
  1086  
  1087  	var b []byte
  1088  
  1089  	if len(st.Subs) == 0 {
  1090  		b, err = json.MarshalIndent(st.SublistStats, "", "  ")
  1091  	} else {
  1092  		b, err = json.MarshalIndent(st, "", "  ")
  1093  	}
  1094  	if err != nil {
  1095  		s.Errorf("Error marshaling response to /subscriptionsz request: %v", err)
  1096  	}
  1097  
  1098  	// Handle response
  1099  	ResponseHandler(w, r, b)
  1100  }
  1101  
  1102  // HandleStacksz processes HTTP requests for getting stacks
  1103  func (s *Server) HandleStacksz(w http.ResponseWriter, r *http.Request) {
  1104  	// Do not get any lock here that would prevent getting the stacks
  1105  	// if we were to have a deadlock somewhere.
  1106  	var defaultBuf [defaultStackBufSize]byte
  1107  	size := defaultStackBufSize
  1108  	buf := defaultBuf[:size]
  1109  	n := 0
  1110  	for {
  1111  		n = runtime.Stack(buf, true)
  1112  		if n < size {
  1113  			break
  1114  		}
  1115  		size *= 2
  1116  		buf = make([]byte, size)
  1117  	}
  1118  	// Handle response
  1119  	ResponseHandler(w, r, buf[:n])
  1120  }
  1121  
  1122  type monitorIPQueue struct {
  1123  	Pending    int `json:"pending"`
  1124  	InProgress int `json:"in_progress,omitempty"`
  1125  }
  1126  
  1127  func (s *Server) HandleIPQueuesz(w http.ResponseWriter, r *http.Request) {
  1128  	all, err := decodeBool(w, r, "all")
  1129  	if err != nil {
  1130  		return
  1131  	}
  1132  	qfilter := r.URL.Query().Get("queues")
  1133  
  1134  	queues := map[string]monitorIPQueue{}
  1135  
  1136  	s.ipQueues.Range(func(k, v any) bool {
  1137  		var pending, inProgress int
  1138  		name := k.(string)
  1139  		queue, ok := v.(interface {
  1140  			len() int
  1141  			inProgress() int64
  1142  		})
  1143  		if ok {
  1144  			pending = queue.len()
  1145  			inProgress = int(queue.inProgress())
  1146  		}
  1147  		if !all && (pending == 0 && inProgress == 0) {
  1148  			return true
  1149  		} else if qfilter != _EMPTY_ && !strings.Contains(name, qfilter) {
  1150  			return true
  1151  		}
  1152  		queues[name] = monitorIPQueue{Pending: pending, InProgress: inProgress}
  1153  		return true
  1154  	})
  1155  
  1156  	b, _ := json.MarshalIndent(queues, "", "   ")
  1157  	ResponseHandler(w, r, b)
  1158  }
  1159  
  1160  // Varz will output server information on the monitoring port at /varz.
  1161  type Varz struct {
  1162  	ID                    string                 `json:"server_id"`
  1163  	Name                  string                 `json:"server_name"`
  1164  	Version               string                 `json:"version"`
  1165  	Proto                 int                    `json:"proto"`
  1166  	GitCommit             string                 `json:"git_commit,omitempty"`
  1167  	GoVersion             string                 `json:"go"`
  1168  	Host                  string                 `json:"host"`
  1169  	Port                  int                    `json:"port"`
  1170  	AuthRequired          bool                   `json:"auth_required,omitempty"`
  1171  	TLSRequired           bool                   `json:"tls_required,omitempty"`
  1172  	TLSVerify             bool                   `json:"tls_verify,omitempty"`
  1173  	TLSOCSPPeerVerify     bool                   `json:"tls_ocsp_peer_verify,omitempty"`
  1174  	IP                    string                 `json:"ip,omitempty"`
  1175  	ClientConnectURLs     []string               `json:"connect_urls,omitempty"`
  1176  	WSConnectURLs         []string               `json:"ws_connect_urls,omitempty"`
  1177  	MaxConn               int                    `json:"max_connections"`
  1178  	MaxSubs               int                    `json:"max_subscriptions,omitempty"`
  1179  	PingInterval          time.Duration          `json:"ping_interval"`
  1180  	MaxPingsOut           int                    `json:"ping_max"`
  1181  	HTTPHost              string                 `json:"http_host"`
  1182  	HTTPPort              int                    `json:"http_port"`
  1183  	HTTPBasePath          string                 `json:"http_base_path"`
  1184  	HTTPSPort             int                    `json:"https_port"`
  1185  	AuthTimeout           float64                `json:"auth_timeout"`
  1186  	MaxControlLine        int32                  `json:"max_control_line"`
  1187  	MaxPayload            int                    `json:"max_payload"`
  1188  	MaxPending            int64                  `json:"max_pending"`
  1189  	Cluster               ClusterOptsVarz        `json:"cluster,omitempty"`
  1190  	Gateway               GatewayOptsVarz        `json:"gateway,omitempty"`
  1191  	LeafNode              LeafNodeOptsVarz       `json:"leaf,omitempty"`
  1192  	MQTT                  MQTTOptsVarz           `json:"mqtt,omitempty"`
  1193  	Websocket             WebsocketOptsVarz      `json:"websocket,omitempty"`
  1194  	JetStream             JetStreamVarz          `json:"jetstream,omitempty"`
  1195  	TLSTimeout            float64                `json:"tls_timeout"`
  1196  	WriteDeadline         time.Duration          `json:"write_deadline"`
  1197  	Start                 time.Time              `json:"start"`
  1198  	Now                   time.Time              `json:"now"`
  1199  	Uptime                string                 `json:"uptime"`
  1200  	Mem                   int64                  `json:"mem"`
  1201  	Cores                 int                    `json:"cores"`
  1202  	MaxProcs              int                    `json:"gomaxprocs"`
  1203  	CPU                   float64                `json:"cpu"`
  1204  	Connections           int                    `json:"connections"`
  1205  	TotalConnections      uint64                 `json:"total_connections"`
  1206  	Routes                int                    `json:"routes"`
  1207  	Remotes               int                    `json:"remotes"`
  1208  	Leafs                 int                    `json:"leafnodes"`
  1209  	InMsgs                int64                  `json:"in_msgs"`
  1210  	OutMsgs               int64                  `json:"out_msgs"`
  1211  	InBytes               int64                  `json:"in_bytes"`
  1212  	OutBytes              int64                  `json:"out_bytes"`
  1213  	SlowConsumers         int64                  `json:"slow_consumers"`
  1214  	Subscriptions         uint32                 `json:"subscriptions"`
  1215  	HTTPReqStats          map[string]uint64      `json:"http_req_stats"`
  1216  	ConfigLoadTime        time.Time              `json:"config_load_time"`
  1217  	Tags                  jwt.TagList            `json:"tags,omitempty"`
  1218  	TrustedOperatorsJwt   []string               `json:"trusted_operators_jwt,omitempty"`
  1219  	TrustedOperatorsClaim []*jwt.OperatorClaims  `json:"trusted_operators_claim,omitempty"`
  1220  	SystemAccount         string                 `json:"system_account,omitempty"`
  1221  	PinnedAccountFail     uint64                 `json:"pinned_account_fails,omitempty"`
  1222  	OCSPResponseCache     *OCSPResponseCacheVarz `json:"ocsp_peer_cache,omitempty"`
  1223  	SlowConsumersStats    *SlowConsumersStats    `json:"slow_consumer_stats"`
  1224  }
  1225  
  1226  // JetStreamVarz contains basic runtime information about jetstream
  1227  type JetStreamVarz struct {
  1228  	Config *JetStreamConfig `json:"config,omitempty"`
  1229  	Stats  *JetStreamStats  `json:"stats,omitempty"`
  1230  	Meta   *MetaClusterInfo `json:"meta,omitempty"`
  1231  }
  1232  
  1233  // ClusterOptsVarz contains monitoring cluster information
  1234  type ClusterOptsVarz struct {
  1235  	Name        string   `json:"name,omitempty"`
  1236  	Host        string   `json:"addr,omitempty"`
  1237  	Port        int      `json:"cluster_port,omitempty"`
  1238  	AuthTimeout float64  `json:"auth_timeout,omitempty"`
  1239  	URLs        []string `json:"urls,omitempty"`
  1240  	TLSTimeout  float64  `json:"tls_timeout,omitempty"`
  1241  	TLSRequired bool     `json:"tls_required,omitempty"`
  1242  	TLSVerify   bool     `json:"tls_verify,omitempty"`
  1243  	PoolSize    int      `json:"pool_size,omitempty"`
  1244  }
  1245  
  1246  // GatewayOptsVarz contains monitoring gateway information
  1247  type GatewayOptsVarz struct {
  1248  	Name           string                  `json:"name,omitempty"`
  1249  	Host           string                  `json:"host,omitempty"`
  1250  	Port           int                     `json:"port,omitempty"`
  1251  	AuthTimeout    float64                 `json:"auth_timeout,omitempty"`
  1252  	TLSTimeout     float64                 `json:"tls_timeout,omitempty"`
  1253  	TLSRequired    bool                    `json:"tls_required,omitempty"`
  1254  	TLSVerify      bool                    `json:"tls_verify,omitempty"`
  1255  	Advertise      string                  `json:"advertise,omitempty"`
  1256  	ConnectRetries int                     `json:"connect_retries,omitempty"`
  1257  	Gateways       []RemoteGatewayOptsVarz `json:"gateways,omitempty"`
  1258  	RejectUnknown  bool                    `json:"reject_unknown,omitempty"` // config got renamed to reject_unknown_cluster
  1259  }
  1260  
  1261  // RemoteGatewayOptsVarz contains monitoring remote gateway information
  1262  type RemoteGatewayOptsVarz struct {
  1263  	Name       string   `json:"name"`
  1264  	TLSTimeout float64  `json:"tls_timeout,omitempty"`
  1265  	URLs       []string `json:"urls,omitempty"`
  1266  }
  1267  
  1268  // LeafNodeOptsVarz contains monitoring leaf node information
  1269  type LeafNodeOptsVarz struct {
  1270  	Host              string               `json:"host,omitempty"`
  1271  	Port              int                  `json:"port,omitempty"`
  1272  	AuthTimeout       float64              `json:"auth_timeout,omitempty"`
  1273  	TLSTimeout        float64              `json:"tls_timeout,omitempty"`
  1274  	TLSRequired       bool                 `json:"tls_required,omitempty"`
  1275  	TLSVerify         bool                 `json:"tls_verify,omitempty"`
  1276  	Remotes           []RemoteLeafOptsVarz `json:"remotes,omitempty"`
  1277  	TLSOCSPPeerVerify bool                 `json:"tls_ocsp_peer_verify,omitempty"`
  1278  }
  1279  
  1280  // DenyRules Contains lists of subjects not allowed to be imported/exported
  1281  type DenyRules struct {
  1282  	Exports []string `json:"exports,omitempty"`
  1283  	Imports []string `json:"imports,omitempty"`
  1284  }
  1285  
  1286  // RemoteLeafOptsVarz contains monitoring remote leaf node information
  1287  type RemoteLeafOptsVarz struct {
  1288  	LocalAccount      string     `json:"local_account,omitempty"`
  1289  	TLSTimeout        float64    `json:"tls_timeout,omitempty"`
  1290  	URLs              []string   `json:"urls,omitempty"`
  1291  	Deny              *DenyRules `json:"deny,omitempty"`
  1292  	TLSOCSPPeerVerify bool       `json:"tls_ocsp_peer_verify,omitempty"`
  1293  }
  1294  
  1295  // MQTTOptsVarz contains monitoring MQTT information
  1296  type MQTTOptsVarz struct {
  1297  	Host              string        `json:"host,omitempty"`
  1298  	Port              int           `json:"port,omitempty"`
  1299  	NoAuthUser        string        `json:"no_auth_user,omitempty"`
  1300  	AuthTimeout       float64       `json:"auth_timeout,omitempty"`
  1301  	TLSMap            bool          `json:"tls_map,omitempty"`
  1302  	TLSTimeout        float64       `json:"tls_timeout,omitempty"`
  1303  	TLSPinnedCerts    []string      `json:"tls_pinned_certs,omitempty"`
  1304  	JsDomain          string        `json:"js_domain,omitempty"`
  1305  	AckWait           time.Duration `json:"ack_wait,omitempty"`
  1306  	MaxAckPending     uint16        `json:"max_ack_pending,omitempty"`
  1307  	TLSOCSPPeerVerify bool          `json:"tls_ocsp_peer_verify,omitempty"`
  1308  }
  1309  
  1310  // WebsocketOptsVarz contains monitoring websocket information
  1311  type WebsocketOptsVarz struct {
  1312  	Host              string        `json:"host,omitempty"`
  1313  	Port              int           `json:"port,omitempty"`
  1314  	Advertise         string        `json:"advertise,omitempty"`
  1315  	NoAuthUser        string        `json:"no_auth_user,omitempty"`
  1316  	JWTCookie         string        `json:"jwt_cookie,omitempty"`
  1317  	HandshakeTimeout  time.Duration `json:"handshake_timeout,omitempty"`
  1318  	AuthTimeout       float64       `json:"auth_timeout,omitempty"`
  1319  	NoTLS             bool          `json:"no_tls,omitempty"`
  1320  	TLSMap            bool          `json:"tls_map,omitempty"`
  1321  	TLSPinnedCerts    []string      `json:"tls_pinned_certs,omitempty"`
  1322  	SameOrigin        bool          `json:"same_origin,omitempty"`
  1323  	AllowedOrigins    []string      `json:"allowed_origins,omitempty"`
  1324  	Compression       bool          `json:"compression,omitempty"`
  1325  	TLSOCSPPeerVerify bool          `json:"tls_ocsp_peer_verify,omitempty"`
  1326  }
  1327  
  1328  // OCSPResponseCacheVarz contains OCSP response cache information
  1329  type OCSPResponseCacheVarz struct {
  1330  	Type      string `json:"cache_type,omitempty"`
  1331  	Hits      int64  `json:"cache_hits,omitempty"`
  1332  	Misses    int64  `json:"cache_misses,omitempty"`
  1333  	Responses int64  `json:"cached_responses,omitempty"`
  1334  	Revokes   int64  `json:"cached_revoked_responses,omitempty"`
  1335  	Goods     int64  `json:"cached_good_responses,omitempty"`
  1336  	Unknowns  int64  `json:"cached_unknown_responses,omitempty"`
  1337  }
  1338  
  1339  // VarzOptions are the options passed to Varz().
  1340  // Currently, there are no options defined.
  1341  type VarzOptions struct{}
  1342  
  1343  // SlowConsumersStats contains information about the slow consumers from different type of connections.
  1344  type SlowConsumersStats struct {
  1345  	Clients  uint64 `json:"clients"`
  1346  	Routes   uint64 `json:"routes"`
  1347  	Gateways uint64 `json:"gateways"`
  1348  	Leafs    uint64 `json:"leafs"`
  1349  }
  1350  
  1351  func myUptime(d time.Duration) string {
  1352  	// Just use total seconds for uptime, and display days / years
  1353  	tsecs := d / time.Second
  1354  	tmins := tsecs / 60
  1355  	thrs := tmins / 60
  1356  	tdays := thrs / 24
  1357  	tyrs := tdays / 365
  1358  
  1359  	if tyrs > 0 {
  1360  		return fmt.Sprintf("%dy%dd%dh%dm%ds", tyrs, tdays%365, thrs%24, tmins%60, tsecs%60)
  1361  	}
  1362  	if tdays > 0 {
  1363  		return fmt.Sprintf("%dd%dh%dm%ds", tdays, thrs%24, tmins%60, tsecs%60)
  1364  	}
  1365  	if thrs > 0 {
  1366  		return fmt.Sprintf("%dh%dm%ds", thrs, tmins%60, tsecs%60)
  1367  	}
  1368  	if tmins > 0 {
  1369  		return fmt.Sprintf("%dm%ds", tmins, tsecs%60)
  1370  	}
  1371  	return fmt.Sprintf("%ds", tsecs)
  1372  }
  1373  
  1374  // HandleRoot will show basic info and links to others handlers.
  1375  func (s *Server) HandleRoot(w http.ResponseWriter, r *http.Request) {
  1376  	// This feels dumb to me, but is required: https://code.google.com/p/go/issues/detail?id=4799
  1377  	if r.URL.Path != s.httpBasePath {
  1378  		http.NotFound(w, r)
  1379  		return
  1380  	}
  1381  	s.mu.Lock()
  1382  	s.httpReqStats[RootPath]++
  1383  	s.mu.Unlock()
  1384  
  1385  	// Calculate source url. If git set go directly to that tag, otherwise just main.
  1386  	var srcUrl string
  1387  	if gitCommit == _EMPTY_ {
  1388  		srcUrl = "https://github.com/nats-io/nats-server"
  1389  	} else {
  1390  		srcUrl = fmt.Sprintf("https://github.com/nats-io/nats-server/tree/%s", gitCommit)
  1391  	}
  1392  
  1393  	fmt.Fprintf(w, `<html lang="en">
  1394  	<head>
  1395  	<link rel="shortcut icon" href="https://nats.io/favicon.ico">
  1396  	<style type="text/css">
  1397  		body { font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif; font-size: 18; font-weight: light-bold; margin-left: 32px }
  1398  		a { display:block; margin-left: 7px; padding-bottom: 6px; color: rgb(72 72 92); text-decoration: none }
  1399  		a:hover { font-weight: 600; color: rgb(59 50 202) }
  1400  		a.help { display:inline; font-weight: 600; color: rgb(59 50 202); font-size: 20}
  1401  		a.last { padding-bottom: 16px }
  1402  		a.version { font-size: 14; font-weight: 400; width: 312px; text-align: right; margin-top: -2rem }
  1403  		a.version:hover { color: rgb(22 22 32) }
  1404  
  1405  	</style>
  1406  	</head>
  1407  	<body>
  1408     <svg xmlns="http://www.w3.org/2000/svg" role="img" width="325" height="110" viewBox="-4.14 -3.89 436.28 119.03"><style>.st1{fill:#fff}.st2{fill:#34a574}</style><path fill="#27aae1" d="M4.3 84.6h42.2L70.7 107V84.6H103v-80H4.3v80zm15.9-61.3h18.5l35.6 33.2V23.3h11.8v42.9H68.2L32 32.4v33.8H20.2V23.3z"/><path d="M32 32.4l36.2 33.8h17.9V23.3H74.3v33.2L38.7 23.3H20.2v42.9H32z" class="st1"/><path d="M159.8 30.7L147 49h25.6z" class="st2"/><path d="M111.3 84.6H210v-80h-98.7v80zm41-61.5H168l30.8 43.2h-14.1l-5.8-8.3h-38.1l-5.8 8.3h-13.5l30.8-43.2z" class="st2"/><path d="M140.8 57.9h38.1l5.8 8.3h14.1L168 23.1h-15.7l-30.8 43.2H135l5.8-8.4zm19-27.2L172.6 49H147l12.8-18.3z" class="st1"/><path fill="#375c93" d="M218.3 84.6H317v-80h-98.7v80zm15.5-61.3h66.7V33h-27.2v33.2h-12.2V33h-27.3v-9.7z"/><path d="M261.1 66.2h12.2V33h27.2v-9.7h-66.7V33h27.3z" class="st1"/><path fill="#8dc63f" d="M325.3 4.6v80H424v-80h-98.7zm76.5 56.7c-3.2 3.2-10.2 5.7-26.8 5.7-12.3 0-24.1-1.9-30.7-4.7v-10c6.3 2.8 20.1 5.5 30.7 5.5 9.3 0 15.8-.3 17.5-2.1.6-.6.7-1.3.7-2 0-.8-.2-1.3-.7-1.8-1-1-2.6-1.7-17.4-2.1-15.7-.4-23.4-2-27-5.6-1.7-1.7-2.6-4.4-2.6-7.5 0-3.3.6-6.2 3.3-8.9 3.6-3.6 10.7-5.3 25.1-5.3 10.8 0 21.6 1.7 27.3 4v10.1c-6.5-2.8-17.8-4.8-27.2-4.8-10.4 0-14.8.6-16.2 2-.5.5-.8 1.1-.8 1.9 0 .9.2 1.5.7 2 1.3 1.3 6.1 1.7 17.3 1.9 16.4.4 23.5 1.8 27 5.2 1.8 1.8 2.8 4.7 2.8 7.7.1 3.2-.6 6.4-3 8.8z"/><path d="M375.2 39.5c-11.2-.2-16-.6-17.3-1.9-.5-.5-.7-1.1-.7-2 0-.8.3-1.4.8-1.9 1.3-1.3 5.8-2 16.2-2 9.4 0 20.7 2 27.2 4.8v-10c-5.7-2.3-16.6-4-27.3-4-14.5 0-21.6 1.8-25.1 5.3-2.7 2.7-3.3 5.6-3.3 8.9 0 3.1 1 5.8 2.6 7.5 3.6 3.6 11.3 5.2 27 5.6 14.8.4 16.4 1.1 17.4 2.1.5.5.7 1 .7 1.8 0 .7-.1 1.3-.7 2-1.8 1.8-8.3 2.1-17.5 2.1-10.6 0-24.3-2.6-30.7-5.5v10.1c6.6 2.8 18.4 4.7 30.7 4.7 16.6 0 23.6-2.5 26.8-5.7 2.4-2.4 3.1-5.6 3.1-8.9 0-3.1-1-5.9-2.8-7.7-3.6-3.5-10.7-4.9-27.1-5.3z" class="st1"/></svg>
  1409  
  1410  	<a href=%s class='version'>v%s</a>
  1411  
  1412  	</div>
  1413  	<br/>
  1414  	<a href=.%s>General</a>
  1415  	<a href=.%s>JetStream</a>
  1416  	<a href=.%s>Connections</a>
  1417  	<a href=.%s>Accounts</a>
  1418  	<a href=.%s>Account Stats</a>
  1419  	<a href=.%s>Subscriptions</a>
  1420  	<a href=.%s>Routes</a>
  1421  	<a href=.%s>LeafNodes</a>
  1422  	<a href=.%s>Gateways</a>
  1423  	<a href=.%s class=last>Health Probe</a>
  1424      <a href=https://docs.nats.io/running-a-nats-service/nats_admin/monitoring class="help">Help</a>
  1425    </body>
  1426  </html>`,
  1427  		srcUrl,
  1428  		VERSION,
  1429  		s.basePath(VarzPath),
  1430  		s.basePath(JszPath),
  1431  		s.basePath(ConnzPath),
  1432  		s.basePath(AccountzPath),
  1433  		s.basePath(AccountStatzPath),
  1434  		s.basePath(SubszPath),
  1435  		s.basePath(RoutezPath),
  1436  		s.basePath(LeafzPath),
  1437  		s.basePath(GatewayzPath),
  1438  		s.basePath(HealthzPath),
  1439  	)
  1440  }
  1441  
  1442  func (s *Server) updateJszVarz(js *jetStream, v *JetStreamVarz, doConfig bool) {
  1443  	if doConfig {
  1444  		js.mu.RLock()
  1445  		// We want to snapshot the config since it will then be available outside
  1446  		// of the js lock. So make a copy first, then point to this copy.
  1447  		cfg := js.config
  1448  		v.Config = &cfg
  1449  		js.mu.RUnlock()
  1450  	}
  1451  	v.Stats = js.usageStats()
  1452  	if mg := js.getMetaGroup(); mg != nil {
  1453  		if ci := s.raftNodeToClusterInfo(mg); ci != nil {
  1454  			v.Meta = &MetaClusterInfo{Name: ci.Name, Leader: ci.Leader, Peer: getHash(ci.Leader), Size: mg.ClusterSize()}
  1455  			if ci.Leader == s.info.Name {
  1456  				v.Meta.Replicas = ci.Replicas
  1457  			}
  1458  		}
  1459  	}
  1460  }
  1461  
  1462  // Varz returns a Varz struct containing the server information.
  1463  func (s *Server) Varz(varzOpts *VarzOptions) (*Varz, error) {
  1464  	var rss, vss int64
  1465  	var pcpu float64
  1466  
  1467  	// We want to do that outside of the lock.
  1468  	pse.ProcUsage(&pcpu, &rss, &vss)
  1469  
  1470  	s.mu.RLock()
  1471  	// We need to create a new instance of Varz (with no reference
  1472  	// whatsoever to anything stored in the server) since the user
  1473  	// has access to the returned value.
  1474  	v := s.createVarz(pcpu, rss)
  1475  	s.mu.RUnlock()
  1476  
  1477  	if js := s.getJetStream(); js != nil {
  1478  		s.updateJszVarz(js, &v.JetStream, true)
  1479  	}
  1480  
  1481  	return v, nil
  1482  }
  1483  
  1484  // Returns a Varz instance.
  1485  // Server lock is held on entry.
  1486  func (s *Server) createVarz(pcpu float64, rss int64) *Varz {
  1487  	info := s.info
  1488  	opts := s.getOpts()
  1489  	c := &opts.Cluster
  1490  	gw := &opts.Gateway
  1491  	ln := &opts.LeafNode
  1492  	mqtt := &opts.MQTT
  1493  	ws := &opts.Websocket
  1494  	clustTlsReq := c.TLSConfig != nil
  1495  	gatewayTlsReq := gw.TLSConfig != nil
  1496  	leafTlsReq := ln.TLSConfig != nil
  1497  	leafTlsVerify := leafTlsReq && ln.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert
  1498  	leafTlsOCSPPeerVerify := s.ocspPeerVerify && leafTlsReq && ln.tlsConfigOpts.OCSPPeerConfig != nil && ln.tlsConfigOpts.OCSPPeerConfig.Verify
  1499  	mqttTlsOCSPPeerVerify := s.ocspPeerVerify && mqtt.TLSConfig != nil && mqtt.tlsConfigOpts.OCSPPeerConfig != nil && mqtt.tlsConfigOpts.OCSPPeerConfig.Verify
  1500  	wsTlsOCSPPeerVerify := s.ocspPeerVerify && ws.TLSConfig != nil && ws.tlsConfigOpts.OCSPPeerConfig != nil && ws.tlsConfigOpts.OCSPPeerConfig.Verify
  1501  	varz := &Varz{
  1502  		ID:           info.ID,
  1503  		Version:      info.Version,
  1504  		Proto:        info.Proto,
  1505  		GitCommit:    info.GitCommit,
  1506  		GoVersion:    info.GoVersion,
  1507  		Name:         info.Name,
  1508  		Host:         info.Host,
  1509  		Port:         info.Port,
  1510  		IP:           info.IP,
  1511  		HTTPHost:     opts.HTTPHost,
  1512  		HTTPPort:     opts.HTTPPort,
  1513  		HTTPBasePath: opts.HTTPBasePath,
  1514  		HTTPSPort:    opts.HTTPSPort,
  1515  		Cluster: ClusterOptsVarz{
  1516  			Name:        info.Cluster,
  1517  			Host:        c.Host,
  1518  			Port:        c.Port,
  1519  			AuthTimeout: c.AuthTimeout,
  1520  			TLSTimeout:  c.TLSTimeout,
  1521  			TLSRequired: clustTlsReq,
  1522  			TLSVerify:   clustTlsReq,
  1523  			PoolSize:    opts.Cluster.PoolSize,
  1524  		},
  1525  		Gateway: GatewayOptsVarz{
  1526  			Name:           gw.Name,
  1527  			Host:           gw.Host,
  1528  			Port:           gw.Port,
  1529  			AuthTimeout:    gw.AuthTimeout,
  1530  			TLSTimeout:     gw.TLSTimeout,
  1531  			TLSRequired:    gatewayTlsReq,
  1532  			TLSVerify:      gatewayTlsReq,
  1533  			Advertise:      gw.Advertise,
  1534  			ConnectRetries: gw.ConnectRetries,
  1535  			Gateways:       []RemoteGatewayOptsVarz{},
  1536  			RejectUnknown:  gw.RejectUnknown,
  1537  		},
  1538  		LeafNode: LeafNodeOptsVarz{
  1539  			Host:              ln.Host,
  1540  			Port:              ln.Port,
  1541  			AuthTimeout:       ln.AuthTimeout,
  1542  			TLSTimeout:        ln.TLSTimeout,
  1543  			TLSRequired:       leafTlsReq,
  1544  			TLSVerify:         leafTlsVerify,
  1545  			TLSOCSPPeerVerify: leafTlsOCSPPeerVerify,
  1546  			Remotes:           []RemoteLeafOptsVarz{},
  1547  		},
  1548  		MQTT: MQTTOptsVarz{
  1549  			Host:              mqtt.Host,
  1550  			Port:              mqtt.Port,
  1551  			NoAuthUser:        mqtt.NoAuthUser,
  1552  			AuthTimeout:       mqtt.AuthTimeout,
  1553  			TLSMap:            mqtt.TLSMap,
  1554  			TLSTimeout:        mqtt.TLSTimeout,
  1555  			JsDomain:          mqtt.JsDomain,
  1556  			AckWait:           mqtt.AckWait,
  1557  			MaxAckPending:     mqtt.MaxAckPending,
  1558  			TLSOCSPPeerVerify: mqttTlsOCSPPeerVerify,
  1559  		},
  1560  		Websocket: WebsocketOptsVarz{
  1561  			Host:              ws.Host,
  1562  			Port:              ws.Port,
  1563  			Advertise:         ws.Advertise,
  1564  			NoAuthUser:        ws.NoAuthUser,
  1565  			JWTCookie:         ws.JWTCookie,
  1566  			AuthTimeout:       ws.AuthTimeout,
  1567  			NoTLS:             ws.NoTLS,
  1568  			TLSMap:            ws.TLSMap,
  1569  			SameOrigin:        ws.SameOrigin,
  1570  			AllowedOrigins:    copyStrings(ws.AllowedOrigins),
  1571  			Compression:       ws.Compression,
  1572  			HandshakeTimeout:  ws.HandshakeTimeout,
  1573  			TLSOCSPPeerVerify: wsTlsOCSPPeerVerify,
  1574  		},
  1575  		Start:                 s.start.UTC(),
  1576  		MaxSubs:               opts.MaxSubs,
  1577  		Cores:                 runtime.NumCPU(),
  1578  		MaxProcs:              runtime.GOMAXPROCS(0),
  1579  		Tags:                  opts.Tags,
  1580  		TrustedOperatorsJwt:   opts.operatorJWT,
  1581  		TrustedOperatorsClaim: opts.TrustedOperators,
  1582  	}
  1583  	if len(opts.Routes) > 0 {
  1584  		varz.Cluster.URLs = urlsToStrings(opts.Routes)
  1585  	}
  1586  	if l := len(gw.Gateways); l > 0 {
  1587  		rgwa := make([]RemoteGatewayOptsVarz, l)
  1588  		for i, r := range gw.Gateways {
  1589  			rgwa[i] = RemoteGatewayOptsVarz{
  1590  				Name:       r.Name,
  1591  				TLSTimeout: r.TLSTimeout,
  1592  			}
  1593  		}
  1594  		varz.Gateway.Gateways = rgwa
  1595  	}
  1596  	if l := len(ln.Remotes); l > 0 {
  1597  		rlna := make([]RemoteLeafOptsVarz, l)
  1598  		for i, r := range ln.Remotes {
  1599  			var deny *DenyRules
  1600  			if len(r.DenyImports) > 0 || len(r.DenyExports) > 0 {
  1601  				deny = &DenyRules{
  1602  					Imports: r.DenyImports,
  1603  					Exports: r.DenyExports,
  1604  				}
  1605  			}
  1606  			remoteTlsOCSPPeerVerify := s.ocspPeerVerify && r.tlsConfigOpts != nil && r.tlsConfigOpts.OCSPPeerConfig != nil && r.tlsConfigOpts.OCSPPeerConfig.Verify
  1607  
  1608  			rlna[i] = RemoteLeafOptsVarz{
  1609  				LocalAccount:      r.LocalAccount,
  1610  				URLs:              urlsToStrings(r.URLs),
  1611  				TLSTimeout:        r.TLSTimeout,
  1612  				Deny:              deny,
  1613  				TLSOCSPPeerVerify: remoteTlsOCSPPeerVerify,
  1614  			}
  1615  		}
  1616  		varz.LeafNode.Remotes = rlna
  1617  	}
  1618  
  1619  	// Finish setting it up with fields that can be updated during
  1620  	// configuration reload and runtime.
  1621  	s.updateVarzConfigReloadableFields(varz)
  1622  	s.updateVarzRuntimeFields(varz, true, pcpu, rss)
  1623  	return varz
  1624  }
  1625  
  1626  func urlsToStrings(urls []*url.URL) []string {
  1627  	sURLs := make([]string, len(urls))
  1628  	for i, u := range urls {
  1629  		sURLs[i] = u.Host
  1630  	}
  1631  	return sURLs
  1632  }
  1633  
  1634  // Invoked during configuration reload once options have possibly be changed
  1635  // and config load time has been set. If s.varz has not been initialized yet
  1636  // (because no pooling of /varz has been made), this function does nothing.
  1637  // Server lock is held on entry.
  1638  func (s *Server) updateVarzConfigReloadableFields(v *Varz) {
  1639  	if v == nil {
  1640  		return
  1641  	}
  1642  	opts := s.getOpts()
  1643  	info := &s.info
  1644  	v.AuthRequired = info.AuthRequired
  1645  	v.TLSRequired = info.TLSRequired
  1646  	v.TLSVerify = info.TLSVerify
  1647  	v.MaxConn = opts.MaxConn
  1648  	v.PingInterval = opts.PingInterval
  1649  	v.MaxPingsOut = opts.MaxPingsOut
  1650  	v.AuthTimeout = opts.AuthTimeout
  1651  	v.MaxControlLine = opts.MaxControlLine
  1652  	v.MaxPayload = int(opts.MaxPayload)
  1653  	v.MaxPending = opts.MaxPending
  1654  	v.TLSTimeout = opts.TLSTimeout
  1655  	v.WriteDeadline = opts.WriteDeadline
  1656  	v.ConfigLoadTime = s.configTime.UTC()
  1657  	// Update route URLs if applicable
  1658  	if s.varzUpdateRouteURLs {
  1659  		v.Cluster.URLs = urlsToStrings(opts.Routes)
  1660  		s.varzUpdateRouteURLs = false
  1661  	}
  1662  	if s.sys != nil && s.sys.account != nil {
  1663  		v.SystemAccount = s.sys.account.GetName()
  1664  	}
  1665  	v.MQTT.TLSPinnedCerts = getPinnedCertsAsSlice(opts.MQTT.TLSPinnedCerts)
  1666  	v.Websocket.TLSPinnedCerts = getPinnedCertsAsSlice(opts.Websocket.TLSPinnedCerts)
  1667  
  1668  	v.TLSOCSPPeerVerify = s.ocspPeerVerify && v.TLSRequired && s.opts.tlsConfigOpts != nil && s.opts.tlsConfigOpts.OCSPPeerConfig != nil && s.opts.tlsConfigOpts.OCSPPeerConfig.Verify
  1669  }
  1670  
  1671  func getPinnedCertsAsSlice(certs PinnedCertSet) []string {
  1672  	if len(certs) == 0 {
  1673  		return nil
  1674  	}
  1675  	res := make([]string, 0, len(certs))
  1676  	for cn := range certs {
  1677  		res = append(res, cn)
  1678  	}
  1679  	return res
  1680  }
  1681  
  1682  // Updates the runtime Varz fields, that is, fields that change during
  1683  // runtime and that should be updated any time Varz() or polling of /varz
  1684  // is done.
  1685  // Server lock is held on entry.
  1686  func (s *Server) updateVarzRuntimeFields(v *Varz, forceUpdate bool, pcpu float64, rss int64) {
  1687  	v.Now = time.Now().UTC()
  1688  	v.Uptime = myUptime(time.Since(s.start))
  1689  	v.Mem = rss
  1690  	v.CPU = pcpu
  1691  	if l := len(s.info.ClientConnectURLs); l > 0 {
  1692  		v.ClientConnectURLs = append([]string(nil), s.info.ClientConnectURLs...)
  1693  	}
  1694  	if l := len(s.info.WSConnectURLs); l > 0 {
  1695  		v.WSConnectURLs = append([]string(nil), s.info.WSConnectURLs...)
  1696  	}
  1697  	v.Connections = len(s.clients)
  1698  	v.TotalConnections = s.totalClients
  1699  	v.Routes = s.numRoutes()
  1700  	v.Remotes = s.numRemotes()
  1701  	v.Leafs = len(s.leafs)
  1702  	v.InMsgs = atomic.LoadInt64(&s.inMsgs)
  1703  	v.InBytes = atomic.LoadInt64(&s.inBytes)
  1704  	v.OutMsgs = atomic.LoadInt64(&s.outMsgs)
  1705  	v.OutBytes = atomic.LoadInt64(&s.outBytes)
  1706  	v.SlowConsumers = atomic.LoadInt64(&s.slowConsumers)
  1707  	v.SlowConsumersStats = &SlowConsumersStats{
  1708  		Clients:  s.NumSlowConsumersClients(),
  1709  		Routes:   s.NumSlowConsumersRoutes(),
  1710  		Gateways: s.NumSlowConsumersGateways(),
  1711  		Leafs:    s.NumSlowConsumersLeafs(),
  1712  	}
  1713  	v.PinnedAccountFail = atomic.LoadUint64(&s.pinnedAccFail)
  1714  
  1715  	// Make sure to reset in case we are re-using.
  1716  	v.Subscriptions = 0
  1717  	s.accounts.Range(func(k, val interface{}) bool {
  1718  		acc := val.(*Account)
  1719  		v.Subscriptions += acc.sl.Count()
  1720  		return true
  1721  	})
  1722  
  1723  	v.HTTPReqStats = make(map[string]uint64, len(s.httpReqStats))
  1724  	for key, val := range s.httpReqStats {
  1725  		v.HTTPReqStats[key] = val
  1726  	}
  1727  
  1728  	// Update Gateway remote urls if applicable
  1729  	gw := s.gateway
  1730  	gw.RLock()
  1731  	if gw.enabled {
  1732  		for i := 0; i < len(v.Gateway.Gateways); i++ {
  1733  			g := &v.Gateway.Gateways[i]
  1734  			rgw := gw.remotes[g.Name]
  1735  			if rgw != nil {
  1736  				rgw.RLock()
  1737  				// forceUpdate is needed if user calls Varz() programmatically,
  1738  				// since we need to create a new instance every time and the
  1739  				// gateway's varzUpdateURLs may have been set to false after
  1740  				// a web /varz inspection.
  1741  				if forceUpdate || rgw.varzUpdateURLs {
  1742  					// Make reuse of backend array
  1743  					g.URLs = g.URLs[:0]
  1744  					// rgw.urls is a map[string]*url.URL where the key is
  1745  					// already in the right format (host:port, without any
  1746  					// user info present).
  1747  					for u := range rgw.urls {
  1748  						g.URLs = append(g.URLs, u)
  1749  					}
  1750  					rgw.varzUpdateURLs = false
  1751  				}
  1752  				rgw.RUnlock()
  1753  			} else if g.Name == gw.name && len(gw.ownCfgURLs) > 0 {
  1754  				// This is a remote that correspond to this very same server.
  1755  				// We report the URLs that were configured (if any).
  1756  				// Since we don't support changes to the gateway configuration
  1757  				// at this time, we could do this only if g.URLs has not been already
  1758  				// set, but let's do it regardless in case we add support for
  1759  				// gateway config reload.
  1760  				g.URLs = g.URLs[:0]
  1761  				g.URLs = append(g.URLs, gw.ownCfgURLs...)
  1762  			}
  1763  		}
  1764  	}
  1765  	gw.RUnlock()
  1766  
  1767  	if s.ocsprc != nil && s.ocsprc.Type() != "none" {
  1768  		stats := s.ocsprc.Stats()
  1769  		if stats != nil {
  1770  			v.OCSPResponseCache = &OCSPResponseCacheVarz{
  1771  				s.ocsprc.Type(),
  1772  				stats.Hits,
  1773  				stats.Misses,
  1774  				stats.Responses,
  1775  				stats.Revokes,
  1776  				stats.Goods,
  1777  				stats.Unknowns,
  1778  			}
  1779  		}
  1780  	}
  1781  }
  1782  
  1783  // HandleVarz will process HTTP requests for server information.
  1784  func (s *Server) HandleVarz(w http.ResponseWriter, r *http.Request) {
  1785  	var rss, vss int64
  1786  	var pcpu float64
  1787  
  1788  	// We want to do that outside of the lock.
  1789  	pse.ProcUsage(&pcpu, &rss, &vss)
  1790  
  1791  	// In response to http requests, we want to minimize mem copies
  1792  	// so we use an object stored in the server. Creating/collecting
  1793  	// server metrics is done under server lock, but we don't want
  1794  	// to marshal under that lock. Still, we need to prevent concurrent
  1795  	// http requests to /varz to update s.varz while marshal is
  1796  	// happening, so we need a new lock that serialize those http
  1797  	// requests and include marshaling.
  1798  	s.varzMu.Lock()
  1799  
  1800  	// Use server lock to create/update the server's varz object.
  1801  	s.mu.Lock()
  1802  	var created bool
  1803  	s.httpReqStats[VarzPath]++
  1804  	if s.varz == nil {
  1805  		s.varz = s.createVarz(pcpu, rss)
  1806  		created = true
  1807  	} else {
  1808  		s.updateVarzRuntimeFields(s.varz, false, pcpu, rss)
  1809  	}
  1810  	s.mu.Unlock()
  1811  	// Since locking is jetStream -> Server, need to update jetstream
  1812  	// varz outside of server lock.
  1813  
  1814  	if js := s.getJetStream(); js != nil {
  1815  		var v JetStreamVarz
  1816  		// Work on stack variable
  1817  		s.updateJszVarz(js, &v, created)
  1818  		// Now update server's varz
  1819  		s.mu.RLock()
  1820  		sv := &s.varz.JetStream
  1821  		if created {
  1822  			sv.Config = v.Config
  1823  		}
  1824  		sv.Stats = v.Stats
  1825  		sv.Meta = v.Meta
  1826  		s.mu.RUnlock()
  1827  	}
  1828  
  1829  	// Do the marshaling outside of server lock, but under varzMu lock.
  1830  	b, err := json.MarshalIndent(s.varz, "", "  ")
  1831  	s.varzMu.Unlock()
  1832  
  1833  	if err != nil {
  1834  		s.Errorf("Error marshaling response to /varz request: %v", err)
  1835  	}
  1836  
  1837  	// Handle response
  1838  	ResponseHandler(w, r, b)
  1839  }
  1840  
  1841  // GatewayzOptions are the options passed to Gatewayz()
  1842  type GatewayzOptions struct {
  1843  	// Name will output only remote gateways with this name
  1844  	Name string `json:"name"`
  1845  
  1846  	// Accounts indicates if accounts with its interest should be included in the results.
  1847  	Accounts bool `json:"accounts"`
  1848  
  1849  	// AccountName will limit the list of accounts to that account name (makes Accounts implicit)
  1850  	AccountName string `json:"account_name"`
  1851  }
  1852  
  1853  // Gatewayz represents detailed information on Gateways
  1854  type Gatewayz struct {
  1855  	ID               string                       `json:"server_id"`
  1856  	Now              time.Time                    `json:"now"`
  1857  	Name             string                       `json:"name,omitempty"`
  1858  	Host             string                       `json:"host,omitempty"`
  1859  	Port             int                          `json:"port,omitempty"`
  1860  	OutboundGateways map[string]*RemoteGatewayz   `json:"outbound_gateways"`
  1861  	InboundGateways  map[string][]*RemoteGatewayz `json:"inbound_gateways"`
  1862  }
  1863  
  1864  // RemoteGatewayz represents information about an outbound connection to a gateway
  1865  type RemoteGatewayz struct {
  1866  	IsConfigured bool               `json:"configured"`
  1867  	Connection   *ConnInfo          `json:"connection,omitempty"`
  1868  	Accounts     []*AccountGatewayz `json:"accounts,omitempty"`
  1869  }
  1870  
  1871  // AccountGatewayz represents interest mode for this account
  1872  type AccountGatewayz struct {
  1873  	Name                  string `json:"name"`
  1874  	InterestMode          string `json:"interest_mode"`
  1875  	NoInterestCount       int    `json:"no_interest_count,omitempty"`
  1876  	InterestOnlyThreshold int    `json:"interest_only_threshold,omitempty"`
  1877  	TotalSubscriptions    int    `json:"num_subs,omitempty"`
  1878  	NumQueueSubscriptions int    `json:"num_queue_subs,omitempty"`
  1879  }
  1880  
  1881  // Gatewayz returns a Gatewayz struct containing information about gateways.
  1882  func (s *Server) Gatewayz(opts *GatewayzOptions) (*Gatewayz, error) {
  1883  	srvID := s.ID()
  1884  	now := time.Now().UTC()
  1885  	gw := s.gateway
  1886  	gw.RLock()
  1887  	if !gw.enabled || gw.info == nil {
  1888  		gw.RUnlock()
  1889  		gwz := &Gatewayz{
  1890  			ID:               srvID,
  1891  			Now:              now,
  1892  			OutboundGateways: map[string]*RemoteGatewayz{},
  1893  			InboundGateways:  map[string][]*RemoteGatewayz{},
  1894  		}
  1895  		return gwz, nil
  1896  	}
  1897  	// Here gateways are enabled, so fill up more.
  1898  	gwz := &Gatewayz{
  1899  		ID:   srvID,
  1900  		Now:  now,
  1901  		Name: gw.name,
  1902  		Host: gw.info.Host,
  1903  		Port: gw.info.Port,
  1904  	}
  1905  	gw.RUnlock()
  1906  
  1907  	gwz.OutboundGateways = s.createOutboundsRemoteGatewayz(opts, now)
  1908  	gwz.InboundGateways = s.createInboundsRemoteGatewayz(opts, now)
  1909  
  1910  	return gwz, nil
  1911  }
  1912  
  1913  // Based on give options struct, returns if there is a filtered
  1914  // Gateway Name and if we should do report Accounts.
  1915  // Note that if Accounts is false but AccountName is not empty,
  1916  // then Accounts is implicitly set to true.
  1917  func getMonitorGWOptions(opts *GatewayzOptions) (string, bool) {
  1918  	var name string
  1919  	var accs bool
  1920  	if opts != nil {
  1921  		if opts.Name != _EMPTY_ {
  1922  			name = opts.Name
  1923  		}
  1924  		accs = opts.Accounts
  1925  		if !accs && opts.AccountName != _EMPTY_ {
  1926  			accs = true
  1927  		}
  1928  	}
  1929  	return name, accs
  1930  }
  1931  
  1932  // Returns a map of gateways outbound connections.
  1933  // Based on options, will include a single or all gateways,
  1934  // with no/single/or all accounts interest information.
  1935  func (s *Server) createOutboundsRemoteGatewayz(opts *GatewayzOptions, now time.Time) map[string]*RemoteGatewayz {
  1936  	targetGWName, doAccs := getMonitorGWOptions(opts)
  1937  
  1938  	if targetGWName != _EMPTY_ {
  1939  		c := s.getOutboundGatewayConnection(targetGWName)
  1940  		if c == nil {
  1941  			return nil
  1942  		}
  1943  		outbounds := make(map[string]*RemoteGatewayz, 1)
  1944  		_, rgw := createOutboundRemoteGatewayz(c, opts, now, doAccs)
  1945  		outbounds[targetGWName] = rgw
  1946  		return outbounds
  1947  	}
  1948  
  1949  	var connsa [16]*client
  1950  	var conns = connsa[:0]
  1951  
  1952  	s.getOutboundGatewayConnections(&conns)
  1953  
  1954  	outbounds := make(map[string]*RemoteGatewayz, len(conns))
  1955  	for _, c := range conns {
  1956  		name, rgw := createOutboundRemoteGatewayz(c, opts, now, doAccs)
  1957  		if rgw != nil {
  1958  			outbounds[name] = rgw
  1959  		}
  1960  	}
  1961  	return outbounds
  1962  }
  1963  
  1964  // Returns a RemoteGatewayz for a given outbound gw connection
  1965  func createOutboundRemoteGatewayz(c *client, opts *GatewayzOptions, now time.Time, doAccs bool) (string, *RemoteGatewayz) {
  1966  	var name string
  1967  	var rgw *RemoteGatewayz
  1968  
  1969  	c.mu.Lock()
  1970  	if c.gw != nil {
  1971  		rgw = &RemoteGatewayz{}
  1972  		if doAccs {
  1973  			rgw.Accounts = createOutboundAccountsGatewayz(opts, c.gw)
  1974  		}
  1975  		if c.gw.cfg != nil {
  1976  			rgw.IsConfigured = !c.gw.cfg.isImplicit()
  1977  		}
  1978  		rgw.Connection = &ConnInfo{}
  1979  		rgw.Connection.fill(c, c.nc, now, false)
  1980  		name = c.gw.name
  1981  	}
  1982  	c.mu.Unlock()
  1983  
  1984  	return name, rgw
  1985  }
  1986  
  1987  // Returns the list of accounts for this outbound gateway connection.
  1988  // Based on the options, it will be a single or all accounts for
  1989  // this outbound.
  1990  func createOutboundAccountsGatewayz(opts *GatewayzOptions, gw *gateway) []*AccountGatewayz {
  1991  	if gw.outsim == nil {
  1992  		return nil
  1993  	}
  1994  
  1995  	var accName string
  1996  	if opts != nil {
  1997  		accName = opts.AccountName
  1998  	}
  1999  	if accName != _EMPTY_ {
  2000  		ei, ok := gw.outsim.Load(accName)
  2001  		if !ok {
  2002  			return nil
  2003  		}
  2004  		a := createAccountOutboundGatewayz(accName, ei)
  2005  		return []*AccountGatewayz{a}
  2006  	}
  2007  
  2008  	accs := make([]*AccountGatewayz, 0, 4)
  2009  	gw.outsim.Range(func(k, v interface{}) bool {
  2010  		name := k.(string)
  2011  		a := createAccountOutboundGatewayz(name, v)
  2012  		accs = append(accs, a)
  2013  		return true
  2014  	})
  2015  	return accs
  2016  }
  2017  
  2018  // Returns an AccountGatewayz for this gateway outbound connection
  2019  func createAccountOutboundGatewayz(name string, ei interface{}) *AccountGatewayz {
  2020  	a := &AccountGatewayz{
  2021  		Name:                  name,
  2022  		InterestOnlyThreshold: gatewayMaxRUnsubBeforeSwitch,
  2023  	}
  2024  	if ei != nil {
  2025  		e := ei.(*outsie)
  2026  		e.RLock()
  2027  		a.InterestMode = e.mode.String()
  2028  		a.NoInterestCount = len(e.ni)
  2029  		a.NumQueueSubscriptions = e.qsubs
  2030  		a.TotalSubscriptions = int(e.sl.Count())
  2031  		e.RUnlock()
  2032  	} else {
  2033  		a.InterestMode = Optimistic.String()
  2034  	}
  2035  	return a
  2036  }
  2037  
  2038  // Returns a map of gateways inbound connections.
  2039  // Each entry is an array of RemoteGatewayz since a given server
  2040  // may have more than one inbound from the same remote gateway.
  2041  // Based on options, will include a single or all gateways,
  2042  // with no/single/or all accounts interest information.
  2043  func (s *Server) createInboundsRemoteGatewayz(opts *GatewayzOptions, now time.Time) map[string][]*RemoteGatewayz {
  2044  	targetGWName, doAccs := getMonitorGWOptions(opts)
  2045  
  2046  	var connsa [16]*client
  2047  	var conns = connsa[:0]
  2048  	s.getInboundGatewayConnections(&conns)
  2049  
  2050  	m := make(map[string][]*RemoteGatewayz)
  2051  	for _, c := range conns {
  2052  		c.mu.Lock()
  2053  		if c.gw != nil && (targetGWName == _EMPTY_ || targetGWName == c.gw.name) {
  2054  			igws := m[c.gw.name]
  2055  			if igws == nil {
  2056  				igws = make([]*RemoteGatewayz, 0, 2)
  2057  			}
  2058  			rgw := &RemoteGatewayz{}
  2059  			if doAccs {
  2060  				rgw.Accounts = createInboundAccountsGatewayz(opts, c.gw)
  2061  			}
  2062  			rgw.Connection = &ConnInfo{}
  2063  			rgw.Connection.fill(c, c.nc, now, false)
  2064  			igws = append(igws, rgw)
  2065  			m[c.gw.name] = igws
  2066  		}
  2067  		c.mu.Unlock()
  2068  	}
  2069  	return m
  2070  }
  2071  
  2072  // Returns the list of accounts for this inbound gateway connection.
  2073  // Based on the options, it will be a single or all accounts for
  2074  // this inbound.
  2075  func createInboundAccountsGatewayz(opts *GatewayzOptions, gw *gateway) []*AccountGatewayz {
  2076  	if gw.insim == nil {
  2077  		return nil
  2078  	}
  2079  
  2080  	var accName string
  2081  	if opts != nil {
  2082  		accName = opts.AccountName
  2083  	}
  2084  	if accName != _EMPTY_ {
  2085  		e, ok := gw.insim[accName]
  2086  		if !ok {
  2087  			return nil
  2088  		}
  2089  		a := createInboundAccountGatewayz(accName, e)
  2090  		return []*AccountGatewayz{a}
  2091  	}
  2092  
  2093  	accs := make([]*AccountGatewayz, 0, 4)
  2094  	for name, e := range gw.insim {
  2095  		a := createInboundAccountGatewayz(name, e)
  2096  		accs = append(accs, a)
  2097  	}
  2098  	return accs
  2099  }
  2100  
  2101  // Returns an AccountGatewayz for this gateway inbound connection
  2102  func createInboundAccountGatewayz(name string, e *insie) *AccountGatewayz {
  2103  	a := &AccountGatewayz{
  2104  		Name:                  name,
  2105  		InterestOnlyThreshold: gatewayMaxRUnsubBeforeSwitch,
  2106  	}
  2107  	if e != nil {
  2108  		a.InterestMode = e.mode.String()
  2109  		a.NoInterestCount = len(e.ni)
  2110  	} else {
  2111  		a.InterestMode = Optimistic.String()
  2112  	}
  2113  	return a
  2114  }
  2115  
  2116  // HandleGatewayz process HTTP requests for route information.
  2117  func (s *Server) HandleGatewayz(w http.ResponseWriter, r *http.Request) {
  2118  	s.mu.Lock()
  2119  	s.httpReqStats[GatewayzPath]++
  2120  	s.mu.Unlock()
  2121  
  2122  	accs, err := decodeBool(w, r, "accs")
  2123  	if err != nil {
  2124  		return
  2125  	}
  2126  	gwName := r.URL.Query().Get("gw_name")
  2127  	accName := r.URL.Query().Get("acc_name")
  2128  	if accName != _EMPTY_ {
  2129  		accs = true
  2130  	}
  2131  
  2132  	opts := &GatewayzOptions{
  2133  		Name:        gwName,
  2134  		Accounts:    accs,
  2135  		AccountName: accName,
  2136  	}
  2137  	gw, err := s.Gatewayz(opts)
  2138  	if err != nil {
  2139  		w.WriteHeader(http.StatusBadRequest)
  2140  		w.Write([]byte(err.Error()))
  2141  		return
  2142  	}
  2143  	b, err := json.MarshalIndent(gw, "", "  ")
  2144  	if err != nil {
  2145  		s.Errorf("Error marshaling response to /gatewayz request: %v", err)
  2146  	}
  2147  
  2148  	// Handle response
  2149  	ResponseHandler(w, r, b)
  2150  }
  2151  
  2152  // Leafz represents detailed information on Leafnodes.
  2153  type Leafz struct {
  2154  	ID       string      `json:"server_id"`
  2155  	Now      time.Time   `json:"now"`
  2156  	NumLeafs int         `json:"leafnodes"`
  2157  	Leafs    []*LeafInfo `json:"leafs"`
  2158  }
  2159  
  2160  // LeafzOptions are options passed to Leafz
  2161  type LeafzOptions struct {
  2162  	// Subscriptions indicates that Leafz will return a leafnode's subscriptions
  2163  	Subscriptions bool   `json:"subscriptions"`
  2164  	Account       string `json:"account"`
  2165  }
  2166  
  2167  // LeafInfo has detailed information on each remote leafnode connection.
  2168  type LeafInfo struct {
  2169  	Name        string   `json:"name"`
  2170  	IsSpoke     bool     `json:"is_spoke"`
  2171  	Account     string   `json:"account"`
  2172  	IP          string   `json:"ip"`
  2173  	Port        int      `json:"port"`
  2174  	RTT         string   `json:"rtt,omitempty"`
  2175  	InMsgs      int64    `json:"in_msgs"`
  2176  	OutMsgs     int64    `json:"out_msgs"`
  2177  	InBytes     int64    `json:"in_bytes"`
  2178  	OutBytes    int64    `json:"out_bytes"`
  2179  	NumSubs     uint32   `json:"subscriptions"`
  2180  	Subs        []string `json:"subscriptions_list,omitempty"`
  2181  	Compression string   `json:"compression,omitempty"`
  2182  }
  2183  
  2184  // Leafz returns a Leafz structure containing information about leafnodes.
  2185  func (s *Server) Leafz(opts *LeafzOptions) (*Leafz, error) {
  2186  	// Grab leafnodes
  2187  	var lconns []*client
  2188  	s.mu.Lock()
  2189  	if len(s.leafs) > 0 {
  2190  		lconns = make([]*client, 0, len(s.leafs))
  2191  		for _, ln := range s.leafs {
  2192  			if opts != nil && opts.Account != _EMPTY_ {
  2193  				ln.mu.Lock()
  2194  				ok := ln.acc.Name == opts.Account
  2195  				ln.mu.Unlock()
  2196  				if !ok {
  2197  					continue
  2198  				}
  2199  			}
  2200  			lconns = append(lconns, ln)
  2201  		}
  2202  	}
  2203  	s.mu.Unlock()
  2204  
  2205  	var leafnodes []*LeafInfo
  2206  	if len(lconns) > 0 {
  2207  		leafnodes = make([]*LeafInfo, 0, len(lconns))
  2208  		for _, ln := range lconns {
  2209  			ln.mu.Lock()
  2210  			lni := &LeafInfo{
  2211  				Name:        ln.leaf.remoteServer,
  2212  				IsSpoke:     ln.isSpokeLeafNode(),
  2213  				Account:     ln.acc.Name,
  2214  				IP:          ln.host,
  2215  				Port:        int(ln.port),
  2216  				RTT:         ln.getRTT().String(),
  2217  				InMsgs:      atomic.LoadInt64(&ln.inMsgs),
  2218  				OutMsgs:     ln.outMsgs,
  2219  				InBytes:     atomic.LoadInt64(&ln.inBytes),
  2220  				OutBytes:    ln.outBytes,
  2221  				NumSubs:     uint32(len(ln.subs)),
  2222  				Compression: ln.leaf.compression,
  2223  			}
  2224  			if opts != nil && opts.Subscriptions {
  2225  				lni.Subs = make([]string, 0, len(ln.subs))
  2226  				for _, sub := range ln.subs {
  2227  					lni.Subs = append(lni.Subs, string(sub.subject))
  2228  				}
  2229  			}
  2230  			ln.mu.Unlock()
  2231  			leafnodes = append(leafnodes, lni)
  2232  		}
  2233  	}
  2234  	return &Leafz{
  2235  		ID:       s.ID(),
  2236  		Now:      time.Now().UTC(),
  2237  		NumLeafs: len(leafnodes),
  2238  		Leafs:    leafnodes,
  2239  	}, nil
  2240  }
  2241  
  2242  // HandleLeafz process HTTP requests for leafnode information.
  2243  func (s *Server) HandleLeafz(w http.ResponseWriter, r *http.Request) {
  2244  	s.mu.Lock()
  2245  	s.httpReqStats[LeafzPath]++
  2246  	s.mu.Unlock()
  2247  
  2248  	subs, err := decodeBool(w, r, "subs")
  2249  	if err != nil {
  2250  		return
  2251  	}
  2252  	l, err := s.Leafz(&LeafzOptions{subs, r.URL.Query().Get("acc")})
  2253  	if err != nil {
  2254  		w.WriteHeader(http.StatusBadRequest)
  2255  		w.Write([]byte(err.Error()))
  2256  		return
  2257  	}
  2258  	b, err := json.MarshalIndent(l, "", "  ")
  2259  	if err != nil {
  2260  		s.Errorf("Error marshaling response to /leafz request: %v", err)
  2261  	}
  2262  
  2263  	// Handle response
  2264  	ResponseHandler(w, r, b)
  2265  }
  2266  
  2267  // Leafz represents detailed information on Leafnodes.
  2268  type AccountStatz struct {
  2269  	ID       string         `json:"server_id"`
  2270  	Now      time.Time      `json:"now"`
  2271  	Accounts []*AccountStat `json:"account_statz"`
  2272  }
  2273  
  2274  // LeafzOptions are options passed to Leafz
  2275  type AccountStatzOptions struct {
  2276  	Accounts      []string `json:"accounts"`
  2277  	IncludeUnused bool     `json:"include_unused"`
  2278  }
  2279  
  2280  // Leafz returns a AccountStatz structure containing summary information about accounts.
  2281  func (s *Server) AccountStatz(opts *AccountStatzOptions) (*AccountStatz, error) {
  2282  	stz := &AccountStatz{
  2283  		ID:       s.ID(),
  2284  		Now:      time.Now().UTC(),
  2285  		Accounts: []*AccountStat{},
  2286  	}
  2287  	if opts == nil || len(opts.Accounts) == 0 {
  2288  		s.accounts.Range(func(key, a interface{}) bool {
  2289  			acc := a.(*Account)
  2290  			acc.mu.RLock()
  2291  			if opts.IncludeUnused || acc.numLocalConnections() != 0 {
  2292  				stz.Accounts = append(stz.Accounts, acc.statz())
  2293  			}
  2294  			acc.mu.RUnlock()
  2295  			return true
  2296  		})
  2297  	} else {
  2298  		for _, a := range opts.Accounts {
  2299  			if acc, ok := s.accounts.Load(a); ok {
  2300  				acc := acc.(*Account)
  2301  				acc.mu.RLock()
  2302  				if opts.IncludeUnused || acc.numLocalConnections() != 0 {
  2303  					stz.Accounts = append(stz.Accounts, acc.statz())
  2304  				}
  2305  				acc.mu.RUnlock()
  2306  			}
  2307  		}
  2308  	}
  2309  	return stz, nil
  2310  }
  2311  
  2312  // HandleAccountStatz process HTTP requests for statz information of all accounts.
  2313  func (s *Server) HandleAccountStatz(w http.ResponseWriter, r *http.Request) {
  2314  	s.mu.Lock()
  2315  	s.httpReqStats[AccountStatzPath]++
  2316  	s.mu.Unlock()
  2317  
  2318  	unused, err := decodeBool(w, r, "unused")
  2319  	if err != nil {
  2320  		return
  2321  	}
  2322  
  2323  	l, err := s.AccountStatz(&AccountStatzOptions{IncludeUnused: unused})
  2324  	if err != nil {
  2325  		w.WriteHeader(http.StatusBadRequest)
  2326  		w.Write([]byte(err.Error()))
  2327  		return
  2328  	}
  2329  	b, err := json.MarshalIndent(l, "", "  ")
  2330  	if err != nil {
  2331  		s.Errorf("Error marshaling response to %s request: %v", AccountStatzPath, err)
  2332  		return
  2333  	}
  2334  
  2335  	// Handle response
  2336  	ResponseHandler(w, r, b)
  2337  }
  2338  
  2339  // ResponseHandler handles responses for monitoring routes.
  2340  func ResponseHandler(w http.ResponseWriter, r *http.Request, data []byte) {
  2341  	handleResponse(http.StatusOK, w, r, data)
  2342  }
  2343  
  2344  // handleResponse handles responses for monitoring routes with a specific HTTP status code.
  2345  func handleResponse(code int, w http.ResponseWriter, r *http.Request, data []byte) {
  2346  	// Get callback from request
  2347  	callback := r.URL.Query().Get("callback")
  2348  	if callback != _EMPTY_ {
  2349  		// Response for JSONP
  2350  		w.Header().Set("Content-Type", "application/javascript")
  2351  		w.WriteHeader(code)
  2352  		fmt.Fprintf(w, "%s(%s)", callback, data)
  2353  	} else {
  2354  		// Otherwise JSON
  2355  		w.Header().Set("Content-Type", "application/json")
  2356  		w.Header().Set("Access-Control-Allow-Origin", "*")
  2357  		w.WriteHeader(code)
  2358  		w.Write(data)
  2359  	}
  2360  }
  2361  
  2362  func (reason ClosedState) String() string {
  2363  	switch reason {
  2364  	case ClientClosed:
  2365  		return "Client Closed"
  2366  	case AuthenticationTimeout:
  2367  		return "Authentication Timeout"
  2368  	case AuthenticationViolation:
  2369  		return "Authentication Failure"
  2370  	case TLSHandshakeError:
  2371  		return "TLS Handshake Failure"
  2372  	case SlowConsumerPendingBytes:
  2373  		return "Slow Consumer (Pending Bytes)"
  2374  	case SlowConsumerWriteDeadline:
  2375  		return "Slow Consumer (Write Deadline)"
  2376  	case WriteError:
  2377  		return "Write Error"
  2378  	case ReadError:
  2379  		return "Read Error"
  2380  	case ParseError:
  2381  		return "Parse Error"
  2382  	case StaleConnection:
  2383  		return "Stale Connection"
  2384  	case ProtocolViolation:
  2385  		return "Protocol Violation"
  2386  	case BadClientProtocolVersion:
  2387  		return "Bad Client Protocol Version"
  2388  	case WrongPort:
  2389  		return "Incorrect Port"
  2390  	case MaxConnectionsExceeded:
  2391  		return "Maximum Connections Exceeded"
  2392  	case MaxAccountConnectionsExceeded:
  2393  		return "Maximum Account Connections Exceeded"
  2394  	case MaxPayloadExceeded:
  2395  		return "Maximum Message Payload Exceeded"
  2396  	case MaxControlLineExceeded:
  2397  		return "Maximum Control Line Exceeded"
  2398  	case MaxSubscriptionsExceeded:
  2399  		return "Maximum Subscriptions Exceeded"
  2400  	case DuplicateRoute:
  2401  		return "Duplicate Route"
  2402  	case RouteRemoved:
  2403  		return "Route Removed"
  2404  	case ServerShutdown:
  2405  		return "Server Shutdown"
  2406  	case AuthenticationExpired:
  2407  		return "Authentication Expired"
  2408  	case WrongGateway:
  2409  		return "Wrong Gateway"
  2410  	case MissingAccount:
  2411  		return "Missing Account"
  2412  	case Revocation:
  2413  		return "Credentials Revoked"
  2414  	case InternalClient:
  2415  		return "Internal Client"
  2416  	case MsgHeaderViolation:
  2417  		return "Message Header Violation"
  2418  	case NoRespondersRequiresHeaders:
  2419  		return "No Responders Requires Headers"
  2420  	case ClusterNameConflict:
  2421  		return "Cluster Name Conflict"
  2422  	case DuplicateRemoteLeafnodeConnection:
  2423  		return "Duplicate Remote LeafNode Connection"
  2424  	case DuplicateClientID:
  2425  		return "Duplicate Client ID"
  2426  	case DuplicateServerName:
  2427  		return "Duplicate Server Name"
  2428  	case MinimumVersionRequired:
  2429  		return "Minimum Version Required"
  2430  	case ClusterNamesIdentical:
  2431  		return "Cluster Names Identical"
  2432  	case Kicked:
  2433  		return "Kicked"
  2434  	}
  2435  
  2436  	return "Unknown State"
  2437  }
  2438  
  2439  // AccountzOptions are options passed to Accountz
  2440  type AccountzOptions struct {
  2441  	// Account indicates that Accountz will return details for the account
  2442  	Account string `json:"account"`
  2443  }
  2444  
  2445  func newExtServiceLatency(l *serviceLatency) *jwt.ServiceLatency {
  2446  	if l == nil {
  2447  		return nil
  2448  	}
  2449  	return &jwt.ServiceLatency{
  2450  		Sampling: jwt.SamplingRate(l.sampling),
  2451  		Results:  jwt.Subject(l.subject),
  2452  	}
  2453  }
  2454  
  2455  type ExtImport struct {
  2456  	jwt.Import
  2457  	Invalid     bool                `json:"invalid"`
  2458  	Share       bool                `json:"share"`
  2459  	Tracking    bool                `json:"tracking"`
  2460  	TrackingHdr http.Header         `json:"tracking_header,omitempty"`
  2461  	Latency     *jwt.ServiceLatency `json:"latency,omitempty"`
  2462  	M1          *ServiceLatency     `json:"m1,omitempty"`
  2463  }
  2464  
  2465  type ExtExport struct {
  2466  	jwt.Export
  2467  	ApprovedAccounts []string             `json:"approved_accounts,omitempty"`
  2468  	RevokedAct       map[string]time.Time `json:"revoked_activations,omitempty"`
  2469  }
  2470  
  2471  type ExtVrIssues struct {
  2472  	Description string `json:"description"`
  2473  	Blocking    bool   `json:"blocking"`
  2474  	Time        bool   `json:"time_check"`
  2475  }
  2476  
  2477  type ExtMap map[string][]*MapDest
  2478  
  2479  type AccountInfo struct {
  2480  	AccountName string               `json:"account_name"`
  2481  	LastUpdate  time.Time            `json:"update_time,omitempty"`
  2482  	IsSystem    bool                 `json:"is_system,omitempty"`
  2483  	Expired     bool                 `json:"expired"`
  2484  	Complete    bool                 `json:"complete"`
  2485  	JetStream   bool                 `json:"jetstream_enabled"`
  2486  	LeafCnt     int                  `json:"leafnode_connections"`
  2487  	ClientCnt   int                  `json:"client_connections"`
  2488  	SubCnt      uint32               `json:"subscriptions"`
  2489  	Mappings    ExtMap               `json:"mappings,omitempty"`
  2490  	Exports     []ExtExport          `json:"exports,omitempty"`
  2491  	Imports     []ExtImport          `json:"imports,omitempty"`
  2492  	Jwt         string               `json:"jwt,omitempty"`
  2493  	IssuerKey   string               `json:"issuer_key,omitempty"`
  2494  	NameTag     string               `json:"name_tag,omitempty"`
  2495  	Tags        jwt.TagList          `json:"tags,omitempty"`
  2496  	Claim       *jwt.AccountClaims   `json:"decoded_jwt,omitempty"`
  2497  	Vr          []ExtVrIssues        `json:"validation_result_jwt,omitempty"`
  2498  	RevokedUser map[string]time.Time `json:"revoked_user,omitempty"`
  2499  	Sublist     *SublistStats        `json:"sublist_stats,omitempty"`
  2500  	Responses   map[string]ExtImport `json:"responses,omitempty"`
  2501  }
  2502  
  2503  type Accountz struct {
  2504  	ID            string       `json:"server_id"`
  2505  	Now           time.Time    `json:"now"`
  2506  	SystemAccount string       `json:"system_account,omitempty"`
  2507  	Accounts      []string     `json:"accounts,omitempty"`
  2508  	Account       *AccountInfo `json:"account_detail,omitempty"`
  2509  }
  2510  
  2511  // HandleAccountz process HTTP requests for account information.
  2512  func (s *Server) HandleAccountz(w http.ResponseWriter, r *http.Request) {
  2513  	s.mu.Lock()
  2514  	s.httpReqStats[AccountzPath]++
  2515  	s.mu.Unlock()
  2516  	if l, err := s.Accountz(&AccountzOptions{r.URL.Query().Get("acc")}); err != nil {
  2517  		w.WriteHeader(http.StatusBadRequest)
  2518  		w.Write([]byte(err.Error()))
  2519  	} else if b, err := json.MarshalIndent(l, "", "  "); err != nil {
  2520  		s.Errorf("Error marshaling response to %s request: %v", AccountzPath, err)
  2521  		w.WriteHeader(http.StatusBadRequest)
  2522  		w.Write([]byte(err.Error()))
  2523  	} else {
  2524  		ResponseHandler(w, r, b) // Handle response
  2525  	}
  2526  }
  2527  
  2528  func (s *Server) Accountz(optz *AccountzOptions) (*Accountz, error) {
  2529  	a := &Accountz{
  2530  		ID:  s.ID(),
  2531  		Now: time.Now().UTC(),
  2532  	}
  2533  	if sacc := s.SystemAccount(); sacc != nil {
  2534  		a.SystemAccount = sacc.GetName()
  2535  	}
  2536  	if optz == nil || optz.Account == _EMPTY_ {
  2537  		a.Accounts = []string{}
  2538  		s.accounts.Range(func(key, value interface{}) bool {
  2539  			a.Accounts = append(a.Accounts, key.(string))
  2540  			return true
  2541  		})
  2542  		return a, nil
  2543  	}
  2544  	aInfo, err := s.accountInfo(optz.Account)
  2545  	if err != nil {
  2546  		return nil, err
  2547  	}
  2548  	a.Account = aInfo
  2549  	return a, nil
  2550  }
  2551  
  2552  func newExtImport(v *serviceImport) ExtImport {
  2553  	imp := ExtImport{
  2554  		Invalid: true,
  2555  		Import:  jwt.Import{Type: jwt.Service},
  2556  	}
  2557  	if v != nil {
  2558  		imp.Share = v.share
  2559  		imp.Tracking = v.tracking
  2560  		imp.Invalid = v.invalid
  2561  		imp.Import = jwt.Import{
  2562  			Subject: jwt.Subject(v.to),
  2563  			Account: v.acc.Name,
  2564  			Type:    jwt.Service,
  2565  			// Deprecated so we duplicate. Use LocalSubject.
  2566  			To:           jwt.Subject(v.from),
  2567  			LocalSubject: jwt.RenamingSubject(v.from),
  2568  		}
  2569  		imp.TrackingHdr = v.trackingHdr
  2570  		imp.Latency = newExtServiceLatency(v.latency)
  2571  		if v.m1 != nil {
  2572  			m1 := *v.m1
  2573  			imp.M1 = &m1
  2574  		}
  2575  	}
  2576  	return imp
  2577  }
  2578  
  2579  func (s *Server) accountInfo(accName string) (*AccountInfo, error) {
  2580  	var a *Account
  2581  	if v, ok := s.accounts.Load(accName); !ok {
  2582  		return nil, fmt.Errorf("Account %s does not exist", accName)
  2583  	} else {
  2584  		a = v.(*Account)
  2585  	}
  2586  	isSys := a == s.SystemAccount()
  2587  	a.mu.RLock()
  2588  	defer a.mu.RUnlock()
  2589  	var vrIssues []ExtVrIssues
  2590  	claim, _ := jwt.DecodeAccountClaims(a.claimJWT) // ignore error
  2591  	if claim != nil {
  2592  		vr := jwt.ValidationResults{}
  2593  		claim.Validate(&vr)
  2594  		vrIssues = make([]ExtVrIssues, len(vr.Issues))
  2595  		for i, v := range vr.Issues {
  2596  			vrIssues[i] = ExtVrIssues{v.Description, v.Blocking, v.TimeCheck}
  2597  		}
  2598  	}
  2599  	collectRevocations := func(revocations map[string]int64) map[string]time.Time {
  2600  		l := len(revocations)
  2601  		if l == 0 {
  2602  			return nil
  2603  		}
  2604  		rev := make(map[string]time.Time, l)
  2605  		for k, v := range revocations {
  2606  			rev[k] = time.Unix(v, 0)
  2607  		}
  2608  		return rev
  2609  	}
  2610  	exports := []ExtExport{}
  2611  	for k, v := range a.exports.services {
  2612  		e := ExtExport{
  2613  			Export: jwt.Export{
  2614  				Subject: jwt.Subject(k),
  2615  				Type:    jwt.Service,
  2616  			},
  2617  			ApprovedAccounts: []string{},
  2618  		}
  2619  		if v != nil {
  2620  			e.Latency = newExtServiceLatency(v.latency)
  2621  			e.TokenReq = v.tokenReq
  2622  			e.ResponseType = jwt.ResponseType(v.respType.String())
  2623  			for name := range v.approved {
  2624  				e.ApprovedAccounts = append(e.ApprovedAccounts, name)
  2625  			}
  2626  			e.RevokedAct = collectRevocations(v.actsRevoked)
  2627  		}
  2628  		exports = append(exports, e)
  2629  	}
  2630  	for k, v := range a.exports.streams {
  2631  		e := ExtExport{
  2632  			Export: jwt.Export{
  2633  				Subject: jwt.Subject(k),
  2634  				Type:    jwt.Stream,
  2635  			},
  2636  			ApprovedAccounts: []string{},
  2637  		}
  2638  		if v != nil {
  2639  			e.TokenReq = v.tokenReq
  2640  			for name := range v.approved {
  2641  				e.ApprovedAccounts = append(e.ApprovedAccounts, name)
  2642  			}
  2643  			e.RevokedAct = collectRevocations(v.actsRevoked)
  2644  		}
  2645  		exports = append(exports, e)
  2646  	}
  2647  	imports := []ExtImport{}
  2648  	for _, v := range a.imports.streams {
  2649  		imp := ExtImport{
  2650  			Invalid: true,
  2651  			Import:  jwt.Import{Type: jwt.Stream},
  2652  		}
  2653  		if v != nil {
  2654  			imp.Invalid = v.invalid
  2655  			imp.Import = jwt.Import{
  2656  				Subject:      jwt.Subject(v.from),
  2657  				Account:      v.acc.Name,
  2658  				Type:         jwt.Stream,
  2659  				LocalSubject: jwt.RenamingSubject(v.to),
  2660  			}
  2661  		}
  2662  		imports = append(imports, imp)
  2663  	}
  2664  	for _, v := range a.imports.services {
  2665  		imports = append(imports, newExtImport(v))
  2666  	}
  2667  	responses := map[string]ExtImport{}
  2668  	for k, v := range a.exports.responses {
  2669  		responses[k] = newExtImport(v)
  2670  	}
  2671  	mappings := ExtMap{}
  2672  	for _, m := range a.mappings {
  2673  		var dests []*MapDest
  2674  		var src string
  2675  		if m == nil {
  2676  			src = "nil"
  2677  			if _, ok := mappings[src]; ok { // only set if not present (keep orig in case nil is used)
  2678  				continue
  2679  			}
  2680  			dests = append(dests, &MapDest{})
  2681  		} else {
  2682  			src = m.src
  2683  			for _, d := range m.dests {
  2684  				dests = append(dests, &MapDest{d.tr.dest, d.weight, _EMPTY_})
  2685  			}
  2686  			for c, cd := range m.cdests {
  2687  				for _, d := range cd {
  2688  					dests = append(dests, &MapDest{d.tr.dest, d.weight, c})
  2689  				}
  2690  			}
  2691  		}
  2692  		mappings[src] = dests
  2693  	}
  2694  	return &AccountInfo{
  2695  		accName,
  2696  		a.updated.UTC(),
  2697  		isSys,
  2698  		a.expired,
  2699  		!a.incomplete,
  2700  		a.js != nil,
  2701  		a.numLocalLeafNodes(),
  2702  		a.numLocalConnections(),
  2703  		a.sl.Count(),
  2704  		mappings,
  2705  		exports,
  2706  		imports,
  2707  		a.claimJWT,
  2708  		a.Issuer,
  2709  		a.nameTag,
  2710  		a.tags,
  2711  		claim,
  2712  		vrIssues,
  2713  		collectRevocations(a.usersRevoked),
  2714  		a.sl.Stats(),
  2715  		responses,
  2716  	}, nil
  2717  }
  2718  
  2719  // JSzOptions are options passed to Jsz
  2720  type JSzOptions struct {
  2721  	Account    string `json:"account,omitempty"`
  2722  	Accounts   bool   `json:"accounts,omitempty"`
  2723  	Streams    bool   `json:"streams,omitempty"`
  2724  	Consumer   bool   `json:"consumer,omitempty"`
  2725  	Config     bool   `json:"config,omitempty"`
  2726  	LeaderOnly bool   `json:"leader_only,omitempty"`
  2727  	Offset     int    `json:"offset,omitempty"`
  2728  	Limit      int    `json:"limit,omitempty"`
  2729  	RaftGroups bool   `json:"raft,omitempty"`
  2730  }
  2731  
  2732  // HealthzOptions are options passed to Healthz
  2733  type HealthzOptions struct {
  2734  	// Deprecated: Use JSEnabledOnly instead
  2735  	JSEnabled     bool   `json:"js-enabled,omitempty"`
  2736  	JSEnabledOnly bool   `json:"js-enabled-only,omitempty"`
  2737  	JSServerOnly  bool   `json:"js-server-only,omitempty"`
  2738  	Account       string `json:"account,omitempty"`
  2739  	Stream        string `json:"stream,omitempty"`
  2740  	Consumer      string `json:"consumer,omitempty"`
  2741  	Details       bool   `json:"details,omitempty"`
  2742  }
  2743  
  2744  // ProfilezOptions are options passed to Profilez
  2745  type ProfilezOptions struct {
  2746  	Name  string `json:"name"`
  2747  	Debug int    `json:"debug"`
  2748  }
  2749  
  2750  // StreamDetail shows information about the stream state and its consumers.
  2751  type StreamDetail struct {
  2752  	Name               string              `json:"name"`
  2753  	Created            time.Time           `json:"created"`
  2754  	Cluster            *ClusterInfo        `json:"cluster,omitempty"`
  2755  	Config             *StreamConfig       `json:"config,omitempty"`
  2756  	State              StreamState         `json:"state,omitempty"`
  2757  	Consumer           []*ConsumerInfo     `json:"consumer_detail,omitempty"`
  2758  	Mirror             *StreamSourceInfo   `json:"mirror,omitempty"`
  2759  	Sources            []*StreamSourceInfo `json:"sources,omitempty"`
  2760  	RaftGroup          string              `json:"stream_raft_group,omitempty"`
  2761  	ConsumerRaftGroups []*RaftGroupDetail  `json:"consumer_raft_groups,omitempty"`
  2762  }
  2763  
  2764  // RaftGroupDetail shows information details about the Raft group.
  2765  type RaftGroupDetail struct {
  2766  	Name      string `json:"name"`
  2767  	RaftGroup string `json:"raft_group,omitempty"`
  2768  }
  2769  
  2770  type AccountDetail struct {
  2771  	Name string `json:"name"`
  2772  	Id   string `json:"id"`
  2773  	JetStreamStats
  2774  	Streams []StreamDetail `json:"stream_detail,omitempty"`
  2775  }
  2776  
  2777  // MetaClusterInfo shows information about the meta group.
  2778  type MetaClusterInfo struct {
  2779  	Name     string      `json:"name,omitempty"`
  2780  	Leader   string      `json:"leader,omitempty"`
  2781  	Peer     string      `json:"peer,omitempty"`
  2782  	Replicas []*PeerInfo `json:"replicas,omitempty"`
  2783  	Size     int         `json:"cluster_size"`
  2784  }
  2785  
  2786  // JSInfo has detailed information on JetStream.
  2787  type JSInfo struct {
  2788  	ID       string          `json:"server_id"`
  2789  	Now      time.Time       `json:"now"`
  2790  	Disabled bool            `json:"disabled,omitempty"`
  2791  	Config   JetStreamConfig `json:"config,omitempty"`
  2792  	JetStreamStats
  2793  	Streams   int              `json:"streams"`
  2794  	Consumers int              `json:"consumers"`
  2795  	Messages  uint64           `json:"messages"`
  2796  	Bytes     uint64           `json:"bytes"`
  2797  	Meta      *MetaClusterInfo `json:"meta_cluster,omitempty"`
  2798  
  2799  	// aggregate raft info
  2800  	AccountDetails []*AccountDetail `json:"account_details,omitempty"`
  2801  }
  2802  
  2803  func (s *Server) accountDetail(jsa *jsAccount, optStreams, optConsumers, optCfg, optRaft bool) *AccountDetail {
  2804  	jsa.mu.RLock()
  2805  	acc := jsa.account
  2806  	name := acc.GetName()
  2807  	id := name
  2808  	if acc.nameTag != _EMPTY_ {
  2809  		name = acc.nameTag
  2810  	}
  2811  	jsa.usageMu.RLock()
  2812  	totalMem, totalStore := jsa.storageTotals()
  2813  	detail := AccountDetail{
  2814  		Name: name,
  2815  		Id:   id,
  2816  		JetStreamStats: JetStreamStats{
  2817  			Memory: totalMem,
  2818  			Store:  totalStore,
  2819  			API: JetStreamAPIStats{
  2820  				Total:  jsa.apiTotal,
  2821  				Errors: jsa.apiErrors,
  2822  			},
  2823  		},
  2824  		Streams: make([]StreamDetail, 0, len(jsa.streams)),
  2825  	}
  2826  	if reserved, ok := jsa.limits[_EMPTY_]; ok {
  2827  		detail.JetStreamStats.ReservedMemory = uint64(reserved.MaxMemory)
  2828  		detail.JetStreamStats.ReservedStore = uint64(reserved.MaxStore)
  2829  	}
  2830  
  2831  	jsa.usageMu.RUnlock()
  2832  	var streams []*stream
  2833  	if optStreams {
  2834  		for _, stream := range jsa.streams {
  2835  			streams = append(streams, stream)
  2836  		}
  2837  	}
  2838  	jsa.mu.RUnlock()
  2839  
  2840  	if js := s.getJetStream(); js != nil && optStreams {
  2841  		for _, stream := range streams {
  2842  			rgroup := stream.raftGroup()
  2843  			ci := js.clusterInfo(rgroup)
  2844  			var cfg *StreamConfig
  2845  			if optCfg {
  2846  				c := stream.config()
  2847  				cfg = &c
  2848  			}
  2849  			sdet := StreamDetail{
  2850  				Name:    stream.name(),
  2851  				Created: stream.createdTime(),
  2852  				State:   stream.state(),
  2853  				Cluster: ci,
  2854  				Config:  cfg,
  2855  				Mirror:  stream.mirrorInfo(),
  2856  				Sources: stream.sourcesInfo(),
  2857  			}
  2858  			if optRaft && rgroup != nil {
  2859  				sdet.RaftGroup = rgroup.Name
  2860  				sdet.ConsumerRaftGroups = make([]*RaftGroupDetail, 0)
  2861  			}
  2862  			if optConsumers {
  2863  				for _, consumer := range stream.getPublicConsumers() {
  2864  					cInfo := consumer.info()
  2865  					if cInfo == nil {
  2866  						continue
  2867  					}
  2868  					if !optCfg {
  2869  						cInfo.Config = nil
  2870  					}
  2871  					sdet.Consumer = append(sdet.Consumer, cInfo)
  2872  					if optRaft {
  2873  						crgroup := consumer.raftGroup()
  2874  						if crgroup != nil {
  2875  							sdet.ConsumerRaftGroups = append(sdet.ConsumerRaftGroups,
  2876  								&RaftGroupDetail{cInfo.Name, crgroup.Name},
  2877  							)
  2878  						}
  2879  					}
  2880  				}
  2881  			}
  2882  			detail.Streams = append(detail.Streams, sdet)
  2883  		}
  2884  	}
  2885  	return &detail
  2886  }
  2887  
  2888  func (s *Server) JszAccount(opts *JSzOptions) (*AccountDetail, error) {
  2889  	js := s.getJetStream()
  2890  	if js == nil {
  2891  		return nil, fmt.Errorf("jetstream not enabled")
  2892  	}
  2893  	acc := opts.Account
  2894  	account, ok := s.accounts.Load(acc)
  2895  	if !ok {
  2896  		return nil, fmt.Errorf("account %q not found", acc)
  2897  	}
  2898  	js.mu.RLock()
  2899  	jsa, ok := js.accounts[account.(*Account).Name]
  2900  	js.mu.RUnlock()
  2901  	if !ok {
  2902  		return nil, fmt.Errorf("account %q not jetstream enabled", acc)
  2903  	}
  2904  	return s.accountDetail(jsa, opts.Streams, opts.Consumer, opts.Config, opts.RaftGroups), nil
  2905  }
  2906  
  2907  // helper to get cluster info from node via dummy group
  2908  func (s *Server) raftNodeToClusterInfo(node RaftNode) *ClusterInfo {
  2909  	if node == nil {
  2910  		return nil
  2911  	}
  2912  	peers := node.Peers()
  2913  	peerList := make([]string, len(peers))
  2914  	for i, p := range peers {
  2915  		peerList[i] = p.ID
  2916  	}
  2917  	group := &raftGroup{
  2918  		Name:  _EMPTY_,
  2919  		Peers: peerList,
  2920  		node:  node,
  2921  	}
  2922  	return s.getJetStream().clusterInfo(group)
  2923  }
  2924  
  2925  // Jsz returns a Jsz structure containing information about JetStream.
  2926  func (s *Server) Jsz(opts *JSzOptions) (*JSInfo, error) {
  2927  	// set option defaults
  2928  	if opts == nil {
  2929  		opts = &JSzOptions{}
  2930  	}
  2931  	if opts.Limit == 0 {
  2932  		opts.Limit = 1024
  2933  	}
  2934  	if opts.Consumer {
  2935  		opts.Streams = true
  2936  	}
  2937  	if opts.Streams {
  2938  		opts.Accounts = true
  2939  	}
  2940  
  2941  	jsi := &JSInfo{
  2942  		ID:  s.ID(),
  2943  		Now: time.Now().UTC(),
  2944  	}
  2945  
  2946  	js := s.getJetStream()
  2947  	if js == nil || !js.isEnabled() {
  2948  		if opts.LeaderOnly {
  2949  			return nil, fmt.Errorf("%w: not leader", errSkipZreq)
  2950  		}
  2951  
  2952  		jsi.Disabled = true
  2953  		return jsi, nil
  2954  	}
  2955  
  2956  	js.mu.RLock()
  2957  	isLeader := js.cluster == nil || js.cluster.isLeader()
  2958  	js.mu.RUnlock()
  2959  
  2960  	if opts.LeaderOnly && !isLeader {
  2961  		return nil, fmt.Errorf("%w: not leader", errSkipZreq)
  2962  	}
  2963  
  2964  	var accounts []*jsAccount
  2965  
  2966  	js.mu.RLock()
  2967  	jsi.Config = js.config
  2968  	for _, info := range js.accounts {
  2969  		accounts = append(accounts, info)
  2970  	}
  2971  	js.mu.RUnlock()
  2972  
  2973  	if mg := js.getMetaGroup(); mg != nil {
  2974  		if ci := s.raftNodeToClusterInfo(mg); ci != nil {
  2975  			jsi.Meta = &MetaClusterInfo{Name: ci.Name, Leader: ci.Leader, Peer: getHash(ci.Leader), Size: mg.ClusterSize()}
  2976  			if isLeader {
  2977  				jsi.Meta.Replicas = ci.Replicas
  2978  			}
  2979  		}
  2980  	}
  2981  
  2982  	jsi.JetStreamStats = *js.usageStats()
  2983  
  2984  	filterIdx := -1
  2985  	for i, jsa := range accounts {
  2986  		if jsa.acc().GetName() == opts.Account {
  2987  			filterIdx = i
  2988  		}
  2989  		jsa.mu.RLock()
  2990  		streams := make([]*stream, 0, len(jsa.streams))
  2991  		for _, stream := range jsa.streams {
  2992  			streams = append(streams, stream)
  2993  		}
  2994  		jsa.mu.RUnlock()
  2995  		jsi.Streams += len(streams)
  2996  		for _, stream := range streams {
  2997  			streamState := stream.state()
  2998  			jsi.Messages += streamState.Msgs
  2999  			jsi.Bytes += streamState.Bytes
  3000  			jsi.Consumers += streamState.Consumers
  3001  		}
  3002  	}
  3003  
  3004  	// filter logic
  3005  	if filterIdx != -1 {
  3006  		accounts = []*jsAccount{accounts[filterIdx]}
  3007  	} else if opts.Accounts {
  3008  		if opts.Offset != 0 {
  3009  			sort.Slice(accounts, func(i, j int) bool {
  3010  				return strings.Compare(accounts[i].acc().Name, accounts[j].acc().Name) < 0
  3011  			})
  3012  			if opts.Offset > len(accounts) {
  3013  				accounts = []*jsAccount{}
  3014  			} else {
  3015  				accounts = accounts[opts.Offset:]
  3016  			}
  3017  		}
  3018  		if opts.Limit != 0 {
  3019  			if opts.Limit < len(accounts) {
  3020  				accounts = accounts[:opts.Limit]
  3021  			}
  3022  		}
  3023  	} else {
  3024  		accounts = []*jsAccount{}
  3025  	}
  3026  	if len(accounts) > 0 {
  3027  		jsi.AccountDetails = make([]*AccountDetail, 0, len(accounts))
  3028  	}
  3029  	// if wanted, obtain accounts/streams/consumer
  3030  	for _, jsa := range accounts {
  3031  		detail := s.accountDetail(jsa, opts.Streams, opts.Consumer, opts.Config, opts.RaftGroups)
  3032  		jsi.AccountDetails = append(jsi.AccountDetails, detail)
  3033  	}
  3034  	return jsi, nil
  3035  }
  3036  
  3037  // HandleJsz process HTTP requests for jetstream information.
  3038  func (s *Server) HandleJsz(w http.ResponseWriter, r *http.Request) {
  3039  	s.mu.Lock()
  3040  	s.httpReqStats[JszPath]++
  3041  	s.mu.Unlock()
  3042  	accounts, err := decodeBool(w, r, "accounts")
  3043  	if err != nil {
  3044  		return
  3045  	}
  3046  	streams, err := decodeBool(w, r, "streams")
  3047  	if err != nil {
  3048  		return
  3049  	}
  3050  	consumers, err := decodeBool(w, r, "consumers")
  3051  	if err != nil {
  3052  		return
  3053  	}
  3054  	config, err := decodeBool(w, r, "config")
  3055  	if err != nil {
  3056  		return
  3057  	}
  3058  	offset, err := decodeInt(w, r, "offset")
  3059  	if err != nil {
  3060  		return
  3061  	}
  3062  	limit, err := decodeInt(w, r, "limit")
  3063  	if err != nil {
  3064  		return
  3065  	}
  3066  	leader, err := decodeBool(w, r, "leader-only")
  3067  	if err != nil {
  3068  		return
  3069  	}
  3070  	rgroups, err := decodeBool(w, r, "raft")
  3071  	if err != nil {
  3072  		return
  3073  	}
  3074  
  3075  	l, err := s.Jsz(&JSzOptions{
  3076  		r.URL.Query().Get("acc"),
  3077  		accounts,
  3078  		streams,
  3079  		consumers,
  3080  		config,
  3081  		leader,
  3082  		offset,
  3083  		limit,
  3084  		rgroups,
  3085  	})
  3086  	if err != nil {
  3087  		w.WriteHeader(http.StatusBadRequest)
  3088  		w.Write([]byte(err.Error()))
  3089  		return
  3090  	}
  3091  	b, err := json.MarshalIndent(l, "", "  ")
  3092  	if err != nil {
  3093  		s.Errorf("Error marshaling response to /jsz request: %v", err)
  3094  	}
  3095  
  3096  	// Handle response
  3097  	ResponseHandler(w, r, b)
  3098  }
  3099  
  3100  type HealthStatus struct {
  3101  	Status     string         `json:"status"`
  3102  	StatusCode int            `json:"status_code,omitempty"`
  3103  	Error      string         `json:"error,omitempty"`
  3104  	Errors     []HealthzError `json:"errors,omitempty"`
  3105  }
  3106  
  3107  type HealthzError struct {
  3108  	Type     HealthZErrorType `json:"type"`
  3109  	Account  string           `json:"account,omitempty"`
  3110  	Stream   string           `json:"stream,omitempty"`
  3111  	Consumer string           `json:"consumer,omitempty"`
  3112  	Error    string           `json:"error,omitempty"`
  3113  }
  3114  
  3115  type HealthZErrorType int
  3116  
  3117  const (
  3118  	HealthzErrorConn HealthZErrorType = iota
  3119  	HealthzErrorBadRequest
  3120  	HealthzErrorJetStream
  3121  	HealthzErrorAccount
  3122  	HealthzErrorStream
  3123  	HealthzErrorConsumer
  3124  )
  3125  
  3126  func (t HealthZErrorType) String() string {
  3127  	switch t {
  3128  	case HealthzErrorConn:
  3129  		return "CONNECTION"
  3130  	case HealthzErrorBadRequest:
  3131  		return "BAD_REQUEST"
  3132  	case HealthzErrorJetStream:
  3133  		return "JETSTREAM"
  3134  	case HealthzErrorAccount:
  3135  		return "ACCOUNT"
  3136  	case HealthzErrorStream:
  3137  		return "STREAM"
  3138  	case HealthzErrorConsumer:
  3139  		return "CONSUMER"
  3140  	default:
  3141  		return "unknown"
  3142  	}
  3143  }
  3144  
  3145  func (t HealthZErrorType) MarshalJSON() ([]byte, error) {
  3146  	return json.Marshal(t.String())
  3147  }
  3148  
  3149  func (t *HealthZErrorType) UnmarshalJSON(data []byte) error {
  3150  	switch string(data) {
  3151  	case `"CONNECTION"`:
  3152  		*t = HealthzErrorConn
  3153  	case `"BAD_REQUEST"`:
  3154  		*t = HealthzErrorBadRequest
  3155  	case `"JETSTREAM"`:
  3156  		*t = HealthzErrorJetStream
  3157  	case `"ACCOUNT"`:
  3158  		*t = HealthzErrorAccount
  3159  	case `"STREAM"`:
  3160  		*t = HealthzErrorStream
  3161  	case `"CONSUMER"`:
  3162  		*t = HealthzErrorConsumer
  3163  	default:
  3164  		return fmt.Errorf("unknown healthz error type %q", data)
  3165  	}
  3166  	return nil
  3167  }
  3168  
  3169  // https://datatracker.ietf.org/doc/html/draft-inadarei-api-health-check
  3170  func (s *Server) HandleHealthz(w http.ResponseWriter, r *http.Request) {
  3171  	s.mu.Lock()
  3172  	s.httpReqStats[HealthzPath]++
  3173  	s.mu.Unlock()
  3174  
  3175  	jsEnabled, err := decodeBool(w, r, "js-enabled")
  3176  	if err != nil {
  3177  		return
  3178  	}
  3179  	if jsEnabled {
  3180  		s.Warnf("Healthcheck: js-enabled deprecated, use js-enabled-only instead")
  3181  	}
  3182  	jsEnabledOnly, err := decodeBool(w, r, "js-enabled-only")
  3183  	if err != nil {
  3184  		return
  3185  	}
  3186  	jsServerOnly, err := decodeBool(w, r, "js-server-only")
  3187  	if err != nil {
  3188  		return
  3189  	}
  3190  
  3191  	includeDetails, err := decodeBool(w, r, "details")
  3192  	if err != nil {
  3193  		return
  3194  	}
  3195  
  3196  	hs := s.healthz(&HealthzOptions{
  3197  		JSEnabled:     jsEnabled,
  3198  		JSEnabledOnly: jsEnabledOnly,
  3199  		JSServerOnly:  jsServerOnly,
  3200  		Account:       r.URL.Query().Get("account"),
  3201  		Stream:        r.URL.Query().Get("stream"),
  3202  		Consumer:      r.URL.Query().Get("consumer"),
  3203  		Details:       includeDetails,
  3204  	})
  3205  
  3206  	code := http.StatusOK
  3207  	if hs.Error != _EMPTY_ {
  3208  		s.Warnf("Healthcheck failed: %q", hs.Error)
  3209  		code = hs.StatusCode
  3210  	}
  3211  	// Remove StatusCode from JSON representation when responding via HTTP
  3212  	// since this is already in the response.
  3213  	hs.StatusCode = 0
  3214  	b, err := json.Marshal(hs)
  3215  	if err != nil {
  3216  		s.Errorf("Error marshaling response to /healthz request: %v", err)
  3217  	}
  3218  
  3219  	handleResponse(code, w, r, b)
  3220  }
  3221  
  3222  // Generate health status.
  3223  func (s *Server) healthz(opts *HealthzOptions) *HealthStatus {
  3224  	var health = &HealthStatus{Status: "ok"}
  3225  
  3226  	// set option defaults
  3227  	if opts == nil {
  3228  		opts = &HealthzOptions{}
  3229  	}
  3230  	details := opts.Details
  3231  	defer func() {
  3232  		// for response with details enabled, set status to either "error" or "ok"
  3233  		if details {
  3234  			if len(health.Errors) != 0 {
  3235  				health.Status = "error"
  3236  			} else {
  3237  				health.Status = "ok"
  3238  			}
  3239  		}
  3240  		// if no specific status code was set, set it based on the presence of errors
  3241  		if health.StatusCode == 0 {
  3242  			if health.Error != _EMPTY_ || len(health.Errors) != 0 {
  3243  				health.StatusCode = http.StatusServiceUnavailable
  3244  			} else {
  3245  				health.StatusCode = http.StatusOK
  3246  			}
  3247  		}
  3248  	}()
  3249  
  3250  	if opts.Account == _EMPTY_ && opts.Stream != _EMPTY_ {
  3251  		health.StatusCode = http.StatusBadRequest
  3252  		if !details {
  3253  			health.Status = "error"
  3254  			health.Error = fmt.Sprintf("%q must not be empty when checking stream health", "account")
  3255  		} else {
  3256  			health.Errors = append(health.Errors, HealthzError{
  3257  				Type:  HealthzErrorBadRequest,
  3258  				Error: fmt.Sprintf("%q must not be empty when checking stream health", "account"),
  3259  			})
  3260  		}
  3261  		return health
  3262  	}
  3263  
  3264  	if opts.Stream == _EMPTY_ && opts.Consumer != _EMPTY_ {
  3265  		health.StatusCode = http.StatusBadRequest
  3266  		if !details {
  3267  			health.Status = "error"
  3268  			health.Error = fmt.Sprintf("%q must not be empty when checking consumer health", "stream")
  3269  		} else {
  3270  			health.Errors = append(health.Errors, HealthzError{
  3271  				Type:  HealthzErrorBadRequest,
  3272  				Error: fmt.Sprintf("%q must not be empty when checking consumer health", "stream"),
  3273  			})
  3274  		}
  3275  		return health
  3276  	}
  3277  
  3278  	if err := s.readyForConnections(time.Millisecond); err != nil {
  3279  		health.StatusCode = http.StatusInternalServerError
  3280  		health.Status = "error"
  3281  		if !details {
  3282  			health.Error = err.Error()
  3283  		} else {
  3284  			health.Errors = append(health.Errors, HealthzError{
  3285  				Type:  HealthzErrorConn,
  3286  				Error: err.Error(),
  3287  			})
  3288  		}
  3289  		return health
  3290  	}
  3291  
  3292  	sopts := s.getOpts()
  3293  
  3294  	// If JS is not enabled in the config, we stop.
  3295  	if !sopts.JetStream {
  3296  		return health
  3297  	}
  3298  
  3299  	// Access the Jetstream state to perform additional checks.
  3300  	js := s.getJetStream()
  3301  	const na = "unavailable"
  3302  	if !js.isEnabled() {
  3303  		health.StatusCode = http.StatusServiceUnavailable
  3304  		health.Status = na
  3305  		if !details {
  3306  			health.Error = NewJSNotEnabledError().Error()
  3307  		} else {
  3308  			health.Errors = append(health.Errors, HealthzError{
  3309  				Type:  HealthzErrorJetStream,
  3310  				Error: NewJSNotEnabledError().Error(),
  3311  			})
  3312  		}
  3313  		return health
  3314  	}
  3315  	// Only check if JS is enabled, skip meta and asset check.
  3316  	if opts.JSEnabledOnly || opts.JSEnabled {
  3317  		return health
  3318  	}
  3319  
  3320  	// Clustered JetStream
  3321  	js.mu.RLock()
  3322  	cc := js.cluster
  3323  	js.mu.RUnlock()
  3324  
  3325  	// Currently single server we make sure the streams were recovered.
  3326  	if cc == nil {
  3327  		sdir := js.config.StoreDir
  3328  		// Whip through account folders and pull each stream name.
  3329  		fis, _ := os.ReadDir(sdir)
  3330  		var accFound, streamFound, consumerFound bool
  3331  		for _, fi := range fis {
  3332  			if fi.Name() == snapStagingDir {
  3333  				continue
  3334  			}
  3335  			if opts.Account != _EMPTY_ {
  3336  				if fi.Name() != opts.Account {
  3337  					continue
  3338  				}
  3339  				accFound = true
  3340  			}
  3341  			acc, err := s.LookupAccount(fi.Name())
  3342  			if err != nil {
  3343  				if !details {
  3344  					health.Status = na
  3345  					health.Error = fmt.Sprintf("JetStream account '%s' could not be resolved", fi.Name())
  3346  					return health
  3347  				}
  3348  				health.Errors = append(health.Errors, HealthzError{
  3349  					Type:    HealthzErrorAccount,
  3350  					Account: fi.Name(),
  3351  					Error:   fmt.Sprintf("JetStream account '%s' could not be resolved", fi.Name()),
  3352  				})
  3353  				continue
  3354  			}
  3355  			sfis, _ := os.ReadDir(filepath.Join(sdir, fi.Name(), "streams"))
  3356  			for _, sfi := range sfis {
  3357  				if opts.Stream != _EMPTY_ {
  3358  					if sfi.Name() != opts.Stream {
  3359  						continue
  3360  					}
  3361  					streamFound = true
  3362  				}
  3363  				stream := sfi.Name()
  3364  				s, err := acc.lookupStream(stream)
  3365  				if err != nil {
  3366  					if !details {
  3367  						health.Status = na
  3368  						health.Error = fmt.Sprintf("JetStream stream '%s > %s' could not be recovered", acc, stream)
  3369  						return health
  3370  					}
  3371  					health.Errors = append(health.Errors, HealthzError{
  3372  						Type:    HealthzErrorStream,
  3373  						Account: acc.Name,
  3374  						Stream:  stream,
  3375  						Error:   fmt.Sprintf("JetStream stream '%s > %s' could not be recovered", acc, stream),
  3376  					})
  3377  					continue
  3378  				}
  3379  				if streamFound {
  3380  					// if consumer option is passed, verify that the consumer exists on stream
  3381  					if opts.Consumer != _EMPTY_ {
  3382  						for _, cons := range s.consumers {
  3383  							if cons.name == opts.Consumer {
  3384  								consumerFound = true
  3385  								break
  3386  							}
  3387  						}
  3388  					}
  3389  					break
  3390  				}
  3391  			}
  3392  			if accFound {
  3393  				break
  3394  			}
  3395  		}
  3396  		if opts.Account != _EMPTY_ && !accFound {
  3397  			health.StatusCode = http.StatusNotFound
  3398  			if !details {
  3399  				health.Status = na
  3400  				health.Error = fmt.Sprintf("JetStream account %q not found", opts.Account)
  3401  			} else {
  3402  				health.Errors = []HealthzError{
  3403  					{
  3404  						Type:    HealthzErrorAccount,
  3405  						Account: opts.Account,
  3406  						Error:   fmt.Sprintf("JetStream account %q not found", opts.Account),
  3407  					},
  3408  				}
  3409  			}
  3410  			return health
  3411  		}
  3412  		if opts.Stream != _EMPTY_ && !streamFound {
  3413  			health.StatusCode = http.StatusNotFound
  3414  			if !details {
  3415  				health.Status = na
  3416  				health.Error = fmt.Sprintf("JetStream stream %q not found on account %q", opts.Stream, opts.Account)
  3417  			} else {
  3418  				health.Errors = []HealthzError{
  3419  					{
  3420  						Type:    HealthzErrorStream,
  3421  						Account: opts.Account,
  3422  						Stream:  opts.Stream,
  3423  						Error:   fmt.Sprintf("JetStream stream %q not found on account %q", opts.Stream, opts.Account),
  3424  					},
  3425  				}
  3426  			}
  3427  			return health
  3428  		}
  3429  		if opts.Consumer != _EMPTY_ && !consumerFound {
  3430  			health.StatusCode = http.StatusNotFound
  3431  			if !details {
  3432  				health.Status = na
  3433  				health.Error = fmt.Sprintf("JetStream consumer %q not found for stream %q on account %q", opts.Consumer, opts.Stream, opts.Account)
  3434  			} else {
  3435  				health.Errors = []HealthzError{
  3436  					{
  3437  						Type:     HealthzErrorConsumer,
  3438  						Account:  opts.Account,
  3439  						Stream:   opts.Stream,
  3440  						Consumer: opts.Consumer,
  3441  						Error:    fmt.Sprintf("JetStream consumer %q not found for stream %q on account %q", opts.Consumer, opts.Stream, opts.Account),
  3442  					},
  3443  				}
  3444  			}
  3445  		}
  3446  		return health
  3447  	}
  3448  
  3449  	// If we are here we want to check for any assets assigned to us.
  3450  	var meta RaftNode
  3451  	js.mu.RLock()
  3452  	meta = cc.meta
  3453  	js.mu.RUnlock()
  3454  
  3455  	// If no meta leader.
  3456  	if meta == nil || meta.GroupLeader() == _EMPTY_ {
  3457  		if !details {
  3458  			health.Status = na
  3459  			health.Error = "JetStream has not established contact with a meta leader"
  3460  		} else {
  3461  			health.Errors = []HealthzError{
  3462  				{
  3463  					Type:  HealthzErrorJetStream,
  3464  					Error: "JetStream has not established contact with a meta leader",
  3465  				},
  3466  			}
  3467  		}
  3468  		return health
  3469  	}
  3470  	// If we are not current with the meta leader.
  3471  	if !meta.Healthy() {
  3472  		if !details {
  3473  			health.Status = na
  3474  			health.Error = "JetStream is not current with the meta leader"
  3475  		} else {
  3476  			health.Errors = []HealthzError{
  3477  				{
  3478  					Type:  HealthzErrorJetStream,
  3479  					Error: "JetStream is not current with the meta leader",
  3480  				},
  3481  			}
  3482  		}
  3483  		return health
  3484  	}
  3485  
  3486  	// If JSServerOnly is true, then do not check further accounts, streams and consumers.
  3487  	if opts.JSServerOnly {
  3488  		return health
  3489  	}
  3490  
  3491  	// Range across all accounts, the streams assigned to them, and the consumers.
  3492  	// If they are assigned to this server check their status.
  3493  	ourID := meta.ID()
  3494  
  3495  	// Copy the meta layer so we do not need to hold the js read lock for an extended period of time.
  3496  	var streams map[string]map[string]*streamAssignment
  3497  	js.mu.RLock()
  3498  	if opts.Account == _EMPTY_ {
  3499  		// Collect all relevant streams and consumers.
  3500  		streams = make(map[string]map[string]*streamAssignment, len(cc.streams))
  3501  		for acc, asa := range cc.streams {
  3502  			nasa := make(map[string]*streamAssignment)
  3503  			for stream, sa := range asa {
  3504  				// If we are a member and we are not being restored, select for check.
  3505  				if sa.Group.isMember(ourID) && sa.Restore == nil {
  3506  					csa := sa.copyGroup()
  3507  					csa.consumers = make(map[string]*consumerAssignment)
  3508  					for consumer, ca := range sa.consumers {
  3509  						if ca.Group.isMember(ourID) {
  3510  							// Use original here. Not a copy.
  3511  							csa.consumers[consumer] = ca
  3512  						}
  3513  					}
  3514  					nasa[stream] = csa
  3515  				}
  3516  			}
  3517  			streams[acc] = nasa
  3518  		}
  3519  	} else {
  3520  		streams = make(map[string]map[string]*streamAssignment, 1)
  3521  		asa, ok := cc.streams[opts.Account]
  3522  		if !ok {
  3523  			health.StatusCode = http.StatusNotFound
  3524  			if !details {
  3525  				health.Status = na
  3526  				health.Error = fmt.Sprintf("JetStream account %q not found", opts.Account)
  3527  			} else {
  3528  				health.Errors = []HealthzError{
  3529  					{
  3530  						Type:    HealthzErrorAccount,
  3531  						Account: opts.Account,
  3532  						Error:   fmt.Sprintf("JetStream account %q not found", opts.Account),
  3533  					},
  3534  				}
  3535  			}
  3536  			js.mu.RUnlock()
  3537  			return health
  3538  		}
  3539  		nasa := make(map[string]*streamAssignment)
  3540  		if opts.Stream != _EMPTY_ {
  3541  			sa, ok := asa[opts.Stream]
  3542  			if !ok || !sa.Group.isMember(ourID) {
  3543  				health.StatusCode = http.StatusNotFound
  3544  				if !details {
  3545  					health.Status = na
  3546  					health.Error = fmt.Sprintf("JetStream stream %q not found on account %q", opts.Stream, opts.Account)
  3547  				} else {
  3548  					health.Errors = []HealthzError{
  3549  						{
  3550  							Type:    HealthzErrorStream,
  3551  							Account: opts.Account,
  3552  							Stream:  opts.Stream,
  3553  							Error:   fmt.Sprintf("JetStream stream %q not found on account %q", opts.Stream, opts.Account),
  3554  						},
  3555  					}
  3556  				}
  3557  				js.mu.RUnlock()
  3558  				return health
  3559  			}
  3560  			csa := sa.copyGroup()
  3561  			csa.consumers = make(map[string]*consumerAssignment)
  3562  			var consumerFound bool
  3563  			for consumer, ca := range sa.consumers {
  3564  				if opts.Consumer != _EMPTY_ {
  3565  					if consumer != opts.Consumer || !ca.Group.isMember(ourID) {
  3566  						continue
  3567  					}
  3568  					consumerFound = true
  3569  				}
  3570  				// If we are a member and we are not being restored, select for check.
  3571  				if sa.Group.isMember(ourID) && sa.Restore == nil {
  3572  					csa.consumers[consumer] = ca
  3573  				}
  3574  				if consumerFound {
  3575  					break
  3576  				}
  3577  			}
  3578  			if opts.Consumer != _EMPTY_ && !consumerFound {
  3579  				health.StatusCode = http.StatusNotFound
  3580  				if !details {
  3581  					health.Status = na
  3582  					health.Error = fmt.Sprintf("JetStream consumer %q not found for stream %q on account %q", opts.Consumer, opts.Stream, opts.Account)
  3583  				} else {
  3584  					health.Errors = []HealthzError{
  3585  						{
  3586  							Type:     HealthzErrorConsumer,
  3587  							Account:  opts.Account,
  3588  							Stream:   opts.Stream,
  3589  							Consumer: opts.Consumer,
  3590  							Error:    fmt.Sprintf("JetStream consumer %q not found for stream %q on account %q", opts.Consumer, opts.Stream, opts.Account),
  3591  						},
  3592  					}
  3593  				}
  3594  				js.mu.RUnlock()
  3595  				return health
  3596  			}
  3597  			nasa[opts.Stream] = csa
  3598  		} else {
  3599  			for stream, sa := range asa {
  3600  				// If we are a member and we are not being restored, select for check.
  3601  				if sa.Group.isMember(ourID) && sa.Restore == nil {
  3602  					csa := sa.copyGroup()
  3603  					csa.consumers = make(map[string]*consumerAssignment)
  3604  					for consumer, ca := range sa.consumers {
  3605  						if ca.Group.isMember(ourID) {
  3606  							csa.consumers[consumer] = ca
  3607  						}
  3608  					}
  3609  					nasa[stream] = csa
  3610  				}
  3611  			}
  3612  		}
  3613  		streams[opts.Account] = nasa
  3614  	}
  3615  	js.mu.RUnlock()
  3616  
  3617  	// Use our copy to traverse so we do not need to hold the js lock.
  3618  	for accName, asa := range streams {
  3619  		acc, err := s.LookupAccount(accName)
  3620  		if err != nil && len(asa) > 0 {
  3621  			if !details {
  3622  				health.Status = na
  3623  				health.Error = fmt.Sprintf("JetStream can not lookup account %q: %v", accName, err)
  3624  				return health
  3625  			}
  3626  			health.Errors = append(health.Errors, HealthzError{
  3627  				Type:    HealthzErrorAccount,
  3628  				Account: accName,
  3629  				Error:   fmt.Sprintf("JetStream can not lookup account %q: %v", accName, err),
  3630  			})
  3631  			continue
  3632  		}
  3633  
  3634  		for stream, sa := range asa {
  3635  			// Make sure we can look up
  3636  			if !js.isStreamHealthy(acc, sa) {
  3637  				if !details {
  3638  					health.Status = na
  3639  					health.Error = fmt.Sprintf("JetStream stream '%s > %s' is not current", accName, stream)
  3640  					return health
  3641  				}
  3642  				health.Errors = append(health.Errors, HealthzError{
  3643  					Type:    HealthzErrorStream,
  3644  					Account: accName,
  3645  					Stream:  stream,
  3646  					Error:   fmt.Sprintf("JetStream stream '%s > %s' is not current", accName, stream),
  3647  				})
  3648  				continue
  3649  			}
  3650  			mset, _ := acc.lookupStream(stream)
  3651  			// Now check consumers.
  3652  			for consumer, ca := range sa.consumers {
  3653  				if !js.isConsumerHealthy(mset, consumer, ca) {
  3654  					if !details {
  3655  						health.Status = na
  3656  						health.Error = fmt.Sprintf("JetStream consumer '%s > %s > %s' is not current", acc, stream, consumer)
  3657  						return health
  3658  					}
  3659  					health.Errors = append(health.Errors, HealthzError{
  3660  						Type:     HealthzErrorConsumer,
  3661  						Account:  accName,
  3662  						Stream:   stream,
  3663  						Consumer: consumer,
  3664  						Error:    fmt.Sprintf("JetStream consumer '%s > %s > %s' is not current", acc, stream, consumer),
  3665  					})
  3666  				}
  3667  			}
  3668  		}
  3669  	}
  3670  	// Success.
  3671  	return health
  3672  }
  3673  
  3674  type ProfilezStatus struct {
  3675  	Profile []byte `json:"profile"`
  3676  	Error   string `json:"error"`
  3677  }
  3678  
  3679  func (s *Server) profilez(opts *ProfilezOptions) *ProfilezStatus {
  3680  	if opts.Name == _EMPTY_ {
  3681  		return &ProfilezStatus{
  3682  			Error: "Profile name not specified",
  3683  		}
  3684  	}
  3685  	profile := pprof.Lookup(opts.Name)
  3686  	if profile == nil {
  3687  		return &ProfilezStatus{
  3688  			Error: fmt.Sprintf("Profile %q not found", opts.Name),
  3689  		}
  3690  	}
  3691  	var buffer bytes.Buffer
  3692  	if err := profile.WriteTo(&buffer, opts.Debug); err != nil {
  3693  		return &ProfilezStatus{
  3694  			Error: fmt.Sprintf("Profile %q error: %s", opts.Name, err),
  3695  		}
  3696  	}
  3697  	return &ProfilezStatus{
  3698  		Profile: buffer.Bytes(),
  3699  	}
  3700  }