mirror of
https://github.com/caddyserver/caddy.git
synced 2025-12-08 06:09:53 +00:00
testing: make it so caddytest launches an instance of caddy per server
This commit is contained in:
parent
c2ccf8690f
commit
b732a7999a
24 changed files with 1071 additions and 881 deletions
6
caddy.go
6
caddy.go
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
|
@ -778,7 +779,10 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
|
||||||
} else {
|
} else {
|
||||||
logger.Error("unclean shutdown")
|
logger.Error("unclean shutdown")
|
||||||
}
|
}
|
||||||
os.Exit(exitCode)
|
// check if we are in test environment, and dont call exit if we are
|
||||||
|
if flag.Lookup("test.v") == nil && !strings.Contains(os.Args[0], ".test") {
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if remoteAdminServer != nil {
|
if remoteAdminServer != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,29 @@
|
||||||
package caddytest
|
package caddytest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"strconv"
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aryann/difflib"
|
|
||||||
|
|
||||||
caddycmd "github.com/caddyserver/caddy/v2/cmd"
|
caddycmd "github.com/caddyserver/caddy/v2/cmd"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
// plug in Caddy modules here
|
// plug in Caddy modules here
|
||||||
_ "github.com/caddyserver/caddy/v2/modules/standard"
|
_ "github.com/caddyserver/caddy/v2/modules/standard"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defaults store any configuration required to make the tests run
|
// Defaults store any configuration required to make the tests run
|
||||||
type Defaults struct {
|
type Defaults struct {
|
||||||
// Port we expect caddy to listening on
|
|
||||||
AdminPort int
|
|
||||||
// Certificates we expect to be loaded before attempting to run the tests
|
// Certificates we expect to be loaded before attempting to run the tests
|
||||||
Certificates []string
|
Certificates []string
|
||||||
// TestRequestTimeout is the time to wait for a http request to
|
// TestRequestTimeout is the time to wait for a http request to
|
||||||
|
|
@ -45,29 +34,31 @@ type Defaults struct {
|
||||||
|
|
||||||
// Default testing values
|
// Default testing values
|
||||||
var Default = Defaults{
|
var Default = Defaults{
|
||||||
AdminPort: 2999, // different from what a real server also running on a developer's machine might be
|
|
||||||
Certificates: []string{"/caddy.localhost.crt", "/caddy.localhost.key"},
|
Certificates: []string{"/caddy.localhost.crt", "/caddy.localhost.key"},
|
||||||
TestRequestTimeout: 5 * time.Second,
|
TestRequestTimeout: 5 * time.Second,
|
||||||
LoadRequestTimeout: 5 * time.Second,
|
LoadRequestTimeout: 5 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
matchKey = regexp.MustCompile(`(/[\w\d\.]+\.key)`)
|
|
||||||
matchCert = regexp.MustCompile(`(/[\w\d\.]+\.crt)`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tester represents an instance of a test client.
|
// Tester represents an instance of a test client.
|
||||||
type Tester struct {
|
type Tester struct {
|
||||||
Client *http.Client
|
Client *http.Client
|
||||||
configLoaded bool
|
|
||||||
t testing.TB
|
adminPort int
|
||||||
|
|
||||||
|
portOne int
|
||||||
|
portTwo int
|
||||||
|
|
||||||
|
started atomic.Bool
|
||||||
|
configLoaded bool
|
||||||
|
configFileName string
|
||||||
|
envFileName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTester will create a new testing client with an attached cookie jar
|
// NewTester will create a new testing client with an attached cookie jar
|
||||||
func NewTester(t testing.TB) *Tester {
|
func NewTester() (*Tester, error) {
|
||||||
jar, err := cookiejar.New(nil)
|
jar, err := cookiejar.New(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create cookiejar: %s", err)
|
return nil, fmt.Errorf("failed to create cookiejar: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Tester{
|
return &Tester{
|
||||||
|
|
@ -77,8 +68,7 @@ func NewTester(t testing.TB) *Tester {
|
||||||
Timeout: Default.TestRequestTimeout,
|
Timeout: Default.TestRequestTimeout,
|
||||||
},
|
},
|
||||||
configLoaded: false,
|
configLoaded: false,
|
||||||
t: t,
|
}, nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type configLoadError struct {
|
type configLoadError struct {
|
||||||
|
|
@ -92,53 +82,73 @@ func timeElapsed(start time.Time, name string) {
|
||||||
log.Printf("%s took %s", name, elapsed)
|
log.Printf("%s took %s", name, elapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitServer this will configure the server with a configurion of a specific
|
// launch caddy will start the server
|
||||||
// type. The configType must be either "json" or the adapter type.
|
func (tc *Tester) LaunchCaddy() error {
|
||||||
func (tc *Tester) InitServer(rawConfig string, configType string) {
|
if !tc.started.CompareAndSwap(false, true) {
|
||||||
if err := tc.initServer(rawConfig, configType); err != nil {
|
return fmt.Errorf("already launched caddy with this tester")
|
||||||
tc.t.Logf("failed to load config: %s", err)
|
|
||||||
tc.t.Fail()
|
|
||||||
}
|
}
|
||||||
if err := tc.ensureConfigRunning(rawConfig, configType); err != nil {
|
if err := tc.startServer(); err != nil {
|
||||||
tc.t.Logf("failed ensuring config is running: %s", err)
|
return fmt.Errorf("failed to start server: %w", err)
|
||||||
tc.t.Fail()
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitServer this will configure the server with a configurion of a specific
|
func (tc *Tester) CleanupCaddy() error {
|
||||||
// type. The configType must be either "json" or the adapter type.
|
// now shutdown the server, since the test is done.
|
||||||
func (tc *Tester) initServer(rawConfig string, configType string) error {
|
defer func() {
|
||||||
if testing.Short() {
|
// try to remove pthe tmp config file we created
|
||||||
tc.t.SkipNow()
|
if tc.configFileName != "" {
|
||||||
return nil
|
os.Remove(tc.configFileName)
|
||||||
}
|
|
||||||
|
|
||||||
err := validateTestPrerequisites(tc.t)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Skipf("skipping tests as failed integration prerequisites. %s", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tc.t.Cleanup(func() {
|
|
||||||
if tc.t.Failed() && tc.configLoaded {
|
|
||||||
res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Log("unable to read the current config")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
body, _ := io.ReadAll(res.Body)
|
|
||||||
|
|
||||||
var out bytes.Buffer
|
|
||||||
_ = json.Indent(&out, body, "", " ")
|
|
||||||
tc.t.Logf("----------- failed with config -----------\n%s", out.String())
|
|
||||||
}
|
}
|
||||||
})
|
if tc.envFileName != "" {
|
||||||
|
os.Remove(tc.envFileName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
resp, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", tc.adminPort), "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't stop caddytest server: %w", err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
for retries := 0; retries < 10; retries++ {
|
||||||
|
if tc.isCaddyAdminRunning() != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
rawConfig = prependCaddyFilePath(rawConfig)
|
return fmt.Errorf("timed out waiting for caddytest server to stop")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *Tester) AdminPort() int {
|
||||||
|
return tc.adminPort
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *Tester) PortOne() int {
|
||||||
|
return tc.portOne
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *Tester) PortTwo() int {
|
||||||
|
return tc.portTwo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *Tester) ReplaceTestingPlaceholders(x string) string {
|
||||||
|
x = strings.ReplaceAll(x, "{$TESTING_CADDY_ADMIN_BIND}", fmt.Sprintf("localhost:%d", tc.adminPort))
|
||||||
|
x = strings.ReplaceAll(x, "{$TESTING_CADDY_ADMIN_PORT}", fmt.Sprintf("%d", tc.adminPort))
|
||||||
|
x = strings.ReplaceAll(x, "{$TESTING_CADDY_PORT_ONE}", fmt.Sprintf("%d", tc.portOne))
|
||||||
|
x = strings.ReplaceAll(x, "{$TESTING_CADDY_PORT_TWO}", fmt.Sprintf("%d", tc.portTwo))
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig loads the config to the tester server and also ensures that the config was loaded
|
||||||
|
// it should not be run
|
||||||
|
func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
|
||||||
|
if tc.adminPort == 0 {
|
||||||
|
return fmt.Errorf("load config called where startServer didnt succeed")
|
||||||
|
}
|
||||||
|
rawConfig = tc.ReplaceTestingPlaceholders(rawConfig)
|
||||||
|
// replace special testing placeholders so we can have our admin api be on a random port
|
||||||
// normalize JSON config
|
// normalize JSON config
|
||||||
if configType == "json" {
|
if configType == "json" {
|
||||||
tc.t.Logf("Before: %s", rawConfig)
|
|
||||||
var conf any
|
var conf any
|
||||||
if err := json.Unmarshal([]byte(rawConfig), &conf); err != nil {
|
if err := json.Unmarshal([]byte(rawConfig), &conf); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -148,16 +158,14 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rawConfig = string(c)
|
rawConfig = string(c)
|
||||||
tc.t.Logf("After: %s", rawConfig)
|
|
||||||
}
|
}
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: Default.LoadRequestTimeout,
|
Timeout: Default.LoadRequestTimeout,
|
||||||
}
|
}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/load", Default.AdminPort), strings.NewReader(rawConfig))
|
req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/load", tc.adminPort), strings.NewReader(rawConfig))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("failed to create request. %s", err)
|
return fmt.Errorf("failed to create request. %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if configType == "json" {
|
if configType == "json" {
|
||||||
|
|
@ -168,16 +176,14 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
|
||||||
|
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("unable to contact caddy server. %s", err)
|
return fmt.Errorf("unable to contact caddy server. %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
timeElapsed(start, "caddytest: config load time")
|
timeElapsed(start, "caddytest: config load time")
|
||||||
|
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
body, err := io.ReadAll(res.Body)
|
body, err := io.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Errorf("unable to read response. %s", err)
|
return fmt.Errorf("unable to read response. %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
|
|
@ -185,133 +191,115 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
tc.configLoaded = true
|
tc.configLoaded = true
|
||||||
|
|
||||||
|
// if the config is not loaded at this point, it is a bug in caddy's config.Load
|
||||||
|
// the contract for config.Load states that the config must be loaded before it returns, and that it will
|
||||||
|
// error if the config fails to apply
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error {
|
func (tc *Tester) GetCurrentConfig(receiver any) error {
|
||||||
expectedBytes := []byte(prependCaddyFilePath(rawConfig))
|
|
||||||
if configType != "json" {
|
|
||||||
adapter := caddyconfig.GetAdapter(configType)
|
|
||||||
if adapter == nil {
|
|
||||||
return fmt.Errorf("adapter of config type is missing: %s", configType)
|
|
||||||
}
|
|
||||||
expectedBytes, _, _ = adapter.Adapt([]byte(rawConfig), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expected any
|
|
||||||
err := json.Unmarshal(expectedBytes, &expected)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: Default.LoadRequestTimeout,
|
Timeout: Default.LoadRequestTimeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchConfig := func(client *http.Client) any {
|
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", tc.adminPort))
|
||||||
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
actualBytes, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var actual any
|
|
||||||
err = json.Unmarshal(actualBytes, &actual)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return actual
|
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
for retries := 10; retries > 0; retries-- {
|
actualBytes, err := io.ReadAll(resp.Body)
|
||||||
if reflect.DeepEqual(expected, fetchConfig(client)) {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
}
|
||||||
tc.t.Errorf("POSTed configuration isn't active")
|
err = json.Unmarshal(actualBytes, receiver)
|
||||||
return errors.New("EnsureConfigRunning: POSTed configuration isn't active")
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const initConfig = `{
|
func getFreePort() (int, error) {
|
||||||
admin localhost:2999
|
lr, err := net.Listen("tcp", "localhost:0")
|
||||||
}
|
if err != nil {
|
||||||
`
|
return 0, err
|
||||||
|
|
||||||
// validateTestPrerequisites ensures the certificates are available in the
|
|
||||||
// designated path and Caddy sub-process is running.
|
|
||||||
func validateTestPrerequisites(t testing.TB) error {
|
|
||||||
// check certificates are found
|
|
||||||
for _, certName := range Default.Certificates {
|
|
||||||
if _, err := os.Stat(getIntegrationDir() + certName); errors.Is(err, fs.ErrNotExist) {
|
|
||||||
return fmt.Errorf("caddy integration test certificates (%s) not found", certName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
port := strings.Split(lr.Addr().String(), ":")
|
||||||
|
if len(port) < 2 {
|
||||||
|
return 0, fmt.Errorf("no port available")
|
||||||
|
}
|
||||||
|
i, err := strconv.Atoi(port[1])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
err = lr.Close()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to close listener: %w", err)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
if isCaddyAdminRunning() != nil {
|
// launches caddy, and then ensures the Caddy sub-process is running.
|
||||||
// setup the init config file, and set the cleanup afterwards
|
func (tc *Tester) startServer() error {
|
||||||
|
if tc.isCaddyAdminRunning() == nil {
|
||||||
|
return fmt.Errorf("caddy test admin port still in use")
|
||||||
|
}
|
||||||
|
a, err := getFreePort()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find a open port to listen on: %w", err)
|
||||||
|
}
|
||||||
|
tc.adminPort = a
|
||||||
|
tc.portOne, err = getFreePort()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find a open portOne: %w", err)
|
||||||
|
}
|
||||||
|
tc.portTwo, err = getFreePort()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find a open portOne: %w", err)
|
||||||
|
}
|
||||||
|
// setup the init config file, and set the cleanup afterwards
|
||||||
|
{
|
||||||
f, err := os.CreateTemp("", "")
|
f, err := os.CreateTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.Cleanup(func() {
|
tc.configFileName = f.Name()
|
||||||
os.Remove(f.Name())
|
|
||||||
})
|
initConfig := fmt.Sprintf(`{
|
||||||
|
admin localhost:%d
|
||||||
|
}`, a)
|
||||||
if _, err := f.WriteString(initConfig); err != nil {
|
if _, err := f.WriteString(initConfig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start inprocess caddy server
|
// start inprocess caddy server
|
||||||
os.Args = []string{"caddy", "run", "--config", f.Name(), "--adapter", "caddyfile"}
|
go func() {
|
||||||
go func() {
|
_ = caddycmd.MainForTesting("run", "--config", tc.configFileName, "--adapter", "caddyfile")
|
||||||
caddycmd.Main()
|
}()
|
||||||
}()
|
// wait for caddy admin api to start. it should happen quickly.
|
||||||
|
for retries := 10; retries > 0 && tc.isCaddyAdminRunning() != nil; retries-- {
|
||||||
// wait for caddy to start serving the initial config
|
time.Sleep(100 * time.Millisecond)
|
||||||
for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// one more time to return the error
|
// one more time to return the error
|
||||||
return isCaddyAdminRunning()
|
return tc.isCaddyAdminRunning()
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCaddyAdminRunning() error {
|
func (tc *Tester) isCaddyAdminRunning() error {
|
||||||
// assert that caddy is running
|
// assert that caddy is running
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: Default.LoadRequestTimeout,
|
Timeout: Default.LoadRequestTimeout,
|
||||||
}
|
}
|
||||||
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
|
resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", tc.adminPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("caddy integration test caddy server not running. Expected to be listening on localhost:%d", Default.AdminPort)
|
return fmt.Errorf("caddy integration test caddy server not running. Expected to be listening on localhost:%d", tc.adminPort)
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIntegrationDir() string {
|
|
||||||
_, filename, _, ok := runtime.Caller(1)
|
|
||||||
if !ok {
|
|
||||||
panic("unable to determine the current file path")
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.Dir(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the convention to replace /[certificatename].[crt|key] with the full path
|
|
||||||
// this helps reduce the noise in test configurations and also allow this
|
|
||||||
// to run in any path
|
|
||||||
func prependCaddyFilePath(rawConfig string) string {
|
|
||||||
r := matchKey.ReplaceAllString(rawConfig, getIntegrationDir()+"$1")
|
|
||||||
r = matchCert.ReplaceAllString(r, getIntegrationDir()+"$1")
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateTestingTransport creates a testing transport that forces call dialing connections to happen locally
|
// CreateTestingTransport creates a testing transport that forces call dialing connections to happen locally
|
||||||
func CreateTestingTransport() *http.Transport {
|
func CreateTestingTransport() *http.Transport {
|
||||||
dialer := net.Dialer{
|
dialer := net.Dialer{
|
||||||
|
|
@ -338,231 +326,3 @@ func CreateTestingTransport() *http.Transport {
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssertLoadError will load a config and expect an error
|
|
||||||
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
|
|
||||||
tc := NewTester(t)
|
|
||||||
|
|
||||||
err := tc.initServer(rawConfig, configType)
|
|
||||||
if !strings.Contains(err.Error(), expectedError) {
|
|
||||||
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertRedirect makes a request and asserts the redirection happens
|
|
||||||
func (tc *Tester) AssertRedirect(requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
|
|
||||||
redirectPolicyFunc := func(req *http.Request, via []*http.Request) error {
|
|
||||||
return http.ErrUseLastResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// using the existing client, we override the check redirect policy for this test
|
|
||||||
old := tc.Client.CheckRedirect
|
|
||||||
tc.Client.CheckRedirect = redirectPolicyFunc
|
|
||||||
defer func() { tc.Client.CheckRedirect = old }()
|
|
||||||
|
|
||||||
resp, err := tc.Client.Get(requestURI)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("failed to call server %s", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedStatusCode != resp.StatusCode {
|
|
||||||
tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", requestURI, expectedStatusCode, resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
loc, err := resp.Location()
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got error: %s", requestURI, expectedToLocation, err)
|
|
||||||
}
|
|
||||||
if loc == nil && expectedToLocation != "" {
|
|
||||||
tc.t.Errorf("requesting \"%s\" expected a Location header, but didn't get one", requestURI)
|
|
||||||
}
|
|
||||||
if loc != nil {
|
|
||||||
if expectedToLocation != loc.String() {
|
|
||||||
tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got \"%s\"", requestURI, expectedToLocation, loc.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompareAdapt adapts a config and then compares it against an expected result
|
|
||||||
func CompareAdapt(t testing.TB, filename, rawConfig string, adapterName string, expectedResponse string) bool {
|
|
||||||
cfgAdapter := caddyconfig.GetAdapter(adapterName)
|
|
||||||
if cfgAdapter == nil {
|
|
||||||
t.Logf("unrecognized config adapter '%s'", adapterName)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
options := make(map[string]any)
|
|
||||||
|
|
||||||
result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("adapting config using %s adapter: %v", adapterName, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// prettify results to keep tests human-manageable
|
|
||||||
var prettyBuf bytes.Buffer
|
|
||||||
err = json.Indent(&prettyBuf, result, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
result = prettyBuf.Bytes()
|
|
||||||
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
for _, w := range warnings {
|
|
||||||
t.Logf("warning: %s:%d: %s: %s", filename, w.Line, w.Directive, w.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diff := difflib.Diff(
|
|
||||||
strings.Split(expectedResponse, "\n"),
|
|
||||||
strings.Split(string(result), "\n"))
|
|
||||||
|
|
||||||
// scan for failure
|
|
||||||
failed := false
|
|
||||||
for _, d := range diff {
|
|
||||||
if d.Delta != difflib.Common {
|
|
||||||
failed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if failed {
|
|
||||||
for _, d := range diff {
|
|
||||||
switch d.Delta {
|
|
||||||
case difflib.Common:
|
|
||||||
fmt.Printf(" %s\n", d.Payload)
|
|
||||||
case difflib.LeftOnly:
|
|
||||||
fmt.Printf(" - %s\n", d.Payload)
|
|
||||||
case difflib.RightOnly:
|
|
||||||
fmt.Printf(" + %s\n", d.Payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertAdapt adapts a config and then tests it against an expected result
|
|
||||||
func AssertAdapt(t testing.TB, rawConfig string, adapterName string, expectedResponse string) {
|
|
||||||
ok := CompareAdapt(t, "Caddyfile", rawConfig, adapterName, expectedResponse)
|
|
||||||
if !ok {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic request functions
|
|
||||||
|
|
||||||
func applyHeaders(t testing.TB, req *http.Request, requestHeaders []string) {
|
|
||||||
requestContentType := ""
|
|
||||||
for _, requestHeader := range requestHeaders {
|
|
||||||
arr := strings.SplitAfterN(requestHeader, ":", 2)
|
|
||||||
k := strings.TrimRight(arr[0], ":")
|
|
||||||
v := strings.TrimSpace(arr[1])
|
|
||||||
if k == "Content-Type" {
|
|
||||||
requestContentType = v
|
|
||||||
}
|
|
||||||
t.Logf("Request header: %s => %s", k, v)
|
|
||||||
req.Header.Set(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if requestContentType == "" {
|
|
||||||
t.Logf("Content-Type header not provided")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertResponseCode will execute the request and verify the status code, returns a response for additional assertions
|
|
||||||
func (tc *Tester) AssertResponseCode(req *http.Request, expectedStatusCode int) *http.Response {
|
|
||||||
resp, err := tc.Client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Fatalf("failed to call server %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedStatusCode != resp.StatusCode {
|
|
||||||
tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", req.URL.RequestURI(), expectedStatusCode, resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertResponse request a URI and assert the status code and the body contains a string
|
|
||||||
func (tc *Tester) AssertResponse(req *http.Request, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
|
||||||
resp := tc.AssertResponseCode(req, expectedStatusCode)
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
bytes, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Fatalf("unable to read the response body %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
body := string(bytes)
|
|
||||||
|
|
||||||
if body != expectedBody {
|
|
||||||
tc.t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", req.RequestURI, expectedBody, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, body
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verb specific test functions
|
|
||||||
|
|
||||||
// AssertGetResponse GET a URI and expect a statusCode and body text
|
|
||||||
func (tc *Tester) AssertGetResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
|
||||||
req, err := http.NewRequest("GET", requestURI, nil)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Fatalf("unable to create request %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertDeleteResponse request a URI and expect a statusCode and body text
|
|
||||||
func (tc *Tester) AssertDeleteResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
|
||||||
req, err := http.NewRequest("DELETE", requestURI, nil)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Fatalf("unable to create request %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertPostResponseBody POST to a URI and assert the response code and body
|
|
||||||
func (tc *Tester) AssertPostResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
|
||||||
req, err := http.NewRequest("POST", requestURI, requestBody)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("failed to create request %s", err)
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
applyHeaders(tc.t, req, requestHeaders)
|
|
||||||
|
|
||||||
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertPutResponseBody PUT to a URI and assert the response code and body
|
|
||||||
func (tc *Tester) AssertPutResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
|
||||||
req, err := http.NewRequest("PUT", requestURI, requestBody)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("failed to create request %s", err)
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
applyHeaders(tc.t, req, requestHeaders)
|
|
||||||
|
|
||||||
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertPatchResponseBody PATCH to a URI and assert the response code and body
|
|
||||||
func (tc *Tester) AssertPatchResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
|
||||||
req, err := http.NewRequest("PATCH", requestURI, requestBody)
|
|
||||||
if err != nil {
|
|
||||||
tc.t.Errorf("failed to create request %s", err)
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
applyHeaders(tc.t, req, requestHeaders)
|
|
||||||
|
|
||||||
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
116
caddytest/caddytest_assert.go
Normal file
116
caddytest/caddytest_assert.go
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
package caddytest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aryann/difflib"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AssertLoadError will load a config and expect an error
|
||||||
|
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
|
||||||
|
tc, err := NewTester()
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = tc.LaunchCaddy()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = tc.LoadConfig(rawConfig, configType)
|
||||||
|
if !strings.Contains(err.Error(), expectedError) {
|
||||||
|
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
|
||||||
|
}
|
||||||
|
_ = tc.CleanupCaddy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAdapt adapts a config and then compares it against an expected result
|
||||||
|
func CompareAdapt(t testing.TB, filename, rawConfig string, adapterName string, expectedResponse string) bool {
|
||||||
|
cfgAdapter := caddyconfig.GetAdapter(adapterName)
|
||||||
|
if cfgAdapter == nil {
|
||||||
|
t.Logf("unrecognized config adapter '%s'", adapterName)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
options := make(map[string]any)
|
||||||
|
|
||||||
|
result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("adapting config using %s adapter: %v", adapterName, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettify results to keep tests human-manageable
|
||||||
|
var prettyBuf bytes.Buffer
|
||||||
|
err = json.Indent(&prettyBuf, result, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
result = prettyBuf.Bytes()
|
||||||
|
|
||||||
|
if len(warnings) > 0 {
|
||||||
|
for _, w := range warnings {
|
||||||
|
t.Logf("warning: %s:%d: %s: %s", filename, w.Line, w.Directive, w.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff := difflib.Diff(
|
||||||
|
strings.Split(expectedResponse, "\n"),
|
||||||
|
strings.Split(string(result), "\n"))
|
||||||
|
|
||||||
|
// scan for failure
|
||||||
|
failed := false
|
||||||
|
for _, d := range diff {
|
||||||
|
if d.Delta != difflib.Common {
|
||||||
|
failed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if failed {
|
||||||
|
for _, d := range diff {
|
||||||
|
switch d.Delta {
|
||||||
|
case difflib.Common:
|
||||||
|
fmt.Printf(" %s\n", d.Payload)
|
||||||
|
case difflib.LeftOnly:
|
||||||
|
fmt.Printf(" - %s\n", d.Payload)
|
||||||
|
case difflib.RightOnly:
|
||||||
|
fmt.Printf(" + %s\n", d.Payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertAdapt adapts a config and then tests it against an expected result
|
||||||
|
func AssertAdapt(t testing.TB, rawConfig string, adapterName string, expectedResponse string) {
|
||||||
|
ok := CompareAdapt(t, "Caddyfile", rawConfig, adapterName, expectedResponse)
|
||||||
|
if !ok {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic request functions
|
||||||
|
|
||||||
|
func applyHeaders(t testing.TB, req *http.Request, requestHeaders []string) {
|
||||||
|
requestContentType := ""
|
||||||
|
for _, requestHeader := range requestHeaders {
|
||||||
|
arr := strings.SplitAfterN(requestHeader, ":", 2)
|
||||||
|
k := strings.TrimRight(arr[0], ":")
|
||||||
|
v := strings.TrimSpace(arr[1])
|
||||||
|
if k == "Content-Type" {
|
||||||
|
requestContentType = v
|
||||||
|
}
|
||||||
|
t.Logf("Request header: %s => %s", k, v)
|
||||||
|
req.Header.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if requestContentType == "" {
|
||||||
|
t.Logf("Content-Type header not provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,22 @@
|
||||||
package caddytest
|
package caddytest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReplaceCertificatePaths(t *testing.T) {
|
func TestReplaceCertificatePaths(t *testing.T) {
|
||||||
rawConfig := `a.caddy.localhost:9443 {
|
rawConfig := `a.caddy.localhost:9443{
|
||||||
tls /caddy.localhost.crt /caddy.localhost.key {
|
tls /caddy.localhost.crt /caddy.localhost.key {
|
||||||
}
|
}
|
||||||
|
|
||||||
redir / https://b.caddy.localhost:9443/version 301
|
redir / https://b.caddy.localhost:9443/version 301
|
||||||
|
|
||||||
respond /version 200 {
|
respond /version 200 {
|
||||||
body "hello from a.caddy.localhost"
|
body "hello from a.caddy.localhost"
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
r := prependCaddyFilePath(rawConfig)
|
r := prependCaddyFilePath(rawConfig)
|
||||||
|
|
@ -34,8 +35,8 @@ func TestReplaceCertificatePaths(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadUnorderedJSON(t *testing.T) {
|
func TestLoadUnorderedJSON(t *testing.T) {
|
||||||
tester := NewTester(t)
|
harness := StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"logging": {
|
"logging": {
|
||||||
"logs": {
|
"logs": {
|
||||||
|
|
@ -68,7 +69,7 @@ func TestLoadUnorderedJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"pki": {
|
"pki": {
|
||||||
|
|
@ -79,13 +80,13 @@ func TestLoadUnorderedJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"servers": {
|
"servers": {
|
||||||
"s_server": {
|
"s_server": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9443",
|
":{$TESTING_CADDY_PORT_ONE}",
|
||||||
":9080"
|
":{$TESTING_CADDY_PORT_TWO}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -120,10 +121,10 @@ func TestLoadUnorderedJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, "json")
|
`, "json")
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tester.AssertResponseCode(req, 200)
|
harness.AssertResponseCode(req, 200)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,13 @@ const acmeChallengePort = 9081
|
||||||
// Test the basic functionality of Caddy's ACME server
|
// Test the basic functionality of Caddy's ACME server
|
||||||
func TestACMEServerWithDefaults(t *testing.T) {
|
func TestACMEServerWithDefaults(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
logger, err := zap.NewDevelopment()
|
harness := caddytest.StartHarness(t)
|
||||||
if err != nil {
|
harness.LoadConfig(`
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tester := caddytest.NewTester(t)
|
|
||||||
tester.InitServer(`
|
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
local_certs
|
local_certs
|
||||||
}
|
}
|
||||||
acme.localhost {
|
acme.localhost {
|
||||||
|
|
@ -44,10 +38,11 @@ func TestACMEServerWithDefaults(t *testing.T) {
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
|
logger := caddy.Log().Named("acmeserver")
|
||||||
client := acmez.Client{
|
client := acmez.Client{
|
||||||
Client: &acme.Client{
|
Client: &acme.Client{
|
||||||
Directory: "https://acme.localhost:9443/acme/local/directory",
|
Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
|
||||||
HTTPClient: tester.Client,
|
HTTPClient: harness.Client(),
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
},
|
},
|
||||||
ChallengeSolvers: map[string]acmez.Solver{
|
ChallengeSolvers: map[string]acmez.Solver{
|
||||||
|
|
@ -97,13 +92,13 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
logger := caddy.Log().Named("acmez")
|
logger := caddy.Log().Named("acmez")
|
||||||
|
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
local_certs
|
local_certs
|
||||||
}
|
}
|
||||||
acme.localhost {
|
acme.localhost {
|
||||||
|
|
@ -115,8 +110,8 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
|
||||||
|
|
||||||
client := acmez.Client{
|
client := acmez.Client{
|
||||||
Client: &acme.Client{
|
Client: &acme.Client{
|
||||||
Directory: "https://acme.localhost:9443/acme/local/directory",
|
Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
|
||||||
HTTPClient: tester.Client,
|
HTTPClient: harness.Client(),
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
},
|
},
|
||||||
ChallengeSolvers: map[string]acmez.Solver{
|
ChallengeSolvers: map[string]acmez.Solver{
|
||||||
|
|
|
||||||
|
|
@ -5,50 +5,51 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddytest"
|
"github.com/caddyserver/caddy/v2/caddytest"
|
||||||
"github.com/mholt/acmez/v2"
|
"github.com/mholt/acmez/v2"
|
||||||
"github.com/mholt/acmez/v2/acme"
|
"github.com/mholt/acmez/v2/acme"
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestACMEServerDirectory(t *testing.T) {
|
func TestACMEServerDirectory(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
local_certs
|
local_certs
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
pki {
|
pki {
|
||||||
ca local {
|
ca local {
|
||||||
name "Caddy Local Authority"
|
name "Caddy Local Authority"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acme.localhost:9443 {
|
acme.localhost:{$TESTING_CADDY_PORT_TWO} {
|
||||||
acme_server
|
acme_server
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
tester.AssertGetResponse(
|
harness.AssertGetResponse(
|
||||||
"https://acme.localhost:9443/acme/local/directory",
|
fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
|
||||||
200,
|
200,
|
||||||
`{"newNonce":"https://acme.localhost:9443/acme/local/new-nonce","newAccount":"https://acme.localhost:9443/acme/local/new-account","newOrder":"https://acme.localhost:9443/acme/local/new-order","revokeCert":"https://acme.localhost:9443/acme/local/revoke-cert","keyChange":"https://acme.localhost:9443/acme/local/key-change"}
|
fmt.Sprintf(`{"newNonce":"https://acme.localhost:%[1]d/acme/local/new-nonce","newAccount":"https://acme.localhost:%[1]d/acme/local/new-account","newOrder":"https://acme.localhost:%[1]d/acme/local/new-order","revokeCert":"https://acme.localhost:%[1]d/acme/local/revoke-cert","keyChange":"https://acme.localhost:%[1]d/acme/local/key-change"}
|
||||||
`)
|
`, harness.Tester().PortTwo()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestACMEServerAllowPolicy(t *testing.T) {
|
func TestACMEServerAllowPolicy(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
local_certs
|
local_certs
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
pki {
|
pki {
|
||||||
ca local {
|
ca local {
|
||||||
name "Caddy Local Authority"
|
name "Caddy Local Authority"
|
||||||
|
|
@ -66,16 +67,12 @@ func TestACMEServerAllowPolicy(t *testing.T) {
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
logger, err := zap.NewDevelopment()
|
logger := caddy.Log().Named("acmez")
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := acmez.Client{
|
client := acmez.Client{
|
||||||
Client: &acme.Client{
|
Client: &acme.Client{
|
||||||
Directory: "https://acme.localhost:9443/acme/local/directory",
|
Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
|
||||||
HTTPClient: tester.Client,
|
HTTPClient: harness.Client(),
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
},
|
},
|
||||||
ChallengeSolvers: map[string]acmez.Solver{
|
ChallengeSolvers: map[string]acmez.Solver{
|
||||||
|
|
@ -131,14 +128,14 @@ func TestACMEServerAllowPolicy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestACMEServerDenyPolicy(t *testing.T) {
|
func TestACMEServerDenyPolicy(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
local_certs
|
local_certs
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
pki {
|
pki {
|
||||||
ca local {
|
ca local {
|
||||||
name "Caddy Local Authority"
|
name "Caddy Local Authority"
|
||||||
|
|
@ -155,16 +152,12 @@ func TestACMEServerDenyPolicy(t *testing.T) {
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
logger, err := zap.NewDevelopment()
|
logger := caddy.Log().Named("acmez")
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := acmez.Client{
|
client := acmez.Client{
|
||||||
Client: &acme.Client{
|
Client: &acme.Client{
|
||||||
Directory: "https://acme.localhost:9443/acme/local/directory",
|
Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
|
||||||
HTTPClient: tester.Client,
|
HTTPClient: harness.Client(),
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
},
|
},
|
||||||
ChallengeSolvers: map[string]acmez.Solver{
|
ChallengeSolvers: map[string]acmez.Solver{
|
||||||
|
|
@ -197,7 +190,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
|
||||||
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"deny.localhost"})
|
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"deny.localhost"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("obtaining certificate for 'deny.localhost' domain")
|
t.Errorf("obtaining certificate for 'deny.localhost' domain")
|
||||||
} else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
|
} else if !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
|
||||||
t.Logf("unexpected error: %v", err)
|
t.Logf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -8,69 +9,69 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAutoHTTPtoHTTPSRedirectsImplicitPort(t *testing.T) {
|
func TestAutoHTTPtoHTTPSRedirectsImplicitPort(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
}
|
}
|
||||||
localhost
|
localhost
|
||||||
respond "Yahaha! You found me!"
|
respond "Yahaha! You found me!"
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
|
harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoHTTPtoHTTPSRedirectsExplicitPortSameAsHTTPSPort(t *testing.T) {
|
func TestAutoHTTPtoHTTPSRedirectsExplicitPortSameAsHTTPSPort(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
}
|
}
|
||||||
localhost:9443
|
localhost:{$TESTING_CADDY_PORT_TWO}
|
||||||
respond "Yahaha! You found me!"
|
respond "Yahaha! You found me!"
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
|
harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T) {
|
func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
}
|
}
|
||||||
localhost:1234
|
localhost:1234
|
||||||
respond "Yahaha! You found me!"
|
respond "Yahaha! You found me!"
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertRedirect("http://localhost:9080/", "https://localhost:1234/", http.StatusPermanentRedirect)
|
harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost:1234/", http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
|
func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"servers": {
|
"servers": {
|
||||||
"ingress_server": {
|
"ingress_server": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9080",
|
":{$TESTING_CADDY_PORT_ONE}",
|
||||||
":9443"
|
":{$TESTING_CADDY_PORT_TWO}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -94,52 +95,52 @@ func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, "json")
|
`, "json")
|
||||||
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
|
harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAll(t *testing.T) {
|
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAll(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
local_certs
|
local_certs
|
||||||
}
|
}
|
||||||
http://:9080 {
|
http://:{$TESTING_CADDY_PORT_ONE} {
|
||||||
respond "Foo"
|
respond "Foo"
|
||||||
}
|
}
|
||||||
http://baz.localhost:9080 {
|
http://baz.localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
respond "Baz"
|
respond "Baz"
|
||||||
}
|
}
|
||||||
bar.localhost {
|
bar.localhost {
|
||||||
respond "Bar"
|
respond "Bar"
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
tester.AssertRedirect("http://bar.localhost:9080/", "https://bar.localhost/", http.StatusPermanentRedirect)
|
harness.AssertRedirect(fmt.Sprintf("http://bar.localhost:%d/", harness.Tester().PortOne()), "https://bar.localhost/", http.StatusPermanentRedirect)
|
||||||
tester.AssertGetResponse("http://foo.localhost:9080/", 200, "Foo")
|
harness.AssertGetResponse(fmt.Sprintf("http://foo.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
|
||||||
tester.AssertGetResponse("http://baz.localhost:9080/", 200, "Baz")
|
harness.AssertGetResponse(fmt.Sprintf("http://baz.localhost:%d/", harness.Tester().PortOne()), 200, "Baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAllWithNoExplicitHTTPSite(t *testing.T) {
|
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAllWithNoExplicitHTTPSite(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
local_certs
|
local_certs
|
||||||
}
|
}
|
||||||
http://:9080 {
|
http://:{$TESTING_CADDY_PORT_ONE} {
|
||||||
respond "Foo"
|
respond "Foo"
|
||||||
}
|
}
|
||||||
bar.localhost {
|
bar.localhost {
|
||||||
respond "Bar"
|
respond "Bar"
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
tester.AssertRedirect("http://bar.localhost:9080/", "https://bar.localhost/", http.StatusPermanentRedirect)
|
harness.AssertRedirect(fmt.Sprintf("http://bar.localhost:%d/", harness.Tester().PortOne()), "https://bar.localhost/", http.StatusPermanentRedirect)
|
||||||
tester.AssertGetResponse("http://foo.localhost:9080/", 200, "Foo")
|
harness.AssertGetResponse(fmt.Sprintf("http://foo.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
|
||||||
tester.AssertGetResponse("http://baz.localhost:9080/", 200, "Foo")
|
harness.AssertGetResponse(fmt.Sprintf("http://baz.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -10,62 +11,63 @@ import (
|
||||||
|
|
||||||
func TestRespond(t *testing.T) {
|
func TestRespond(t *testing.T) {
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
respond /version 200 {
|
respond /version 200 {
|
||||||
body "hello from localhost"
|
body "hello from localhost"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost")
|
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRedirect(t *testing.T) {
|
func TestRedirect(t *testing.T) {
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
|
|
||||||
redir / http://localhost:9080/hello 301
|
redir / http://localhost:{$TESTING_CADDY_PORT_ONE}/hello 301
|
||||||
|
|
||||||
respond /hello 200 {
|
respond /hello 200 {
|
||||||
body "hello from localhost"
|
body "hello from localhost"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertRedirect("http://localhost:9080/", "http://localhost:9080/hello", 301)
|
harness.AssertRedirect(target, target+"hello", 301)
|
||||||
|
|
||||||
// follow redirect
|
// follow redirect
|
||||||
tester.AssertGetResponse("http://localhost:9080/", 200, "hello from localhost")
|
harness.AssertGetResponse(target, 200, "hello from localhost")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDuplicateHosts(t *testing.T) {
|
func TestDuplicateHosts(t *testing.T) {
|
||||||
// act and assert
|
// act and assert
|
||||||
caddytest.AssertLoadError(t,
|
caddytest.AssertLoadError(t,
|
||||||
`
|
`
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
"caddyfile",
|
"caddyfile",
|
||||||
|
|
@ -80,18 +82,18 @@ func TestReadCookie(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.Client.Jar.SetCookies(localhost, []*http.Cookie{&cookie})
|
harness.Client().Jar.SetCookies(localhost, []*http.Cookie{&cookie})
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
templates {
|
templates {
|
||||||
root testdata
|
root testdata
|
||||||
}
|
}
|
||||||
|
|
@ -102,21 +104,22 @@ func TestReadCookie(t *testing.T) {
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/cookie.html", 200, "<h2>Cookie.ClientName caddytest</h2>")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"cookie.html", 200, "<h2>Cookie.ClientName caddytest</h2>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplIndex(t *testing.T) {
|
func TestReplIndex(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
templates {
|
templates {
|
||||||
root testdata
|
root testdata
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +131,8 @@ func TestReplIndex(t *testing.T) {
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/", 200, "")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target, 200, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidPrefix(t *testing.T) {
|
func TestInvalidPrefix(t *testing.T) {
|
||||||
|
|
@ -481,40 +485,42 @@ func TestValidPrefix(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUriReplace(t *testing.T) {
|
func TestUriReplace(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri replace "\}" %7D
|
uri replace "\}" %7D
|
||||||
uri replace "\{" %7B
|
uri replace "\{" %7B
|
||||||
|
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUriOps(t *testing.T) {
|
func TestUriOps(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query +foo bar
|
uri query +foo bar
|
||||||
uri query -baz
|
uri query -baz
|
||||||
uri query taz test
|
uri query taz test
|
||||||
uri query key=value example
|
uri query key=value example
|
||||||
uri query changethis>changed
|
uri query changethis>changed
|
||||||
|
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest&changethis=val", 200, "changed=val&foo=bar0&foo=bar&key%3Dvalue=example&taz=test")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?foo=bar0&baz=buz&taz=nottest&changethis=val", 200, "changed=val&foo=bar0&foo=bar&key%3Dvalue=example&taz=test")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests the `http.request.local.port` placeholder.
|
// Tests the `http.request.local.port` placeholder.
|
||||||
|
|
@ -523,204 +529,215 @@ func TestUriOps(t *testing.T) {
|
||||||
// refer to 127.0.0.1 or ::1.
|
// refer to 127.0.0.1 or ::1.
|
||||||
// TODO: Test each http version separately (especially http/3)
|
// TODO: Test each http version separately (especially http/3)
|
||||||
func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
|
func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
respond "{http.request.local.port}"`, "caddyfile")
|
respond "{http.request.local.port}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/", 200, "9080")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target, 200, fmt.Sprintf("%d", harness.Tester().PortOne()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetThenAddQueryParams(t *testing.T) {
|
func TestSetThenAddQueryParams(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query foo bar
|
uri query foo bar
|
||||||
uri query +foo baz
|
uri query +foo baz
|
||||||
|
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint", 200, "foo=bar&foo=baz")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint", 200, "foo=bar&foo=baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetThenDeleteParams(t *testing.T) {
|
func TestSetThenDeleteParams(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query bar foo{query.foo}
|
uri query bar foo{query.foo}
|
||||||
uri query -foo
|
uri query -foo
|
||||||
|
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=foobar")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "bar=foobar")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenameAndOtherOps(t *testing.T) {
|
func TestRenameAndOtherOps(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query foo>bar
|
uri query foo>bar
|
||||||
uri query bar taz
|
uri query bar taz
|
||||||
uri query +bar baz
|
uri query +bar baz
|
||||||
|
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=taz&bar=baz")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "bar=taz&bar=baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplaceOps(t *testing.T) {
|
func TestReplaceOps(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query foo bar baz
|
uri query foo bar baz
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplaceWithReplacementPlaceholder(t *testing.T) {
|
func TestReplaceWithReplacementPlaceholder(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query foo bar {query.placeholder}
|
uri query foo bar {query.placeholder}
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplaceWithKeyPlaceholder(t *testing.T) {
|
func TestReplaceWithKeyPlaceholder(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query {query.placeholder} bar baz
|
uri query {query.placeholder} bar baz
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPartialReplacement(t *testing.T) {
|
func TestPartialReplacement(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query foo ar az
|
uri query foo ar az
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNonExistingSearch(t *testing.T) {
|
func TestNonExistingSearch(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query foo var baz
|
uri query foo var baz
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=bar")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplaceAllOps(t *testing.T) {
|
func TestReplaceAllOps(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query * bar baz
|
uri query * bar baz
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUriOpsBlock(t *testing.T) {
|
func TestUriOpsBlock(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
|
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
:9080
|
:{$TESTING_CADDY_PORT_ONE}
|
||||||
uri query {
|
uri query {
|
||||||
+foo bar
|
+foo bar
|
||||||
-baz
|
-baz
|
||||||
taz test
|
taz test
|
||||||
}
|
}
|
||||||
respond "{query}"`, "caddyfile")
|
respond "{query}"`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest", 200, "foo=bar0&foo=bar&taz=test")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target+"endpoint?foo=bar0&baz=buz&taz=nottest", 200, "foo=bar0&foo=bar&taz=test")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorSimpleCodes(t *testing.T) {
|
func TestHandleErrorSimpleCodes(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`{
|
harness.LoadConfig(`{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
root * /srv
|
root * /srv
|
||||||
error /private* "Unauthorized" 410
|
error /private* "Unauthorized" 410
|
||||||
error /hidden* "Not found" 404
|
error /hidden* "Not found" 404
|
||||||
|
|
||||||
handle_errors 404 410 {
|
handle_errors 404 410 {
|
||||||
respond "404 or 410 error"
|
respond "404 or 410 error"
|
||||||
}
|
}
|
||||||
}`, "caddyfile")
|
}`, "caddyfile")
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/private", 410, "404 or 410 error")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
tester.AssertGetResponse("http://localhost:9080/hidden", 404, "404 or 410 error")
|
harness.AssertGetResponse(target+"private", 410, "404 or 410 error")
|
||||||
|
harness.AssertGetResponse(target+"hidden", 404, "404 or 410 error")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorRange(t *testing.T) {
|
func TestHandleErrorRange(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`{
|
harness.LoadConfig(`{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
root * /srv
|
root * /srv
|
||||||
error /private* "Unauthorized" 410
|
error /private* "Unauthorized" 410
|
||||||
error /hidden* "Not found" 404
|
error /hidden* "Not found" 404
|
||||||
|
|
@ -730,17 +747,18 @@ func TestHandleErrorRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`, "caddyfile")
|
}`, "caddyfile")
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
|
harness.AssertGetResponse(target+"private", 410, "Error in the [400 .. 499] range")
|
||||||
|
harness.AssertGetResponse(target+"hidden", 404, "Error in the [400 .. 499] range")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorSort(t *testing.T) {
|
func TestHandleErrorSort(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`{
|
harness.LoadConfig(`{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
root * /srv
|
root * /srv
|
||||||
error /private* "Unauthorized" 410
|
error /private* "Unauthorized" 410
|
||||||
error /hidden* "Not found" 404
|
error /hidden* "Not found" 404
|
||||||
|
|
@ -754,17 +772,18 @@ func TestHandleErrorSort(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`, "caddyfile")
|
}`, "caddyfile")
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Fallback route: code outside the [400..499] range")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
|
harness.AssertGetResponse(target+"internalerr", 500, "Fallback route: code outside the [400..499] range")
|
||||||
|
harness.AssertGetResponse(target+"hidden", 404, "Error in the [400 .. 499] range")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleErrorRangeAndCodes(t *testing.T) {
|
func TestHandleErrorRangeAndCodes(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`{
|
harness.LoadConfig(`{
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
}
|
}
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
root * /srv
|
root * /srv
|
||||||
error /private* "Unauthorized" 410
|
error /private* "Unauthorized" 410
|
||||||
error /threehundred* "Moved Permanently" 301
|
error /threehundred* "Moved Permanently" 301
|
||||||
|
|
@ -778,9 +797,10 @@ func TestHandleErrorRangeAndCodes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`, "caddyfile")
|
}`, "caddyfile")
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Error code is equal to 500 or in the [300..399] range")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
tester.AssertGetResponse("http://localhost:9080/threehundred", 301, "Error code is equal to 500 or in the [300..399] range")
|
harness.AssertGetResponse(target+"internalerr", 500, "Error code is equal to 500 or in the [300..399] range")
|
||||||
tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
|
harness.AssertGetResponse(target+"threehundred", 301, "Error code is equal to 500 or in the [300..399] range")
|
||||||
|
harness.AssertGetResponse(target+"private", 410, "Error in the [400 .. 499] range")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidSiteAddressesAsDirectives(t *testing.T) {
|
func TestInvalidSiteAddressesAsDirectives(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -9,36 +10,36 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBrowse(t *testing.T) {
|
func TestBrowse(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
http://localhost:9080 {
|
http://localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
file_server browse
|
file_server browse
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tester.AssertResponseCode(req, 200)
|
harness.AssertResponseCode(req, 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRespondWithJSON(t *testing.T) {
|
func TestRespondWithJSON(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
localhost {
|
localhost {
|
||||||
|
|
@ -46,7 +47,7 @@ func TestRespondWithJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
res, _ := tester.AssertPostResponseBody("https://localhost:9443/",
|
res, _ := harness.AssertPostResponseBody(fmt.Sprintf("https://localhost:%d/", harness.Tester().PortTwo()),
|
||||||
nil,
|
nil,
|
||||||
bytes.NewBufferString(`{
|
bytes.NewBufferString(`{
|
||||||
"greeting": "Hello, world!"
|
"greeting": "Hello, world!"
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/caddytest"
|
"github.com/caddyserver/caddy/v2/caddytest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIntercept(t *testing.T) {
|
func TestIntercept(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`{
|
harness.LoadConfig(`{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
respond /intercept "I'm a teapot" 408
|
respond /intercept "I'm a teapot" 408
|
||||||
respond /no-intercept "I'm not a teapot"
|
respond /no-intercept "I'm not a teapot"
|
||||||
|
|
||||||
|
|
@ -25,10 +26,10 @@ func TestIntercept(t *testing.T) {
|
||||||
handle_response @teapot {
|
handle_response @teapot {
|
||||||
respond /intercept "I'm a combined coffee/tea pot that is temporarily out of coffee" 503
|
respond /intercept "I'm a combined coffee/tea pot that is temporarily out of coffee" 503
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/intercept", 503, "I'm a combined coffee/tea pot that is temporarily out of coffee")
|
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/intercept", harness.Tester().PortOne()), 503, "I'm a combined coffee/tea pot that is temporarily out of coffee")
|
||||||
tester.AssertGetResponse("http://localhost:9080/no-intercept", 200, "I'm not a teapot")
|
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/no-intercept", harness.Tester().PortOne()), 200, "I'm not a teapot")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLeafCertLoaders(t *testing.T) {
|
func TestLeafCertLoaders(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"grace_period": 1,
|
"grace_period": 1,
|
||||||
"servers": {
|
"servers": {
|
||||||
"srv0": {
|
"srv0": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9443"
|
":{$TESTING_CADDY_PORT_TWO}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"github.com/caddyserver/caddy/v2/caddytest"
|
"github.com/caddyserver/caddy/v2/caddytest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddytest.Tester {
|
func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddytest.TestHarness {
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to listen: %s", err)
|
t.Fatalf("failed to listen: %s", err)
|
||||||
|
|
@ -28,15 +28,15 @@ func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddy
|
||||||
_ = srv.Close()
|
_ = srv.Close()
|
||||||
_ = l.Close()
|
_ = l.Close()
|
||||||
})
|
})
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(fmt.Sprintf(`
|
harness.LoadConfig(fmt.Sprintf(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
local_certs
|
local_certs
|
||||||
servers :9443 {
|
servers :{$TESTING_CADDY_PORT_TWO} {
|
||||||
listener_wrappers {
|
listener_wrappers {
|
||||||
http_redirect
|
http_redirect
|
||||||
tls
|
tls
|
||||||
|
|
@ -47,7 +47,7 @@ func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddy
|
||||||
reverse_proxy %s
|
reverse_proxy %s
|
||||||
}
|
}
|
||||||
`, l.Addr().String()), "caddyfile")
|
`, l.Addr().String()), "caddyfile")
|
||||||
return tester
|
return harness
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
|
func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
|
||||||
|
|
@ -56,7 +56,7 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
|
||||||
body := make([]byte, uploadSize)
|
body := make([]byte, uploadSize)
|
||||||
rand.New(rand.NewSource(0)).Read(body)
|
rand.New(rand.NewSource(0)).Read(body)
|
||||||
|
|
||||||
tester := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
|
harness := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
_, err := buf.ReadFrom(request.Body)
|
_, err := buf.ReadFrom(request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -69,7 +69,7 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
|
||||||
|
|
||||||
writer.WriteHeader(http.StatusNoContent)
|
writer.WriteHeader(http.StatusNoContent)
|
||||||
})
|
})
|
||||||
resp, err := tester.Client.Post("https://localhost:9443", "application/octet-stream", bytes.NewReader(body))
|
resp, err := harness.Client().Post(fmt.Sprintf("https://localhost:%d", harness.Tester().PortTwo()), "application/octet-stream", bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to post: %s", err)
|
t.Fatalf("failed to post: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -80,14 +80,14 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLargeHttpRequest(t *testing.T) {
|
func TestLargeHttpRequest(t *testing.T) {
|
||||||
tester := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
|
harness := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
|
||||||
t.Fatal("not supposed to handle a request")
|
t.Fatal("not supposed to handle a request")
|
||||||
})
|
})
|
||||||
|
|
||||||
// We never read the body in any way, set an extra long header instead.
|
// We never read the body in any way, set an extra long header instead.
|
||||||
req, _ := http.NewRequest("POST", "http://localhost:9443", nil)
|
req, _ := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d", harness.Tester().PortTwo()), nil)
|
||||||
req.Header.Set("Long-Header", strings.Repeat("X", 1024*1024))
|
req.Header.Set("Long-Header", strings.Repeat("X", 1024*1024))
|
||||||
_, err := tester.Client.Do(req)
|
_, err := harness.Client().Do(req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("not supposed to succeed")
|
t.Fatal("not supposed to succeed")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/caddytest"
|
"github.com/caddyserver/caddy/v2/caddytest"
|
||||||
|
|
@ -9,16 +10,16 @@ import (
|
||||||
|
|
||||||
func TestMap(t *testing.T) {
|
func TestMap(t *testing.T) {
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`{
|
harness.LoadConfig(`{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
|
|
||||||
map {http.request.method} {dest-1} {dest-2} {
|
map {http.request.method} {dest-1} {dest-2} {
|
||||||
default unknown1 unknown2
|
default unknown1 unknown2
|
||||||
|
|
@ -28,50 +29,50 @@ func TestMap(t *testing.T) {
|
||||||
|
|
||||||
respond /version 200 {
|
respond /version 200 {
|
||||||
body "hello from localhost {dest-1} {dest-2}"
|
body "hello from localhost {dest-1} {dest-2}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost GET-called unknown2")
|
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost GET-called unknown2")
|
||||||
tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called foobar")
|
harness.AssertPostResponseBody(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called foobar")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapRespondWithDefault(t *testing.T) {
|
func TestMapRespondWithDefault(t *testing.T) {
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`{
|
harness.LoadConfig(`{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:9080 {
|
localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
|
|
||||||
map {http.request.method} {dest-name} {
|
map {http.request.method} {dest-name} {
|
||||||
default unknown
|
default unknown
|
||||||
GET get-called
|
GET get-called
|
||||||
}
|
}
|
||||||
|
|
||||||
respond /version 200 {
|
respond /version 200 {
|
||||||
body "hello from localhost {dest-name}"
|
body "hello from localhost {dest-name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost get-called")
|
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost get-called")
|
||||||
tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost unknown")
|
harness.AssertPostResponseBody(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost unknown")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapAsJSON(t *testing.T) {
|
func TestMapAsJSON(t *testing.T) {
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"pki": {
|
"pki": {
|
||||||
|
|
@ -82,12 +83,12 @@ func TestMapAsJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"servers": {
|
"servers": {
|
||||||
"srv0": {
|
"srv0": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9080"
|
":{$TESTING_CADDY_PORT_ONE}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -145,7 +146,7 @@ func TestMapAsJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`, "json")
|
}`, "json")
|
||||||
|
target := fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne())
|
||||||
tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost get-called")
|
harness.AssertGetResponse(target, 200, "hello from localhost get-called")
|
||||||
tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called")
|
harness.AssertPostResponseBody(target, []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSRVReverseProxy(t *testing.T) {
|
func TestSRVReverseProxy(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"pki": {
|
"pki": {
|
||||||
|
|
@ -87,11 +87,11 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
|
||||||
})
|
})
|
||||||
runtime.Gosched() // Allow other goroutines to run
|
runtime.Gosched() // Allow other goroutines to run
|
||||||
|
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"pki": {
|
"pki": {
|
||||||
|
|
@ -135,15 +135,15 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Header.Set("X-Caddy-Upstream-Dial", socketName)
|
req.Header.Set("X-Caddy-Upstream-Dial", socketName)
|
||||||
tester.AssertResponse(req, 200, "Hello, World!")
|
harness.AssertResponse(req, 200, "Hello, World!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
|
func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"pki": {
|
"pki": {
|
||||||
|
|
@ -186,7 +186,7 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
|
||||||
},
|
},
|
||||||
"srv1": {
|
"srv1": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9080"
|
":{$TESTING_CADDY_PORT_ONE}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -199,7 +199,7 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
|
||||||
],
|
],
|
||||||
"handle": [
|
"handle": [
|
||||||
{
|
{
|
||||||
|
|
||||||
"handler": "reverse_proxy",
|
"handler": "reverse_proxy",
|
||||||
"upstreams": [
|
"upstreams": [
|
||||||
{
|
{
|
||||||
|
|
@ -223,21 +223,21 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
|
||||||
}
|
}
|
||||||
`, "json")
|
`, "json")
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d", harness.Tester().PortOne()), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Header.Set("X-Caddy-Upstream-Dial", "localhost:18080")
|
req.Header.Set("X-Caddy-Upstream-Dial", "localhost:18080")
|
||||||
tester.AssertResponse(req, 200, "Hello, World!")
|
harness.AssertResponse(req, 200, "Hello, World!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
|
func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"pki": {
|
"pki": {
|
||||||
|
|
@ -280,7 +280,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
|
||||||
},
|
},
|
||||||
"srv1": {
|
"srv1": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9080"
|
":{$TESTING_CADDY_PORT_ONE}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -293,7 +293,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
|
||||||
],
|
],
|
||||||
"handle": [
|
"handle": [
|
||||||
{
|
{
|
||||||
|
|
||||||
"handler": "reverse_proxy",
|
"handler": "reverse_proxy",
|
||||||
"upstreams": [
|
"upstreams": [
|
||||||
{
|
{
|
||||||
|
|
@ -317,23 +317,23 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
|
||||||
}
|
}
|
||||||
`, "json")
|
`, "json")
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d", harness.Tester().PortOne()), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Header.Set("X-Caddy-Upstream-Dial", "localhost")
|
req.Header.Set("X-Caddy-Upstream-Dial", "localhost")
|
||||||
tester.AssertResponse(req, 200, "Hello, World!")
|
harness.AssertResponse(req, 200, "Hello, World!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReverseProxyHealthCheck(t *testing.T) {
|
func TestReverseProxyHealthCheck(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
http://localhost:2020 {
|
http://localhost:2020 {
|
||||||
|
|
@ -342,10 +342,10 @@ func TestReverseProxyHealthCheck(t *testing.T) {
|
||||||
http://localhost:2021 {
|
http://localhost:2021 {
|
||||||
respond "ok"
|
respond "ok"
|
||||||
}
|
}
|
||||||
http://localhost:9080 {
|
http://localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
reverse_proxy {
|
reverse_proxy {
|
||||||
to localhost:2020
|
to localhost:2020
|
||||||
|
|
||||||
health_uri /health
|
health_uri /health
|
||||||
health_port 2021
|
health_port 2021
|
||||||
health_interval 10ms
|
health_interval 10ms
|
||||||
|
|
@ -357,14 +357,15 @@ func TestReverseProxyHealthCheck(t *testing.T) {
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
time.Sleep(100 * time.Millisecond) // TODO: for some reason this test seems particularly flaky, getting 503 when it should be 200, unless we wait
|
time.Sleep(100 * time.Millisecond) // TODO: for some reason this test seems particularly flaky, getting 503 when it should be 200, unless we wait
|
||||||
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target, 200, "Hello, World!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
|
func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
f, err := os.CreateTemp("", "*.sock")
|
f, err := os.CreateTemp("", "*.sock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to create TempFile: %s", err)
|
t.Errorf("failed to create TempFile: %s", err)
|
||||||
|
|
@ -395,18 +396,18 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
|
||||||
})
|
})
|
||||||
runtime.Gosched() // Allow other goroutines to run
|
runtime.Gosched() // Allow other goroutines to run
|
||||||
|
|
||||||
tester.InitServer(fmt.Sprintf(`
|
harness.LoadConfig(fmt.Sprintf(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
http://localhost:9080 {
|
http://localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
reverse_proxy {
|
reverse_proxy {
|
||||||
to unix/%s
|
to unix/%s
|
||||||
|
|
||||||
health_uri /health
|
health_uri /health
|
||||||
health_port 2021
|
health_port 2021
|
||||||
health_interval 2s
|
health_interval 2s
|
||||||
|
|
@ -415,14 +416,15 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
|
||||||
}
|
}
|
||||||
`, socketName), "caddyfile")
|
`, socketName), "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
|
target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
|
||||||
|
harness.AssertGetResponse(target, 200, "Hello, World!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
|
func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
f, err := os.CreateTemp("", "*.sock")
|
f, err := os.CreateTemp("", "*.sock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to create TempFile: %s", err)
|
t.Errorf("failed to create TempFile: %s", err)
|
||||||
|
|
@ -453,18 +455,18 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
|
||||||
})
|
})
|
||||||
runtime.Gosched() // Allow other goroutines to run
|
runtime.Gosched() // Allow other goroutines to run
|
||||||
|
|
||||||
tester.InitServer(fmt.Sprintf(`
|
harness.LoadConfig(fmt.Sprintf(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin {$TESTING_CADDY_ADMIN_BIND}
|
||||||
http_port 9080
|
http_port {$TESTING_CADDY_PORT_ONE}
|
||||||
https_port 9443
|
https_port {$TESTING_CADDY_PORT_TWO}
|
||||||
grace_period 1ns
|
grace_period 1ns
|
||||||
}
|
}
|
||||||
http://localhost:9080 {
|
http://localhost:{$TESTING_CADDY_PORT_ONE} {
|
||||||
reverse_proxy {
|
reverse_proxy {
|
||||||
to unix/%s
|
to unix/%s
|
||||||
|
|
||||||
health_uri /health
|
health_uri /health
|
||||||
health_interval 2s
|
health_interval 2s
|
||||||
health_timeout 5s
|
health_timeout 5s
|
||||||
|
|
@ -472,5 +474,5 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
|
||||||
}
|
}
|
||||||
`, socketName), "caddyfile")
|
`, socketName), "caddyfile")
|
||||||
|
|
||||||
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
|
harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), 200, "Hello, World!")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/caddytest"
|
"github.com/caddyserver/caddy/v2/caddytest"
|
||||||
|
|
@ -8,20 +9,20 @@ import (
|
||||||
|
|
||||||
func TestDefaultSNI(t *testing.T) {
|
func TestDefaultSNI(t *testing.T) {
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`{
|
harness.LoadConfig(`{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"grace_period": 1,
|
"grace_period": 1,
|
||||||
"servers": {
|
"servers": {
|
||||||
"srv0": {
|
"srv0": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9443"
|
":{$TESTING_CADDY_PORT_TWO}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -102,26 +103,27 @@ func TestDefaultSNI(t *testing.T) {
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
// makes a request with no sni
|
// makes a request with no sni
|
||||||
tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a.caddy.localhost")
|
target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
|
||||||
|
harness.AssertGetResponse(target+"version", 200, "hello from a.caddy.localhost")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
|
func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"grace_period": 1,
|
"grace_period": 1,
|
||||||
"servers": {
|
"servers": {
|
||||||
"srv0": {
|
"srv0": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9443"
|
":{$TESTING_CADDY_PORT_TWO}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -206,26 +208,27 @@ func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
// makes a request with no sni
|
// makes a request with no sni
|
||||||
tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a")
|
target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
|
||||||
|
harness.AssertGetResponse(target+"version", 200, "hello from a")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
|
func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
|
||||||
// arrange
|
// arrange
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"grace_period": 1,
|
"grace_period": 1,
|
||||||
"servers": {
|
"servers": {
|
||||||
"srv0": {
|
"srv0": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9443"
|
":{$TESTING_CADDY_PORT_TWO}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -282,7 +285,8 @@ func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
|
||||||
|
|
||||||
// act and assert
|
// act and assert
|
||||||
// makes a request with no sni
|
// makes a request with no sni
|
||||||
tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a.caddy.localhost")
|
target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
|
||||||
|
harness.AssertGetResponse(target+"version", 200, "hello from a.caddy.localhost")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHttpOnlyOnDomainWithSNI(t *testing.T) {
|
func TestHttpOnlyOnDomainWithSNI(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -20,21 +20,21 @@ import (
|
||||||
|
|
||||||
// (see https://github.com/caddyserver/caddy/issues/3556 for use case)
|
// (see https://github.com/caddyserver/caddy/issues/3556 for use case)
|
||||||
func TestH2ToH2CStream(t *testing.T) {
|
func TestH2ToH2CStream(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"grace_period": 1,
|
"grace_period": 1,
|
||||||
"servers": {
|
"servers": {
|
||||||
"srv0": {
|
"srv0": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9443"
|
":{$TESTING_CADDY_PORT_TWO}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -102,7 +102,7 @@ func TestH2ToH2CStream(t *testing.T) {
|
||||||
|
|
||||||
expectedBody := "some data to be echoed"
|
expectedBody := "some data to be echoed"
|
||||||
// start the server
|
// start the server
|
||||||
server := testH2ToH2CStreamServeH2C(t)
|
server := testH2ToH2CStreamServeH2C(harness, t)
|
||||||
go server.ListenAndServe()
|
go server.ListenAndServe()
|
||||||
defer func() {
|
defer func() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
|
||||||
|
|
@ -116,7 +116,7 @@ func TestH2ToH2CStream(t *testing.T) {
|
||||||
Body: io.NopCloser(r),
|
Body: io.NopCloser(r),
|
||||||
URL: &url.URL{
|
URL: &url.URL{
|
||||||
Scheme: "https",
|
Scheme: "https",
|
||||||
Host: "127.0.0.1:9443",
|
Host: fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()),
|
||||||
Path: "/tov2ray",
|
Path: "/tov2ray",
|
||||||
},
|
},
|
||||||
Proto: "HTTP/2",
|
Proto: "HTTP/2",
|
||||||
|
|
@ -127,7 +127,7 @@ func TestH2ToH2CStream(t *testing.T) {
|
||||||
// Disable any compression method from server.
|
// Disable any compression method from server.
|
||||||
req.Header.Set("Accept-Encoding", "identity")
|
req.Header.Set("Accept-Encoding", "identity")
|
||||||
|
|
||||||
resp := tester.AssertResponseCode(req, http.StatusOK)
|
resp := harness.AssertResponseCode(req, http.StatusOK)
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +149,7 @@ func TestH2ToH2CStream(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
|
func testH2ToH2CStreamServeH2C(harness *caddytest.TestHarness, t *testing.T) *http.Server {
|
||||||
h2s := &http2.Server{}
|
h2s := &http2.Server{}
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
rstring, err := httputil.DumpRequest(r, false)
|
rstring, err := httputil.DumpRequest(r, false)
|
||||||
|
|
@ -163,7 +163,7 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Host != "127.0.0.1:9443" {
|
if r.Host != fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()) {
|
||||||
t.Errorf("r.Host doesn't match, %v!", r.Host)
|
t.Errorf("r.Host doesn't match, %v!", r.Host)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
|
|
@ -204,28 +204,21 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
|
||||||
|
|
||||||
// (see https://github.com/caddyserver/caddy/issues/3606 for use case)
|
// (see https://github.com/caddyserver/caddy/issues/3606 for use case)
|
||||||
func TestH2ToH1ChunkedResponse(t *testing.T) {
|
func TestH2ToH1ChunkedResponse(t *testing.T) {
|
||||||
tester := caddytest.NewTester(t)
|
harness := caddytest.StartHarness(t)
|
||||||
tester.InitServer(`
|
harness.LoadConfig(`
|
||||||
{
|
{
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "{$TESTING_CADDY_ADMIN_BIND}"
|
||||||
},
|
},
|
||||||
"logging": {
|
|
||||||
"logs": {
|
|
||||||
"default": {
|
|
||||||
"level": "DEBUG"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apps": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": {$TESTING_CADDY_PORT_ONE},
|
||||||
"https_port": 9443,
|
"https_port": {$TESTING_CADDY_PORT_TWO},
|
||||||
"grace_period": 1,
|
"grace_period": 1,
|
||||||
"servers": {
|
"servers": {
|
||||||
"srv0": {
|
"srv0": {
|
||||||
"listen": [
|
"listen": [
|
||||||
":9443"
|
":{$TESTING_CADDY_PORT_TWO}"
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|
@ -312,7 +305,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// start the server
|
// start the server
|
||||||
server := testH2ToH1ChunkedResponseServeH1(t)
|
server := testH2ToH1ChunkedResponseServeH1(harness, t)
|
||||||
go server.ListenAndServe()
|
go server.ListenAndServe()
|
||||||
defer func() {
|
defer func() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
|
||||||
|
|
@ -326,7 +319,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
|
||||||
Body: io.NopCloser(r),
|
Body: io.NopCloser(r),
|
||||||
URL: &url.URL{
|
URL: &url.URL{
|
||||||
Scheme: "https",
|
Scheme: "https",
|
||||||
Host: "127.0.0.1:9443",
|
Host: fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()),
|
||||||
Path: "/tov2ray",
|
Path: "/tov2ray",
|
||||||
},
|
},
|
||||||
Proto: "HTTP/2",
|
Proto: "HTTP/2",
|
||||||
|
|
@ -340,7 +333,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
|
||||||
fmt.Fprint(w, expectedBody)
|
fmt.Fprint(w, expectedBody)
|
||||||
w.Close()
|
w.Close()
|
||||||
}()
|
}()
|
||||||
resp := tester.AssertResponseCode(req, http.StatusOK)
|
resp := harness.AssertResponseCode(req, http.StatusOK)
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -358,9 +351,9 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testH2ToH1ChunkedResponseServeH1(t *testing.T) *http.Server {
|
func testH2ToH1ChunkedResponseServeH1(harness *caddytest.TestHarness, t *testing.T) *http.Server {
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Host != "127.0.0.1:9443" {
|
if r.Host != fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()) {
|
||||||
t.Errorf("r.Host doesn't match, %v!", r.Host)
|
t.Errorf("r.Host doesn't match, %v!", r.Host)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
241
caddytest/testing_harness.go
Normal file
241
caddytest/testing_harness.go
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
package caddytest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// use the convention to replace /[certificatename].[crt|key] with the full path
|
||||||
|
// this helps reduce the noise in test configurations and also allow this
|
||||||
|
// to run in any path
|
||||||
|
func prependCaddyFilePath(rawConfig string) string {
|
||||||
|
r := matchKey.ReplaceAllString(rawConfig, getIntegrationDir()+"$1")
|
||||||
|
r = matchCert.ReplaceAllString(r, getIntegrationDir()+"$1")
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIntegrationDir() string {
|
||||||
|
_, filename, _, ok := runtime.Caller(1)
|
||||||
|
if !ok {
|
||||||
|
panic("unable to determine the current file path")
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.Dir(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
matchKey = regexp.MustCompile(`(/[\w\d\.]+\.key)`)
|
||||||
|
matchCert = regexp.MustCompile(`(/[\w\d\.]+\.crt)`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestHarness struct {
|
||||||
|
t testing.TB
|
||||||
|
|
||||||
|
tester *Tester
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartHarness creates and starts a test harness environment which spans the lifetime a single caddy instance
|
||||||
|
// This is used for the integration tests
|
||||||
|
func StartHarness(t *testing.T) *TestHarness {
|
||||||
|
if testing.Short() {
|
||||||
|
t.SkipNow()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
o := &TestHarness{t: t}
|
||||||
|
o.init()
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TestHarness) Tester() *Tester {
|
||||||
|
return tc.tester
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TestHarness) Client() *http.Client {
|
||||||
|
return tc.tester.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TestHarness) LoadConfig(rawConfig, configType string) {
|
||||||
|
rawConfig = prependCaddyFilePath(rawConfig)
|
||||||
|
err := tc.tester.LoadConfig(rawConfig, configType)
|
||||||
|
require.NoError(tc.t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TestHarness) init() {
|
||||||
|
// start the server
|
||||||
|
tester, err := NewTester()
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("Failed to create caddy tester: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tc.tester = tester
|
||||||
|
err = tc.tester.LaunchCaddy()
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("Failed to launch caddy server: %s", err)
|
||||||
|
tc.t.FailNow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// cleanup
|
||||||
|
tc.t.Cleanup(func() {
|
||||||
|
func() {
|
||||||
|
if tc.t.Failed() {
|
||||||
|
res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", tc.tester.adminPort))
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Log("unable to read the current config")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
body, _ := io.ReadAll(res.Body)
|
||||||
|
|
||||||
|
var out bytes.Buffer
|
||||||
|
_ = json.Indent(&out, body, "", " ")
|
||||||
|
tc.t.Logf("----------- failed with config -----------\n%s", out.String())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// shutdown server after extracing the config
|
||||||
|
err = tc.tester.CleanupCaddy()
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("failed to clean up caddy instance: %s", err)
|
||||||
|
tc.t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertRedirect makes a request and asserts the redirection happens
|
||||||
|
func (tc *TestHarness) AssertRedirect(requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
|
||||||
|
redirectPolicyFunc := func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// using the existing client, we override the check redirect policy for this test
|
||||||
|
old := tc.tester.Client.CheckRedirect
|
||||||
|
tc.tester.Client.CheckRedirect = redirectPolicyFunc
|
||||||
|
defer func() { tc.tester.Client.CheckRedirect = old }()
|
||||||
|
|
||||||
|
resp, err := tc.tester.Client.Get(requestURI)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("failed to call server %s", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if expectedStatusCode != resp.StatusCode {
|
||||||
|
tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", requestURI, expectedStatusCode, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
loc, err := resp.Location()
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got error: %s", requestURI, expectedToLocation, err)
|
||||||
|
}
|
||||||
|
if loc == nil && expectedToLocation != "" {
|
||||||
|
tc.t.Errorf("requesting \"%s\" expected a Location header, but didn't get one", requestURI)
|
||||||
|
}
|
||||||
|
if loc != nil {
|
||||||
|
if expectedToLocation != loc.String() {
|
||||||
|
tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got \"%s\"", requestURI, expectedToLocation, loc.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertResponseCode will execute the request and verify the status code, returns a response for additional assertions
|
||||||
|
func (tc *TestHarness) AssertResponseCode(req *http.Request, expectedStatusCode int) *http.Response {
|
||||||
|
resp, err := tc.tester.Client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Fatalf("failed to call server %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expectedStatusCode != resp.StatusCode {
|
||||||
|
tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", req.URL.RequestURI(), expectedStatusCode, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertResponse request a URI and assert the status code and the body contains a string
|
||||||
|
func (tc *TestHarness) AssertResponse(req *http.Request, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
||||||
|
resp := tc.AssertResponseCode(req, expectedStatusCode)
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
bytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Fatalf("unable to read the response body %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := string(bytes)
|
||||||
|
|
||||||
|
if body != expectedBody {
|
||||||
|
tc.t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", req.RequestURI, expectedBody, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, body
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verb specific test functions
|
||||||
|
|
||||||
|
// AssertGetResponse GET a URI and expect a statusCode and body text
|
||||||
|
func (tc *TestHarness) AssertGetResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
||||||
|
req, err := http.NewRequest("GET", requestURI, nil)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Fatalf("unable to create request %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertDeleteResponse request a URI and expect a statusCode and body text
|
||||||
|
func (tc *TestHarness) AssertDeleteResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
||||||
|
req, err := http.NewRequest("DELETE", requestURI, nil)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Fatalf("unable to create request %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertPostResponseBody POST to a URI and assert the response code and body
|
||||||
|
func (tc *TestHarness) AssertPostResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
||||||
|
req, err := http.NewRequest("POST", requestURI, requestBody)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("failed to create request %s", err)
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
applyHeaders(tc.t, req, requestHeaders)
|
||||||
|
|
||||||
|
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertPutResponseBody PUT to a URI and assert the response code and body
|
||||||
|
func (tc *TestHarness) AssertPutResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
||||||
|
req, err := http.NewRequest("PUT", requestURI, requestBody)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("failed to create request %s", err)
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
applyHeaders(tc.t, req, requestHeaders)
|
||||||
|
|
||||||
|
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertPatchResponseBody PATCH to a URI and assert the response code and body
|
||||||
|
func (tc *TestHarness) AssertPatchResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
|
||||||
|
req, err := http.NewRequest("PATCH", requestURI, requestBody)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("failed to create request %s", err)
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
applyHeaders(tc.t, req, requestHeaders)
|
||||||
|
|
||||||
|
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
|
||||||
|
}
|
||||||
26
cmd/cobra.go
26
cmd/cobra.go
|
|
@ -8,9 +8,10 @@ import (
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var defaultFactory = NewRootCommandFactory(func() *cobra.Command {
|
||||||
Use: "caddy",
|
return &cobra.Command{
|
||||||
Long: `Caddy is an extensible server platform written in Go.
|
Use: "caddy",
|
||||||
|
Long: `Caddy is an extensible server platform written in Go.
|
||||||
|
|
||||||
At its core, Caddy merely manages configuration. Modules are plugged
|
At its core, Caddy merely manages configuration. Modules are plugged
|
||||||
in statically at compile-time to provide useful functionality. Caddy's
|
in statically at compile-time to provide useful functionality. Caddy's
|
||||||
|
|
@ -91,23 +92,26 @@ package installers: https://caddyserver.com/docs/install
|
||||||
Instructions for running Caddy in production are also available:
|
Instructions for running Caddy in production are also available:
|
||||||
https://caddyserver.com/docs/running
|
https://caddyserver.com/docs/running
|
||||||
`,
|
`,
|
||||||
Example: ` $ caddy run
|
Example: ` $ caddy run
|
||||||
$ caddy run --config caddy.json
|
$ caddy run --config caddy.json
|
||||||
$ caddy reload --config caddy.json
|
$ caddy reload --config caddy.json
|
||||||
$ caddy stop`,
|
$ caddy stop`,
|
||||||
|
|
||||||
// kind of annoying to have all the help text printed out if
|
// kind of annoying to have all the help text printed out if
|
||||||
// caddy has an error provisioning its modules, for instance...
|
// caddy has an error provisioning its modules, for instance...
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
Version: onlyVersionText(),
|
Version: onlyVersionText(),
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const fullDocsFooter = `Full documentation is available at:
|
const fullDocsFooter = `Full documentation is available at:
|
||||||
https://caddyserver.com/docs/command-line`
|
https://caddyserver.com/docs/command-line`
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.SetVersionTemplate("{{.Version}}\n")
|
defaultFactory.Use(func(cmd *cobra.Command) {
|
||||||
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
|
cmd.SetVersionTemplate("{{.Version}}\n")
|
||||||
|
cmd.SetHelpTemplate(cmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func onlyVersionText() string {
|
func onlyVersionText() string {
|
||||||
|
|
|
||||||
28
cmd/commandfactory.go
Normal file
28
cmd/commandfactory.go
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package caddycmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RootCommandFactory struct {
|
||||||
|
constructor func() *cobra.Command
|
||||||
|
options []func(*cobra.Command)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRootCommandFactory(fn func() *cobra.Command) *RootCommandFactory {
|
||||||
|
return &RootCommandFactory{
|
||||||
|
constructor: fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RootCommandFactory) Use(fn func(cmd *cobra.Command)) {
|
||||||
|
f.options = append(f.options, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RootCommandFactory) Build() *cobra.Command {
|
||||||
|
o := f.constructor()
|
||||||
|
for _, v := range f.options {
|
||||||
|
v(o)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
|
@ -257,6 +258,7 @@ func cmdRun(fl Flags) (int, error) {
|
||||||
|
|
||||||
// if enabled, reload config file automatically on changes
|
// if enabled, reload config file automatically on changes
|
||||||
// (this better only be used in dev!)
|
// (this better only be used in dev!)
|
||||||
|
// do not enable this during tests, it will cause leaks
|
||||||
if watchFlag {
|
if watchFlag {
|
||||||
go watchConfigFile(configFile, configAdapterFlag)
|
go watchConfigFile(configFile, configAdapterFlag)
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +282,11 @@ func cmdRun(fl Flags) (int, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select {}
|
if flag.Lookup("test.v") == nil || !strings.Contains(os.Args[0], ".test") {
|
||||||
|
select {}
|
||||||
|
} else {
|
||||||
|
return caddy.ExitCodeSuccess, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdStop(fl Flags) (int, error) {
|
func cmdStop(fl Flags) (int, error) {
|
||||||
|
|
|
||||||
|
|
@ -459,7 +459,8 @@ argument of --directory. If the directory does not exist, it will be created.
|
||||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
return caddy.ExitCodeFailedQuit, err
|
return caddy.ExitCodeFailedQuit, err
|
||||||
}
|
}
|
||||||
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
|
ccmd := defaultFactory.Build()
|
||||||
|
if err := doc.GenManTree(ccmd, &doc.GenManHeader{
|
||||||
Title: "Caddy",
|
Title: "Caddy",
|
||||||
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
|
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
|
||||||
}, dir); err != nil {
|
}, dir); err != nil {
|
||||||
|
|
@ -471,10 +472,11 @@ argument of --directory. If the directory does not exist, it will be created.
|
||||||
})
|
})
|
||||||
|
|
||||||
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
|
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
|
||||||
rootCmd.AddCommand(&cobra.Command{
|
defaultFactory.Use(func(ccmd *cobra.Command) {
|
||||||
Use: "completion [bash|zsh|fish|powershell]",
|
ccmd.AddCommand(&cobra.Command{
|
||||||
Short: "Generate completion script",
|
Use: "completion [bash|zsh|fish|powershell]",
|
||||||
Long: fmt.Sprintf(`To load completions:
|
Short: "Generate completion script",
|
||||||
|
Long: fmt.Sprintf(`To load completions:
|
||||||
|
|
||||||
Bash:
|
Bash:
|
||||||
|
|
||||||
|
|
@ -512,24 +514,25 @@ argument of --directory. If the directory does not exist, it will be created.
|
||||||
# To load completions for every new session, run:
|
# To load completions for every new session, run:
|
||||||
PS> %[1]s completion powershell > %[1]s.ps1
|
PS> %[1]s completion powershell > %[1]s.ps1
|
||||||
# and source this file from your PowerShell profile.
|
# and source this file from your PowerShell profile.
|
||||||
`, rootCmd.Root().Name()),
|
`, defaultFactory.constructor().Name()),
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "bash":
|
case "bash":
|
||||||
return cmd.Root().GenBashCompletion(os.Stdout)
|
return cmd.Root().GenBashCompletion(os.Stdout)
|
||||||
case "zsh":
|
case "zsh":
|
||||||
return cmd.Root().GenZshCompletion(os.Stdout)
|
return cmd.Root().GenZshCompletion(os.Stdout)
|
||||||
case "fish":
|
case "fish":
|
||||||
return cmd.Root().GenFishCompletion(os.Stdout, true)
|
return cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||||
case "powershell":
|
case "powershell":
|
||||||
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
|
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unrecognized shell: %s", args[0])
|
return fmt.Errorf("unrecognized shell: %s", args[0])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -563,7 +566,9 @@ func RegisterCommand(cmd Command) {
|
||||||
if !commandNameRegex.MatchString(cmd.Name) {
|
if !commandNameRegex.MatchString(cmd.Name) {
|
||||||
panic("invalid command name")
|
panic("invalid command name")
|
||||||
}
|
}
|
||||||
rootCmd.AddCommand(caddyCmdToCobra(cmd))
|
defaultFactory.Use(func(ccmd *cobra.Command) {
|
||||||
|
ccmd.AddCommand(caddyCmdToCobra(cmd))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
|
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
|
||||||
|
|
|
||||||
14
cmd/main.go
14
cmd/main.go
|
|
@ -71,7 +71,7 @@ func Main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
|
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
rootCmd := defaultFactory.Build()
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
var exitError *exitError
|
var exitError *exitError
|
||||||
if errors.As(err, &exitError) {
|
if errors.As(err, &exitError) {
|
||||||
|
|
@ -81,6 +81,18 @@ func Main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MainForTesting implements the main function of the caddy command, used internally for testing
|
||||||
|
func MainForTesting(args ...string) error {
|
||||||
|
// create a root command for testing which will not pollute the global namespace, and does not
|
||||||
|
// call os.Exit().
|
||||||
|
rootCmd := defaultFactory.Build()
|
||||||
|
rootCmd.SetArgs(args)
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// handlePingbackConn reads from conn and ensures it matches
|
// handlePingbackConn reads from conn and ensures it matches
|
||||||
// the bytes in expect, or returns an error if it doesn't.
|
// the bytes in expect, or returns an error if it doesn't.
|
||||||
func handlePingbackConn(conn net.Conn, expect []byte) error {
|
func handlePingbackConn(conn net.Conn, expect []byte) error {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ package caddy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
|
@ -699,7 +700,13 @@ type defaultCustomLog struct {
|
||||||
// and enables INFO-level logs and higher.
|
// and enables INFO-level logs and higher.
|
||||||
func newDefaultProductionLog() (*defaultCustomLog, error) {
|
func newDefaultProductionLog() (*defaultCustomLog, error) {
|
||||||
cl := new(CustomLog)
|
cl := new(CustomLog)
|
||||||
cl.writerOpener = StderrWriter{}
|
f := flag.Lookup("test.v")
|
||||||
|
if (f != nil && f.Value.String() != "true") || strings.Contains(os.Args[0], ".test") {
|
||||||
|
cl.writerOpener = &DiscardWriter{}
|
||||||
|
} else {
|
||||||
|
cl.writerOpener = StderrWriter{}
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
cl.writer, err = cl.writerOpener.OpenWriter()
|
cl.writer, err = cl.writerOpener.OpenWriter()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
package fileserver
|
package fileserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -691,10 +690,6 @@ func (fsrv *FileServer) getEtagFromFile(fileSystem fs.FS, filename string) (stri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("cannot read etag from file %s: %v", etagFilename, err)
|
return "", fmt.Errorf("cannot read etag from file %s: %v", etagFilename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Etags should not contain newline characters
|
|
||||||
etag = bytes.ReplaceAll(etag, []byte("\n"), []byte{})
|
|
||||||
|
|
||||||
return string(etag), nil
|
return string(etag), nil
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue