// Copyright 2012-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"math"
	"net"
	"net/url"
	"reflect"
	"regexp"
	"runtime"
	"slices"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/klauspost/compress/s2"
	"github.com/nats-io/jwt/v2"
	"github.com/nats-io/nats.go"
	"github.com/nats-io/nkeys"
)

type serverInfo struct {
	ID           string   `json:"server_id"`
	Host         string   `json:"host"`
	Port         uint     `json:"port"`
	Version      string   `json:"version"`
	AuthRequired bool     `json:"auth_required"`
	TLSRequired  bool     `json:"tls_required"`
	MaxPayload   int64    `json:"max_payload"`
	Headers      bool     `json:"headers"`
	ConnectURLs  []string `json:"connect_urls,omitempty"`
	LameDuckMode bool     `json:"ldm,omitempty"`
	CID          uint64   `json:"client_id,omitempty"`
}

type testAsyncClient struct {
	*client
	parseAsync func(string)
	quitCh     chan bool
}

func (c *testAsyncClient) close() {
	c.client.closeConnection(ClientClosed)
	c.quitCh <- true
}

func (c *testAsyncClient) parse(proto []byte) error {
	err := c.client.parse(proto)
	c.client.flushClients(0)
	return err
}

func (c *testAsyncClient) parseAndClose(proto []byte) {
	c.client.parse(proto)
	c.client.flushClients(0)
	c.closeConnection(ClientClosed)
}

func createClientAsync(ch chan *client, s *Server, cli net.Conn) {
	// Normally, those type of clients are used against non running servers.
	// However, some don't, which would then cause the writeLoop to be
	// started twice for the same client (since createClient() start both
	// read and write loop if it is detected as running).
	startWriteLoop := !s.isRunning()
	if startWriteLoop {
		s.grWG.Add(1)
	}
	go func() {
		c := s.createClient(cli)
		// Must be here to suppress +OK
		c.opts.Verbose = false
		if startWriteLoop {
			go c.writeLoop()
		}
		ch <- c
	}()
}

func newClientForServer(s *Server) (*testAsyncClient, *bufio.Reader, string) {
	cli, srv := net.Pipe()
	cr := bufio.NewReaderSize(cli, maxBufSize)
	ch := make(chan *client)
	createClientAsync(ch, s, srv)
	// So failing tests don't just hang.
	cli.SetReadDeadline(time.Now().Add(10 * time.Second))
	l, _ := cr.ReadString('\n')
	// Grab client
	c := <-ch
	parse, quitCh := genAsyncParser(c)
	asyncClient := &testAsyncClient{
		client:     c,
		parseAsync: parse,
		quitCh:     quitCh,
	}
	return asyncClient, cr, l
}

func genAsyncParser(c *client) (func(string), chan bool) {
	pab := make(chan []byte, 16)
	pas := func(cs string) { pab <- []byte(cs) }
	quit := make(chan bool)
	go func() {
		for {
			select {
			case cs := <-pab:
				c.parse(cs)
				c.flushClients(0)
			case <-quit:
				return
			}
		}
	}()
	return pas, quit
}

var defaultServerOptions = Options{
	Host:                  "127.0.0.1",
	Port:                  -1,
	Trace:                 true,
	Debug:                 true,
	DisableShortFirstPing: true,
	NoLog:                 true,
	NoSigs:                true,
}

func rawSetup(serverOptions Options) (*Server, *testAsyncClient, *bufio.Reader, string) {
	s := New(&serverOptions)
	c, cr, l := newClientForServer(s)
	return s, c, cr, l
}

func setUpClientWithResponse() (*testAsyncClient, string) {
	_, c, _, l := rawSetup(defaultServerOptions)
	return c, l
}

func setupClient() (*Server, *testAsyncClient, *bufio.Reader) {
	s, c, cr, _ := rawSetup(defaultServerOptions)
	return s, c, cr
}

func checkClientsCount(t *testing.T, s *Server, expected int) {
	t.Helper()
	checkFor(t, 2*time.Second, 15*time.Millisecond, func() error {
		if nc := s.NumClients(); nc != expected {
			return fmt.Errorf("The number of expected connections was %v, got %v", expected, nc)
		}
		return nil
	})
}

func checkAccClientsCount(t *testing.T, acc *Account, expected int) {
	t.Helper()
	checkFor(t, 4*time.Second, 10*time.Millisecond, func() error {
		if nc := acc.NumConnections(); nc != expected {
			return fmt.Errorf("Expected account %q to have %v clients, got %v",
				acc.Name, expected, nc)
		}
		return nil
	})
}

func TestAsyncClientWithRunningServer(t *testing.T) {
	o := DefaultOptions()
	s := RunServer(o)
	defer s.Shutdown()

	c, _, _ := newClientForServer(s)
	defer c.close()

	buf := make([]byte, 1000000)
	writeLoopTxt := fmt.Sprintf("writeLoop(%p)", c.client)
	checkFor(t, time.Second, 15*time.Millisecond, func() error {
		n := runtime.Stack(buf, true)
		if count := strings.Count(string(buf[:n]), writeLoopTxt); count != 1 {
			return fmt.Errorf("writeLoop for client should have been started only once: %v", count)
		}
		return nil
	})
}

func TestClientCreateAndInfo(t *testing.T) {
	s, c, _, l := rawSetup(defaultServerOptions)
	defer c.close()

	if c.cid != 1 {
		t.Fatalf("Expected cid of 1 vs %d\n", c.cid)
	}
	if c.state != OP_START {
		t.Fatal("Expected state to be OP_START")
	}

	if !strings.HasPrefix(l, "INFO ") {
		t.Fatalf("INFO response incorrect: %s\n", l)
	}
	// Make sure payload is proper json
	var info serverInfo
	err := json.Unmarshal([]byte(l[5:]), &info)
	if err != nil {
		t.Fatalf("Could not parse INFO json: %v\n", err)
	}
	// Sanity checks
	if info.MaxPayload != MAX_PAYLOAD_SIZE ||
		info.AuthRequired || info.TLSRequired ||
		int(info.Port) != s.opts.Port {
		t.Fatalf("INFO inconsistent: %+v\n", info)
	}
}

func TestClientNoResponderSupport(t *testing.T) {
	opts := defaultServerOptions
	s := New(&opts)

	c, _, _ := newClientForServer(s)
	defer c.close()

	// Force header support if you want to do no_responders. Make sure headers are set.
	if err := c.parse([]byte("CONNECT {\"no_responders\":true}\r\n")); err == nil {
		t.Fatalf("Expected error")
	}

	c, cr, _ := newClientForServer(s)
	defer c.close()

	c.parseAsync("CONNECT {\"headers\":true, \"no_responders\":true}\r\nSUB reply 1\r\nPUB foo reply 2\r\nok\r\n")

	l, err := cr.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}

	am := hmsgPat.FindAllStringSubmatch(l, -1)
	if len(am) == 0 {
		t.Fatalf("Did not get a match for %q", l)
	}
	checkPayload(cr, []byte("NATS/1.0 503\r\nNats-Subject: foo\r\n\r\n\r\n"), t)
}

func TestServerHeaderSupport(t *testing.T) {
	opts := defaultServerOptions
	s := New(&opts)

	c, _, l := newClientForServer(s)
	defer c.close()

	if !strings.HasPrefix(l, "INFO ") {
		t.Fatalf("INFO response incorrect: %s\n", l)
	}
	var info serverInfo
	if err := json.Unmarshal([]byte(l[5:]), &info); err != nil {
		t.Fatalf("Could not parse INFO json: %v\n", err)
	}
	if !info.Headers {
		t.Fatalf("Expected by default for header support to be enabled")
	}

	opts.NoHeaderSupport = true
	opts.Port = -1
	s = New(&opts)

	c, _, l = newClientForServer(s)
	defer c.close()

	if err := json.Unmarshal([]byte(l[5:]), &info); err != nil {
		t.Fatalf("Could not parse INFO json: %v\n", err)
	}
	if info.Headers {
		t.Fatalf("Expected header support to be disabled")
	}
}

// This test specifically is not testing how headers are encoded in a raw msg.
// It wants to make sure the serve and clients agreement on when to use headers
// is bi-directional and functions properly.
func TestClientHeaderSupport(t *testing.T) {
	opts := defaultServerOptions
	s := New(&opts)

	c, _, _ := newClientForServer(s)
	defer c.close()

	// Even though the server supports headers we need to explicitly say we do in the
	// CONNECT. If we do not we should get an error.
	if err := c.parse([]byte("CONNECT {}\r\nHPUB foo 0 2\r\nok\r\n")); err != ErrMsgHeadersNotSupported {
		t.Fatalf("Expected to receive an error, got %v", err)
	}

	// This should succeed.
	c, _, _ = newClientForServer(s)
	defer c.close()

	if err := c.parse([]byte("CONNECT {\"headers\":true}\r\nHPUB foo 0 2\r\nok\r\n")); err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	// Now start a server without support.
	opts.NoHeaderSupport = true
	opts.Port = -1
	s = New(&opts)

	c, _, _ = newClientForServer(s)
	defer c.close()
	if err := c.parse([]byte("CONNECT {\"headers\":true}\r\nHPUB foo 0 2\r\nok\r\n")); err != ErrMsgHeadersNotSupported {
		t.Fatalf("Expected to receive an error, got %v", err)
	}
}

var hmsgPat = regexp.MustCompile(`HMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)[^\S\r\n]+(\d+)\r\n`)

func TestClientHeaderDeliverMsg(t *testing.T) {
	opts := defaultServerOptions
	s := New(&opts)

	c, cr, _ := newClientForServer(s)
	defer c.close()

	connect := "CONNECT {\"headers\":true}"
	subOp := "SUB foo 1"
	pubOp := "HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"
	cmd := strings.Join([]string{connect, subOp, pubOp}, "\r\n")

	c.parseAsync(cmd)
	l, err := cr.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}

	am := hmsgPat.FindAllStringSubmatch(l, -1)
	if len(am) == 0 {
		t.Fatalf("Did not get a match for %q", l)
	}
	matches := am[0]
	if len(matches) != 7 {
		t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 7)
	}
	if matches[SUB_INDEX] != "foo" {
		t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX])
	}
	if matches[SID_INDEX] != "1" {
		t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX])
	}
	if matches[HDR_INDEX] != "12" {
		t.Fatalf("Did not get correct msg length: '%s'\n", matches[HDR_INDEX])
	}
	if matches[TLEN_INDEX] != "14" {
		t.Fatalf("Did not get correct msg length: '%s'\n", matches[TLEN_INDEX])
	}
	checkPayload(cr, []byte("Name:Derek\r\nOK\r\n"), t)
}

var smsgPat = regexp.MustCompile(`^MSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n`)

func TestClientHeaderDeliverStrippedMsg(t *testing.T) {
	opts := defaultServerOptions
	s := New(&opts)

	c, _, _ := newClientForServer(s)
	defer c.close()

	b, br, _ := newClientForServer(s)
	defer b.close()

	// Does not support headers
	b.parseAsync("SUB foo 1\r\nPING\r\n")
	if _, err := br.ReadString('\n'); err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}

	connect := "CONNECT {\"headers\":true}"
	pubOp := "HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"
	cmd := strings.Join([]string{connect, pubOp}, "\r\n")
	c.parseAsync(cmd)
	// Read from 'b' client.
	l, err := br.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}
	am := smsgPat.FindAllStringSubmatch(l, -1)
	if len(am) == 0 {
		t.Fatalf("Did not get a correct match for %q", l)
	}
	matches := am[0]
	if len(matches) != 6 {
		t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 6)
	}
	if matches[SUB_INDEX] != "foo" {
		t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX])
	}
	if matches[SID_INDEX] != "1" {
		t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX])
	}
	if matches[LEN_INDEX] != "2" {
		t.Fatalf("Did not get correct msg length: '%s'\n", matches[LEN_INDEX])
	}
	checkPayload(br, []byte("OK\r\n"), t)
	if br.Buffered() != 0 {
		t.Fatalf("Expected no extra bytes to be buffered, got %d", br.Buffered())
	}
}

func TestClientHeaderDeliverQueueSubStrippedMsg(t *testing.T) {
	opts := defaultServerOptions
	s := New(&opts)

	c, _, _ := newClientForServer(s)
	defer c.close()

	b, br, _ := newClientForServer(s)
	defer b.close()

	// Does not support headers
	b.parseAsync("SUB foo bar 1\r\nPING\r\n")
	if _, err := br.ReadString('\n'); err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}

	connect := "CONNECT {\"headers\":true}"
	pubOp := "HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"
	cmd := strings.Join([]string{connect, pubOp}, "\r\n")
	c.parseAsync(cmd)
	// Read from 'b' client.
	l, err := br.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}
	am := smsgPat.FindAllStringSubmatch(l, -1)
	if len(am) == 0 {
		t.Fatalf("Did not get a correct match for %q", l)
	}
	matches := am[0]
	if len(matches) != 6 {
		t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 6)
	}
	if matches[SUB_INDEX] != "foo" {
		t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX])
	}
	if matches[SID_INDEX] != "1" {
		t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX])
	}
	if matches[LEN_INDEX] != "2" {
		t.Fatalf("Did not get correct msg length: '%s'\n", matches[LEN_INDEX])
	}
	checkPayload(br, []byte("OK\r\n"), t)
}

func TestNonTLSConnectionState(t *testing.T) {
	_, c, _ := setupClient()
	defer c.close()
	state := c.GetTLSConnectionState()
	if state != nil {
		t.Error("GetTLSConnectionState() returned non-nil")
	}
}

func TestClientConnect(t *testing.T) {
	_, c, _ := setupClient()
	defer c.close()

	// Basic Connect setting flags
	connectOp := []byte("CONNECT {\"verbose\":true,\"pedantic\":true,\"tls_required\":false,\"echo\":false}\r\n")
	err := c.parse(connectOp)
	if err != nil {
		t.Fatalf("Received error: %v\n", err)
	}
	if c.state != OP_START {
		t.Fatalf("Expected state of OP_START vs %d\n", c.state)
	}
	if !reflect.DeepEqual(c.opts, ClientOpts{Verbose: true, Pedantic: true, Echo: false}) {
		t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts)
	}

	// Test that we can capture user/pass
	connectOp = []byte("CONNECT {\"user\":\"derek\",\"pass\":\"foo\"}\r\n")
	c.opts = defaultOpts
	err = c.parse(connectOp)
	if err != nil {
		t.Fatalf("Received error: %v\n", err)
	}
	if c.state != OP_START {
		t.Fatalf("Expected state of OP_START vs %d\n", c.state)
	}
	if !reflect.DeepEqual(c.opts, ClientOpts{Echo: true, Verbose: true, Pedantic: true, Username: "derek", Password: "foo"}) {
		t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts)
	}

	// Test that we can capture client name
	connectOp = []byte("CONNECT {\"user\":\"derek\",\"pass\":\"foo\",\"name\":\"router\"}\r\n")
	c.opts = defaultOpts
	err = c.parse(connectOp)
	if err != nil {
		t.Fatalf("Received error: %v\n", err)
	}
	if c.state != OP_START {
		t.Fatalf("Expected state of OP_START vs %d\n", c.state)
	}

	if !reflect.DeepEqual(c.opts, ClientOpts{Echo: true, Verbose: true, Pedantic: true, Username: "derek", Password: "foo", Name: "router"}) {
		t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts)
	}

	// Test that we correctly capture auth tokens
	connectOp = []byte("CONNECT {\"auth_token\":\"YZZ222\",\"name\":\"router\"}\r\n")
	c.opts = defaultOpts
	err = c.parse(connectOp)
	if err != nil {
		t.Fatalf("Received error: %v\n", err)
	}
	if c.state != OP_START {
		t.Fatalf("Expected state of OP_START vs %d\n", c.state)
	}

	if !reflect.DeepEqual(c.opts, ClientOpts{Echo: true, Verbose: true, Pedantic: true, Token: "YZZ222", Name: "router"}) {
		t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts)
	}
}

func TestClientConnectProto(t *testing.T) {
	_, c, r := setupClient()
	defer c.close()

	// Basic Connect setting flags, proto should be zero (original proto)
	connectOp := []byte("CONNECT {\"verbose\":true,\"pedantic\":true,\"tls_required\":false}\r\n")
	err := c.parse(connectOp)
	if err != nil {
		t.Fatalf("Received error: %v\n", err)
	}
	if c.state != OP_START {
		t.Fatalf("Expected state of OP_START vs %d\n", c.state)
	}
	if !reflect.DeepEqual(c.opts, ClientOpts{Echo: true, Verbose: true, Pedantic: true, Protocol: ClientProtoZero}) {
		t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts)
	}

	// ProtoInfo
	connectOp = []byte(fmt.Sprintf("CONNECT {\"verbose\":true,\"pedantic\":true,\"tls_required\":false,\"protocol\":%d}\r\n", ClientProtoInfo))
	err = c.parse(connectOp)
	if err != nil {
		t.Fatalf("Received error: %v\n", err)
	}
	if c.state != OP_START {
		t.Fatalf("Expected state of OP_START vs %d\n", c.state)
	}
	if !reflect.DeepEqual(c.opts, ClientOpts{Echo: true, Verbose: true, Pedantic: true, Protocol: ClientProtoInfo}) {
		t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts)
	}
	if c.opts.Protocol != ClientProtoInfo {
		t.Fatalf("Protocol should have been set to %v, but is set to %v", ClientProtoInfo, c.opts.Protocol)
	}

	// Illegal Option
	connectOp = []byte("CONNECT {\"protocol\":22}\r\n")
	wg := sync.WaitGroup{}
	wg.Add(1)
	// The client here is using a pipe, we need to be dequeuing
	// data otherwise the server would be blocked trying to send
	// the error back to it.
	go func() {
		defer wg.Done()
		for {
			if _, _, err := r.ReadLine(); err != nil {
				return
			}
		}
	}()
	err = c.parse(connectOp)
	if err == nil {
		t.Fatalf("Expected to receive an error\n")
	}
	if err != ErrBadClientProtocol {
		t.Fatalf("Expected err of %q, got  %q\n", ErrBadClientProtocol, err)
	}
	wg.Wait()
}

func TestRemoteAddress(t *testing.T) {
	rc := &client{}

	// though in reality this will panic if it does not, adding coverage anyway
	if rc.RemoteAddress() != nil {
		t.Errorf("RemoteAddress() did not handle nil connection correctly")
	}

	_, c, _ := setupClient()
	defer c.close()
	addr := c.RemoteAddress()

	if addr.Network() != "pipe" {
		t.Errorf("RemoteAddress() returned invalid network: %s", addr.Network())
	}

	if addr.String() != "pipe" {
		t.Errorf("RemoteAddress() returned invalid string: %s", addr.String())
	}
}

func TestClientPing(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()

	// PING
	pingOp := "PING\r\n"
	c.parseAsync(pingOp)
	l, err := cr.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving info from server: %v\n", err)
	}
	if !strings.HasPrefix(l, "PONG\r\n") {
		t.Fatalf("PONG response incorrect: %s\n", l)
	}
}

var msgPat = regexp.MustCompile(`MSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n`)

const (
	SUB_INDEX   = 1
	SID_INDEX   = 2
	REPLY_INDEX = 4
	LEN_INDEX   = 5
	HDR_INDEX   = 5
	TLEN_INDEX  = 6
)

func grabPayload(cr *bufio.Reader, expected int) []byte {
	d := make([]byte, expected)
	n, _ := cr.Read(d)
	cr.ReadString('\n')
	return d[:n]
}

func checkPayload(cr *bufio.Reader, expected []byte, t *testing.T) {
	t.Helper()
	// Read in payload
	d := make([]byte, len(expected))
	n, err := cr.Read(d)
	if err != nil {
		t.Fatalf("Error receiving msg payload from server: %v\n", err)
	}
	if n != len(expected) {
		t.Fatalf("Did not read correct amount of bytes: %d vs %d\n", n, len(expected))
	}
	if !bytes.Equal(d, expected) {
		t.Fatalf("Did not read correct payload:: <%s>\n", d)
	}
}

func TestClientSimplePubSub(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()
	// SUB/PUB
	c.parseAsync("SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n")
	l, err := cr.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}
	matches := msgPat.FindAllStringSubmatch(l, -1)[0]
	if len(matches) != 6 {
		t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 6)
	}
	if matches[SUB_INDEX] != "foo" {
		t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX])
	}
	if matches[SID_INDEX] != "1" {
		t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX])
	}
	if matches[LEN_INDEX] != "5" {
		t.Fatalf("Did not get correct msg length: '%s'\n", matches[LEN_INDEX])
	}
	checkPayload(cr, []byte("hello\r\n"), t)
}

func TestClientPubSubNoEcho(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()
	// Specify no echo
	connectOp := []byte("CONNECT {\"echo\":false}\r\n")
	err := c.parse(connectOp)
	if err != nil {
		t.Fatalf("Received error: %v\n", err)
	}
	// SUB/PUB
	c.parseAsync("SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n")
	l, err := cr.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}
	// We should not receive anything but a PONG since we specified no echo.
	if !strings.HasPrefix(l, "PONG\r\n") {
		t.Fatalf("PONG response incorrect: %q\n", l)
	}
}

func TestClientSimplePubSubWithReply(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()

	// SUB/PUB
	c.parseAsync("SUB foo 1\r\nPUB foo bar 5\r\nhello\r\nPING\r\n")
	l, err := cr.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}
	matches := msgPat.FindAllStringSubmatch(l, -1)[0]
	if len(matches) != 6 {
		t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 6)
	}
	if matches[SUB_INDEX] != "foo" {
		t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX])
	}
	if matches[SID_INDEX] != "1" {
		t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX])
	}
	if matches[REPLY_INDEX] != "bar" {
		t.Fatalf("Did not get correct reply subject: '%s'\n", matches[REPLY_INDEX])
	}
	if matches[LEN_INDEX] != "5" {
		t.Fatalf("Did not get correct msg length: '%s'\n", matches[LEN_INDEX])
	}
}

func TestClientNoBodyPubSubWithReply(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()

	// SUB/PUB
	c.parseAsync("SUB foo 1\r\nPUB foo bar 0\r\n\r\nPING\r\n")
	l, err := cr.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving msg from server: %v\n", err)
	}
	matches := msgPat.FindAllStringSubmatch(l, -1)[0]
	if len(matches) != 6 {
		t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 6)
	}
	if matches[SUB_INDEX] != "foo" {
		t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX])
	}
	if matches[SID_INDEX] != "1" {
		t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX])
	}
	if matches[REPLY_INDEX] != "bar" {
		t.Fatalf("Did not get correct reply subject: '%s'\n", matches[REPLY_INDEX])
	}
	if matches[LEN_INDEX] != "0" {
		t.Fatalf("Did not get correct msg length: '%s'\n", matches[LEN_INDEX])
	}
}

func TestClientPubWithQueueSub(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()

	num := 100

	// Queue SUB/PUB
	subs := []byte("SUB foo g1 1\r\nSUB foo g1 2\r\n")
	pubs := []byte("PUB foo bar 5\r\nhello\r\n")
	op := []byte{}
	op = append(op, subs...)
	for i := 0; i < num; i++ {
		op = append(op, pubs...)
	}

	go c.parseAndClose(op)

	var n1, n2, received int
	for ; ; received++ {
		l, err := cr.ReadString('\n')
		if err != nil {
			break
		}
		matches := msgPat.FindAllStringSubmatch(l, -1)[0]

		// Count which sub
		switch matches[SID_INDEX] {
		case "1":
			n1++
		case "2":
			n2++
		}
		checkPayload(cr, []byte("hello\r\n"), t)
	}
	if received != num {
		t.Fatalf("Received wrong # of msgs: %d vs %d\n", received, num)
	}
	// Threshold for randomness for now
	if n1 < 20 || n2 < 20 {
		t.Fatalf("Received wrong # of msgs per subscriber: %d - %d\n", n1, n2)
	}
}

func TestSplitSubjectQueue(t *testing.T) {
	cases := []struct {
		name        string
		sq          string
		wantSubject []byte
		wantQueue   []byte
		wantErr     bool
	}{
		{name: "single subject",
			sq: "foo", wantSubject: []byte("foo"), wantQueue: nil},
		{name: "subject and queue",
			sq: "foo bar", wantSubject: []byte("foo"), wantQueue: []byte("bar")},
		{name: "subject and queue with surrounding spaces",
			sq: " foo bar ", wantSubject: []byte("foo"), wantQueue: []byte("bar")},
		{name: "subject and queue with extra spaces in the middle",
			sq: "foo  bar", wantSubject: []byte("foo"), wantQueue: []byte("bar")},
		{name: "subject, queue, and extra token",
			sq: "foo  bar fizz", wantSubject: []byte(nil), wantQueue: []byte(nil), wantErr: true},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			sub, que, err := splitSubjectQueue(c.sq)
			if err == nil && c.wantErr {
				t.Fatal("Expected error, but got nil")
			}
			if err != nil && !c.wantErr {
				t.Fatalf("Expected nil error, but got %v", err)
			}

			if !reflect.DeepEqual(sub, c.wantSubject) {
				t.Fatalf("Expected to get subject %#v, but instead got %#v", c.wantSubject, sub)
			}
			if !reflect.DeepEqual(que, c.wantQueue) {
				t.Fatalf("Expected to get queue %#v, but instead got %#v", c.wantQueue, que)
			}
		})
	}
}

func TestTypeString(t *testing.T) {
	cases := []struct {
		intType    int
		stringType string
	}{
		{
			intType:    CLIENT,
			stringType: "Client",
		},
		{
			intType:    ROUTER,
			stringType: "Router",
		},
		{
			intType:    GATEWAY,
			stringType: "Gateway",
		},
		{
			intType:    LEAF,
			stringType: "Leafnode",
		},
		{
			intType:    JETSTREAM,
			stringType: "JetStream",
		},
		{
			intType:    ACCOUNT,
			stringType: "Account",
		},
		{
			intType:    SYSTEM,
			stringType: "System",
		},
		{
			intType:    -1,
			stringType: "Unknown Type",
		},
	}
	for _, cs := range cases {
		c := &client{kind: cs.intType}
		typeStringVal := c.kindString()

		if typeStringVal != cs.stringType {
			t.Fatalf("Expected typeString  value  %q, but instead received %q", cs.stringType, typeStringVal)
		}
	}
}

func TestQueueSubscribePermissions(t *testing.T) {
	cases := []struct {
		name    string
		perms   *SubjectPermission
		subject string
		queue   string
		want    string
	}{
		{
			name:    "plain subscription on foo",
			perms:   &SubjectPermission{Allow: []string{"foo"}},
			subject: "foo",
			want:    "+OK\r\n",
		},
		{
			name:    "queue subscribe with allowed group",
			perms:   &SubjectPermission{Allow: []string{"foo bar"}},
			subject: "foo",
			queue:   "bar",
			want:    "+OK\r\n",
		},
		{
			name:    "queue subscribe with wildcard allowed group",
			perms:   &SubjectPermission{Allow: []string{"foo bar.*"}},
			subject: "foo",
			queue:   "bar.fizz",
			want:    "+OK\r\n",
		},
		{
			name:    "queue subscribe with full wildcard subject and subgroup",
			perms:   &SubjectPermission{Allow: []string{"> bar.>"}},
			subject: "whizz",
			queue:   "bar.bang",
			want:    "+OK\r\n",
		},
		{
			name:    "plain subscribe with full wildcard subject and subgroup",
			perms:   &SubjectPermission{Allow: []string{"> bar.>"}},
			subject: "whizz",
			want:    "-ERR 'Permissions Violation for Subscription to \"whizz\"'\r\n",
		},
		{
			name:    "deny plain subscription on foo",
			perms:   &SubjectPermission{Allow: []string{">"}, Deny: []string{"foo"}},
			subject: "foo",
			queue:   "bar",
			want:    "-ERR 'Permissions Violation for Subscription to \"foo\" using queue \"bar\"'\r\n",
		},
		{
			name:    "allow plain subscription, except foo",
			perms:   &SubjectPermission{Allow: []string{">"}, Deny: []string{"foo"}},
			subject: "bar",
			want:    "+OK\r\n",
		},
		{
			name:    "deny everything",
			perms:   &SubjectPermission{Allow: []string{">"}, Deny: []string{">"}},
			subject: "foo",
			queue:   "bar",
			want:    "-ERR 'Permissions Violation for Subscription to \"foo\" using queue \"bar\"'\r\n",
		},
		{
			name:    "can only subscribe to queues v1",
			perms:   &SubjectPermission{Allow: []string{"> v1.>"}},
			subject: "foo",
			queue:   "v1.prod",
			want:    "+OK\r\n",
		},
		{
			name:    "cannot subscribe to queues, plain subscribe ok",
			perms:   &SubjectPermission{Allow: []string{">"}, Deny: []string{"> >"}},
			subject: "foo",
			want:    "+OK\r\n",
		},
		{
			name:    "cannot subscribe to queues, queue subscribe not ok",
			perms:   &SubjectPermission{Deny: []string{"> >"}},
			subject: "foo",
			queue:   "bar",
			want:    "-ERR 'Permissions Violation for Subscription to \"foo\" using queue \"bar\"'\r\n",
		},
		{
			name:    "deny all queue subscriptions on dev or stg only",
			perms:   &SubjectPermission{Deny: []string{"> *.dev", "> *.stg"}},
			subject: "foo",
			queue:   "bar",
			want:    "+OK\r\n",
		},
		{
			name:    "allow only queue subscription on dev or stg",
			perms:   &SubjectPermission{Allow: []string{"> *.dev", "> *.stg"}},
			subject: "foo",
			queue:   "bar",
			want:    "-ERR 'Permissions Violation for Subscription to \"foo\" using queue \"bar\"'\r\n",
		},
		{
			name:    "deny queue subscriptions with subject foo",
			perms:   &SubjectPermission{Deny: []string{"foo >"}},
			subject: "foo",
			queue:   "bar",
			want:    "-ERR 'Permissions Violation for Subscription to \"foo\" using queue \"bar\"'\r\n",
		},
		{
			name:    "plain sub is allowed, but queue subscribe with queue not in list",
			perms:   &SubjectPermission{Allow: []string{"foo bar"}},
			subject: "foo",
			queue:   "fizz",
			want:    "-ERR 'Permissions Violation for Subscription to \"foo\" using queue \"fizz\"'\r\n",
		},
		{
			name:    "allow plain sub, but do queue subscribe",
			perms:   &SubjectPermission{Allow: []string{"foo"}},
			subject: "foo",
			queue:   "bar",
			want:    "+OK\r\n",
		},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			_, client, r := setupClient()
			defer client.close()

			client.RegisterUser(&User{
				Permissions: &Permissions{Subscribe: c.perms},
			})
			connect := []byte("CONNECT {\"verbose\":true}\r\n")
			qsub := []byte(fmt.Sprintf("SUB %s %s 1\r\n", c.subject, c.queue))

			go client.parseAndClose(append(connect, qsub...))

			var buf bytes.Buffer
			if _, err := io.Copy(&buf, r); err != nil {
				t.Fatal(err)
			}

			// Extra OK is from the successful CONNECT.
			want := "+OK\r\n" + c.want
			if got := buf.String(); got != want {
				t.Fatalf("Expected to receive %q, but instead received %q", want, got)
			}
		})
	}
}

func TestClientPubWithQueueSubNoEcho(t *testing.T) {
	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	nc1, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
	if err != nil {
		t.Fatalf("Error on connect: %v", err)
	}
	defer nc1.Close()

	// Grab the client from server and set no echo by hand.
	s.mu.Lock()
	lc := len(s.clients)
	c := s.clients[s.gcid]
	s.mu.Unlock()

	if lc != 1 {
		t.Fatalf("Expected only 1 client but got %d\n", lc)
	}
	if c == nil {
		t.Fatal("Expected to retrieve client\n")
	}
	c.mu.Lock()
	c.echo = false
	c.mu.Unlock()

	// Queue sub on nc1.
	_, err = nc1.QueueSubscribe("foo", "bar", func(*nats.Msg) {})
	if err != nil {
		t.Fatalf("Error on subscribe: %v", err)
	}
	nc1.Flush()

	nc2, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
	if err != nil {
		t.Fatalf("Error on connect: %v", err)
	}
	defer nc2.Close()

	n := int32(0)
	cb := func(m *nats.Msg) {
		atomic.AddInt32(&n, 1)
	}

	_, err = nc2.QueueSubscribe("foo", "bar", cb)
	if err != nil {
		t.Fatalf("Error on subscribe: %v", err)
	}
	nc2.Flush()

	// Now publish 100 messages on nc1 which does not allow echo.
	for i := 0; i < 100; i++ {
		nc1.Publish("foo", []byte("Hello"))
	}
	nc1.Flush()
	nc2.Flush()

	checkFor(t, 5*time.Second, 10*time.Millisecond, func() error {
		num := atomic.LoadInt32(&n)
		if num != int32(100) {
			return fmt.Errorf("Expected all the msgs to be received by nc2, got %d\n", num)
		}
		return nil
	})
}

func TestClientUnSub(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()

	num := 1

	// SUB/PUB
	subs := []byte("SUB foo 1\r\nSUB foo 2\r\n")
	unsub := []byte("UNSUB 1\r\n")
	pub := []byte("PUB foo bar 5\r\nhello\r\n")

	op := []byte{}
	op = append(op, subs...)
	op = append(op, unsub...)
	op = append(op, pub...)

	go c.parseAndClose(op)

	var received int
	for ; ; received++ {
		l, err := cr.ReadString('\n')
		if err != nil {
			break
		}
		matches := msgPat.FindAllStringSubmatch(l, -1)[0]
		if matches[SID_INDEX] != "2" {
			t.Fatalf("Received msg on unsubscribed subscription!\n")
		}
		checkPayload(cr, []byte("hello\r\n"), t)
	}
	if received != num {
		t.Fatalf("Received wrong # of msgs: %d vs %d\n", received, num)
	}
}

func TestClientUnSubMax(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()

	num := 10
	exp := 5

	// SUB/PUB
	subs := []byte("SUB foo 1\r\n")
	unsub := []byte("UNSUB 1 5\r\n")
	pub := []byte("PUB foo bar 5\r\nhello\r\n")

	op := []byte{}
	op = append(op, subs...)
	op = append(op, unsub...)
	for i := 0; i < num; i++ {
		op = append(op, pub...)
	}

	go c.parseAndClose(op)

	var received int
	for ; ; received++ {
		l, err := cr.ReadString('\n')
		if err != nil {
			break
		}
		matches := msgPat.FindAllStringSubmatch(l, -1)[0]
		if matches[SID_INDEX] != "1" {
			t.Fatalf("Received msg on unsubscribed subscription!\n")
		}
		checkPayload(cr, []byte("hello\r\n"), t)
	}
	if received != exp {
		t.Fatalf("Received wrong # of msgs: %d vs %d\n", received, exp)
	}
}

func TestClientAutoUnsubExactReceived(t *testing.T) {
	_, c, _ := setupClient()
	defer c.close()

	// SUB/PUB
	subs := []byte("SUB foo 1\r\n")
	unsub := []byte("UNSUB 1 1\r\n")
	pub := []byte("PUB foo bar 2\r\nok\r\n")

	op := []byte{}
	op = append(op, subs...)
	op = append(op, unsub...)
	op = append(op, pub...)

	c.parse(op)

	// We should not have any subscriptions in place here.
	if len(c.subs) != 0 {
		t.Fatalf("Wrong number of subscriptions: expected 0, got %d\n", len(c.subs))
	}
}

func TestClientUnsubAfterAutoUnsub(t *testing.T) {
	_, c, _ := setupClient()
	defer c.close()

	// SUB/UNSUB/UNSUB
	subs := []byte("SUB foo 1\r\n")
	asub := []byte("UNSUB 1 1\r\n")
	unsub := []byte("UNSUB 1\r\n")

	op := []byte{}
	op = append(op, subs...)
	op = append(op, asub...)
	op = append(op, unsub...)

	c.parse(op)

	// We should not have any subscriptions in place here.
	if len(c.subs) != 0 {
		t.Fatalf("Wrong number of subscriptions: expected 0, got %d\n", len(c.subs))
	}
}

func TestClientRemoveSubsOnDisconnect(t *testing.T) {
	s, c, _ := setupClient()
	defer c.close()
	subs := []byte("SUB foo 1\r\nSUB bar 2\r\n")

	c.parse(subs)

	if s.NumSubscriptions() != 2 {
		t.Fatalf("Should have 2 subscriptions, got %d\n", s.NumSubscriptions())
	}
	c.closeConnection(ClientClosed)
	checkExpectedSubs(t, 0, s)
}

func TestClientDoesNotAddSubscriptionsWhenConnectionClosed(t *testing.T) {
	_, c, _ := setupClient()
	c.close()
	subs := []byte("SUB foo 1\r\nSUB bar 2\r\n")

	c.parse(subs)

	if c.acc.sl.Count() != 0 {
		t.Fatalf("Should have no subscriptions after close, got %d\n", c.acc.sl.Count())
	}
}

func TestClientMapRemoval(t *testing.T) {
	s, c, _ := setupClient()
	c.close()

	checkClientsCount(t, s, 0)
}

func TestAuthorizationTimeout(t *testing.T) {
	serverOptions := DefaultOptions()
	serverOptions.Authorization = "my_token"
	serverOptions.AuthTimeout = 0.4
	s := RunServer(serverOptions)
	defer s.Shutdown()

	conn, err := net.Dial("tcp", net.JoinHostPort(serverOptions.Host, fmt.Sprintf("%d", serverOptions.Port)))
	if err != nil {
		t.Fatalf("Error dialing server: %v\n", err)
	}
	defer conn.Close()
	client := bufio.NewReaderSize(conn, maxBufSize)
	if _, err := client.ReadString('\n'); err != nil {
		t.Fatalf("Error receiving info from server: %v\n", err)
	}
	time.Sleep(3 * secondsToDuration(serverOptions.AuthTimeout))
	l, err := client.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving info from server: %v\n", err)
	}
	if !strings.Contains(l, "Authentication Timeout") {
		t.Fatalf("Authentication Timeout response incorrect: %q\n", l)
	}
}

// This is from bug report #18
func TestTwoTokenPubMatchSingleTokenSub(t *testing.T) {
	_, c, cr := setupClient()
	defer c.close()
	test := "PUB foo.bar 5\r\nhello\r\nSUB foo 1\r\nPING\r\nPUB foo.bar 5\r\nhello\r\nPING\r\n"
	c.parseAsync(test)
	l, err := cr.ReadString('\n')
	if err != nil {
		t.Fatalf("Error receiving info from server: %v\n", err)
	}
	if !strings.HasPrefix(l, "PONG\r\n") {
		t.Fatalf("PONG response incorrect: %q\n", l)
	}
	// Expect just a pong, no match should exist here..
	l, _ = cr.ReadString('\n')
	if !strings.HasPrefix(l, "PONG\r\n") {
		t.Fatalf("PONG response was expected, got: %q\n", l)
	}
}

func TestUnsubRace(t *testing.T) {
	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	url := fmt.Sprintf("nats://%s:%d",
		s.getOpts().Host,
		s.Addr().(*net.TCPAddr).Port,
	)
	nc, err := nats.Connect(url)
	if err != nil {
		t.Fatalf("Error creating client to %s: %v\n", url, err)
	}
	defer nc.Close()

	ncp, err := nats.Connect(url)
	if err != nil {
		t.Fatalf("Error creating client: %v\n", err)
	}
	defer ncp.Close()

	sub, _ := nc.Subscribe("foo", func(m *nats.Msg) {
		// Just eat it..
	})
	nc.Flush()

	var wg sync.WaitGroup

	wg.Add(1)

	go func() {
		for i := 0; i < 10000; i++ {
			ncp.Publish("foo", []byte("hello"))
		}
		wg.Done()
	}()

	time.Sleep(5 * time.Millisecond)

	sub.Unsubscribe()

	wg.Wait()
}

func TestClientCloseTLSConnection(t *testing.T) {
	opts, err := ProcessConfigFile("./configs/tls.conf")
	if err != nil {
		t.Fatalf("Error processing config file: %v", err)
	}
	opts.TLSTimeout = 100
	opts.NoLog = true
	opts.NoSigs = true
	s := RunServer(opts)
	defer s.Shutdown()

	endpoint := net.JoinHostPort(opts.Host, fmt.Sprintf("%d", opts.Port))
	conn, err := net.DialTimeout("tcp", endpoint, 2*time.Second)
	if err != nil {
		t.Fatalf("Unexpected error on dial: %v", err)
	}
	defer conn.Close()
	br := bufio.NewReaderSize(conn, 100)
	if _, err := br.ReadString('\n'); err != nil {
		t.Fatalf("Unexpected error reading INFO: %v", err)
	}

	tlsConn := tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
	defer tlsConn.Close()
	if err := tlsConn.Handshake(); err != nil {
		t.Fatalf("Unexpected error during handshake: %v", err)
	}
	br = bufio.NewReaderSize(tlsConn, 100)
	connectOp := []byte("CONNECT {\"user\":\"derek\",\"pass\":\"foo\",\"verbose\":false,\"pedantic\":false,\"tls_required\":true}\r\n")
	if _, err := tlsConn.Write(connectOp); err != nil {
		t.Fatalf("Unexpected error writing CONNECT: %v", err)
	}
	if _, err := tlsConn.Write([]byte("PING\r\n")); err != nil {
		t.Fatalf("Unexpected error writing PING: %v", err)
	}
	if _, err := br.ReadString('\n'); err != nil {
		t.Fatalf("Unexpected error reading PONG: %v", err)
	}

	// Check that client is registered.
	checkClientsCount(t, s, 1)
	var cli *client
	s.mu.Lock()
	for _, c := range s.clients {
		cli = c
		break
	}
	s.mu.Unlock()
	if cli == nil {
		t.Fatal("Did not register client on time")
	}
	// Test GetTLSConnectionState
	state := cli.GetTLSConnectionState()
	if state == nil {
		t.Error("GetTLSConnectionState() returned nil")
	}

	// Test RemoteAddress
	addr := cli.RemoteAddress()
	if addr == nil {
		t.Error("RemoteAddress() returned nil")
	}

	if addr.(*net.TCPAddr).IP.String() != "127.0.0.1" {
		t.Error("RemoteAddress() returned incorrect ip " + addr.String())
	}

	// Fill the buffer. We want to timeout on write so that nc.Close()
	// would block due to a write that cannot complete.
	buf := make([]byte, 64*1024)
	done := false
	for !done {
		cli.nc.SetWriteDeadline(time.Now().Add(time.Second))
		if _, err := cli.nc.Write(buf); err != nil {
			done = true
		}
		cli.nc.SetWriteDeadline(time.Time{})
	}
	ch := make(chan bool)
	go func() {
		select {
		case <-ch:
			return
		case <-time.After(3 * time.Second):
			fmt.Println("!!!! closeConnection is blocked, test will hang !!!")
			return
		}
	}()
	// Close the client
	cli.closeConnection(ClientClosed)
	ch <- true
}

// This tests issue #558
func TestWildcardCharsInLiteralSubjectWorks(t *testing.T) {
	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
	if err != nil {
		t.Fatalf("Error on connect: %v", err)
	}
	defer nc.Close()

	ch := make(chan bool, 1)
	// This subject is a literal even though it contains `*` and `>`,
	// they are not treated as wildcards.
	subj := "foo.bar,*,>,baz"
	cb := func(_ *nats.Msg) {
		ch <- true
	}
	for i := 0; i < 2; i++ {
		sub, err := nc.Subscribe(subj, cb)
		if err != nil {
			t.Fatalf("Error on subscribe: %v", err)
		}
		if err := nc.Flush(); err != nil {
			t.Fatalf("Error on flush: %v", err)
		}
		if err := nc.LastError(); err != nil {
			t.Fatalf("Server reported error: %v", err)
		}
		if err := nc.Publish(subj, []byte("msg")); err != nil {
			t.Fatalf("Error on publish: %v", err)
		}
		select {
		case <-ch:
		case <-time.After(time.Second):
			t.Fatalf("Should have received the message")
		}
		if err := sub.Unsubscribe(); err != nil {
			t.Fatalf("Error on unsubscribe: %v", err)
		}
	}
}

// This test ensures that coalescing into the fixed-size output
// queues works as expected. When bytes are queued up, they should
// not overflow a buffer until the capacity is exceeded, at which
// point a new buffer should be added.
func TestClientOutboundQueueCoalesce(t *testing.T) {
	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
	if err != nil {
		t.Fatalf("Error on connect: %v", err)
	}
	defer nc.Close()

	clients := s.GlobalAccount().getClients()
	if len(clients) != 1 {
		t.Fatal("Expecting a client to exist")
	}
	client := clients[0]
	client.mu.Lock()
	defer client.mu.Unlock()

	// First up, queue something small into the queue.
	client.queueOutbound([]byte{1, 2, 3, 4, 5})

	if len(client.out.nb) != 1 {
		t.Fatal("Expecting a single queued buffer")
	}
	if l := len(client.out.nb[0]); l != 5 {
		t.Fatalf("Expecting only 5 bytes in the first queued buffer, found %d instead", l)
	}

	// Then queue up a few more bytes, but not enough
	// to overflow into the next buffer.
	client.queueOutbound([]byte{6, 7, 8, 9, 10})

	if len(client.out.nb) != 1 {
		t.Fatal("Expecting a single queued buffer")
	}
	if l := len(client.out.nb[0]); l != 10 {
		t.Fatalf("Expecting 10 bytes in the first queued buffer, found %d instead", l)
	}

	// Finally, queue up something that is guaranteed
	// to overflow.
	b := nbPoolSmall.Get().(*[nbPoolSizeSmall]byte)[:]
	b = b[:cap(b)]
	client.queueOutbound(b)
	if len(client.out.nb) != 2 {
		t.Fatal("Expecting buffer to have overflowed")
	}
	if l := len(client.out.nb[0]); l != cap(b) {
		t.Fatalf("Expecting %d bytes in the first queued buffer, found %d instead", cap(b), l)
	}
	if l := len(client.out.nb[1]); l != 10 {
		t.Fatalf("Expecting 10 bytes in the second queued buffer, found %d instead", l)
	}
}

func TestClientTraceRace(t *testing.T) {
	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	// Activate trace logging
	s.SetLogger(&DummyLogger{}, false, true)

	nc1, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
	if err != nil {
		t.Fatalf("Error on connect: %v", err)
	}
	defer nc1.Close()
	total := 10000
	count := 0
	ch := make(chan bool, 1)
	if _, err := nc1.Subscribe("foo", func(_ *nats.Msg) {
		count++
		if count == total {
			ch <- true
		}
	}); err != nil {
		t.Fatalf("Error on subscribe: %v", err)
	}
	nc2, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
	if err != nil {
		t.Fatalf("Error on connect: %v", err)
	}
	defer nc2.Close()

	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer wg.Done()
		for i := 0; i < total; i++ {
			nc1.Publish("bar", []byte("hello"))
		}
	}()
	for i := 0; i < total; i++ {
		nc2.Publish("foo", []byte("hello"))
	}
	if err := wait(ch); err != nil {
		t.Fatal("Did not get all our messages")
	}
	wg.Wait()
}

func TestClientUserInfo(t *testing.T) {
	pnkey := "UD6AYQSOIN2IN5OGC6VQZCR4H3UFMIOXSW6NNS6N53CLJA4PB56CEJJI"
	c := &client{
		cid: 1024,
		opts: ClientOpts{
			Nkey: pnkey,
		},
	}
	got := c.getAuthUser()
	expected := `Nkey "UD6AYQSOIN2IN5OGC6VQZCR4H3UFMIOXSW6NNS6N53CLJA4PB56CEJJI"`
	if got != expected {
		t.Errorf("Expected %q, got %q", expected, got)
	}

	c = &client{
		cid: 1024,
		opts: ClientOpts{
			Username: "foo",
		},
	}
	got = c.getAuthUser()
	expected = `User "foo"`
	if got != expected {
		t.Errorf("Expected %q, got %q", expected, got)
	}

	c = &client{
		cid:  1024,
		opts: ClientOpts{},
	}
	got = c.getAuthUser()
	expected = `User "N/A"`
	if got != expected {
		t.Errorf("Expected %q, got %q", expected, got)
	}

	c = &client{
		cid: 1024,
		opts: ClientOpts{
			Token: "s3cr3t!",
		},
	}
	got = c.getAuthUser()
	expected = `Token "[REDACTED]"`
	if got != expected {
		t.Errorf("Expected %q, got %q", expected, got)
	}
}

type captureWarnLogger struct {
	DummyLogger
	warn chan string
}

func (l *captureWarnLogger) Warnf(format string, v ...any) {
	select {
	case l.warn <- fmt.Sprintf(format, v...):
	default:
	}
}

func TestReadloopWarning(t *testing.T) {
	readLoopReportThreshold = 100 * time.Millisecond
	defer func() { readLoopReportThreshold = readLoopReport }()

	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	l := &captureWarnLogger{warn: make(chan string, 1)}
	s.SetLogger(l, false, false)

	url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)
	nc := natsConnect(t, url)
	defer nc.Close()
	natsSubSync(t, nc, "foo")
	natsFlush(t, nc)
	cid, _ := nc.GetClientID()

	sender := natsConnect(t, url)
	defer sender.Close()

	wg := sync.WaitGroup{}
	wg.Add(1)
	c := s.getClient(cid)
	c.mu.Lock()
	go func() {
		defer wg.Done()
		time.Sleep(250 * time.Millisecond)
		c.mu.Unlock()
	}()

	natsPub(t, sender, "foo", make([]byte, 100))
	natsFlush(t, sender)

	select {
	case warn := <-l.warn:
		if !strings.Contains(warn, "Readloop") {
			t.Fatalf("unexpected warning: %v", warn)
		}
	case <-time.After(2 * time.Second):
		t.Fatalf("No warning printed")
	}
	wg.Wait()
}

func TestTraceMsg(t *testing.T) {
	c := &client{}

	// Enable message trace
	c.trace = true

	cases := []struct {
		Desc            string
		Msg             []byte
		Wanted          string
		MaxTracedMsgLen int
	}{
		{
			Desc:            "normal length",
			Msg:             []byte(fmt.Sprintf("normal%s", CR_LF)),
			Wanted:          "<<- MSG_PAYLOAD: [\"normal\"]",
			MaxTracedMsgLen: 10,
		},
		{
			Desc:            "over length",
			Msg:             []byte(fmt.Sprintf("over length%s", CR_LF)),
			Wanted:          "<<- MSG_PAYLOAD: [\"over lengt...\"]",
			MaxTracedMsgLen: 10,
		},
		{
			Desc:            "unlimited length",
			Msg:             []byte(fmt.Sprintf("unlimited length%s", CR_LF)),
			Wanted:          "<<- MSG_PAYLOAD: [\"unlimited length\"]",
			MaxTracedMsgLen: 0,
		},
		{
			Desc:            "negative max traced msg len",
			Msg:             []byte(fmt.Sprintf("negative max traced msg len%s", CR_LF)),
			Wanted:          "<<- MSG_PAYLOAD: [\"negative max traced msg len\"]",
			MaxTracedMsgLen: -1,
		},
	}

	for _, ut := range cases {
		c.srv = &Server{
			opts: &Options{MaxTracedMsgLen: ut.MaxTracedMsgLen},
		}
		c.srv.SetLogger(&DummyLogger{}, true, true)

		c.traceMsg(ut.Msg)

		got := c.srv.logging.logger.(*DummyLogger).Msg
		if !reflect.DeepEqual(ut.Wanted, got) {
			t.Errorf("Desc: %s. Msg %q. Traced msg want: %s, got: %s", ut.Desc, ut.Msg, ut.Wanted, got)
		}
	}
}

func TestTraceMsgHeadersOnly(t *testing.T) {
	c := &client{}
	// Enable message trace
	c.trace = true

	hdr := fmt.Sprintf(`NATS/1.0%sFoo: 1%s%s`, CR_LF, CR_LF, CR_LF)
	hdr2 := fmt.Sprintf(`NATS/1.0%sFoo: 1%sBar: 2%s%s`, CR_LF, CR_LF, CR_LF, CR_LF)

	cases := []struct {
		Desc            string
		Msg             []byte
		Hdr             int
		Wanted          string
		MaxTracedMsgLen int
	}{
		{
			Desc:            "payload only",
			Msg:             []byte(`test\r\n`),
			Hdr:             0,
			Wanted:          _EMPTY_,
			MaxTracedMsgLen: 0,
		},
		{
			Desc:            "header only",
			Msg:             []byte(hdr),
			Hdr:             len(hdr),
			Wanted:          `<<- MSG_PAYLOAD: ["NATS/1.0\r\nFoo: 1"]`,
			MaxTracedMsgLen: 0,
		},
		{
			Desc:            "with header and payload",
			Msg:             []byte(fmt.Sprintf("%stest%s", hdr, CR_LF)),
			Hdr:             len(hdr),
			Wanted:          `<<- MSG_PAYLOAD: ["NATS/1.0\r\nFoo: 1"]`,
			MaxTracedMsgLen: 0,
		},
		{
			Desc:            "max length",
			Msg:             []byte(hdr),
			Hdr:             len(hdr),
			Wanted:          `<<- MSG_PAYLOAD: ["NATS/1..."]`,
			MaxTracedMsgLen: 6,
		},
		{
			Desc:            "two headers max length",
			Msg:             []byte(hdr2),
			Hdr:             len(hdr2),
			Wanted:          `<<- MSG_PAYLOAD: ["NATS/1.0\r\nFoo: 1\r\nBar..."]`,
			MaxTracedMsgLen: 21,
		},
	}

	for _, ut := range cases {
		t.Run(ut.Desc, func(t *testing.T) {
			c.srv = &Server{
				opts: &Options{MaxTracedMsgLen: ut.MaxTracedMsgLen, TraceHeaders: true},
			}
			c.srv.SetLogger(&DummyLogger{}, true, true)
			c.pa.hdr = ut.Hdr

			c.traceMsg(ut.Msg)

			got := c.srv.logging.logger.(*DummyLogger).Msg
			require_Equal(t, string(ut.Wanted), got)
		})
	}
}

func TestTraceMsgDelivery(t *testing.T) {
	logger := &DummyLogger{}

	opts := DefaultOptions()
	opts.Trace = true
	s := RunServer(opts)
	s.SetLogger(logger, true, true)
	defer s.Shutdown()

	nc, err := nats.Connect(s.ClientURL())
	require_NoError(t, err)
	defer nc.Close()

	ncp, err := nats.Connect(s.ClientURL())
	require_NoError(t, err)
	defer ncp.Close()

	_, err = nc.Subscribe("foo", func(msg *nats.Msg) {
		m := nats.NewMsg(msg.Reply)
		m.Header["A"] = []string{"1"}
		m.Header["B"] = []string{"2"}
		m.Data = []byte("Hi Traced")
		msg.RespondMsg(m)
	})
	require_NoError(t, err)
	nc.Flush()

	msg := nats.NewMsg("foo")
	msg.Header = nats.Header{}
	msg.Header["A"] = []string{"A:1"}
	msg.Header["B"] = []string{"B:2"}
	msg.Data = []byte("Hello Traced")
	_, err = ncp.RequestMsg(msg, 100*time.Millisecond)
	require_NoError(t, err)

	// Wait for logging to settle and safely read the message.
	time.Sleep(50 * time.Millisecond)
	logger.Lock()
	m := logger.Msg
	logger.Unlock()
	require_Contains(t, m, "->> MSG_PAYLOAD:")
	require_Contains(t, m, "NATS/1.0")
	require_Contains(t, m, "Hi Traced")

	_, err = nc.Subscribe("bar", func(msg *nats.Msg) {
		m := nats.NewMsg(msg.Reply)
		m.Data = []byte("Plain Response")
		msg.RespondMsg(m)
	})
	require_NoError(t, err)
	nc.Flush()

	msg = nats.NewMsg("bar")
	_, err = ncp.RequestMsg(msg, 100*time.Millisecond)
	require_NoError(t, err)

	// Wait and safely read again.
	time.Sleep(50 * time.Millisecond)
	logger.Lock()
	m = logger.Msg
	logger.Unlock()
	require_Contains(t, m, "->> MSG_PAYLOAD:")
	require_Contains(t, m, "Plain Response")
}

func TestTraceMsgDeliveryWithHeaders(t *testing.T) {
	c := &client{}
	c.trace = true
	hdr := fmt.Sprintf(`NATS/1.0%sFoo: 1%s%s`, CR_LF, CR_LF, CR_LF)
	hdr2 := fmt.Sprintf(`NATS/1.0%sFoo: bar%sBar: baz%s%s`, CR_LF, CR_LF, CR_LF, CR_LF)

	cases := []struct {
		name         string
		msg          []byte
		hdr          int
		traceDeliver bool
		traceHeaders bool
		expected     string
	}{
		{
			name:         "delivery with headers enabled",
			msg:          []byte(hdr),
			hdr:          len(hdr),
			traceHeaders: true,
			expected:     `->> MSG_PAYLOAD: ["NATS/1.0\r\nFoo: 1"]`,
		},
		{
			name:         "delivery with full message",
			msg:          []byte(fmt.Sprintf("%stest%s", hdr, CR_LF)),
			hdr:          len(hdr),
			traceHeaders: false,
			expected:     `->> MSG_PAYLOAD: ["NATS/1.0\r\nFoo: 1\r\n\r\ntest"]`,
		},
		{
			name:         "delivery with headers only",
			msg:          []byte(fmt.Sprintf("%stest%s", hdr, CR_LF)),
			hdr:          len(hdr),
			traceHeaders: true,
			expected:     `->> MSG_PAYLOAD: ["NATS/1.0\r\nFoo: 1"]`,
		},
		{
			name:         "delivery multiple headers",
			msg:          []byte(hdr2),
			hdr:          len(hdr2),
			traceHeaders: true,
			expected:     `->> MSG_PAYLOAD: ["NATS/1.0\r\nFoo: bar\r\nBar: baz"]`,
		},
		{
			name:         "delivery with payload but headers only tracing",
			msg:          []byte(fmt.Sprintf("%spayload data%s", hdr2, CR_LF)),
			hdr:          len(hdr2),
			traceHeaders: true,
			expected:     `->> MSG_PAYLOAD: ["NATS/1.0\r\nFoo: bar\r\nBar: baz"]`,
		},
		{
			name:         "delivery no headers but tracing enabled",
			msg:          []byte(fmt.Sprintf("plain message%s", CR_LF)),
			hdr:          0,
			traceHeaders: false,
			expected:     `->> MSG_PAYLOAD: ["plain message"]`,
		},
		{
			name:         "delivery headers disabled but deliver enabled",
			msg:          []byte(fmt.Sprintf("%spayload%s", hdr, CR_LF)),
			hdr:          len(hdr),
			traceHeaders: false,
			expected:     `->> MSG_PAYLOAD: ["NATS/1.0\r\nFoo: 1\r\n\r\npayload"]`,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			logger := &DummyLogger{}

			c.srv = &Server{
				opts: &Options{
					Trace:        true,
					TraceHeaders: tc.traceHeaders,
				},
			}
			c.srv.SetLogger(logger, true, true)
			c.pa.hdr = tc.hdr
			c.traceMsgDelivery(tc.msg, tc.hdr)
			got := logger.Msg
			if tc.expected == "" {
				// Disabled
				require_Equal(t, tc.expected, got)
			} else {
				require_True(t, strings.Contains(got, "->> MSG_PAYLOAD:"))
				require_Equal(t, tc.expected, got)
			}
		})
	}
}

func TestClientMaxPending(t *testing.T) {
	opts := DefaultOptions()
	opts.MaxPending = math.MaxInt32 + 1
	s := RunServer(opts)
	defer s.Shutdown()

	nc := natsConnect(t, fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
	defer nc.Close()

	sub := natsSubSync(t, nc, "foo")
	natsPub(t, nc, "foo", []byte("msg"))
	natsNexMsg(t, sub, 100*time.Millisecond)
}

func TestResponsePermissions(t *testing.T) {
	for i, test := range []struct {
		name  string
		perms *ResponsePermission
	}{
		{"max_msgs", &ResponsePermission{MaxMsgs: 2, Expires: time.Hour}},
		{"no_expire_limit", &ResponsePermission{MaxMsgs: 3, Expires: -1 * time.Millisecond}},
		{"expire", &ResponsePermission{MaxMsgs: 1000, Expires: 100 * time.Millisecond}},
		{"no_msgs_limit", &ResponsePermission{MaxMsgs: -1, Expires: 100 * time.Millisecond}},
	} {
		t.Run(test.name, func(t *testing.T) {
			opts := DefaultOptions()
			u1 := &User{
				Username:    "service",
				Password:    "pwd",
				Permissions: &Permissions{Response: test.perms},
			}
			u2 := &User{Username: "ivan", Password: "pwd"}
			opts.Users = []*User{u1, u2}
			s := RunServer(opts)
			defer s.Shutdown()

			svcNC := natsConnect(t, fmt.Sprintf("nats://service:pwd@%s:%d", opts.Host, opts.Port))
			defer svcNC.Close()
			reqSub := natsSubSync(t, svcNC, "request")
			natsFlush(t, svcNC)

			nc := natsConnect(t, fmt.Sprintf("nats://ivan:pwd@%s:%d", opts.Host, opts.Port))
			defer nc.Close()

			replySub := natsSubSync(t, nc, "reply")

			natsPubReq(t, nc, "request", "reply", []byte("req1"))

			req1 := natsNexMsg(t, reqSub, 100*time.Millisecond)

			checkFailed := func(t *testing.T) {
				t.Helper()
				if reply, err := replySub.NextMsg(100 * time.Millisecond); err != nats.ErrTimeout {
					if reply != nil {
						t.Fatalf("Expected to receive timeout, got reply=%q", reply.Data)
					} else {
						t.Fatalf("Unexpected error: %v", err)
					}
				}
			}

			switch i {
			case 0:
				// Should allow only 2 replies...
				for i := 0; i < 10; i++ {
					natsPub(t, svcNC, req1.Reply, []byte("reply"))
				}
				natsNexMsg(t, replySub, 100*time.Millisecond)
				natsNexMsg(t, replySub, 100*time.Millisecond)
				// The next should fail...
				checkFailed(t)
			case 1:
				// Expiration is set to -1ms, which should count as infinite...
				natsPub(t, svcNC, req1.Reply, []byte("reply"))
				// Sleep a bit before next send
				time.Sleep(50 * time.Millisecond)
				natsPub(t, svcNC, req1.Reply, []byte("reply"))
				// Make sure we receive both
				natsNexMsg(t, replySub, 100*time.Millisecond)
				natsNexMsg(t, replySub, 100*time.Millisecond)
			case 2:
				fallthrough
			case 3:
				// Expire set to 100ms so make sure we wait more between
				// next publish
				natsPub(t, svcNC, req1.Reply, []byte("reply"))
				time.Sleep(200 * time.Millisecond)
				natsPub(t, svcNC, req1.Reply, []byte("reply"))
				// Should receive one, and fail on the other
				natsNexMsg(t, replySub, 100*time.Millisecond)
				checkFailed(t)
			}
			// When testing expiration, sleep before sending next reply
			if i >= 2 {
				time.Sleep(400 * time.Millisecond)
			}
		})
	}
}

func TestPingNotSentTooSoon(t *testing.T) {
	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	doneCh := make(chan bool, 1)
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			s.Connz(nil)
			select {
			case <-doneCh:
				return
			case <-time.After(time.Millisecond):
			}
		}
	}()

	for i := 0; i < 100; i++ {
		nc, err := nats.Connect(s.ClientURL())
		if err != nil {
			t.Fatalf("Error on connect: %v", err)
		}
		nc.Close()
	}
	close(doneCh)
	wg.Wait()

	c, br, _ := newClientForServer(s)
	defer c.close()
	connectOp := []byte("CONNECT {\"user\":\"ivan\",\"pass\":\"bar\"}\r\n")
	c.parse(connectOp)

	// Since client has not send PING, having server try to send RTT ping
	// to client should not do anything
	if c.sendRTTPing() {
		t.Fatalf("RTT ping should not have been sent")
	}
	// Used to move c.start here but it is often causing race conditions,
	// so we'll just wait instead.
	time.Sleep(maxNoRTTPingBeforeFirstPong)

	errCh := make(chan error, 1)
	go func() {
		l, _ := br.ReadString('\n')
		if l != "PING\r\n" {
			errCh <- fmt.Errorf("expected to get PING, got %s", l)
			return
		}
		errCh <- nil
	}()
	if !c.sendRTTPing() {
		t.Fatalf("RTT ping should have been sent")
	}
	wg.Wait()
	if e := <-errCh; e != nil {
		t.Fatal(e.Error())
	}
}

func TestClientCheckUseOfGWReplyPrefix(t *testing.T) {
	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	ech := make(chan error, 1)
	nc, err := nats.Connect(s.ClientURL(),
		nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, e error) {
			ech <- e
		}))
	if err != nil {
		t.Fatalf("Error on connect: %v", err)
	}
	defer nc.Close()

	// Expect to fail if publish on gateway reply prefix
	nc.Publish(gwReplyPrefix+"anything", []byte("should fail"))

	// Wait for publish violation error
	select {
	case e := <-ech:
		if e == nil || !strings.Contains(strings.ToLower(e.Error()), "violation for publish") {
			t.Fatalf("Expected violation error, got %v", e)
		}
	case <-time.After(time.Second):
		t.Fatalf("Did not receive permissions violation error")
	}

	// Now publish a message with a reply set to the prefix,
	// it should be rejected too.
	nc.PublishRequest("foo", gwReplyPrefix+"anything", []byte("should fail"))

	// Wait for publish violation error with reply
	select {
	case e := <-ech:
		if e == nil || !strings.Contains(strings.ToLower(e.Error()), "violation for publish with reply") {
			t.Fatalf("Expected violation error, got %v", e)
		}
	case <-time.After(time.Second):
		t.Fatalf("Did not receive permissions violation error")
	}
}

func TestNoClientLeakOnSlowConsumer(t *testing.T) {
	opts := DefaultOptions()
	s := RunServer(opts)
	defer s.Shutdown()

	c, err := net.Dial("tcp", net.JoinHostPort(opts.Host, fmt.Sprintf("%d", opts.Port)))
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	defer c.Close()

	cr := bufio.NewReader(c)

	// Wait for INFO...
	line, _, _ := cr.ReadLine()
	var info serverInfo
	if err = json.Unmarshal(line[5:], &info); err != nil {
		t.Fatalf("Could not parse INFO json: %v\n", err)
	}

	// Send our connect
	if _, err := c.Write([]byte("CONNECT {\"verbose\": false}\r\nSUB foo 1\r\nPING\r\n")); err != nil {
		t.Fatalf("Error sending CONNECT and SUB: %v", err)
	}
	// Wait for PONG
	line, _, _ = cr.ReadLine()
	if string(line) != "PONG" {
		t.Fatalf("Expected 'PONG' but got %q", line)
	}

	// Get the client from server map
	cli := s.GetClient(info.CID)
	if cli == nil {
		t.Fatalf("No client registered")
	}
	// Change the write deadline to very low value
	cli.mu.Lock()
	cli.out.wdl = time.Nanosecond
	cli.mu.Unlock()

	nc := natsConnect(t, s.ClientURL())
	defer nc.Close()

	// Send some messages to cause write deadline error on "cli"
	payload := make([]byte, 1000)
	for i := 0; i < 100; i++ {
		natsPub(t, nc, "foo", payload)
	}
	natsFlush(t, nc)
	nc.Close()

	// Now make sure that the number of clients goes to 0.
	checkClientsCount(t, s, 0)
}

func TestClientSlowConsumerWithoutConnect(t *testing.T) {
	opts := DefaultOptions()
	opts.WriteDeadline = 100 * time.Millisecond
	s := RunServer(opts)
	defer s.Shutdown()

	url := fmt.Sprintf("127.0.0.1:%d", opts.Port)
	c, err := net.Dial("tcp", url)
	if err != nil {
		t.Fatalf("Error on dial: %v", err)
	}
	defer c.Close()
	c.Write([]byte("SUB foo 1\r\n"))

	payload := make([]byte, 10000)
	nc, err := nats.Connect(url)
	if err != nil {
		t.Fatalf("Error on connect: %v", err)
	}
	defer nc.Close()
	for i := 0; i < 10000; i++ {
		nc.Publish("foo", payload)
	}
	nc.Flush()
	checkFor(t, time.Second, 15*time.Millisecond, func() error {
		// Expect slow consumer..
		if n := atomic.LoadInt64(&s.slowConsumers); n != 1 {
			return fmt.Errorf("Expected 1 slow consumer, got: %v", n)
		}
		if n := s.scStats.clients.Load(); n != 1 {
			return fmt.Errorf("Expected 1 slow consumer, got: %v", n)
		}
		return nil
	})
	varz, err := s.Varz(nil)
	if err != nil {
		t.Fatal(err)
	}
	if varz.SlowConsumersStats.Clients != 1 {
		t.Error("Expected a slow consumer client in varz")
	}
}

func TestClientNoSlowConsumerIfConnectExpected(t *testing.T) {
	opts := DefaultOptions()
	opts.Username = "ivan"
	opts.Password = "pass"
	// Make it very slow so that the INFO sent to client fails...
	opts.WriteDeadline = time.Nanosecond
	s := RunServer(opts)
	defer s.Shutdown()

	// Expect server to close the connection, but will bump the slow
	// consumer count.
	nc, err := nats.Connect(fmt.Sprintf("nats://ivan:pass@%s:%d", opts.Host, opts.Port))
	if err == nil {
		nc.Close()
		t.Fatal("Expected connect error")
	}
	if n := atomic.LoadInt64(&s.slowConsumers); n != 0 {
		t.Fatalf("Expected 0 slow consumer, got: %v", n)
	}
}

func TestClientIPv6Address(t *testing.T) {
	opts := DefaultOptions()
	opts.Host = "0.0.0.0"
	s := RunServer(opts)
	defer s.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("nats://[::1]:%v", opts.Port))
	// Travis may not accept IPv6, in that case, skip the test.
	if err != nil {
		t.Skipf("Skipping test because could not connect: %v", err)
	}
	defer nc.Close()

	cid, _ := nc.GetClientID()
	c := s.GetClient(cid)
	c.mu.Lock()
	ncs := c.String()
	c.mu.Unlock()
	if !strings.HasPrefix(ncs, "[::1]") {
		t.Fatalf("Wrong string representation of an IPv6 address: %q", ncs)
	}
}

func TestPBNotIncreasedOnMaxPending(t *testing.T) {
	opts := DefaultOptions()
	opts.MaxPending = 100
	s := &Server{opts: opts}
	c := &client{srv: s}
	c.initClient()

	c.mu.Lock()
	c.queueOutbound(make([]byte, 200))
	pb := c.out.pb
	c.mu.Unlock()

	if pb != 0 {
		t.Fatalf("c.out.pb should be 0, got %v", pb)
	}
}

type testConnWritePartial struct {
	net.Conn
	partial bool
	buf     bytes.Buffer
}

func (c *testConnWritePartial) Write(p []byte) (int, error) {
	n := len(p)
	if c.partial {
		n = n/2 + 1
	}
	return c.buf.Write(p[:n])
}

func (c *testConnWritePartial) RemoteAddr() net.Addr {
	return nil
}

func (c *testConnWritePartial) SetWriteDeadline(_ time.Time) error {
	return nil
}

func TestFlushOutboundNoSliceReuseIfPartial(t *testing.T) {
	opts := DefaultOptions()
	opts.MaxPending = 1024
	s := &Server{opts: opts}

	fakeConn := &testConnWritePartial{partial: true}
	c := &client{srv: s, nc: fakeConn}
	c.initClient()

	bufs := [][]byte{
		[]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
		[]byte("------"),
		[]byte("0123456789"),
	}
	expected := bytes.Buffer{}
	for _, buf := range bufs {
		expected.Write(buf)
		c.mu.Lock()
		c.queueOutbound(buf)
		c.flushOutbound()
		fakeConn.partial = false
		c.mu.Unlock()
	}
	// Ensure everything is flushed.
	for done := false; !done; {
		c.mu.Lock()
		if c.out.pb > 0 {
			c.flushOutbound()
		} else {
			done = true
		}
		c.mu.Unlock()
	}
	if !bytes.Equal(expected.Bytes(), fakeConn.buf.Bytes()) {
		t.Fatalf("Expected\n%q\ngot\n%q", expected.String(), fakeConn.buf.String())
	}
}

type captureNoticeLogger struct {
	DummyLogger
	notices []string
}

func (l *captureNoticeLogger) Noticef(format string, v ...any) {
	l.Lock()
	l.notices = append(l.notices, fmt.Sprintf(format, v...))
	l.Unlock()
}

func TestCloseConnectionLogsReason(t *testing.T) {
	o1 := DefaultOptions()
	s1 := RunServer(o1)
	defer s1.Shutdown()

	l := &captureNoticeLogger{}
	s1.SetLogger(l, true, true)

	o2 := DefaultOptions()
	o2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", o1.Cluster.Port))
	s2 := RunServer(o2)
	defer s2.Shutdown()

	checkClusterFormed(t, s1, s2)
	s2.Shutdown()

	checkFor(t, time.Second, 15*time.Millisecond, func() error {
		if s1.NumRoutes() != 0 {
			return fmt.Errorf("route still connected")
		}
		return nil
	})
	// Now check that s1 has logged that the connection is closed and that the reason is included.
	ok := false
	l.Lock()
	for _, n := range l.notices {
		if strings.Contains(n, "connection closed: "+ClientClosed.String()) {
			ok = true
			break
		}
	}
	l.Unlock()
	if !ok {
		t.Fatal("Log does not contain closed reason")
	}
}

func TestCloseConnectionVeryEarly(t *testing.T) {
	for _, test := range []struct {
		name   string
		useTLS bool
	}{
		{"no_tls", false},
		{"tls", true},
	} {
		t.Run(test.name, func(t *testing.T) {
			o := DefaultOptions()
			if test.useTLS {
				tc := &TLSConfigOpts{
					CertFile: "../test/configs/certs/server-cert.pem",
					KeyFile:  "../test/configs/certs/server-key.pem",
					CaFile:   "../test/configs/certs/ca.pem",
				}
				tlsConfig, err := GenTLSConfig(tc)
				if err != nil {
					t.Fatalf("Error generating tls config: %v", err)
				}
				o.TLSConfig = tlsConfig
			}
			s := RunServer(o)
			defer s.Shutdown()

			// The issue was with a connection that would break right when
			// server was sending the INFO. Creating a bare TCP connection
			// and closing it right away won't help reproduce the problem.
			// So testing in 2 steps.

			// Get a normal TCP connection to the server.
			c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", o.Port))
			if err != nil {
				t.Fatalf("Unable to create tcp connection")
			}
			// Now close it.
			c.Close()

			// Wait that num clients falls to 0.
			checkClientsCount(t, s, 0)

			// Call again with this closed connection. Alternatively, we
			// would have to call with a fake connection that implements
			// net.Conn but returns an error on Write.
			s.createClient(c)

			// This connection should not have been added to the server.
			checkClientsCount(t, s, 0)
		})
	}
}

type connAddrString struct {
	net.Addr
}

func (a *connAddrString) String() string {
	return "[fe80::abc:def:ghi:123%utun0]:4222"
}

type connString struct {
	net.Conn
}

func (c *connString) RemoteAddr() net.Addr {
	return &connAddrString{}
}

func TestClientConnectionName(t *testing.T) {
	s, err := NewServer(DefaultOptions())
	if err != nil {
		t.Fatalf("Error creating server: %v", err)
	}
	l := &DummyLogger{}
	s.SetLogger(l, true, true)

	for _, test := range []struct {
		name    string
		kind    int
		kindStr string
		ws      bool
		mqtt    bool
	}{
		{"client", CLIENT, "cid:", false, false},
		{"ws client", CLIENT, "wid:", true, false},
		{"mqtt client", CLIENT, "mid:", false, true},
		{"route", ROUTER, "rid:", false, false},
		{"gateway", GATEWAY, "gid:", false, false},
		{"leafnode", LEAF, "lid:", false, false},
	} {
		t.Run(test.name, func(t *testing.T) {
			c := &client{srv: s, nc: &connString{}, kind: test.kind}
			if test.ws {
				c.ws = &websocket{}
			}
			if test.mqtt {
				c.mqtt = &mqtt{}
			}
			c.initClient()

			if host := "fe80::abc:def:ghi:123%utun0"; host != c.host {
				t.Fatalf("expected host to be %q, got %q", host, c.host)
			}
			if port := uint16(4222); port != c.port {
				t.Fatalf("expected port to be %v, got %v", port, c.port)
			}

			checkLog := func(suffix string) {
				t.Helper()
				l.Lock()
				msg := l.Msg
				l.Unlock()
				if strings.Contains(msg, "(MISSING)") {
					t.Fatalf("conn name was not escaped properly, got MISSING: %s", msg)
				}
				if !strings.Contains(l.Msg, test.kindStr) {
					t.Fatalf("expected kind to be %q, got: %s", test.kindStr, msg)
				}
				if !strings.HasSuffix(l.Msg, suffix) {
					t.Fatalf("expected statement to end with %q, got %s", suffix, msg)
				}
			}

			c.Debugf("debug: %v", 1)
			checkLog(" 1")
			c.Tracef("trace: %s", "2")
			checkLog(" 2")
			c.Warnf("warn: %s %d", "3", 4)
			checkLog(" 3 4")
			c.Errorf("error: %v %s", 5, "6")
			checkLog(" 5 6")
		})
	}
}

func TestClientLimits(t *testing.T) {
	accKp, err := nkeys.CreateAccount()
	if err != nil {
		t.Fatalf("Error creating account key: %v", err)
	}
	uKp, err := nkeys.CreateUser()
	if err != nil {
		t.Fatalf("Error creating user key: %v", err)
	}
	uPub, err := uKp.PublicKey()
	if err != nil {
		t.Fatalf("Error obtaining publicKey: %v", err)
	}
	s, err := NewServer(DefaultOptions())
	if err != nil {
		t.Fatalf("Error creating server: %v", err)
	}
	for _, test := range []struct {
		client int32
		acc    int32
		srv    int32
		expect int32
	}{
		// all identical
		{1, 1, 1, 1},
		{-1, -1, 0, -1},
		// only one value unlimited
		{1, -1, 0, 1},
		{-1, 1, 0, 1},
		{-1, -1, 1, 1},
		// all combinations of distinct values
		{1, 2, 3, 1},
		{1, 3, 2, 1},
		{2, 1, 3, 1},
		{2, 3, 1, 1},
		{3, 1, 2, 1},
		{3, 2, 1, 1},
	} {
		t.Run("", func(t *testing.T) {
			s.opts.MaxPayload = test.srv
			s.opts.MaxSubs = int(test.srv)
			c := &client{srv: s, acc: &Account{
				limits: limits{mpay: test.acc, msubs: test.acc},
			}}
			uc := jwt.NewUserClaims(uPub)
			uc.Limits.Subs = int64(test.client)
			uc.Limits.Payload = int64(test.client)
			c.opts.JWT, err = uc.Encode(accKp)
			if err != nil {
				t.Fatalf("Error encoding jwt: %v", err)
			}
			c.applyAccountLimits()
			if c.mpay != test.expect {
				t.Fatalf("payload %d not as expected %d", c.mpay, test.expect)
			}
			if c.msubs != test.expect {
				t.Fatalf("subscriber %d not as expected %d", c.msubs, test.expect)
			}
		})
	}
}

func TestClientClampMaxSubsErrReport(t *testing.T) {
	maxSubLimitReportThreshold = int64(100 * time.Millisecond)
	defer func() { maxSubLimitReportThreshold = defaultMaxSubLimitReportThreshold }()

	o1 := DefaultOptions()
	o1.MaxSubs = 1
	o1.LeafNode.Host = "127.0.0.1"
	o1.LeafNode.Port = -1
	s1 := RunServer(o1)
	defer s1.Shutdown()

	l := &captureErrorLogger{errCh: make(chan string, 10)}
	s1.SetLogger(l, false, false)

	o2 := DefaultOptions()
	o2.Cluster.Name = "xyz"
	u, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", o1.LeafNode.Port))
	o2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}}
	s2 := RunServer(o2)
	defer s2.Shutdown()

	checkLeafNodeConnected(t, s1)
	checkLeafNodeConnected(t, s2)

	nc := natsConnect(t, s2.ClientURL())
	defer nc.Close()
	natsSubSync(t, nc, "foo")
	natsSubSync(t, nc, "bar")

	// Make sure we receive only 1
	check := func() {
		t.Helper()
		for i := 0; i < 2; i++ {
			select {
			case errStr := <-l.errCh:
				if i > 0 {
					t.Fatalf("Should not have logged a second time: %s", errStr)
				}
				if !strings.Contains(errStr, "maximum subscriptions") {
					t.Fatalf("Unexpected error: %s", errStr)
				}
			case <-time.After(300 * time.Millisecond):
				if i == 0 {
					t.Fatal("Error should have been logged")
				}
			}
		}
	}
	check()

	// The above will have waited long enough to clear the report threshold.
	// So create two new subs and check again that we get only 1 report.
	natsSubSync(t, nc, "baz")
	natsSubSync(t, nc, "bat")
	check()
}

func TestClientDenySysGroupSub(t *testing.T) {
	s := RunServer(DefaultOptions())
	defer s.Shutdown()

	nc, err := nats.Connect(s.ClientURL(), nats.ErrorHandler(func(*nats.Conn, *nats.Subscription, error) {}))
	require_NoError(t, err)
	defer nc.Close()

	_, err = nc.QueueSubscribeSync("foo", sysGroup)
	require_NoError(t, err)
	nc.Flush()
	err = nc.LastError()
	require_Error(t, err)
	require_Contains(t, err.Error(), "Permissions Violation")
}

func TestClientAuthRequiredNoAuthUser(t *testing.T) {
	conf := createConfFile(t, []byte(`
		listen: 127.0.0.1:-1
		accounts: {
			A: { users: [ { user: user, password: pass } ] }
		}
		no_auth_user: user
	`))

	s, _ := RunServerWithConfig(conf)
	defer s.Shutdown()

	nc, err := nats.Connect(s.ClientURL())
	require_NoError(t, err)
	defer nc.Close()

	if nc.AuthRequired() {
		t.Fatalf("Expected AuthRequired to be false due to 'no_auth_user'")
	}
}

func TestClientUserInfoReq(t *testing.T) {
	conf := createConfFile(t, []byte(`
		listen: 127.0.0.1:-1
		PERMS = {
			publish = { allow: "$SYS.REQ.>", deny: "$SYS.REQ.ACCOUNT.>" }
			subscribe = "_INBOX.>"
			allow_responses: true
		}
		accounts: {
			A: { users: [ { user: dlc, password: pass, permissions: $PERMS } ] }
			$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
		}
		no_auth_user: dlc
	`))
	defer removeFile(t, conf)

	s, _ := RunServerWithConfig(conf)
	defer s.Shutdown()

	nc, err := nats.Connect(s.ClientURL())
	require_NoError(t, err)
	defer nc.Close()

	resp, err := nc.Request("$SYS.REQ.USER.INFO", nil, time.Second)
	require_NoError(t, err)

	response := ServerAPIResponse{Data: &UserInfo{}}
	err = json.Unmarshal(resp.Data, &response)
	require_NoError(t, err)

	userInfo := response.Data.(*UserInfo)

	dlc := &UserInfo{
		UserID:      "dlc",
		Account:     "A",
		AccountName: "A",
		Permissions: &Permissions{
			Publish: &SubjectPermission{
				Allow: []string{"$SYS.REQ.>"},
				Deny:  []string{"$SYS.REQ.ACCOUNT.>"},
			},
			Subscribe: &SubjectPermission{
				Allow: []string{"_INBOX.>"},
			},
			Response: &ResponsePermission{
				MaxMsgs: DEFAULT_ALLOW_RESPONSE_MAX_MSGS,
				Expires: DEFAULT_ALLOW_RESPONSE_EXPIRATION,
			},
		},
	}
	if !reflect.DeepEqual(dlc, userInfo) {
		t.Fatalf("User info for %q did not match", "dlc")
	}

	// Make sure system users work ok too.
	nc, err = nats.Connect(s.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
	require_NoError(t, err)
	defer nc.Close()

	resp, err = nc.Request("$SYS.REQ.USER.INFO", nil, time.Second)
	require_NoError(t, err)

	response = ServerAPIResponse{Data: &UserInfo{}}
	err = json.Unmarshal(resp.Data, &response)
	require_NoError(t, err)

	userInfo = response.Data.(*UserInfo)

	admin := &UserInfo{
		UserID:      "admin",
		Account:     "$SYS",
		AccountName: "$SYS",
	}
	if !reflect.DeepEqual(admin, userInfo) {
		t.Fatalf("User info for %q did not match", "admin")
	}
}

func TestTLSClientHandshakeFirst(t *testing.T) {
	tmpl := `
		listen: "127.0.0.1:-1"
		tls {
			cert_file: 	"../test/configs/certs/server-cert.pem"
			key_file:  	"../test/configs/certs/server-key.pem"
			timeout: 	1
			first: 		%s
		}
	`
	conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, "true")))
	s, o := RunServerWithConfig(conf)
	defer s.Shutdown()

	connect := func(tlsfirst, expectedOk bool) {
		opts := []nats.Option{nats.RootCAs("../test/configs/certs/ca.pem")}
		if tlsfirst {
			opts = append(opts, nats.TLSHandshakeFirst())
		}
		nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", o.Port), opts...)
		if expectedOk {
			defer nc.Close()
			if err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}
			if tlsfirst {
				cz, err := s.Connz(nil)
				if err != nil {
					t.Fatalf("Error getting connz: %v", err)
				}
				if !cz.Conns[0].TLSFirst {
					t.Fatal("Expected TLSFirst boolean to be set, it was not")
				}
			}
		} else if !expectedOk && err == nil {
			nc.Close()
			t.Fatal("Expected error, got none")
		}
	}

	// Server is TLS first, but client is not, so should fail.
	connect(false, false)

	// Now client is TLS first too, so should work.
	connect(true, true)

	// Config reload the server and disable tls first
	reloadUpdateConfig(t, s, conf, fmt.Sprintf(tmpl, "false"))

	// Now if client wants TLS first, connection should fail.
	connect(true, false)

	// But if it does not, should be ok.
	connect(false, true)

	// Config reload the server again and enable tls first
	reloadUpdateConfig(t, s, conf, fmt.Sprintf(tmpl, "true"))

	// If both client and server are TLS first, this should work.
	connect(true, true)
}

func TestTLSClientHandshakeFirstFallbackDelayConfigValues(t *testing.T) {
	tmpl := `
		listen: "127.0.0.1:-1"
		tls {
			cert_file: 	"../test/configs/certs/server-cert.pem"
			key_file:  	"../test/configs/certs/server-key.pem"
			timeout: 	1
			first: 		%s
		}
	`
	for _, test := range []struct {
		name  string
		val   string
		first bool
		delay time.Duration
	}{
		{"first as boolean true", "true", true, 0},
		{"first as boolean false", "false", false, 0},
		{"first as string true", "\"true\"", true, 0},
		{"first as string false", "\"false\"", false, 0},
		{"first as string on", "on", true, 0},
		{"first as string off", "off", false, 0},
		{"first as string auto", "auto", true, DEFAULT_TLS_HANDSHAKE_FIRST_FALLBACK_DELAY},
		{"first as string auto_fallback", "auto_fallback", true, DEFAULT_TLS_HANDSHAKE_FIRST_FALLBACK_DELAY},
		{"first as fallback duration", "300ms", true, 300 * time.Millisecond},
	} {
		t.Run(test.name, func(t *testing.T) {
			conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, test.val)))
			s, o := RunServerWithConfig(conf)
			defer s.Shutdown()

			if test.first {
				if !o.TLSHandshakeFirst {
					t.Fatal("Expected tls first to be true, was not")
				}
				if test.delay != o.TLSHandshakeFirstFallback {
					t.Fatalf("Expected fallback delay to be %v, got %v", test.delay, o.TLSHandshakeFirstFallback)
				}
			} else {
				if o.TLSHandshakeFirst {
					t.Fatal("Expected tls first to be false, was not")
				}
				if o.TLSHandshakeFirstFallback != 0 {
					t.Fatalf("Expected fallback delay to be 0, got %v", o.TLSHandshakeFirstFallback)
				}
			}
		})
	}
}

type pauseAfterDial struct {
	delay time.Duration
}

func (d *pauseAfterDial) Dial(network, address string) (net.Conn, error) {
	c, err := net.Dial(network, address)
	if err != nil {
		return nil, err
	}
	time.Sleep(d.delay)
	return c, nil
}

func TestTLSClientHandshakeFirstFallbackDelay(t *testing.T) {
	// Using certificates with RSA 4K to make sure that the fallback does
	// not prevent a client with TLS first to successfully connect.
	tmpl := `
		listen: "127.0.0.1:-1"
		tls {
			cert_file:  "./configs/certs/tls/benchmark-server-cert-rsa-4096.pem"
			key_file:   "./configs/certs/tls/benchmark-server-key-rsa-4096.pem"
			timeout: 	1
			first: 		%s
		}
	`
	conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, "auto")))
	s, o := RunServerWithConfig(conf)
	defer s.Shutdown()

	url := fmt.Sprintf("tls://localhost:%d", o.Port)
	d := &pauseAfterDial{delay: DEFAULT_TLS_HANDSHAKE_FIRST_FALLBACK_DELAY + 100*time.Millisecond}

	// Connect a client without "TLS first" and it should be accepted.
	nc, err := nats.Connect(url,
		nats.SetCustomDialer(d),
		nats.Secure(&tls.Config{
			ServerName: "reuben.nats.io",
			MinVersion: tls.VersionTLS12,
		}),
		nats.RootCAs("./configs/certs/tls/benchmark-ca-cert.pem"))
	require_NoError(t, err)
	defer nc.Close()
	// Check that the TLS first in monitoring is set to false
	cs, err := s.Connz(nil)
	require_NoError(t, err)
	if cs.Conns[0].TLSFirst {
		t.Fatal("Expected monitoring ConnInfo.TLSFirst to be false, it was not")
	}
	nc.Close()

	// Wait for the client to be removed
	checkClientsCount(t, s, 0)

	// Increase the fallback delay with config reload.
	reloadUpdateConfig(t, s, conf, fmt.Sprintf(tmpl, "\"1s\""))

	// This time, start the client with "TLS first".
	// We will also make sure that we did not wait for the fallback delay
	// in order to connect.
	start := time.Now()
	nc, err = nats.Connect(url,
		nats.SetCustomDialer(d),
		nats.Secure(&tls.Config{
			ServerName: "reuben.nats.io",
			MinVersion: tls.VersionTLS12,
		}),
		nats.RootCAs("./configs/certs/tls/benchmark-ca-cert.pem"),
		nats.TLSHandshakeFirst())
	require_NoError(t, err)
	require_True(t, time.Since(start) < 500*time.Millisecond)
	defer nc.Close()

	// Check that the TLS first in monitoring is set to true.
	cs, err = s.Connz(nil)
	require_NoError(t, err)
	if !cs.Conns[0].TLSFirst {
		t.Fatal("Expected monitoring ConnInfo.TLSFirst to be true, it was not")
	}
	nc.Close()
}

func TestTLSClientHandshakeFirstFallbackDelayAndAllowNonTLS(t *testing.T) {
	tmpl := `
		listen: "127.0.0.1:-1"
		tls {
			cert_file: 	"../test/configs/certs/server-cert.pem"
			key_file:  	"../test/configs/certs/server-key.pem"
			timeout: 	1
			first: 		%s
		}
		allow_non_tls: true
	`
	conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, "true")))
	s, o := RunServerWithConfig(conf)
	defer s.Shutdown()

	// We first start with a server that has handshake first set to true
	// and allow_non_tls. In that case, only "TLS first" clients should be
	// accepted.
	url := fmt.Sprintf("tls://localhost:%d", o.Port)
	nc, err := nats.Connect(url,
		nats.RootCAs("../test/configs/certs/ca.pem"),
		nats.TLSHandshakeFirst())
	require_NoError(t, err)
	defer nc.Close()
	// Check that the TLS first in monitoring is set to true
	cs, err := s.Connz(nil)
	require_NoError(t, err)
	if !cs.Conns[0].TLSFirst {
		t.Fatal("Expected monitoring ConnInfo.TLSFirst to be true, it was not")
	}
	nc.Close()

	// Client not using "TLS First" should fail.
	nc, err = nats.Connect(url, nats.RootCAs("../test/configs/certs/ca.pem"))
	if err == nil {
		nc.Close()
		t.Fatal("Expected connection to fail, it did not")
	}

	// And non TLS clients should also fail to connect.
	nc, err = nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", o.Port))
	if err == nil {
		nc.Close()
		t.Fatal("Expected connection to fail, it did not")
	}

	// Now we will replace TLS first in server with a fallback delay.
	reloadUpdateConfig(t, s, conf, fmt.Sprintf(tmpl, "\"25ms\""))

	// Clients with "TLS first" should still be able to connect
	nc, err = nats.Connect(url,
		nats.RootCAs("../test/configs/certs/ca.pem"),
		nats.TLSHandshakeFirst())
	require_NoError(t, err)
	defer nc.Close()

	checkConnInfo := func(isTLS, isTLSFirst bool) {
		t.Helper()
		cs, err = s.Connz(nil)
		require_NoError(t, err)
		conn := cs.Conns[0]
		if !isTLS {
			if conn.TLSVersion != _EMPTY_ {
				t.Fatalf("Being a non TLS client, there should not be TLSVersion set, got %v", conn.TLSVersion)
			}
			if conn.TLSFirst {
				t.Fatal("Being a non TLS client, TLSFirst should not be set, but it was")
			}
			return
		}
		if isTLSFirst && !conn.TLSFirst {
			t.Fatal("Expected monitoring ConnInfo.TLSFirst to be true, it was not")
		} else if !isTLSFirst && conn.TLSFirst {
			t.Fatal("Expected monitoring ConnInfo.TLSFirst to be false, it was not")
		}
		nc.Close()

		checkClientsCount(t, s, 0)
	}
	checkConnInfo(true, true)

	// Clients with TLS but not "TLS first" should also be able to connect.
	nc, err = nats.Connect(url, nats.RootCAs("../test/configs/certs/ca.pem"))
	require_NoError(t, err)
	defer nc.Close()
	checkConnInfo(true, false)

	// And non TLS clients should also be able to connect.
	nc, err = nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", o.Port))
	require_NoError(t, err)
	defer nc.Close()
	checkConnInfo(false, false)
}

func TestTLSClientHandshakeFirstAndInProcessConnection(t *testing.T) {
	conf := createConfFile(t, []byte(`
		listen: "127.0.0.1:-1"
		tls {
			cert_file: 	"../test/configs/certs/server-cert.pem"
			key_file:  	"../test/configs/certs/server-key.pem"
			timeout: 	1
			first: 		true
		}
	`))
	s, _ := RunServerWithConfig(conf)
	defer s.Shutdown()

	// Check that we can create an in process connection that does not use TLS
	nc, err := nats.Connect(_EMPTY_, nats.InProcessServer(s))
	require_NoError(t, err)
	defer nc.Close()
	if nc.TLSRequired() {
		t.Fatalf("Shouldn't have required TLS for in-process connection")
	}
	if _, err = nc.TLSConnectionState(); err == nil {
		t.Fatal("Should have got an error retrieving TLS connection state")
	}
	nc.Close()

	// If the client wants TLS, it should get a TLS connection.
	nc, err = nats.Connect(_EMPTY_,
		nats.InProcessServer(s),
		nats.RootCAs("../test/configs/certs/ca.pem"))
	require_NoError(t, err)
	defer nc.Close()
	if _, err = nc.TLSConnectionState(); err != nil {
		t.Fatal("Should have not got an error retrieving TLS connection state")
	}
	// However, the server would not have sent that TLS was required,
	// but instead it is available.
	if nc.TLSRequired() {
		t.Fatalf("Shouldn't have required TLS for in-process connection")
	}
	nc.Close()

	// The in-process connection with TLS and "TLS first" should also be working.
	nc, err = nats.Connect(_EMPTY_,
		nats.InProcessServer(s),
		nats.RootCAs("../test/configs/certs/ca.pem"),
		nats.TLSHandshakeFirst())
	require_NoError(t, err)
	defer nc.Close()
	if !nc.TLSRequired() {
		t.Fatalf("The server should have sent that TLS is required")
	}
	if _, err = nc.TLSConnectionState(); err != nil {
		t.Fatal("Should have not got an error retrieving TLS connection state")
	}
}

func TestRemoveHeaderIfPrefixPresent(t *testing.T) {
	hdr := []byte("NATS/1.0\r\n\r\n")

	hdr = genHeader(hdr, "a", "1")
	hdr = genHeader(hdr, JSExpectedStream, "my-stream")
	hdr = genHeader(hdr, JSExpectedLastSeq, "22")
	hdr = genHeader(hdr, "b", "2")
	hdr = genHeader(hdr, JSExpectedLastSubjSeq, "24")
	hdr = genHeader(hdr, JSExpectedLastMsgId, "1")
	hdr = genHeader(hdr, "c", "3")

	hdr = removeHeaderIfPrefixPresent(hdr, "Nats-Expected-")

	if !bytes.Equal(hdr, []byte("NATS/1.0\r\na: 1\r\nb: 2\r\nc: 3\r\n\r\n")) {
		t.Fatalf("Expected headers to be stripped, got %q", hdr)
	}
}

func TestRemoveHeaderIfPresentDuplicates(t *testing.T) {
	hdr := []byte("NATS/1.0\r\n\r\n")

	hdr = genHeader(hdr, "a", "1")
	hdr = genHeader(hdr, "a", "2")
	hdr = genHeader(hdr, "c", "3")
	hdr = genHeader(hdr, "a", "4")
	hdr = genHeader(hdr, "a", "5")

	hdr = removeHeaderIfPresent(hdr, "a")

	if !bytes.Equal(hdr, []byte("NATS/1.0\r\nc: 3\r\n\r\n")) {
		t.Fatalf("Expected headers to be stripped, got %q", hdr)
	}
}

func TestSliceHeader(t *testing.T) {
	hdr := []byte("NATS/1.0\r\n\r\n")

	hdr = genHeader(hdr, "a", "1")
	hdr = genHeader(hdr, JSExpectedStream, "my-stream")
	hdr = genHeader(hdr, JSExpectedLastSeq, "22")
	hdr = genHeader(hdr, "b", "2")
	hdr = genHeader(hdr, JSExpectedLastSubjSeq, "24")
	hdr = genHeader(hdr, JSExpectedLastMsgId, "1")
	hdr = genHeader(hdr, "c", "3")

	sliced := sliceHeader(JSExpectedLastSubjSeq, hdr)
	copied := getHeader(JSExpectedLastSubjSeq, hdr)

	require_NotNil(t, sliced)
	require_Equal(t, cap(sliced), 2)

	require_NotNil(t, copied)
	require_Equal(t, cap(copied), len(copied))

	require_True(t, bytes.Equal(sliced, copied))
}

func TestSliceHeaderOrderingPrefix(t *testing.T) {
	hdr := []byte("NATS/1.0\r\n\r\n")

	// These headers share the same prefix, the longer subject
	// must not invalidate the existence of the shorter one.
	hdr = genHeader(hdr, JSExpectedLastSubjSeqSubj, "foo")
	hdr = genHeader(hdr, JSExpectedLastSubjSeq, "24")

	sliced := sliceHeader(JSExpectedLastSubjSeq, hdr)
	copied := getHeader(JSExpectedLastSubjSeq, hdr)

	require_NotNil(t, sliced)
	require_Equal(t, cap(sliced), 2)

	require_NotNil(t, copied)
	require_Equal(t, cap(copied), len(copied))

	require_True(t, bytes.Equal(sliced, copied))
}

func TestSliceHeaderOrderingSuffix(t *testing.T) {
	hdr := []byte("NATS/1.0\r\n\r\n")

	// These headers share the same suffix, the longer subject
	// must not invalidate the existence of the shorter one.
	hdr = genHeader(hdr, "Previous-Nats-Msg-Id", "user")
	hdr = genHeader(hdr, "Nats-Msg-Id", "control")

	sliced := sliceHeader("Nats-Msg-Id", hdr)
	copied := getHeader("Nats-Msg-Id", hdr)

	require_NotNil(t, sliced)
	require_NotNil(t, copied)
	require_True(t, bytes.Equal(sliced, copied))
	require_Equal(t, string(copied), "control")
}

func TestRemoveHeaderIfPresentOrderingPrefix(t *testing.T) {
	hdr := []byte("NATS/1.0\r\n\r\n")

	// These headers share the same prefix, the longer subject
	// must not invalidate the existence of the shorter one.
	hdr = genHeader(hdr, JSExpectedLastSubjSeqSubj, "foo")
	hdr = genHeader(hdr, JSExpectedLastSubjSeq, "24")

	hdr = removeHeaderIfPresent(hdr, JSExpectedLastSubjSeq)
	ehdr := genHeader(nil, JSExpectedLastSubjSeqSubj, "foo")
	require_True(t, bytes.Equal(hdr, ehdr))
}

func TestRemoveHeaderIfPresentOrderingSuffix(t *testing.T) {
	hdr := []byte("NATS/1.0\r\n\r\n")

	// These headers share the same suffix, the longer subject
	// must not invalidate the existence of the shorter one.
	hdr = genHeader(hdr, "Previous-Nats-Msg-Id", "user")
	hdr = genHeader(hdr, "Nats-Msg-Id", "control")

	hdr = removeHeaderIfPresent(hdr, "Nats-Msg-Id")
	ehdr := genHeader(nil, "Previous-Nats-Msg-Id", "user")
	require_True(t, bytes.Equal(hdr, ehdr))
}

func TestMsgPartsCapsHdrSlice(t *testing.T) {
	c := &client{}
	hdrContent := hdrLine + "Key1: Val1\r\nKey2: Val2\r\n\r\n"
	msgBody := "hello\r\n"
	buf := slices.Clone([]byte(hdrContent + msgBody))
	c.pa.hdr = len(hdrContent)

	hdr, msg := c.msgParts(buf)
	// Make sure "hdr" and "msg" are as expected.
	require_Equal(t, string(hdr), hdrContent)
	require_Equal(t, string(msg), msgBody)
	// Previously, cap(hdr) would have been the same than the one of "buf",
	// but now this is not the case.
	require_True(t, cap(hdr) < cap(buf))
	// Just to make sure, try to add something (smaller than "hello\r\n")
	// to "hdr" and make sure the "msg" content is not modified.
	hdr = append(hdr, "test"...)
	require_Equal(t, string(hdr), hdrContent+"test")
	require_Equal(t, string(msg), "hello\r\n")
}

func TestSetHeaderDoesNotOverwriteUnderlyingBuffer(t *testing.T) {
	initialHdrContent := "NATS/1.0\r\nKey1: Val1\r\nKey2: Val2\r\n\r\n"
	msgBody := "this is the message body\r\n"
	for _, test := range []struct {
		name        string
		key         string
		val         string
		expectedHdr string
		newBuf      bool
	}{
		{"existing key new value larger", "Key1", "Val1Updated", "NATS/1.0\r\nKey1: Val1Updated\r\nKey2: Val2\r\n\r\n", true},
		{"existing key new value smaller", "Key1", "v1", "NATS/1.0\r\nKey1: v1\r\nKey2: Val2\r\n\r\n", false},
		{"new key", "Key3", "Val3", "NATS/1.0\r\nKey1: Val1\r\nKey2: Val2\r\nKey3: Val3\r\n\r\n", true},
	} {
		t.Run(test.name, func(t *testing.T) {
			buf := make([]byte, 0, len(initialHdrContent)+len(msgBody))
			buf = append(buf, initialHdrContent...)
			msgStart := len(buf)
			buf = append(buf, msgBody...)

			// Simulate that we create slices out of the underlying buffer,
			// separating the header and body parts.
			hdr, msg := buf[:msgStart], buf[msgStart:]
			hdr = setHeader(test.key, test.val, hdr)
			require_Equal(t, string(hdr), test.expectedHdr)
			require_Equal(t, string(msg), msgBody)
			if test.newBuf {
				// The "hdr" part of the underlying buffer should not have
				// been changed.
				require_Equal(t, string(buf[:len(initialHdrContent)]), initialHdrContent)
			} else {
				// The "hdr" part of the underlying buffer has been changed.
				require_Equal(t, string(buf[:len(test.expectedHdr)]), test.expectedHdr)
			}
		})
	}
}

func TestSetHeaderOrderingPrefix(t *testing.T) {
	for _, space := range []bool{true, false} {
		title := "Normal"
		if !space {
			title = "Trimmed"
		}
		t.Run(title, func(t *testing.T) {
			hdr := []byte("NATS/1.0\r\n\r\n")

			// These headers share the same prefix, the longer subject
			// must not invalidate the existence of the shorter one.
			hdr = genHeader(hdr, JSExpectedLastSubjSeqSubj, "foo")
			hdr = genHeader(hdr, JSExpectedLastSubjSeq, "24")
			if !space {
				hdr = bytes.ReplaceAll(hdr, []byte(" "), nil)
			}

			hdr = setHeader(JSExpectedLastSubjSeq, "12", hdr)
			ehdr := genHeader(nil, JSExpectedLastSubjSeqSubj, "foo")
			ehdr = genHeader(ehdr, JSExpectedLastSubjSeq, "12")
			if !space {
				ehdr = bytes.ReplaceAll(ehdr, []byte(" "), nil)
			}
			require_True(t, bytes.Equal(hdr, ehdr))
		})
	}
}

func TestSetHeaderOrderingSuffix(t *testing.T) {
	for _, space := range []bool{true, false} {
		title := "Normal"
		if !space {
			title = "Trimmed"
		}
		t.Run(title, func(t *testing.T) {
			hdr := []byte("NATS/1.0\r\n\r\n")

			// These headers share the same suffix, the longer subject
			// must not invalidate the existence of the shorter one.
			hdr = genHeader(hdr, "Previous-Nats-Msg-Id", "user")
			hdr = genHeader(hdr, "Nats-Msg-Id", "control")
			if !space {
				hdr = bytes.ReplaceAll(hdr, []byte(" "), nil)
			}

			hdr = setHeader("Nats-Msg-Id", "other", hdr)
			ehdr := genHeader(nil, "Previous-Nats-Msg-Id", "user")
			ehdr = genHeader(ehdr, "Nats-Msg-Id", "other")
			if !space {
				ehdr = bytes.ReplaceAll(ehdr, []byte(" "), nil)
			}
			require_True(t, bytes.Equal(hdr, ehdr))
		})
	}
}

func TestInProcessAllowedConnectionType(t *testing.T) {
	tmpl := `
		listen: "127.0.0.1:-1"
		accounts {
			A { users: [{user: "test", password: "pwd", allowed_connection_types: ["%s"]}] }
		}
		write_deadline: "500ms"
	`
	for _, test := range []struct {
		name          string
		ct            string
		inProcessOnly bool
	}{
		{"conf inprocess", jwt.ConnectionTypeInProcess, true},
		{"conf standard", jwt.ConnectionTypeStandard, false},
	} {
		t.Run(test.name, func(t *testing.T) {
			conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, test.ct)))
			s, _ := RunServerWithConfig(conf)
			defer s.Shutdown()

			// Create standard connection
			nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("test", "pwd"))
			if test.inProcessOnly && err == nil {
				nc.Close()
				t.Fatal("Expected standard connection to fail, it did not")
			}
			// Works if nc is nil (which it will if only in-process are allowed)
			nc.Close()

			// Create inProcess connection
			nc, err = nats.Connect(_EMPTY_, nats.UserInfo("test", "pwd"), nats.InProcessServer(s))
			if !test.inProcessOnly && err == nil {
				nc.Close()
				t.Fatal("Expected in-process connection to fail, it did not")
			}
			// Works if nc is nil (which it will if only standard are allowed)
			nc.Close()
		})
	}
	for _, test := range []struct {
		name          string
		ct            string
		inProcessOnly bool
	}{
		{"jwt inprocess", jwt.ConnectionTypeInProcess, true},
		{"jwt standard", jwt.ConnectionTypeStandard, false},
	} {
		t.Run(test.name, func(t *testing.T) {
			skp, _ := nkeys.FromSeed(oSeed)
			spub, _ := skp.PublicKey()

			o := defaultServerOptions
			o.TrustedKeys = []string{spub}
			o.WriteDeadline = 500 * time.Millisecond
			s := RunServer(&o)
			defer s.Shutdown()

			buildMemAccResolver(s)

			kp, _ := nkeys.CreateAccount()
			aPub, _ := kp.PublicKey()
			claim := jwt.NewAccountClaims(aPub)
			aJwt, err := claim.Encode(oKp)
			require_NoError(t, err)

			addAccountToMemResolver(s, aPub, aJwt)

			creds := createUserWithLimit(t, kp, time.Time{},
				func(j *jwt.UserPermissionLimits) {
					j.AllowedConnectionTypes.Add(test.ct)
				})
			// Create standard connection
			nc, err := nats.Connect(s.ClientURL(), nats.UserCredentials(creds))
			if test.inProcessOnly && err == nil {
				nc.Close()
				t.Fatal("Expected standard connection to fail, it did not")
			}
			// Works if nc is nil (which it will if only in-process are allowed)
			nc.Close()

			// Create inProcess connection
			nc, err = nats.Connect(_EMPTY_, nats.UserCredentials(creds), nats.InProcessServer(s))
			if !test.inProcessOnly && err == nil {
				nc.Close()
				t.Fatal("Expected in-process connection to fail, it did not")
			}
			// Works if nc is nil (which it will if only standard are allowed)
			nc.Close()
		})
	}
}

func TestClientFlushOutboundNoSlowConsumer(t *testing.T) {
	opts := DefaultOptions()
	opts.MaxPending = 1024 * 1024 * 140 // 140MB
	opts.MaxPayload = 1024 * 1024 * 16  // 16MB
	opts.WriteDeadline = time.Second * 30
	s := RunServer(opts)
	defer s.Shutdown()

	nc := natsConnect(t, fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
	defer nc.Close()

	proxy := newNetProxy(0, 1024*1024*8, 1024*1024*8, s.ClientURL()) // 8MB/s
	defer proxy.stop()

	wait := make(chan error)

	nca, err := nats.Connect(proxy.clientURL(), nats.NoCallbacksAfterClientClose())
	require_NoError(t, err)
	defer nca.Close()
	nca.SetDisconnectErrHandler(func(c *nats.Conn, err error) {
		wait <- err
		close(wait)
	})

	ncb, err := nats.Connect(s.ClientURL())
	require_NoError(t, err)
	defer ncb.Close()

	_, err = nca.Subscribe("test", func(msg *nats.Msg) {
		wait <- nil
	})
	require_NoError(t, err)

	// Publish 128MB of data onto the test subject. This will
	// mean that the outbound queue for nca has more than 64MB,
	// which is the max we will send into a single writev call.
	payload := make([]byte, 1024*1024*16) // 16MB
	for i := 0; i < 8; i++ {
		require_NoError(t, ncb.Publish("test", payload))
	}

	// Get the client ID for nca.
	cid, err := nca.GetClientID()
	require_NoError(t, err)

	// Check that the client queue has more than 64MB queued
	// up in it.
	s.mu.RLock()
	ca := s.clients[cid]
	s.mu.RUnlock()
	ca.mu.Lock()
	pba := ca.out.pb
	ca.mu.Unlock()
	require_True(t, pba > 1024*1024*64)

	// Wait for our messages to be delivered. This will take
	// a few seconds as the client is limited to 8MB/s, so it
	// can't deliver messages to us as quickly as the other
	// client can publish them.
	var msgs int
	for err := range wait {
		require_NoError(t, err)
		msgs++
		if msgs == 8 {
			break
		}
	}
	require_Equal(t, msgs, 8)
}

func TestClientRejectsNRGSubjects(t *testing.T) {
	conf := createConfFile(t, []byte(`
		listen: 127.0.0.1:-1
		accounts: {
			A: { users: [ { user: nat, password: pass } ] }
			$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
		}
		no_auth_user: nat
	`))
	defer removeFile(t, conf)

	s, _ := RunServerWithConfig(conf)
	defer s.Shutdown()

	t.Run("System", func(t *testing.T) {
		ech := make(chan error, 1)
		nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("admin", "s3cr3t!"),
			nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, e error) {
				ech <- e
			}),
		)
		require_NoError(t, err)
		defer nc.Close()

		// System account clients are allowed to publish to these subjects.
		require_NoError(t, nc.Publish("$NRG.foo", nil))
		require_NoChanRead(t, ech, time.Second)
	})

	t.Run("Normal", func(t *testing.T) {
		ech := make(chan error, 1)
		nc, err := nats.Connect(s.ClientURL(),
			nats.ErrorHandler(func(_ *nats.Conn, _ *nats.Subscription, e error) {
				ech <- e
			}),
		)
		require_NoError(t, err)
		defer nc.Close()

		// Non-system clients should receive a pub permission error.
		require_NoError(t, nc.Publish("$NRG.foo", nil))
		err = require_ChanRead(t, ech, time.Second)
		require_Error(t, err)
		require_True(t, strings.HasPrefix(err.Error(), "nats: permissions violation"))
	})
}

func TestConnectionStringWithLogConnectionInfo(t *testing.T) {
	opts := DefaultOptions()
	s, c, _, _ := rawSetup(*opts)
	defer c.close()
	defer s.Shutdown()

	c.kind = CLIENT
	connectArg := []byte("{\"verbose\":false,\"pedantic\":false,\"version\":\"1.0.0\",\"lang\":\"go\",\"name\":\"test-client\"}")

	err := c.processConnect(connectArg)
	if err != nil {
		t.Fatalf("Received error on first processConnect: %v", err)
	}

	// Get the connection string after first processConnect.
	firstConnStr := c.ncs.Load()
	if firstConnStr == nil {
		return
	}
	firstStr := firstConnStr.(string)
	firstLen := len(firstStr)
	require_Equal(t, firstStr, `pipe - cid:1 - "v1.0.0:go:test-client"`)

	// Process connect multiple times.
	for i := 0; i < 3; i++ {
		err = c.processConnect(connectArg)
		if err != nil {
			t.Fatalf("Received error on processConnect attempt %d: %v", i+2, err)
		}
	}

	// Get the connection string after multiple calls.
	finalConnStr := c.ncs.Load()
	require_NotNil(t, finalConnStr)

	finalStr := finalConnStr.(string)
	require_Equal(t, firstStr, finalStr)

	// Now send a different connect over the same connection.
	connectArg2 := []byte("{\"verbose\":false,\"pedantic\":false,\"version\":\"1.0.0\",\"lang\":\"go\",\"name\":\"test-client:new\"}")

	err = c.processConnect(connectArg2)
	if err != nil {
		t.Fatalf("Received error on processConnect: %v", err)
	}
	finalConnStr = c.ncs.Load()
	require_NotNil(t, finalConnStr)

	// Check that it remains the same size after a different connect.
	finalStr = finalConnStr.(string)
	finalLen := len(finalStr)
	if finalLen > firstLen {
		t.Fatalf("Connection string grew from %d to %d characters", firstLen, finalLen)
	}
}

func TestLogConnectionAuthInfo(t *testing.T) {
	t.Run("username_password", func(t *testing.T) {
		opts := DefaultOptions()
		opts.Username = "testuser"
		opts.Password = "testpass"
		s, c, _, _ := rawSetup(*opts)
		defer c.close()
		defer s.Shutdown()

		c.kind = CLIENT
		connectArg := []byte(`{"verbose":false,"pedantic":false,"user":"testuser","pass":"testpass"}`)

		err := c.processConnect(connectArg)
		require_NoError(t, err)

		connStr := c.ncs.Load()
		require_NotNil(t, connStr)
		str := connStr.(string)
		require_Contains(t, str, `"$G/user:testuser"`)
	})
	t.Run("token", func(t *testing.T) {
		opts := DefaultOptions()
		opts.Authorization = "secret-token"
		s, c, _, _ := rawSetup(*opts)
		defer c.close()
		defer s.Shutdown()

		c.kind = CLIENT
		connectArg := []byte(`{"verbose":false,"pedantic":false,"auth_token":"secret-token"}`)

		err := c.processConnect(connectArg)
		require_NoError(t, err)

		connStr := c.ncs.Load()
		require_NotNil(t, connStr)
		str := connStr.(string)
		require_Contains(t, str, `"$G/token"`)
	})
	t.Run("nkey", func(t *testing.T) {
		kp, _ := nkeys.CreateUser()
		pub, _ := kp.PublicKey()
		nkey := string(pub)

		opts := DefaultOptions()
		opts.Nkeys = []*NkeyUser{{Nkey: nkey}}
		s, c, _, _ := rawSetup(*opts)
		defer c.close()
		defer s.Shutdown()

		c.kind = CLIENT
		nonce := make([]byte, 32)
		c.nonce = nonce
		sig, _ := kp.Sign(nonce)
		sigEncoded := base64.RawURLEncoding.EncodeToString(sig)

		connectArg := fmt.Sprintf(`{"verbose":false,"pedantic":false,"nkey":"%s","sig":"%s"}`, nkey, sigEncoded)

		err := c.processConnect([]byte(connectArg))
		require_NoError(t, err)

		connStr := c.ncs.Load()
		require_NotNil(t, connStr)
		str := connStr.(string)
		require_Contains(t, str, fmt.Sprintf(`"$G/nkey:%s"`, nkey))
	})
	t.Run("combined_info_and_auth", func(t *testing.T) {
		opts := DefaultOptions()
		opts.Username = "testuser"
		opts.Password = "testpass"
		s, c, _, _ := rawSetup(*opts)
		defer c.close()
		defer s.Shutdown()

		c.kind = CLIENT
		connectArg := []byte(`{"verbose":false,"pedantic":false,"user":"testuser","pass":"testpass","version":"1.0.0","lang":"go","name":"test-client"}`)

		err := c.processConnect(connectArg)
		require_NoError(t, err)

		connStr := c.ncs.Load()
		require_NotNil(t, connStr)
		str := connStr.(string)
		require_Contains(t, str, `"v1.0.0:go:test-client"`)
		require_Contains(t, str, `"$G/user:testuser"`)
	})
	t.Run("no_auth", func(t *testing.T) {
		opts := DefaultOptions()
		s, c, _, _ := rawSetup(*opts)
		defer c.close()
		defer s.Shutdown()

		c.kind = CLIENT
		connectArg := []byte(`{"verbose":false,"pedantic":false}`)

		err := c.processConnect(connectArg)
		require_NoError(t, err)

		connStr := c.ncs.Load()
		if connStr != nil {
			str := connStr.(string)
			if strings.Contains(str, "$G/") {
				t.Fatalf("Expected no auth info when no authentication provided, got: %s", str)
			}
		}
	})
}

func TestClientConfigureWriteTimeoutPolicy(t *testing.T) {
	for name, policy := range map[string]WriteTimeoutPolicy{
		"Default": WriteTimeoutPolicyDefault,
		"Retry":   WriteTimeoutPolicyRetry,
		"Close":   WriteTimeoutPolicyClose,
	} {
		t.Run(name, func(t *testing.T) {
			opts := DefaultOptions()
			opts.WriteTimeout = policy
			s := RunServer(opts)
			defer s.Shutdown()

			nc := natsConnect(t, fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
			defer nc.Close()

			s.mu.RLock()
			defer s.mu.RUnlock()

			for _, r := range s.clients {
				if policy == WriteTimeoutPolicyDefault {
					require_Equal(t, r.out.wtp, WriteTimeoutPolicyClose)
				} else {
					require_Equal(t, r.out.wtp, policy)
				}
			}
		})
	}
}

// TestClientFlushOutboundWriteTimeoutPolicy relies on specifically having
// written at least one byte in order to not trip the "written == 0" close
// condition, so just setting an unrealistically low write deadline won't
// work. Instead what we'll do is write the first byte very quickly and then
// slow down, so that we can trip a more honest slow consumer condition.
type writeTimeoutPolicyWriter struct {
	net.Conn
	deadline time.Time
	written  int
}

func (w *writeTimeoutPolicyWriter) SetWriteDeadline(deadline time.Time) error {
	w.deadline = deadline
	return w.Conn.SetWriteDeadline(deadline)
}

func (w *writeTimeoutPolicyWriter) Write(b []byte) (int, error) {
	if w.written == 0 {
		w.written++
		return w.Conn.Write(b[:1])
	}
	time.Sleep(time.Until(w.deadline) + 10*time.Millisecond)
	return w.Conn.Write(b)
}

func TestClientFlushOutboundWriteTimeoutPolicy(t *testing.T) {
	for name, policy := range map[string]WriteTimeoutPolicy{
		"Retry": WriteTimeoutPolicyRetry,
		"Close": WriteTimeoutPolicyClose,
	} {
		t.Run(name, func(t *testing.T) {
			opts := DefaultOptions()
			opts.PingInterval = 250 * time.Millisecond
			opts.WriteDeadline = 100 * time.Millisecond
			opts.WriteTimeout = policy
			s := RunServer(opts)
			defer s.Shutdown()

			nc1 := natsConnect(t, fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
			defer nc1.Close()

			_, err := nc1.Subscribe("test", func(_ *nats.Msg) {})
			require_NoError(t, err)

			nc2 := natsConnect(t, fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port))
			defer nc2.Close()

			cid, err := nc1.GetClientID()
			require_NoError(t, err)

			client := s.getClient(cid)
			client.mu.Lock()
			client.out.wdl = 100 * time.Millisecond
			client.nc = &writeTimeoutPolicyWriter{Conn: client.nc}
			client.mu.Unlock()

			require_NoError(t, nc2.Publish("test", make([]byte, 1024*1024)))

			checkFor(t, 5*time.Second, 10*time.Millisecond, func() error {
				client.mu.Lock()
				defer client.mu.Unlock()
				switch {
				case !client.flags.isSet(connMarkedClosed):
					return fmt.Errorf("connection not closed yet")
				case policy == WriteTimeoutPolicyRetry && client.flags.isSet(isSlowConsumer):
					// Retry policy should have marked the client as a slow consumer and
					// continued to retry flushes.
					return nil
				case policy == WriteTimeoutPolicyClose && !client.flags.isSet(isSlowConsumer):
					// Close policy shouldn't have marked the client as a slow consumer,
					// it will just close it instead.
					return nil
				default:
					return fmt.Errorf("client not in correct state yet")
				}
			})
		})
	}
}

func TestFlushOutboundS2CompressionPoolBufferRecycling(t *testing.T) {
	opts := DefaultOptions()
	s := &Server{opts: opts}

	fakeConn := &testConnWritePartial{}
	c := &client{srv: s, nc: fakeConn, kind: ROUTER}
	c.initClient()
	c.out.cw = s2.NewWriter(nil, s2.WriterConcurrency(1))

	payload := make([]byte, 256)
	for i := range payload {
		payload[i] = byte(i)
	}

	// Queue multiple buffers per iteration so each leaked pool
	// buffer adds one extra allocation, making the leak obvious.
	const numBuffers = 10

	// Warm up: run a few iterations to populate the pool.
	for i := 0; i < 5; i++ {
		c.mu.Lock()
		for j := 0; j < numBuffers; j++ {
			c.queueOutbound(payload)
		}
		c.flushOutbound()
		c.mu.Unlock()
	}

	allocs := testing.AllocsPerRun(100, func() {
		c.mu.Lock()
		for j := 0; j < numBuffers; j++ {
			c.queueOutbound(payload)
		}
		c.flushOutbound()
		c.mu.Unlock()
	})
	if allocs > 15 {
		t.Fatalf("Too many allocs per iteration (%.1f); pool buffers are likely being leaked", allocs)
	}
}
