mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
package servermock
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
)
|
|
|
|
// RequestBodyLink represents a handler utility to validate HTTP request bodies against a predefined byte slice.
|
|
type RequestBodyLink struct {
|
|
body []byte
|
|
filename string
|
|
ignoreWhitespace bool
|
|
}
|
|
|
|
// CheckRequestBody creates a [RequestBodyLink] initialized with the provided request body string.
|
|
func CheckRequestBody(body string) *RequestBodyLink {
|
|
return &RequestBodyLink{body: []byte(body)}
|
|
}
|
|
|
|
// CheckRequestBodyFromFile creates a [RequestBodyLink] initialized with the provided request body file.
|
|
func CheckRequestBodyFromFile(filename string) *RequestBodyLink {
|
|
return &RequestBodyLink{filename: filename}
|
|
}
|
|
|
|
// CheckRequestBodyFromFixture creates a [RequestBodyLink] initialized with the provided request body file from the `fixtures` directory.
|
|
func CheckRequestBodyFromFixture(filename string) *RequestBodyLink {
|
|
return CheckRequestBodyFromFile(filepath.Join("fixtures", filename))
|
|
}
|
|
|
|
// CheckRequestBodyFromInternal creates a [RequestBodyLink] initialized with the provided request body file from the `internal/fixtures directory.
|
|
func CheckRequestBodyFromInternal(filename string) *RequestBodyLink {
|
|
return CheckRequestBodyFromFile(filepath.Join("internal", "fixtures", filename))
|
|
}
|
|
|
|
func (l *RequestBodyLink) Bind(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
if req.ContentLength == 0 {
|
|
http.Error(rw, fmt.Sprintf("%s: empty request body", req.URL.Path), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
body, err := io.ReadAll(req.Body)
|
|
if err != nil {
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
_ = req.Body.Close()
|
|
|
|
expectedRaw := slices.Clone(l.body)
|
|
|
|
if l.filename != "" {
|
|
expectedRaw, err = os.ReadFile(l.filename)
|
|
if err != nil {
|
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(expectedRaw) == 0 {
|
|
http.Error(rw, fmt.Sprintf("%s: empty expected request body", req.URL.Path), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if l.ignoreWhitespace {
|
|
body = trimLineSpace(body)
|
|
expectedRaw = trimLineSpace(expectedRaw)
|
|
}
|
|
|
|
if !bytes.Equal(bytes.TrimSpace(expectedRaw), bytes.TrimSpace(body)) {
|
|
msg := fmt.Sprintf("%s: request body differences: got: %s, want: %s", req.URL.Path,
|
|
string(bytes.TrimSpace(body)), string(bytes.TrimSpace(expectedRaw)))
|
|
http.Error(rw, msg, http.StatusBadRequest)
|
|
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(rw, req)
|
|
})
|
|
}
|
|
|
|
func (l *RequestBodyLink) IgnoreWhitespace() *RequestBodyLink {
|
|
l.ignoreWhitespace = true
|
|
|
|
return l
|
|
}
|
|
|
|
func trimLineSpace(body []byte) []byte {
|
|
buf := bytes.NewBuffer(nil)
|
|
for line := range bytes.Lines(body) {
|
|
buf.Write(bytes.TrimSpace(line))
|
|
}
|
|
|
|
return buf.Bytes()
|
|
}
|