mirror of
https://github.com/caddyserver/caddy.git
synced 2025-12-08 06:09:53 +00:00
Merge 01828e38bb into 31960dc998
This commit is contained in:
commit
f11c0a6159
17 changed files with 1463 additions and 0 deletions
106
.github/workflows/ci.yml
vendored
106
.github/workflows/ci.yml
vendored
|
|
@ -63,6 +63,7 @@ jobs:
|
|||
contents: read
|
||||
pull-requests: read
|
||||
actions: write # to allow uploading artifacts and cache
|
||||
checks: write
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
|
||||
|
|
@ -152,6 +153,111 @@ jobs:
|
|||
# echo "step_test ${{ steps.step_test.outputs.status }}\n"
|
||||
# exit 1
|
||||
|
||||
spec-test:
|
||||
permissions:
|
||||
checks: write
|
||||
pull-requests: write
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- linux
|
||||
go:
|
||||
- '1.25'
|
||||
|
||||
include:
|
||||
# Set the minimum Go patch version for the given Go minor
|
||||
# Usable via ${{ matrix.GO_SEMVER }}
|
||||
- go: '1.25'
|
||||
GO_SEMVER: '~1.25.0'
|
||||
|
||||
# Set some variables per OS, usable via ${{ matrix.VAR }}
|
||||
# OS_LABEL: the VM label from GitHub Actions (see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories)
|
||||
# CADDY_BIN_PATH: the path to the compiled Caddy binary, for artifact publishing
|
||||
# SUCCESS: the typical value for $? per OS (Windows/pwsh returns 'True')
|
||||
- os: linux
|
||||
OS_LABEL: ubuntu-latest
|
||||
CADDY_BIN_PATH: ./cmd/caddy/caddy
|
||||
SUCCESS: 0
|
||||
|
||||
runs-on: ${{ matrix.OS_LABEL }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ matrix.GO_SEMVER }}
|
||||
check-latest: true
|
||||
|
||||
- name: Print Go version and environment
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
printf "curl version: $(curl --version)\n"
|
||||
printf "Using go at: $(which go)\n"
|
||||
printf "Go version: $(go version)\n"
|
||||
printf "\n\nGo environment:\n\n"
|
||||
go env
|
||||
printf "\n\nSystem environment:\n\n"
|
||||
env
|
||||
printf "Git version: $(git version)\n\n"
|
||||
# Calculate the short SHA1 hash of the git commit
|
||||
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
go get -v -t ./...
|
||||
# mkdir test-results
|
||||
- name: Build Caddy
|
||||
working-directory: ./cmd/caddy
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
run: |
|
||||
go build -cover -tags nobadger,nopgx,nomysql -trimpath -ldflags="-w -s" -v
|
||||
|
||||
- name: Install Hurl
|
||||
env:
|
||||
HURL_VERSION: "7.0.0"
|
||||
run: |
|
||||
curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/${HURL_VERSION}/hurl_${HURL_VERSION}_amd64.deb
|
||||
sudo dpkg -i hurl_${HURL_VERSION}_amd64.deb
|
||||
hurl --version
|
||||
|
||||
- name: Run Caddy
|
||||
run: |
|
||||
./cmd/caddy/caddy environ
|
||||
mkdir coverdir
|
||||
export GOCOVERDIR=./coverdir
|
||||
./cmd/caddy/caddy start
|
||||
sleep 5
|
||||
|
||||
- name: Run tests with Hurl
|
||||
run: |
|
||||
mkdir hurl-report
|
||||
find . -name *.hurl -exec hurl --jobs 1 --variables-file caddytest/spec/hurl_vars.properties --very-verbose --verbose --test --report-junit hurl-report/junit.xml --color {} \;
|
||||
|
||||
- name: Publish Test Results
|
||||
uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0
|
||||
with:
|
||||
files: |
|
||||
hurl-report/junit.xml
|
||||
|
||||
- name: Generate Coverage Data
|
||||
run: |
|
||||
export GOCOVERDIR=./coverdir
|
||||
./cmd/caddy/caddy stop
|
||||
go tool covdata textfmt -i=coverdir -o hurl-report/caddy_cover_${{ steps.vars.outputs.short_sha }}.txt
|
||||
go tool cover -html hurl-report/caddy_cover_${{ steps.vars.outputs.short_sha }}.txt -o hurl-report/caddy_cover_${{ steps.vars.outputs.short_sha }}.html
|
||||
|
||||
|
||||
- name: Publish Coverage Profile
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
path: hurl-report/caddy_cover_${{ steps.vars.outputs.short_sha }}.html
|
||||
compression-level: 0
|
||||
|
||||
s390x-test:
|
||||
name: test (s390x on IBM Z)
|
||||
permissions:
|
||||
|
|
|
|||
38
caddytest/spec/http/basicauth/spec.hurl
Normal file
38
caddytest/spec/http/basicauth/spec.hurl
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Configure Caddy
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
debug
|
||||
}
|
||||
localhost {
|
||||
log
|
||||
basic_auth {
|
||||
john $2a$14$x4HlYwA9Zeer4RkMEYbUzug9XxWmncneR.dcMs.UjalR95URnHg5.
|
||||
}
|
||||
respond "Hello, World!"
|
||||
}
|
||||
```
|
||||
|
||||
# requests without `Authorization` header are rejected with 401
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 401
|
||||
[Asserts]
|
||||
header "WWW-Authenticate" == "Basic realm=\"restricted\""
|
||||
|
||||
|
||||
# requests with `Authorization` header are accepted with 200
|
||||
GET https://localhost:9443
|
||||
[BasicAuth]
|
||||
john:password
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
`Hello, World!`
|
||||
150
caddytest/spec/http/error/spec.hurl
Normal file
150
caddytest/spec/http/error/spec.hurl
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# Configure Caddy with error directive
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
error /forbidden* "Access denied" 403
|
||||
respond "OK"
|
||||
}
|
||||
```
|
||||
|
||||
# error directive triggers 403 for matching paths
|
||||
GET https://localhost:9443/forbidden/resource
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 403
|
||||
|
||||
|
||||
# error directive does not trigger for non-matching paths
|
||||
GET https://localhost:9443/allowed
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "OK"
|
||||
|
||||
|
||||
# Configure Caddy with error and handle_errors
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
error /admin* "Forbidden" 403
|
||||
handle_errors {
|
||||
respond "Custom error: {err.status_code} - {err.status_text}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# error with handle_errors shows custom error page
|
||||
GET https://localhost:9443/admin/panel
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 403
|
||||
[Asserts]
|
||||
body == "Custom error: 403 - Forbidden"
|
||||
|
||||
|
||||
# Configure Caddy with conditional error
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
@admin path /admin*
|
||||
error @admin 404
|
||||
respond "Public content"
|
||||
}
|
||||
```
|
||||
|
||||
# error with named matcher triggers on match
|
||||
GET https://localhost:9443/admin/users
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 404
|
||||
|
||||
|
||||
# error with named matcher doesn't trigger on non-match
|
||||
GET https://localhost:9443/public
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Public content"
|
||||
|
||||
|
||||
# Configure Caddy with error for specific methods
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
@post method POST
|
||||
error @post "Method not allowed" 405
|
||||
respond "GET OK"
|
||||
}
|
||||
```
|
||||
|
||||
# error blocks POST requests
|
||||
POST https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 405
|
||||
|
||||
|
||||
# error allows GET requests
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "GET OK"
|
||||
|
||||
|
||||
# Configure Caddy with dynamic error message
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
error /error* "Path {path} not found" 404
|
||||
handle_errors {
|
||||
respond "{err.message}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# error message can use placeholders
|
||||
GET https://localhost:9443/error/test
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 404
|
||||
[Asserts]
|
||||
body == "Path /error/test not found"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Index.html Title</title>
|
||||
</head>
|
||||
<body>
|
||||
Index.html
|
||||
</body>
|
||||
</html>
|
||||
1
caddytest/spec/http/file_server/assets/indexed/index.txt
Normal file
1
caddytest/spec/http/file_server/assets/indexed/index.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
index.txt
|
||||
119
caddytest/spec/http/file_server/spec.hurl
Normal file
119
caddytest/spec/http/file_server/spec.hurl
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# Configure Caddy with default configuration
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
debug
|
||||
}
|
||||
localhost {
|
||||
root {{indexed_root}}
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
# requests without specific file receive index file per
|
||||
# the default index list: index.html, index.txt
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Index.html Title</title>
|
||||
</head>
|
||||
<body>
|
||||
Index.html
|
||||
</body>
|
||||
</html>```
|
||||
|
||||
|
||||
# if index.txt is specifically requested, we expect index.txt
|
||||
GET https://localhost:9443/index.txt
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "index.txt"
|
||||
|
||||
# requests for sub-folder followed by .. result in sanitized path
|
||||
GET https://localhost:9443/non-existent/../index.txt
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "index.txt"
|
||||
|
||||
# results out of root folder are sanitized,
|
||||
# and conform to default index list sequence.
|
||||
GET https://localhost:9443/../
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Index.html Title</title>
|
||||
</head>
|
||||
<body>
|
||||
Index.html
|
||||
</body>
|
||||
</html>```
|
||||
|
||||
|
||||
# Configure Caddy with custsom index "index.txt"
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
debug
|
||||
}
|
||||
localhost {
|
||||
root {{indexed_root}}
|
||||
file_server {
|
||||
index index.txt
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "index.txt"
|
||||
|
||||
|
||||
# Configure with a root not containing index files
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
debug
|
||||
}
|
||||
localhost {
|
||||
root {{unindexed_root}}
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 404
|
||||
132
caddytest/spec/http/forward_auth/spec.hurl
Normal file
132
caddytest/spec/http/forward_auth/spec.hurl
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# Configure Caddy with forward_auth directive
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
forward_auth localhost:9080 {
|
||||
uri /auth
|
||||
}
|
||||
respond "Protected content"
|
||||
}
|
||||
http://localhost:9080 {
|
||||
handle /auth {
|
||||
respond 200
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# forward_auth allows request when auth endpoint returns 2xx
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
delay: 500ms
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Protected content"
|
||||
|
||||
|
||||
# Configure Caddy with forward_auth rejecting
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
forward_auth localhost:9080 {
|
||||
uri /auth
|
||||
}
|
||||
respond "Protected content"
|
||||
}
|
||||
http://localhost:9080 {
|
||||
handle /auth {
|
||||
respond 401
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# forward_auth blocks request when auth endpoint returns 4xx
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
delay: 500ms
|
||||
insecure: true
|
||||
HTTP 401
|
||||
|
||||
|
||||
# Configure Caddy with forward_auth copying headers
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
forward_auth localhost:9080 {
|
||||
uri /auth
|
||||
copy_headers X-User-ID X-User-Email
|
||||
}
|
||||
respond "User: {header.X-User-ID}, Email: {header.X-User-Email}"
|
||||
}
|
||||
http://localhost:9080 {
|
||||
handle /auth {
|
||||
header X-User-ID "user123"
|
||||
header X-User-Email "user@example.com"
|
||||
respond 200
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# forward_auth copies specified headers from auth response
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
delay: 500ms
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "User: user123, Email: user@example.com"
|
||||
|
||||
|
||||
# Configure Caddy with forward_auth and custom headers
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
forward_auth localhost:9080 {
|
||||
uri /auth
|
||||
header_up X-Original-URL {uri}
|
||||
}
|
||||
respond "OK"
|
||||
}
|
||||
http://localhost:9080 {
|
||||
handle /auth {
|
||||
respond "{header.X-Original-URL}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# forward_auth can send custom headers to auth endpoint
|
||||
GET https://localhost:9443/test/path
|
||||
[Options]
|
||||
delay: 500ms
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "OK"
|
||||
22
caddytest/spec/http/headers/spec.hurl
Normal file
22
caddytest/spec/http/headers/spec.hurl
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Configure Caddy
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
debug
|
||||
}
|
||||
localhost {
|
||||
header "X-Custom-Header" "Custom-Value"
|
||||
}
|
||||
```
|
||||
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
header "X-Custom-Header" == "Custom-Value"
|
||||
190
caddytest/spec/http/request_header/spec.hurl
Normal file
190
caddytest/spec/http/request_header/spec.hurl
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# Configure Caddy with request_header directive
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
request_header X-Custom-Header "CustomValue"
|
||||
respond "{header.X-Custom-Header}"
|
||||
}
|
||||
```
|
||||
|
||||
# request_header adds headers to request
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "CustomValue"
|
||||
|
||||
|
||||
# Configure Caddy with request_header removing headers
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
request_header -User-Agent
|
||||
respond "UA: {header.User-Agent}"
|
||||
}
|
||||
```
|
||||
|
||||
# request_header can remove headers
|
||||
GET https://localhost:9443
|
||||
User-Agent: TestAgent/1.0
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "UA: "
|
||||
|
||||
|
||||
# Configure Caddy with request_header replacing headers
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
request_header Host "example.com"
|
||||
respond "Host: {host}"
|
||||
}
|
||||
```
|
||||
|
||||
# request_header can replace Host header
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Host: example.com"
|
||||
|
||||
|
||||
# Configure Caddy with request_header using placeholders
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
request_header X-Original-Path {path}
|
||||
respond "Path: {header.X-Original-Path}"
|
||||
}
|
||||
```
|
||||
|
||||
# request_header can use placeholders
|
||||
GET https://localhost:9443/test/path
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Path: /test/path"
|
||||
|
||||
|
||||
# Configure Caddy with conditional request_header
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
@api path /api/*
|
||||
request_header @api X-API "true"
|
||||
respond "API: {header.X-API}"
|
||||
}
|
||||
```
|
||||
|
||||
# request_header applies conditionally based on matcher
|
||||
GET https://localhost:9443/api/test
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "API: true"
|
||||
|
||||
|
||||
# request_header doesn't apply when matcher doesn't match
|
||||
GET https://localhost:9443/other
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "API: "
|
||||
|
||||
|
||||
# Configure Caddy with multiple request_header operations
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
request_header X-First "1"
|
||||
request_header X-Second "2"
|
||||
request_header X-Third "3"
|
||||
respond "{header.X-First},{header.X-Second},{header.X-Third}"
|
||||
}
|
||||
```
|
||||
|
||||
# multiple request_header directives are applied
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "1,2,3"
|
||||
|
||||
|
||||
# Configure Caddy with request_header and reverse_proxy
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
debug
|
||||
}
|
||||
localhost {
|
||||
request_header X-Custom-Header "Value"
|
||||
reverse_proxy localhost:9450
|
||||
}
|
||||
http://localhost:9450 {
|
||||
respond "{header.X-Custom-Header}"
|
||||
}
|
||||
```
|
||||
|
||||
# request_header adds header before reverse_proxy
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Value"
|
||||
36
caddytest/spec/http/requestbody/spec.hurl
Normal file
36
caddytest/spec/http/requestbody/spec.hurl
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Configure Caddy
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
log
|
||||
request_body {
|
||||
max_size 2B
|
||||
}
|
||||
reverse_proxy localhost:8000 # to fake body reading
|
||||
handle_errors 4xx {
|
||||
respond "OK"
|
||||
}
|
||||
}
|
||||
http://localhost:8000 {
|
||||
respond "Failed"
|
||||
}
|
||||
```
|
||||
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
delay: 1s
|
||||
insecure: true
|
||||
```
|
||||
Hello
|
||||
```
|
||||
HTTP 413
|
||||
`OK`
|
||||
|
||||
# TODO: how to test{read,write}_timeout?
|
||||
66
caddytest/spec/http/rewrite/spec.hurl
Normal file
66
caddytest/spec/http/rewrite/spec.hurl
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Configure Caddy
|
||||
POST http://localhost:2019/load
|
||||
User-Agent: hurl/ci
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
rewrite /from /to
|
||||
respond {uri}
|
||||
}
|
||||
```
|
||||
|
||||
# simple scenario: rewriting /from to /to produces expected result of seeing /to
|
||||
GET https://localhost:9443/from
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/to"
|
||||
|
||||
# unmatched path is passed through unchanged
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/"
|
||||
|
||||
# having a query parameter does not trip the rewrite and retains the query
|
||||
GET https://localhost:9443/from?query_param=value
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/to?query_param=value"
|
||||
|
||||
|
||||
# Configure Caddy
|
||||
POST http://localhost:2019/load
|
||||
User-Agent: hurl/ci
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
rewrite /from /to?a=b
|
||||
respond {uri}
|
||||
}
|
||||
```
|
||||
|
||||
# a rewrite with query parameters affects the parameters
|
||||
GET https://localhost:9443/from?query_param=value
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/to?a=b"
|
||||
171
caddytest/spec/http/route/spec.hurl
Normal file
171
caddytest/spec/http/route/spec.hurl
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
# Configure Caddy with route directive
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
route /api/* {
|
||||
uri strip_prefix /api
|
||||
respond "API: {uri}"
|
||||
}
|
||||
respond "Not API"
|
||||
}
|
||||
```
|
||||
|
||||
# route groups handlers and maintains order
|
||||
GET https://localhost:9443/api/users
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "API: /users"
|
||||
|
||||
|
||||
# route doesn't match non-matching paths
|
||||
GET https://localhost:9443/other
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Not API"
|
||||
|
||||
|
||||
# Configure Caddy with nested routes
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
route /api/* {
|
||||
uri strip_prefix /api
|
||||
route /v1/* {
|
||||
uri strip_prefix /v1
|
||||
respond "API v1: {uri}"
|
||||
}
|
||||
respond "API: {uri}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# nested routes process sequentially
|
||||
GET https://localhost:9443/api/v1/users
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "API v1: /users"
|
||||
|
||||
|
||||
# outer route processes when inner doesn't match
|
||||
GET https://localhost:9443/api/users
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "API: /users"
|
||||
|
||||
|
||||
# Configure Caddy with route and terminal handlers
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
route {
|
||||
header X-First "1"
|
||||
respond "Response"
|
||||
header X-Second "2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# route stops at terminal handler (respond)
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
header "X-First" == "1"
|
||||
header "X-Second" not exists
|
||||
|
||||
|
||||
# Configure Caddy with route preserving handler order
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
route {
|
||||
vars step1 "done"
|
||||
vars step2 "done"
|
||||
vars step3 "done"
|
||||
respond "{vars.step1},{vars.step2},{vars.step3}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# route preserves exact handler order
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "done,done,done"
|
||||
|
||||
|
||||
# Configure Caddy with route and matchers
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
route {
|
||||
@api path /api/*
|
||||
vars @api type "api"
|
||||
vars type "other"
|
||||
respond "{vars.type}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# route applies matchers in sequence
|
||||
GET https://localhost:9443/api/test
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "other"
|
||||
|
||||
|
||||
# route continues when matcher doesn't match
|
||||
GET https://localhost:9443/test
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "other"
|
||||
105
caddytest/spec/http/static_response/spec.hurl
Normal file
105
caddytest/spec/http/static_response/spec.hurl
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# Configure Caddy
|
||||
POST http://localhost:2019/load
|
||||
User-Agent: hurl/ci
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
log
|
||||
respond "Hello, World!"
|
||||
}
|
||||
```
|
||||
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
`Hello, World!`
|
||||
|
||||
|
||||
GET https://localhost:9443/foo
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
`Hello, World!`
|
||||
|
||||
# Configure Caddy
|
||||
POST http://localhost:2019/load
|
||||
User-Agent: hurl/ci
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
respond "New text!"
|
||||
}
|
||||
```
|
||||
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP/2 200
|
||||
[Asserts]
|
||||
`New text!`
|
||||
|
||||
|
||||
GET https://localhost:9443/foo
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP/2 200
|
||||
[Asserts]
|
||||
`New text!`
|
||||
|
||||
GET https://localhost:9443/foo
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP/2 200
|
||||
[Asserts]
|
||||
body != "Hello, World!"
|
||||
|
||||
# Configure Caddy
|
||||
# The body is a placeholder
|
||||
POST http://localhost:2019/load
|
||||
User-Agent: hurl/ci
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
log
|
||||
respond {http.request.body}
|
||||
}
|
||||
```
|
||||
|
||||
# handler responds with the "application/json" if the response body is valid JSON
|
||||
POST https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
```json
|
||||
{
|
||||
"greeting": "Hello, world!"
|
||||
}
|
||||
```
|
||||
HTTP/2 200
|
||||
[Asserts]
|
||||
header "Content-Type" == "application/json"
|
||||
```json
|
||||
{
|
||||
"greeting": "Hello, world!"
|
||||
}
|
||||
```
|
||||
191
caddytest/spec/http/uri/spec.hurl
Normal file
191
caddytest/spec/http/uri/spec.hurl
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
# Configure Caddy with uri strip_prefix
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
uri strip_prefix /api
|
||||
respond {uri}
|
||||
}
|
||||
```
|
||||
|
||||
# strip_prefix removes the prefix from the URI
|
||||
GET https://localhost:9443/api/users
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/users"
|
||||
|
||||
|
||||
# URI without prefix is unchanged
|
||||
GET https://localhost:9443/users
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/users"
|
||||
|
||||
|
||||
# Configure Caddy with uri strip_suffix
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
uri strip_suffix .php
|
||||
respond {uri}
|
||||
}
|
||||
```
|
||||
|
||||
# strip_suffix removes the suffix from the URI
|
||||
GET https://localhost:9443/index.php
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/index"
|
||||
|
||||
|
||||
# URI without suffix is unchanged
|
||||
GET https://localhost:9443/index.html
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/index.html"
|
||||
|
||||
|
||||
# Configure Caddy with uri replace
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
uri replace old new
|
||||
respond {uri}
|
||||
}
|
||||
```
|
||||
|
||||
# replace substitutes all occurrences
|
||||
GET https://localhost:9443/old/path/old
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/new/path/new"
|
||||
|
||||
|
||||
# Configure Caddy with uri path_regexp
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
uri path_regexp /([0-9]+) /$1/id
|
||||
respond {uri}
|
||||
}
|
||||
```
|
||||
|
||||
# path_regexp replaces using regular expressions
|
||||
GET https://localhost:9443/123
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "/123/id"
|
||||
|
||||
|
||||
# Configure Caddy with uri query operations
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
uri query +foo bar
|
||||
respond {query}
|
||||
}
|
||||
```
|
||||
|
||||
# query operations add parameters
|
||||
GET https://localhost:9443/?existing=value
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "existing=value&foo=bar"
|
||||
|
||||
|
||||
# Configure Caddy with uri query delete
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
uri query -sensitive
|
||||
respond {query}
|
||||
}
|
||||
```
|
||||
|
||||
# query operations delete parameters
|
||||
GET https://localhost:9443/?keep=this&sensitive=secret
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "keep=this"
|
||||
|
||||
|
||||
# Configure Caddy with uri query rename
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
uri query old>new
|
||||
respond {query}
|
||||
}
|
||||
```
|
||||
|
||||
# query operations rename parameters
|
||||
GET https://localhost:9443/?old=value
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "new=value"
|
||||
125
caddytest/spec/http/vars/spec.hurl
Normal file
125
caddytest/spec/http/vars/spec.hurl
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# Configure Caddy with vars directive
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
vars my_var "custom_value"
|
||||
vars another_var "another_value"
|
||||
respond "{vars.my_var} {vars.another_var}"
|
||||
}
|
||||
```
|
||||
|
||||
# Variables are accessible in placeholders
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "custom_value another_value"
|
||||
|
||||
|
||||
# Configure Caddy with vars using placeholders
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
vars request_path {path}
|
||||
vars request_method {method}
|
||||
respond "Path: {vars.request_path}, Method: {vars.request_method}"
|
||||
}
|
||||
```
|
||||
|
||||
# Variables can be set from request placeholders
|
||||
GET https://localhost:9443/test/path
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Path: /test/path, Method: GET"
|
||||
|
||||
|
||||
# POST method is captured correctly
|
||||
POST https://localhost:9443/another
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Path: /another, Method: POST"
|
||||
|
||||
|
||||
# Configure Caddy with vars in route
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
route /api/* {
|
||||
vars api_version "v1"
|
||||
respond "API {vars.api_version}"
|
||||
}
|
||||
respond "Not API"
|
||||
}
|
||||
```
|
||||
|
||||
# Variables are scoped to their route
|
||||
GET https://localhost:9443/api/users
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "API v1"
|
||||
|
||||
|
||||
# Outside the route, variables are not set
|
||||
GET https://localhost:9443/other
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "Not API"
|
||||
|
||||
|
||||
# Configure Caddy with vars overwriting
|
||||
POST http://localhost:2019/load
|
||||
Content-Type: text/caddyfile
|
||||
```
|
||||
{
|
||||
skip_install_trust
|
||||
http_port 9080
|
||||
https_port 9443
|
||||
local_certs
|
||||
}
|
||||
localhost {
|
||||
# without `route`, middlewares are sorted an unstable sort
|
||||
route {
|
||||
vars my_var "2"
|
||||
vars my_var "1"
|
||||
}
|
||||
respond "{vars.my_var}"
|
||||
}
|
||||
```
|
||||
|
||||
# Later vars directives overwrite earlier ones
|
||||
GET https://localhost:9443
|
||||
[Options]
|
||||
insecure: true
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body == "1"
|
||||
2
caddytest/spec/hurl_vars.properties
Normal file
2
caddytest/spec/hurl_vars.properties
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
indexed_root=caddytest/spec/http/file_server/assets/indexed
|
||||
unindexed_root=caddytest/spec/http/file_server/assets/unindexed
|
||||
Loading…
Add table
Add a link
Reference in a new issue