mirror of
https://github.com/caddyserver/caddy.git
synced 2026-10-31 08:21:39 -04:00
90 lines
2.8 KiB
Go
90 lines
2.8 KiB
Go
package fastcgi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/http/fcgi"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/caddyserver/caddy/v2"
|
|
)
|
|
|
|
func TestRequestTrailersFastCGI(t *testing.T) {
|
|
backend, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
done := make(chan error, 1)
|
|
go func() {
|
|
done <- fcgi.Serve(backend, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
t.Error(err)
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
// FastCGI carries a length and stdin bytes, not HTTP trailers.
|
|
if len(r.Trailer) != 0 || len(r.TransferEncoding) != 0 {
|
|
t.Errorf("unexpected HTTP framing: %v %v", r.Trailer, r.TransferEncoding)
|
|
}
|
|
w.Header().Set("X-Body-Length", fmt.Sprint(r.ContentLength))
|
|
_, _ = w.Write(body)
|
|
}))
|
|
}()
|
|
defer func() { backend.Close(); <-done }()
|
|
reserve, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addr := reserve.Addr().String()
|
|
reserve.Close()
|
|
config, err := json.Marshal(map[string]any{
|
|
"admin": map[string]any{"disabled": true},
|
|
"apps": map[string]any{"http": map[string]any{"servers": map[string]any{"test": map[string]any{
|
|
"listen": []string{addr}, "automatic_https": map[string]any{"disable": true},
|
|
"routes": []any{map[string]any{"handle": []any{map[string]any{
|
|
"handler": "reverse_proxy", "upstreams": []any{map[string]any{"dial": backend.Addr().String()}},
|
|
"transport": map[string]any{"protocol": "fastcgi", "root": "/", "split_path": []string{".php"}},
|
|
}}}},
|
|
}}}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Omit request_buffers so provisioning applies FastCGI's default 4096-byte buffer.
|
|
if err := caddy.Load(config, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer caddy.Stop()
|
|
client := &http.Client{Transport: &http.Transport{}, Timeout: 5 * time.Second}
|
|
defer client.CloseIdleConnections()
|
|
for _, body := range []string{"", "hello", strings.Repeat("x", 4095)} {
|
|
for _, trailers := range []bool{false, true} {
|
|
t.Run(fmt.Sprintf("length=%d/trailers=%t", len(body), trailers), func(t *testing.T) {
|
|
req, err := http.NewRequest(http.MethodPost, "http://"+addr+"/index.php", io.NopCloser(strings.NewReader(body)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req.ContentLength = -1
|
|
if trailers {
|
|
req.Trailer = http.Header{"Content-Digest": {"digest"}}
|
|
}
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := io.ReadAll(res.Body)
|
|
res.Body.Close()
|
|
if err != nil || res.StatusCode != 200 || string(got) != body || res.Header.Get("X-Body-Length") != fmt.Sprint(len(body)) {
|
|
t.Fatalf("status=%d length=%q body bytes=%d (want %d), error=%v", res.StatusCode, res.Header.Get("X-Body-Length"), len(got), len(body), err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|