// Copyright 2021-2023 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 test

import (
	"bytes"
	"context"
	"crypto"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"golang.org/x/crypto/ocsp"

	"github.com/nats-io/nats-server/v2/server"
	"github.com/nats-io/nats.go"
)

const (
	defaultResponseTTL = 4 * time.Second
	defaultAddress     = "127.0.0.1:8888"
)

func TestOCSPAlwaysMustStapleAndShutdown(t *testing.T) {
	// Certs that have must staple will auto shutdown the server.
	const (
		caCert     = "configs/certs/ocsp/ca-cert.pem"
		caKey      = "configs/certs/ocsp/ca-key.pem"
		serverCert = "configs/certs/ocsp/server-cert.pem"
		serverKey  = "configs/certs/ocsp/server-key.pem"
	)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)

	opts := server.Options{}
	opts.Host = "127.0.0.1"
	opts.NoLog = true
	opts.NoSigs = true
	opts.MaxControlLine = 4096
	opts.Port = -1
	opts.TLSCert = serverCert
	opts.TLSKey = serverKey
	opts.TLSCaCert = caCert
	opts.TLSTimeout = 5
	tcOpts := &server.TLSConfigOpts{
		CertFile: opts.TLSCert,
		KeyFile:  opts.TLSKey,
		CaFile:   opts.TLSCaCert,
		Timeout:  opts.TLSTimeout,
	}

	tlsConf, err := server.GenTLSConfig(tcOpts)
	if err != nil {
		t.Fatal(err)
	}
	opts.TLSConfig = tlsConf

	opts.OCSPConfig = &server.OCSPConfig{
		Mode:         server.OCSPModeAlways,
		OverrideURLs: []string{addr},
	}
	srv := RunServer(&opts)
	defer srv.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				resp, err := getOCSPStatus(s)
				if err != nil {
					return err
				}
				if resp.Status != ocsp.Good {
					return fmt.Errorf("invalid staple")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// The server will shutdown because the server becomes revoked
	// and the policy is to always must-staple.  The OCSP Responder
	// instructs the NATS Server to fetch OCSP Staples every 2 seconds.
	time.Sleep(2 * time.Second)
	setOCSPStatus(t, addr, serverCert, ocsp.Revoked)
	time.Sleep(2 * time.Second)

	// Should be connection refused since server will abort now.
	_, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nats.ErrNoServers {
		t.Errorf("Expected connection refused")
	}
	// Verify that the server finishes shutdown
	srv.WaitForShutdown()
}

func TestOCSPMustStapleShutdown(t *testing.T) {
	const (
		caCert     = "configs/certs/ocsp/ca-cert.pem"
		caKey      = "configs/certs/ocsp/ca-key.pem"
		serverCert = "configs/certs/ocsp/server-status-request-cert.pem"
		serverKey  = "configs/certs/ocsp/server-status-request-key.pem"
	)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)

	opts := server.Options{}
	opts.Host = "127.0.0.1"
	opts.NoLog = true
	opts.NoSigs = true
	opts.MaxControlLine = 4096
	opts.Port = -1
	opts.TLSCert = serverCert
	opts.TLSKey = serverKey
	opts.TLSCaCert = caCert
	opts.TLSTimeout = 5
	tlsConfigOpts := &server.TLSConfigOpts{
		CertFile: opts.TLSCert,
		KeyFile:  opts.TLSKey,
		CaFile:   opts.TLSCaCert,
		Timeout:  opts.TLSTimeout,
	}

	tlsConf, err := server.GenTLSConfig(tlsConfigOpts)
	if err != nil {
		t.Fatal(err)
	}
	opts.TLSConfig = tlsConf

	opts.OCSPConfig = &server.OCSPConfig{
		Mode:         server.OCSPModeMust,
		OverrideURLs: []string{addr},
	}

	srv := RunServer(&opts)
	defer srv.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				resp, err := getOCSPStatus(s)
				if err != nil {
					return err
				}
				if resp.Status != ocsp.Good {
					return fmt.Errorf("invalid staple")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// The server will shutdown because the server becomes revoked
	// and the policy is to always must-staple.  The OCSP Responder
	// instructs the NATS Server to fetch OCSP Staples every 2 seconds.
	time.Sleep(2 * time.Second)
	setOCSPStatus(t, addr, serverCert, ocsp.Revoked)
	time.Sleep(2 * time.Second)

	// Should be connection refused since server will abort now.
	_, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nats.ErrNoServers {
		t.Errorf("Expected connection refused")
	}
}

func TestOCSPMustStapleAutoDoesNotShutdown(t *testing.T) {
	const (
		caCert     = "configs/certs/ocsp/ca-cert.pem"
		caKey      = "configs/certs/ocsp/ca-key.pem"
		serverCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)

	content := `
		port: -1

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	conf := createConfFile(t, []byte(content))
	s, opts := RunServerWithConfig(conf)
	defer s.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				resp, err := getOCSPStatus(s)
				if err != nil {
					return err
				}
				if resp.Status != ocsp.Good {
					t.Errorf("Expected valid OCSP staple status")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// The server will shutdown because the server becomes revoked
	// and the policy is to always must-staple.  The OCSP Responder
	// instructs the NATS Server to fetch OCSP Staples every 2 seconds.
	time.Sleep(2 * time.Second)
	setOCSPStatus(t, addr, serverCert, ocsp.Revoked)
	time.Sleep(2 * time.Second)

	// Should not be connection refused, the client will continue running and
	// be served the stale OCSP staple instead.
	nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				resp, err := getOCSPStatus(s)
				if err != nil {
					return err
				}
				if resp.Status != ocsp.Revoked {
					t.Errorf("Expected revoked status")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()
}

func TestOCSPAutoWithoutMustStapleDoesNotShutdownOnRevoke(t *testing.T) {
	const (
		caCert     = "configs/certs/ocsp/ca-cert.pem"
		caKey      = "configs/certs/ocsp/ca-key.pem"
		serverCert = "configs/certs/ocsp/server-cert.pem"
		serverKey  = "configs/certs/ocsp/server-key.pem"
	)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)

	opts := server.Options{}
	opts.Host = "127.0.0.1"
	opts.NoLog = true
	opts.NoSigs = true
	opts.MaxControlLine = 4096
	opts.Port = -1
	opts.TLSCert = serverCert
	opts.TLSKey = serverKey
	opts.TLSCaCert = caCert
	opts.TLSTimeout = 5
	tlsConfigOpts := &server.TLSConfigOpts{
		CertFile: opts.TLSCert,
		KeyFile:  opts.TLSKey,
		CaFile:   opts.TLSCaCert,
		Timeout:  opts.TLSTimeout,
	}
	tlsConf, err := server.GenTLSConfig(tlsConfigOpts)
	if err != nil {
		t.Fatal(err)
	}
	opts.TLSConfig = tlsConf

	opts.OCSPConfig = &server.OCSPConfig{
		Mode:         server.OCSPModeAuto,
		OverrideURLs: []string{addr},
	}

	srv := RunServer(&opts)
	defer srv.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse != nil {
					return fmt.Errorf("Unexpected OCSP staple for certificate")
				}
				return nil
			},
		}),

		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// Revoke the client certificate, nothing will happens since does
	// not have MustStaple.
	time.Sleep(2 * time.Second)
	setOCSPStatus(t, addr, serverCert, ocsp.Revoked)
	time.Sleep(2 * time.Second)

	// Should not be connection refused since server will continue running.
	nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Errorf("Unexpected error: %s", err)
	}
	nc.Close()
}

func TestOCSPClient(t *testing.T) {
	const (
		caCert     = "configs/certs/ocsp/ca-cert.pem"
		caKey      = "configs/certs/ocsp/ca-key.pem"
		serverCert = "configs/certs/ocsp/server-cert.pem"
		serverKey  = "configs/certs/ocsp/server-key.pem"
	)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	ocspURL := fmt.Sprintf("http://%s", ocspr.Addr)
	defer ocspr.Shutdown(ctx)

	for _, test := range []struct {
		name      string
		config    string
		opts      []nats.Option
		err       error
		rerr      error
		configure func()
	}{
		{
			"OCSP Stapling makes server fail to boot if status is unknown",
			`
				port: -1

				# Enable OCSP stapling with policy to honor must staple if present.
				ocsp: true

				tls {
					cert_file: "configs/certs/ocsp/server-cert.pem"
					key_file: "configs/certs/ocsp/server-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() {},
		},
		{
			"OCSP Stapling ignored by default if server without must staple status",
			`
				port: -1

				ocsp: true

				tls {
					cert_file: "configs/certs/ocsp/server-cert.pem"
					key_file: "configs/certs/ocsp/server-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) },
		},
		{
			"OCSP Stapling honored by default if server has must staple status",
			`
				port: -1

				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() {
				setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
			},
		},
		{
			"OCSP Stapling can be disabled even if server has must staple status",
			`
				port: -1

				ocsp: false

				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() {
				setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Revoked)
			},
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			test.configure()
			content := test.config
			conf := createConfFile(t, []byte(content))
			s, opts := RunServerWithConfig(conf)
			defer s.Shutdown()

			nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), test.opts...)
			if test.err == nil && err != nil {
				t.Errorf("Expected to connect, got %v", err)
			} else if test.err != nil && err == nil {
				t.Errorf("Expected error on connect")
			} else if test.err != nil && err != nil {
				// Error on connect was expected
				if test.err.Error() != err.Error() {
					t.Errorf("Expected error %s, got: %s", test.err, err)
				}
				return
			}
			defer nc.Close()

			nc.Subscribe("ping", func(m *nats.Msg) {
				m.Respond([]byte("pong"))
			})
			nc.Flush()

			_, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond)
			if test.rerr != nil && err == nil {
				t.Errorf("Expected error getting response")
			} else if test.rerr == nil && err != nil {
				t.Errorf("Expected response")
			}
		})
	}
}

func TestOCSPReloadRotateTLSCertWithNoURL(t *testing.T) {
	const (
		caCert            = "configs/certs/ocsp/ca-cert.pem"
		caKey             = "configs/certs/ocsp/ca-key.pem"
		serverCert        = "configs/certs/ocsp/server-status-request-url-01-cert.pem"
		updatedServerCert = "configs/certs/ocsp/server-status-request-cert.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)

	content := `
		port: -1

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	conf := createConfFile(t, []byte(content))
	s, opts := RunServerWithConfig(conf)
	defer s.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				resp, err := getOCSPStatus(s)
				if err != nil {
					return err
				}
				if resp.Status != ocsp.Good {
					t.Errorf("Expected valid OCSP staple status")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// Change the contents with another that will fail to get a staple
	// since it does not have an URL.
	content = `
		port: -1

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	if err := os.WriteFile(conf, []byte(content), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	// Reload show warning because of cert missing OCSP Url so cannot be used
	// with OCSP stapling.
	if err := s.Reload(); err != nil {
		t.Fatal(err)
	}
	expectedErr := fmt.Errorf("missing OCSP response")
	// The server will not shutdown because the reload will fail.
	_, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				// The new certificate does not have OCSP Staples since
				// it could not fetch one from a OCSP server.
				if s.OCSPResponse == nil {
					return expectedErr
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != expectedErr {
		t.Fatalf("Unexpected error: %s", expectedErr)
	}
}

func TestOCSPReloadRotateTLSCertDisableMustStaple(t *testing.T) {
	const (
		caCert            = "configs/certs/ocsp/ca-cert.pem"
		caKey             = "configs/certs/ocsp/ca-key.pem"
		serverCert        = "configs/certs/ocsp/server-status-request-url-01-cert.pem"
		updatedServerCert = "configs/certs/ocsp/server-status-request-cert.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)

	storeDir := t.TempDir()

	originalContent := `
		port: -1

		store_dir: '%s'

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`

	content := fmt.Sprintf(originalContent, storeDir)
	conf := createConfFile(t, []byte(content))
	s, opts := RunServerWithConfig(conf)
	defer s.Shutdown()

	var staple []byte
	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				staple = s.OCSPResponse
				resp, err := getOCSPStatus(s)
				if err != nil {
					return err
				}
				if resp.Status != ocsp.Good {
					t.Errorf("Expected valid OCSP staple status")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	files := []string{}
	err = filepath.Walk(storeDir+"/ocsp/", func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		files = append(files, path)
		return nil
	})
	if err != nil {
		t.Fatal(err)
	}
	found := false
	for _, file := range files {
		data, err := os.ReadFile(file)
		if err != nil {
			t.Error(err)
		}
		if bytes.Equal(staple, data) {
			found = true
		}
	}
	if !found {
		t.Error("Could not find OCSP Staple")
	}

	// Change the contents with another that has OCSP Stapling disabled.
	updatedContent := `
		port: -1

		store_dir: '%s'

		tls {
			cert_file: "configs/certs/ocsp/server-cert.pem"
			key_file: "configs/certs/ocsp/server-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	content = fmt.Sprintf(updatedContent, storeDir)
	if err := os.WriteFile(conf, []byte(content), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := s.Reload(); err != nil {
		t.Fatal(err)
	}

	// The new certificate does not have must staple so they will be missing.
	time.Sleep(4 * time.Second)

	nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse != nil {
					return fmt.Errorf("unexpected OCSP Staple!")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// Re-enable OCSP Stapling
	content = fmt.Sprintf(originalContent, storeDir)
	if err := os.WriteFile(conf, []byte(content), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := s.Reload(); err != nil {
		t.Fatal(err)
	}

	var newStaple []byte
	nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				newStaple = s.OCSPResponse
				resp, err := getOCSPStatus(s)
				if err != nil {
					return err
				}
				if resp.Status != ocsp.Good {
					t.Errorf("Expected valid OCSP staple status")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// Confirm that it got a new staple.
	files = []string{}
	err = filepath.Walk(storeDir+"/ocsp/", func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		files = append(files, path)
		return nil
	})
	if err != nil {
		t.Fatal(err)
	}
	found = false
	for _, file := range files {
		data, err := os.ReadFile(file)
		if err != nil {
			t.Error(err)
		}
		if bytes.Equal(newStaple, data) {
			found = true
		}
	}
	if !found {
		t.Error("Could not find OCSP Staple")
	}
	if bytes.Equal(staple, newStaple) {
		t.Error("Expected new OCSP Staple")
	}
}

func TestOCSPReloadRotateTLSCertEnableMustStaple(t *testing.T) {
	const (
		caCert            = "configs/certs/ocsp/ca-cert.pem"
		caKey             = "configs/certs/ocsp/ca-key.pem"
		serverCert        = "configs/certs/ocsp/server-cert.pem"
		updatedServerCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)
	setOCSPStatus(t, addr, updatedServerCert, ocsp.Good)

	// Start without OCSP Stapling MustStaple
	content := `
		port: -1

		tls {
			cert_file: "configs/certs/ocsp/server-cert.pem"
			key_file: "configs/certs/ocsp/server-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	conf := createConfFile(t, []byte(content))
	s, opts := RunServerWithConfig(conf)
	defer s.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse != nil {
					return fmt.Errorf("unexpected OCSP Staple!")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// Change the contents with another that has OCSP Stapling enabled.
	content = `
		port: -1

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	if err := os.WriteFile(conf, []byte(content), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := s.Reload(); err != nil {
		t.Fatal(err)
	}

	// The new certificate does not have must staple so they will be missing.
	time.Sleep(2 * time.Second)

	nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				resp, err := getOCSPStatus(s)
				if err != nil {
					return err
				}
				if resp.Status != ocsp.Good {
					t.Errorf("Expected valid OCSP staple status")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()
}

func TestOCSPCluster(t *testing.T) {
	const (
		caCert = "configs/certs/ocsp/ca-cert.pem"
		caKey  = "configs/certs/ocsp/ca-key.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good)

	// Store Dirs
	storeDirA := t.TempDir()
	storeDirB := t.TempDir()
	storeDirC := t.TempDir()

	// Seed server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		cluster {
			name: AB
			host: "127.0.0.1"
			advertise: 127.0.0.1
			port: -1
			pool_size: -1

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	// The rest
	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "BBB"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		cluster {
			name: AB
			host: "127.0.0.1"
			advertise: 127.0.0.1
			port: -1
			pool_size: -1

			routes: [ nats://127.0.0.1:%d ]
			connect_retries: 30

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-04-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-04-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.Cluster.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, optsB := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", optsA.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cA.Close()
	checkClusterFormed(t, srvA, srvB)

	// Revoke the seed server cluster certificate, following servers will not be able to verify connection.
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Revoked)

	// Original set of servers still can communicate to each other, even though the cert has been revoked.
	// NOTE: Should we unplug from the cluster in case our server is revoke and OCSP policy is always or must?
	checkClusterFormed(t, srvA, srvB)

	// Wait for seed server to notice that its certificate has been revoked,
	// so that new routes can't connect to it.
	time.Sleep(6 * time.Second)

	// Start another server against the seed server that has an invalid OCSP Staple
	srvConfC := `
		host: "127.0.0.1"
		port: -1

		server_name: "CCC"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		cluster {
			name: AB
			host: "127.0.0.1"
			advertise: 127.0.0.1
			port: -1
			pool_size: -1

			routes: [ nats://127.0.0.1:%d ]
			connect_retries: 30

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}
	`
	srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.Cluster.Port)
	conf = createConfFile(t, []byte(srvConfC))
	srvC, optsC := RunServerWithConfig(conf)
	defer srvC.Shutdown()

	cB, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", optsB.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cB.Close()
	cC, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", optsC.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cC.Close()

	// There should be no connectivity between the clients due to the revoked staple.
	_, err = cA.Subscribe("foo", func(m *nats.Msg) {
		m.Respond(nil)
	})
	if err != nil {
		t.Errorf("%v", err)
	}
	cA.Flush()
	_, err = cB.Subscribe("bar", func(m *nats.Msg) {
		m.Respond(nil)
	})
	if err != nil {
		t.Fatal(err)
	}
	cB.Flush()
	resp, err := cC.Request("foo", nil, 2*time.Second)
	if err == nil {
		t.Errorf("Unexpected success, response: %+v", resp)
	}
	resp, err = cC.Request("bar", nil, 2*time.Second)
	if err == nil {
		t.Errorf("Unexpected success, response: %+v", resp)
	}

	// Switch the certs from the seed server to new ones that are not revoked,
	// this should restart OCSP Stapling for the cluster routes.
	srvConfA = `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		cluster {
			port: -1
			pool_size: -1
			compression: "disabled"
			name: AB
			host: "127.0.0.1"
			advertise: 127.0.0.1
			connect_retries: 30

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	if err := os.WriteFile(sconfA, []byte(srvConfA), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := srvA.Reload(); err != nil {
		t.Fatal(err)
	}
	// Wait to get a new OCSP Staple.
	time.Sleep(10 * time.Second)
	checkClusterFormed(t, srvA, srvB, srvC)

	// Now clients connect to C can communicate with B and A.
	_, err = cC.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}
	_, err = cC.Request("bar", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}
}

func TestOCSPLeaf(t *testing.T) {
	const (
		caCert = "configs/certs/ocsp/ca-cert.pem"
		caKey  = "configs/certs/ocsp/ca-key.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/client-cert.pem", ocsp.Good)

	// Store Dirs
	storeDirA := t.TempDir()
	storeDirB := t.TempDir()
	storeDirC := t.TempDir()

	// LeafNode server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		leafnodes {
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				# Leaf connection must present certs.
				verify: true
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	// LeafNode that has the original as a remote and running
	// without OCSP Stapling for the leaf remote.
	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "BBB"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		leafnodes {
			remotes: [ {
				url: "tls://127.0.0.1:%d"
				tls {
					# Cert without OCSP Stapling enabled is able to connect.
					cert_file: "configs/certs/ocsp/client-cert.pem"
					key_file: "configs/certs/ocsp/client-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			} ]
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.LeafNode.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, optsB := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cA.Close()
	checkLeafNodeConnected(t, srvA)

	// Revoke the seed server cluster certificate, following servers will not be able to verify connection.
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Revoked)

	// Original set of servers still can communicate to each other via leafnode, even though the staple
	// for the leaf server has been revoked.
	checkLeafNodeConnected(t, srvA)

	// Wait for seed server to notice that its certificate has been revoked.
	time.Sleep(6 * time.Second)

	// Start another server against the seed server that has an revoked OCSP Staple.
	srvConfC := `
		host: "127.0.0.1"
		port: -1

		server_name: "CCC"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		leafnodes {
			remotes: [ {
				url: "tls://127.0.0.1:%d"
				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			} ]
		}
	`
	srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.LeafNode.Port)
	conf = createConfFile(t, []byte(srvConfC))
	srvC, optsC := RunServerWithConfig(conf)
	defer srvC.Shutdown()

	cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cB.Close()
	cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cC.Close()

	// There should be connectivity between the clients even if there is a revoked staple
	// from a leafnode connection.
	_, err = cA.Subscribe("foo", func(m *nats.Msg) {
		m.Respond(nil)
	})
	if err != nil {
		t.Errorf("%v", err)
	}
	cA.Flush()
	_, err = cB.Subscribe("bar", func(m *nats.Msg) {
		m.Respond(nil)
	})
	if err != nil {
		t.Fatal(err)
	}
	cB.Flush()
	_, err = cC.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("Expected success, got: %+v", err)
	}
	_, err = cC.Request("bar", nil, 2*time.Second)
	if err != nil {
		t.Errorf("Expected success, got: %+v", err)
	}

	// Switch the certs from the leafnode server to new ones that are not revoked,
	// this should restart OCSP Stapling for the leafnode server.
	srvConfA = `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		leafnodes {
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	if err := os.WriteFile(sconfA, []byte(srvConfA), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := srvA.Reload(); err != nil {
		t.Fatal(err)
	}
	time.Sleep(4 * time.Second)

	// A <-> A
	_, err = cA.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}

	// B <-> A
	_, err = cB.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}

	// C <-> A
	_, err = cC.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}
	// C <-> B via leafnode A
	_, err = cC.Request("bar", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}
}

func TestOCSPLeafNoVerify(t *testing.T) {
	const (
		caCert = "configs/certs/ocsp/ca-cert.pem"
		caKey  = "configs/certs/ocsp/ca-key.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/client-cert.pem", ocsp.Good)

	// Store Dirs
	storeDirA := t.TempDir()
	storeDirB := t.TempDir()
	storeDirC := t.TempDir()

	// LeafNode server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		leafnodes {
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"
			# for this test, explicitly disable compression because we do it
			# in RunServer but here we do a config reload...
			compression: off

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				# Leaf server does not require certs for clients.
				verify: false
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	// LeafNode remote that will connect to A and will not present certs.
	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "BBB"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		leafnodes {
			remotes: [ {
				url: "tls://127.0.0.1:%d"
				tls {
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			} ]
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.LeafNode.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, optsB := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cA.Close()
	checkLeafNodeConnected(t, srvA)

	// Revoke the seed server cluster certificate, following servers will not be able to verify connection.
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Revoked)

	// Original set of servers still can communicate to each other, even though the cert has been revoked.
	checkLeafNodeConnected(t, srvA)

	// Wait for seed server to notice that its certificate has been revoked.
	time.Sleep(6 * time.Second)

	// Start another server against the seed server that has an revoked OCSP Staple.
	srvConfC := `
		host: "127.0.0.1"
		port: -1

		server_name: "CCC"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		leafnodes {
			remotes: [ {
				url: "tls://127.0.0.1:%d"
				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
					timeout: 5
				}
			} ]
		}
	`
	srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.LeafNode.Port)
	conf = createConfFile(t, []byte(srvConfC))
	srvC, optsC := RunServerWithConfig(conf)
	defer srvC.Shutdown()

	cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cB.Close()
	cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cC.Close()

	// There should be connectivity between the clients even if there is a revoked staple
	// from a leafnode connection.
	_, err = cA.Subscribe("foo", func(m *nats.Msg) {
		m.Respond(nil)
	})
	if err != nil {
		t.Errorf("%v", err)
	}
	cA.Flush()
	_, err = cB.Subscribe("bar", func(m *nats.Msg) {
		m.Respond(nil)
	})
	if err != nil {
		t.Fatal(err)
	}
	cB.Flush()
	_, err = cC.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("Expected success, got: %+v", err)
	}
	_, err = cC.Request("bar", nil, 2*time.Second)
	if err != nil {
		t.Errorf("Expected success, got: %+v", err)
	}

	// Switch the certs from the leafnode server to new ones that are not revoked,
	// this should restart OCSP Stapling for the leafnode server.
	srvConfA = `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		leafnodes {
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"
			compression: off

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	if err := os.WriteFile(sconfA, []byte(srvConfA), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := srvA.Reload(); err != nil {
		t.Fatal(err)
	}
	time.Sleep(4 * time.Second)

	// A <-> A
	_, err = cA.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}

	// B <-> A
	_, err = cB.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}

	// C <-> A
	_, err = cC.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}
	// C <-> B via leafnode A
	_, err = cC.Request("bar", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}
}

func TestOCSPLeafVerifyLeafRemote(t *testing.T) {
	const (
		caCert = "configs/certs/ocsp/ca-cert.pem"
		caKey  = "configs/certs/ocsp/ca-key.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/client-cert.pem", ocsp.Good)

	// Store Dirs
	storeDirA := t.TempDir()
	storeDirB := t.TempDir()

	// LeafNode server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		leafnodes {
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	// LeafNode remote that will connect to A and will not present certs.
	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "BBB"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		leafnodes {
			remotes: [ {
				url: "tls://127.0.0.1:%d"
				tls {
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			} ]
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.LeafNode.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, _ := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cA.Close()

	// Should not have been able to connect.
	checkLeafNodeConnections(t, srvA, 0)
}

func TestOCSPLeafVerifyAndMapLeafRemote(t *testing.T) {
	const (
		caCert = "configs/certs/ocsp/ca-cert.pem"
		caKey  = "configs/certs/ocsp/ca-key.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/client-cert.pem", ocsp.Good)

	// Store Dirs
	storeDirA := t.TempDir()
	storeDirB := t.TempDir()

	// LeafNode server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
			verify_and_map: true
		}
		store_dir: '%s'

		leafnodes {
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				verify_and_map: true
			}
		}

		accounts: {
			leaf: {
			  users: [ {user: "C=US, ST=CA, L=San Francisco, O=Synadia, OU=nats.io, CN=localhost server-status-request-url-04"} ]
			}
			client: {
			  users: [ {user: "C=US, ST=CA, L=San Francisco, O=Synadia, OU=nats.io, CN=localhost client"} ]
			}
		}

	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	// LeafNode remote that will connect to A and will not present certs.
	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "BBB"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		leafnodes {
			remotes: [ {
				url: "tls://127.0.0.1:%d"
				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-04-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-04-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			} ]
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.LeafNode.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, _ := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cA.Close()
	checkLeafNodeConnections(t, srvA, 1)
}

func TestOCSPGateway(t *testing.T) {
	const (
		caCert = "configs/certs/ocsp/ca-cert.pem"
		caKey  = "configs/certs/ocsp/ca-key.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good)

	// Store Dirs
	storeDirA := t.TempDir()
	storeDirB := t.TempDir()
	storeDirC := t.TempDir()

	// Gateway server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		gateway {
			name: A
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	// LeafNode that has the original as a remote.
	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "BBB"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		gateway {
			name: B
			host: "127.0.0.1"
			advertise: "127.0.0.1"
			port: -1
			gateways: [{
				name: "A"
				url: "nats://127.0.0.1:%d"
				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-04-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-04-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			}]
			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-04-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-04-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.Gateway.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, optsB := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cA.Close()
	waitForOutboundGateways(t, srvB, 1, 5*time.Second)

	// Revoke the seed server cluster certificate, following servers will not be able to verify connection.
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Revoked)

	// Original set of servers still can communicate to each other, even though the cert has been revoked.
	waitForOutboundGateways(t, srvA, 1, 5*time.Second)
	waitForOutboundGateways(t, srvB, 1, 5*time.Second)

	// Wait for gateway A to notice that its certificate has been revoked,
	// so that new gateways can't connect to it.
	time.Sleep(6 * time.Second)

	// Start another server against the seed server that has an invalid OCSP Staple
	srvConfC := `
		host: "127.0.0.1"
		port: -1

		server_name: "CCC"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		gateway {
			name: C
			host: "127.0.0.1"
			advertise: "127.0.0.1"
			port: -1
			gateways: [{name: "A", url: "nats://127.0.0.1:%d" }]
			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}
	`
	srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.Gateway.Port)
	conf = createConfFile(t, []byte(srvConfC))
	srvC, optsC := RunServerWithConfig(conf)
	defer srvC.Shutdown()

	cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cB.Close()
	cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cC.Close()

	// There should be no connectivity between the clients due to the revoked staple.
	_, err = cA.Subscribe("foo", func(m *nats.Msg) {
		m.Respond(nil)
	})
	if err != nil {
		t.Errorf("%v", err)
	}
	cA.Flush()
	_, err = cB.Subscribe("bar", func(m *nats.Msg) {
		m.Respond(nil)
	})
	if err != nil {
		t.Fatal(err)
	}
	cB.Flush()

	// Gateway C was not able to mesh with Gateway A because of the revoked OCSP staple
	// so these requests to A and B should fail.
	resp, err := cC.Request("foo", nil, 2*time.Second)
	if err == nil {
		t.Errorf("Unexpected success, response: %+v", resp)
	}
	// Make request to B
	resp, err = cC.Request("bar", nil, 2*time.Second)
	if err == nil {
		t.Errorf("Unexpected success, response: %+v", resp)
	}

	// Switch the certs from the seed server to new ones that are not revoked,
	// this should restart OCSP Stapling for the cluster routes.
	srvConfA = `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		gateway {
			name: A
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}
	`

	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	if err := os.WriteFile(sconfA, []byte(srvConfA), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := srvA.Reload(); err != nil {
		t.Fatal(err)
	}
	time.Sleep(4 * time.Second)
	waitForOutboundGateways(t, srvA, 2, 5*time.Second)
	waitForOutboundGateways(t, srvB, 2, 5*time.Second)
	waitForOutboundGateways(t, srvC, 2, 5*time.Second)

	// Now clients connect to C can communicate with B and A.
	_, err = cC.Request("foo", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}
	_, err = cC.Request("bar", nil, 2*time.Second)
	if err != nil {
		t.Errorf("%v", err)
	}
}

func TestOCSPGatewayIntermediate(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	intermediateCA1Responder := newOCSPResponderIntermediateCA1(t)
	intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr)
	defer intermediateCA1Responder.Shutdown(ctx)
	setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem", ocsp.Good)
	setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/server1/TestServer2_cert.pem", ocsp.Good)

	// Gateway server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "AAA"

		ocsp: {
			mode: always
			url: %s
		}

		gateway {
			name: A
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem"
				key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
				ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
				timeout: 5
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, intermediateCA1ResponderURL)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "BBB"

		ocsp: {
			mode: always
			url: %s
		}

		gateway {
			name: B
			host: "127.0.0.1"
			advertise: "127.0.0.1"
			port: -1
			gateways: [{
				name: "A"
				url: "nats://127.0.0.1:%d"
			}]
			tls {
				cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer2_bundle.pem"
				key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer2_keypair.pem"
				ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
				timeout: 5
			}
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, intermediateCA1ResponderURL, optsA.Gateway.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, optsB := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", optsA.Port),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cA.Close()
	waitForOutboundGateways(t, srvB, 1, 5*time.Second)

	cB, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", optsB.Port),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cB.Close()
}

func TestOCSPCustomConfig(t *testing.T) {
	const (
		caCert     = "configs/certs/ocsp/ca-cert.pem"
		caKey      = "configs/certs/ocsp/ca-key.pem"
		serverCert = "configs/certs/ocsp/server-cert.pem"
		serverKey  = "configs/certs/ocsp/server-key.pem"
	)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	ocspURL := fmt.Sprintf("http://%s", ocspr.Addr)
	defer ocspr.Shutdown(ctx)

	var (
		errExpectedNoStaple = fmt.Errorf("expected no staple")
		errMissingStaple    = fmt.Errorf("missing OCSP Staple from server")
	)

	for _, test := range []struct {
		name      string
		config    string
		opts      []nats.Option
		err       error
		rerr      error
		configure func()
	}{
		{
			"OCSP Stapling in auto mode makes server fail to boot if status is revoked",
			`
				port: -1

				ocsp {
					mode: auto
				}

				tls {
					cert_file: "configs/certs/ocsp/server-cert.pem"
					key_file: "configs/certs/ocsp/server-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse != nil {
							return errExpectedNoStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Revoked) },
		},
		{
			"OCSP Stapling must staple ignored if disabled with ocsp: false",
			`
				port: -1

				ocsp: false

				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse != nil {
							return errExpectedNoStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() {
				setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
			},
		},
		{
			"OCSP Stapling must staple ignored if disabled with ocsp mode never",
			`
				port: -1

				ocsp: { mode: never }

				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse != nil {
							return errExpectedNoStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() {
				setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
			},
		},
		{
			"OCSP Stapling in always mode fetches a staple even if cert does not have one",
			`
				port: -1

				ocsp {
					mode: always
					url: "http://127.0.0.1:8888"
				}

				tls {
					cert_file: "configs/certs/ocsp/server-cert.pem"
					key_file: "configs/certs/ocsp/server-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse == nil {
							return errMissingStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) },
		},
		{
			"OCSP Stapling in must staple mode does not fetch staple if there is no must staple flag",
			`
				port: -1

				ocsp {
					mode: must
					url: "http://127.0.0.1:8888"
				}

				tls {
					cert_file: "configs/certs/ocsp/server-cert.pem"
					key_file: "configs/certs/ocsp/server-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse != nil {
							return errExpectedNoStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() { setOCSPStatus(t, ocspURL, serverCert, ocsp.Good) },
		},
		{
			"OCSP Stapling in must staple mode fetches staple if there is a must staple flag",
			`
				port: -1

				ocsp {
					mode: must
					url: "http://127.0.0.1:8888"
				}

				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse == nil {
							return errMissingStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(caCert),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			func() {
				setOCSPStatus(t, ocspURL, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
			},
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			test.configure()
			content := test.config
			conf := createConfFile(t, []byte(content))
			s, opts := RunServerWithConfig(conf)
			defer s.Shutdown()

			nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), test.opts...)
			if test.err == nil && err != nil {
				t.Errorf("Expected to connect, got %v", err)
			} else if test.err != nil && err == nil {
				t.Errorf("Expected error on connect")
			} else if test.err != nil && err != nil {
				// Error on connect was expected
				if test.err.Error() != err.Error() {
					t.Errorf("Expected error %s, got: %s", test.err, err)
				}
				return
			}
			defer nc.Close()

			nc.Subscribe("ping", func(m *nats.Msg) {
				m.Respond([]byte("pong"))
			})
			nc.Flush()

			_, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond)
			if test.rerr != nil && err == nil {
				t.Errorf("Expected error getting response")
			} else if test.rerr == nil && err != nil {
				t.Errorf("Expected response")
			}
		})
	}
}

func TestOCSPCustomConfigReloadDisable(t *testing.T) {
	const (
		caCert            = "configs/certs/ocsp/ca-cert.pem"
		caKey             = "configs/certs/ocsp/ca-key.pem"
		serverCert        = "configs/certs/ocsp/server-cert.pem"
		updatedServerCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)
	setOCSPStatus(t, addr, updatedServerCert, ocsp.Good)

	// Start with server without OCSP Stapling MustStaple
	content := `
		port: -1

		ocsp: { mode: always, url: "http://127.0.0.1:8888" }

		tls {
			cert_file: "configs/certs/ocsp/server-cert.pem"
			key_file: "configs/certs/ocsp/server-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	conf := createConfFile(t, []byte(content))
	s, opts := RunServerWithConfig(conf)
	defer s.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple!")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// Change and disable OCSP Stapling.
	content = `
		port: -1

		ocsp: { mode: never }

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	if err := os.WriteFile(conf, []byte(content), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := s.Reload(); err != nil {
		t.Fatal(err)
	}

	// The new certificate has must staple but OCSP Stapling is disabled.
	time.Sleep(2 * time.Second)

	nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse != nil {
					return fmt.Errorf("unexpected OCSP Staple!")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()
}

func TestOCSPCustomConfigReloadEnable(t *testing.T) {
	const (
		caCert            = "configs/certs/ocsp/ca-cert.pem"
		caKey             = "configs/certs/ocsp/ca-key.pem"
		serverCert        = "configs/certs/ocsp/server-cert.pem"
		updatedServerCert = "configs/certs/ocsp/server-status-request-url-01-cert.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, serverCert, ocsp.Good)
	setOCSPStatus(t, addr, updatedServerCert, ocsp.Good)

	// Start with server without OCSP Stapling MustStaple
	content := `
		port: -1

		ocsp: { mode: never, url: "http://127.0.0.1:8888" }

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	conf := createConfFile(t, []byte(content))
	s, opts := RunServerWithConfig(conf)
	defer s.Shutdown()

	nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse != nil {
					return fmt.Errorf("unexpected OCSP Staple!")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer nc.Close()
	sub, err := nc.SubscribeSync("foo")
	if err != nil {
		t.Fatal(err)
	}
	nc.Publish("foo", []byte("hello world"))
	nc.Flush()

	_, err = sub.NextMsg(1 * time.Second)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()

	// Change and disable OCSP Stapling.
	content = `
		port: -1

		ocsp: { mode: always, url: "http://127.0.0.1:8888" }

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
	`
	if err := os.WriteFile(conf, []byte(content), 0666); err != nil {
		t.Fatalf("Error writing config: %v", err)
	}
	if err := s.Reload(); err != nil {
		t.Fatal(err)
	}
	time.Sleep(2 * time.Second)

	nc, err = nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple!")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	nc.Close()
}

func newOCSPResponderCustomAddress(t *testing.T, issuerCertPEM, issuerKeyPEM string, addr string) *http.Server {
	t.Helper()
	return newOCSPResponderBase(t, issuerCertPEM, issuerCertPEM, issuerKeyPEM, false, addr, defaultResponseTTL)
}

func newOCSPResponder(t *testing.T, issuerCertPEM, issuerKeyPEM string) *http.Server {
	t.Helper()
	return newOCSPResponderBase(t, issuerCertPEM, issuerCertPEM, issuerKeyPEM, false, defaultAddress, defaultResponseTTL)
}

func newOCSPResponderDesignatedCustomAddress(t *testing.T, issuerCertPEM, respCertPEM, respKeyPEM string, addr string) *http.Server {
	t.Helper()
	return newOCSPResponderBase(t, issuerCertPEM, respCertPEM, respKeyPEM, true, addr, defaultResponseTTL)
}

func newOCSPResponderBase(t *testing.T, issuerCertPEM, respCertPEM, respKeyPEM string, embed bool, addr string, responseTTL time.Duration) *http.Server {
	t.Helper()
	var mu sync.Mutex
	status := make(map[string]int)

	issuerCert := parseCertPEM(t, issuerCertPEM)
	respCert := parseCertPEM(t, respCertPEM)
	respKey := parseKeyPEM(t, respKeyPEM)

	mux := http.NewServeMux()
	// The "/statuses/" endpoint is for directly setting a key-value pair in
	// the CA's status database.
	mux.HandleFunc("/statuses/", func(rw http.ResponseWriter, r *http.Request) {
		defer r.Body.Close()

		key := r.URL.Path[len("/statuses/"):]
		switch r.Method {
		case "GET":
			mu.Lock()
			n, ok := status[key]
			if !ok {
				n = ocsp.Unknown
			}
			mu.Unlock()

			fmt.Fprintf(rw, "%s %d", key, n)
		case "POST":
			data, err := io.ReadAll(r.Body)
			if err != nil {
				http.Error(rw, err.Error(), http.StatusBadRequest)
				return
			}

			n, err := strconv.Atoi(string(data))
			if err != nil {
				http.Error(rw, err.Error(), http.StatusBadRequest)
				return
			}

			mu.Lock()
			status[key] = n
			mu.Unlock()

			fmt.Fprintf(rw, "%s %d", key, n)
		default:
			http.Error(rw, "Method Not Allowed", http.StatusMethodNotAllowed)
			return
		}
	})
	// The "/" endpoint is for normal OCSP requests. This actually parses an
	// OCSP status request and signs a response with a CA. Lightly based off:
	// https://www.ietf.org/rfc/rfc2560.txt
	mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.Error(rw, "Method Not Allowed", http.StatusMethodNotAllowed)
			return
		}

		reqData, err := base64.StdEncoding.DecodeString(r.URL.Path[1:])
		if err != nil {
			http.Error(rw, err.Error(), http.StatusBadRequest)
			return
		}

		ocspReq, err := ocsp.ParseRequest(reqData)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusBadRequest)
			return
		}

		mu.Lock()
		n, ok := status[ocspReq.SerialNumber.String()]
		if !ok {
			n = ocsp.Unknown
		}
		mu.Unlock()

		tmpl := ocsp.Response{
			Status:       n,
			SerialNumber: ocspReq.SerialNumber,
			ThisUpdate:   time.Now(),
		}
		if responseTTL != 0 {
			tmpl.NextUpdate = tmpl.ThisUpdate.Add(responseTTL)
		}
		if embed {
			tmpl.Certificate = respCert
		}
		respData, err := ocsp.CreateResponse(issuerCert, respCert, tmpl, respKey)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		rw.Header().Set("Content-Type", "application/ocsp-response")
		rw.Header().Set("Content-Length", fmt.Sprint(len(respData)))

		fmt.Fprint(rw, string(respData))
	})

	srv := &http.Server{
		Addr:    addr,
		Handler: mux,
	}
	go srv.ListenAndServe()
	time.Sleep(1 * time.Second)
	return srv
}

func setOCSPStatus(t *testing.T, ocspURL, certPEM string, status int) {
	t.Helper()

	cert := parseCertPEM(t, certPEM)

	hc := &http.Client{Timeout: 10 * time.Second}
	resp, err := hc.Post(
		fmt.Sprintf("%s/statuses/%s", ocspURL, cert.SerialNumber),
		"",
		strings.NewReader(fmt.Sprint(status)),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("failed to read OCSP HTTP response body: %s", err)
	}

	if got, want := resp.Status, "200 OK"; got != want {
		t.Error(strings.TrimSpace(string(data)))
		t.Fatalf("unexpected OCSP HTTP set status, got %q, want %q", got, want)
	}
}

func parseCertPEM(t *testing.T, certPEM string) *x509.Certificate {
	t.Helper()
	block := parsePEM(t, certPEM)

	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		t.Fatalf("failed to parse cert '%s': %s", certPEM, err)
	}
	return cert
}

func parseKeyPEM(t *testing.T, keyPEM string) crypto.Signer {
	t.Helper()
	block := parsePEM(t, keyPEM)

	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		key, err = x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			t.Fatalf("failed to parse ikey %s: %s", keyPEM, err)
		}
	}
	keyc := key.(crypto.Signer)
	return keyc
}

func parsePEM(t *testing.T, pemPath string) *pem.Block {
	t.Helper()
	data, err := os.ReadFile(pemPath)
	if err != nil {
		t.Fatal(err)
	}

	block, _ := pem.Decode(data)
	if block == nil {
		t.Fatalf("failed to decode PEM %s", pemPath)
	}
	return block
}

func getOCSPStatus(s tls.ConnectionState) (*ocsp.Response, error) {
	if len(s.VerifiedChains) == 0 {
		return nil, fmt.Errorf("missing TLS verified chains")
	}
	chain := s.VerifiedChains[0]

	if got, want := len(chain), 2; got < want {
		return nil, fmt.Errorf("incomplete cert chain, got %d, want at least %d", got, want)
	}
	leaf, issuer := chain[0], chain[1]

	resp, err := ocsp.ParseResponseForCert(s.OCSPResponse, leaf, issuer)
	if err != nil {
		return nil, fmt.Errorf("failed to parse OCSP response: %w", err)
	}
	if err := resp.CheckSignatureFrom(issuer); err != nil {
		return resp, err
	}
	return resp, nil
}

func TestOCSPTLSConfigNoLeafSet(t *testing.T) {
	o := DefaultTestOptions
	o.HTTPHost = "127.0.0.1"
	o.HTTPSPort = -1
	o.TLSConfig = &tls.Config{ServerName: "localhost"}
	cert, err := tls.LoadX509KeyPair("configs/certs/server-cert.pem", "configs/certs/server-key.pem")
	if err != nil {
		t.Fatalf("Got error reading certificates: %s", err)
	}
	o.TLSConfig.Certificates = []tls.Certificate{cert}
	s := RunServer(&o)
	s.Shutdown()
}

func TestOCSPSuperCluster(t *testing.T) {
	const (
		caCert = "configs/certs/ocsp/ca-cert.pem"
		caKey  = "configs/certs/ocsp/ca-key.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ocspr := newOCSPResponder(t, caCert, caKey)
	defer ocspr.Shutdown(ctx)
	addr := fmt.Sprintf("http://%s", ocspr.Addr)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-01-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-02-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-03-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-04-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-05-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-06-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-07-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-status-request-url-08-cert.pem", ocsp.Good)
	setOCSPStatus(t, addr, "configs/certs/ocsp/server-cert.pem", ocsp.Good)

	// Store Dirs
	storeDirA := t.TempDir()
	storeDirB := t.TempDir()
	storeDirC := t.TempDir()
	storeDirD := t.TempDir()

	// Gateway server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "A"

		ocsp { mode: "always" }

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		cluster {
			name: A
			host: "127.0.0.1"
			advertise: 127.0.0.1
			port: -1

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}

		gateway {
			name: A
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	// Server that has the original as a cluster.
	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "B"

		ocsp { mode: "always" }

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-01-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-01-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		cluster {
			name: A
			host: "127.0.0.1"
			advertise: 127.0.0.1
			port: -1

			routes: [ nats://127.0.0.1:%d ]

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-02-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-02-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
			}
		}

		gateway {
			name: A
			host: "127.0.0.1"
			advertise: "127.0.0.1"
			port: -1

			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-03-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-03-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.Cluster.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, optsB := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)

	}
	defer cA.Close()

	// Start another server that will make connect as a gateway to cluster A.
	srvConfC := `
		host: "127.0.0.1"
		port: -1

		server_name: "C"

		ocsp { mode: "always" }

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-05-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-05-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		gateway {
			name: C
			host: "127.0.0.1"
			advertise: "127.0.0.1"
			port: -1
			gateways: [{
				name: "A",
				urls: ["nats://127.0.0.1:%d"]
				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}
			}]
			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-06-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-06-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.Gateway.Port)
	conf = createConfFile(t, []byte(srvConfC))
	srvC, optsC := RunServerWithConfig(conf)
	defer srvC.Shutdown()

	// Check that server is connected to any server from the other cluster.
	checkClusterFormed(t, srvA, srvB)
	waitForOutboundGateways(t, srvC, 1, 5*time.Second)

	// Start one more server that will become another gateway.
	srvConfD := `
		host: "127.0.0.1"
		port: -1

		server_name: "D"

		ocsp { mode: "auto", url: "%s" }

		tls {
			cert_file: "configs/certs/ocsp/server-status-request-url-07-cert.pem"
			key_file: "configs/certs/ocsp/server-status-request-url-07-key.pem"
			ca_file: "configs/certs/ocsp/ca-cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		gateway {
			name: D
			host: "127.0.0.1"
			advertise: "127.0.0.1"
			port: -1
			gateways: [{
				name: "A",
				urls: ["nats://127.0.0.1:%d"]
				tls {
					cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem"
					key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem"
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					timeout: 5
				}},
				{
				name: "C",
				urls: ["nats://127.0.0.1:%d"]

				####################################################################
				## TEST NOTE: This cert does not have an OCSP Staple intentionally##
				####################################################################
				tls {
					ca_file: "configs/certs/ocsp/ca-cert.pem"
					cert_file: "configs/certs/ocsp/server-cert.pem"
					key_file: "configs/certs/ocsp/server-key.pem"
					timeout: 5
				}}
			]
			tls {
				cert_file: "configs/certs/ocsp/server-status-request-url-08-cert.pem"
				key_file: "configs/certs/ocsp/server-status-request-url-08-key.pem"
				ca_file: "configs/certs/ocsp/ca-cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfD = fmt.Sprintf(srvConfD, addr, storeDirD, optsA.Gateway.Port, optsC.Gateway.Port)
	conf = createConfFile(t, []byte(srvConfD))
	srvD, _ := RunServerWithConfig(conf)
	defer srvD.Shutdown()

	// There should be a single gateway here because one of the gateway connections does not have a OCSP staple.
	waitForOutboundGateways(t, srvD, 1, 10*time.Second)

	// Connect to cluster A using server B.
	cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cB.Close()

	// Connects to cluster C using server C.
	cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cC.Close()

	_, err = cA.Subscribe("foo", func(m *nats.Msg) {
		m.Respond([]byte("From Server A"))
	})
	if err != nil {
		t.Errorf("%v", err)
	}
	cA.Flush()

	_, err = cB.Subscribe("bar", func(m *nats.Msg) {
		m.Respond([]byte("From Server B"))
	})
	if err != nil {
		t.Fatal(err)
	}
	cB.Flush()

	// Confirm that a message from server C can flow back to server A via gateway..
	var (
		resp *nats.Msg
		lerr error
	)
	for i := 0; i < 10; i++ {
		resp, lerr = cC.Request("foo", nil, 500*time.Millisecond)
		if lerr != nil {
			continue
		}
		got := string(resp.Data)
		expected := "From Server A"
		if got != expected {
			t.Fatalf("Expected %v, got: %v", expected, got)
		}

		// Make request to B
		resp, lerr = cC.Request("bar", nil, 500*time.Millisecond)
		if lerr != nil {
			continue
		}
		got = string(resp.Data)
		expected = "From Server B"
		if got != expected {
			t.Errorf("Expected %v, got: %v", expected, got)
		}
		lerr = nil
		break
	}
	if lerr != nil {
		t.Errorf("Unexpected error: %v", lerr)
	}
	if n := srvD.NumOutboundGateways(); n > 1 {
		t.Errorf("Expected single gateway, got: %v", n)
	}
}

func TestOCSPLocalIssuerDetermination(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	intermediateCA1Responder := newOCSPResponderIntermediateCA1(t)
	intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr)
	defer intermediateCA1Responder.Shutdown(ctx)

	// Test constants
	ocspURL := intermediateCA1ResponderURL
	clientTrustBundle := "configs/certs/ocsp_peer/mini-ca/misc/trust_config1_bundle.pem"
	serverCert := "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem"

	var (
		errMissingStaple = fmt.Errorf("missing OCSP Staple from server")
	)

	for _, test := range []struct {
		name        string
		config      string
		opts        []nats.Option
		err         error
		rerr        error
		serverStart bool
		configure   func()
	}{
		{
			"Correct issuer configured in cert bundle",
			`
				port: -1

				ocsp {
					mode: always
				}

				tls {
					cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem"
					key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
					ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse == nil {
							return errMissingStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(clientTrustBundle),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			true,
			func() {
				setOCSPStatus(t, ocspURL, serverCert, ocsp.Good)
			},
		},
		{
			"Wrong issuer configured in cert bundle, server no start",
			`
				port: -1

				ocsp {
					mode: always
				}

				tls {
					cert_file: "configs/certs/ocsp_peer/mini-ca/misc/misconfig_TestServer1_bundle.pem"
					key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
					ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse == nil {
							return errMissingStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(clientTrustBundle),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			false,
			func() {
				setOCSPStatus(t, ocspURL, serverCert, ocsp.Good)
			},
		},
		{
			"Issuer configured in CA bundle only, configuration 1",
			`
				port: -1

				ocsp {
					mode: always
				}

				tls {
					cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem"
					key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
					ca_file: "configs/certs/ocsp_peer/mini-ca/misc/trust_config1_bundle.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse == nil {
							return errMissingStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(clientTrustBundle),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			true,
			func() {
				setOCSPStatus(t, ocspURL, serverCert, ocsp.Good)
			},
		},
		{
			"Issuer configured in CA bundle only, configuration 2",
			`
				port: -1

				ocsp {
					mode: always
				}

				tls {
					cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem"
					key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
					ca_file: "configs/certs/ocsp_peer/mini-ca/misc/trust_config2_bundle.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse == nil {
							return errMissingStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(clientTrustBundle),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			true,
			func() {
				setOCSPStatus(t, ocspURL, serverCert, ocsp.Good)
			},
		},
		{
			"Issuer configured in CA bundle only, configuration 3",
			`
				port: -1

				ocsp {
					mode: always
				}

				tls {
					cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem"
					key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
					ca_file: "configs/certs/ocsp_peer/mini-ca/misc/trust_config3_bundle.pem"
					timeout: 5
				}
			`,
			[]nats.Option{
				nats.Secure(&tls.Config{
					VerifyConnection: func(s tls.ConnectionState) error {
						if s.OCSPResponse == nil {
							return errMissingStaple
						}
						return nil
					},
				}),
				nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
				nats.RootCAs(clientTrustBundle),
				nats.ErrorHandler(noOpErrHandler),
			},
			nil,
			nil,
			true,
			func() {
				setOCSPStatus(t, ocspURL, serverCert, ocsp.Good)
			},
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			defer func() {
				r := recover()
				if r != nil && test.serverStart {
					t.Fatalf("Expected server start, unexpected panic: %v", r)
				}
				if r == nil && !test.serverStart {
					t.Fatalf("Expected server to not start and panic thrown")
				}
			}()
			test.configure()
			content := test.config
			conf := createConfFile(t, []byte(content))
			s, opts := RunServerWithConfig(conf)
			// server may not start for some tests
			if s != nil {
				defer s.Shutdown()
			}

			nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port), test.opts...)
			if test.err == nil && err != nil {
				t.Errorf("Expected to connect, got %v", err)
			} else if test.err != nil && err == nil {
				t.Errorf("Expected error on connect")
			} else if test.err != nil && err != nil {
				// Error on connect was expected
				if test.err.Error() != err.Error() {
					t.Errorf("Expected error %s, got: %s", test.err, err)
				}
				return
			}
			defer nc.Close()

			nc.Subscribe("ping", func(m *nats.Msg) {
				m.Respond([]byte("pong"))
			})
			nc.Flush()

			_, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond)
			if test.rerr != nil && err == nil {
				t.Errorf("Expected error getting response")
			} else if test.rerr == nil && err != nil {
				t.Errorf("Expected response")
			}
		})
	}
}

func TestMixedCAOCSPSuperCluster(t *testing.T) {
	const (
		caCert = "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
		caKey  = "configs/certs/ocsp/ca-key.pem"
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	intermediateCA1Responder := newOCSPResponderIntermediateCA1(t)
	intermediateCA1ResponderURL := fmt.Sprintf("http://%s", intermediateCA1Responder.Addr)
	defer intermediateCA1Responder.Shutdown(ctx)
	setOCSPStatus(t, intermediateCA1ResponderURL, "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_cert.pem", ocsp.Good)

	intermediateCA2Responder := newOCSPResponderIntermediateCA2(t)
	intermediateCA2ResponderURL := fmt.Sprintf("http://%s", intermediateCA2Responder.Addr)
	defer intermediateCA2Responder.Shutdown(ctx)
	setOCSPStatus(t, intermediateCA2ResponderURL, "configs/certs/ocsp_peer/mini-ca/server2/TestServer3_cert.pem", ocsp.Good)

	// Store Dirs
	storeDirA := t.TempDir()
	storeDirB := t.TempDir()
	storeDirC := t.TempDir()

	// Gateway server configuration
	srvConfA := `
		host: "127.0.0.1"
		port: -1

		server_name: "A"

		ocsp { mode: "always" }

		tls {
			cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem"
			key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
			ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		cluster {
			name: A
			host: "127.0.0.1"
			advertise: 127.0.0.1
			port: -1

			tls {
				cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem"
				key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
				ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
				timeout: 5
			}
		}

		gateway {
			name: A
			host: "127.0.0.1"
			port: -1
			advertise: "127.0.0.1"

			tls {
				cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem"
				key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
				ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfA = fmt.Sprintf(srvConfA, storeDirA)
	sconfA := createConfFile(t, []byte(srvConfA))
	srvA, optsA := RunServerWithConfig(sconfA)
	defer srvA.Shutdown()

	// Server that has the original as a cluster.
	srvConfB := `
		host: "127.0.0.1"
		port: -1

		server_name: "B"

		ocsp { mode: "always" }

		tls {
			cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem"
			key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
			ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
			timeout: 5
		}
		store_dir: '%s'

		cluster {
			name: A
			host: "127.0.0.1"
			advertise: 127.0.0.1
			port: -1

			routes: [ nats://127.0.0.1:%d ]

			tls {
				cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem"
				key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
				ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
				timeout: 5
			}
		}

		gateway {
			name: A
			host: "127.0.0.1"
			advertise: "127.0.0.1"
			port: -1

			tls {
				cert_file: "configs/certs/ocsp_peer/mini-ca/server1/TestServer1_bundle.pem"
				key_file: "configs/certs/ocsp_peer/mini-ca/server1/private/TestServer1_keypair.pem"
				ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfB = fmt.Sprintf(srvConfB, storeDirB, optsA.Cluster.Port)
	conf := createConfFile(t, []byte(srvConfB))
	srvB, optsB := RunServerWithConfig(conf)
	defer srvB.Shutdown()

	// Client connects to server A.
	cA, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsA.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)

	}
	defer cA.Close()

	// Start another server that will make connect as a gateway to cluster A but with different CA issuer.
	srvConfC := `
		host: "127.0.0.1"
		port: -1

		server_name: "C"

		ocsp { mode: "always" }

		tls {
			cert_file: "configs/certs/ocsp_peer/mini-ca/server2/TestServer3_bundle.pem"
			key_file: "configs/certs/ocsp_peer/mini-ca/server2/private/TestServer3_keypair.pem"
			ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
			timeout: 5
		}
		store_dir: '%s'
		gateway {
			name: C
			host: "127.0.0.1"
			advertise: "127.0.0.1"
			port: -1
			gateways: [{
				name: "A",
				urls: ["nats://127.0.0.1:%d"]
				tls {
					cert_file: "configs/certs/ocsp_peer/mini-ca/server2/TestServer3_bundle.pem"
					key_file: "configs/certs/ocsp_peer/mini-ca/server2/private/TestServer3_keypair.pem"
					ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
					timeout: 5
				}
			}]
			tls {
				cert_file: "configs/certs/ocsp_peer/mini-ca/server2/TestServer3_bundle.pem"
				key_file: "configs/certs/ocsp_peer/mini-ca/server2/private/TestServer3_keypair.pem"
				ca_file: "configs/certs/ocsp_peer/mini-ca/root/root_cert.pem"
				timeout: 5
				verify: true
			}
		}
	`
	srvConfC = fmt.Sprintf(srvConfC, storeDirC, optsA.Gateway.Port)
	conf = createConfFile(t, []byte(srvConfC))
	srvC, optsC := RunServerWithConfig(conf)
	defer srvC.Shutdown()

	// Check that server is connected to any server from the other cluster.
	checkClusterFormed(t, srvA, srvB)
	waitForOutboundGateways(t, srvC, 1, 5*time.Second)

	// Connect to cluster A using server B.
	cB, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsB.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cB.Close()

	// Connects to cluster C using server C.
	cC, err := nats.Connect(fmt.Sprintf("tls://127.0.0.1:%d", optsC.Port),
		nats.Secure(&tls.Config{
			VerifyConnection: func(s tls.ConnectionState) error {
				if s.OCSPResponse == nil {
					return fmt.Errorf("missing OCSP Staple from server")
				}
				return nil
			},
		}),
		nats.RootCAs(caCert),
		nats.ErrorHandler(noOpErrHandler),
	)
	if err != nil {
		t.Fatal(err)
	}
	defer cC.Close()

	_, err = cA.Subscribe("foo", func(m *nats.Msg) {
		m.Respond([]byte("From Server A"))
	})
	if err != nil {
		t.Errorf("%v", err)
	}
	cA.Flush()

	_, err = cB.Subscribe("bar", func(m *nats.Msg) {
		m.Respond([]byte("From Server B"))
	})
	if err != nil {
		t.Fatal(err)
	}
	cB.Flush()

	// Confirm that a message from server C can flow back to server A via gateway..
	var (
		resp *nats.Msg
		lerr error
	)
	for i := 0; i < 10; i++ {
		resp, lerr = cC.Request("foo", nil, 500*time.Millisecond)
		if lerr != nil {
			continue
		}
		got := string(resp.Data)
		expected := "From Server A"
		if got != expected {
			t.Fatalf("Expected %v, got: %v", expected, got)
		}

		// Make request to B
		resp, lerr = cC.Request("bar", nil, 500*time.Millisecond)
		if lerr != nil {
			continue
		}
		got = string(resp.Data)
		expected = "From Server B"
		if got != expected {
			t.Errorf("Expected %v, got: %v", expected, got)
		}
		lerr = nil
		break
	}
	if lerr != nil {
		t.Errorf("Unexpected error: %v", lerr)
	}
}
