Files

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)
}
})
}
}
}