You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
3.9 KiB
Go
156 lines
3.9 KiB
Go
2 years ago
|
package main
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"git.blauwelle.com/go/crate/log"
|
||
|
)
|
||
|
|
||
|
func newHandler() http.HandlerFunc {
|
||
|
return func(rw http.ResponseWriter, r *http.Request) {
|
||
|
start := time.Now()
|
||
|
|
||
|
var response Response
|
||
|
|
||
|
response.Request.TransferEncoding = r.TransferEncoding
|
||
|
response.Request.Proto = r.Proto
|
||
|
response.Request.Host = r.Host
|
||
|
response.Request.Method = r.Method
|
||
|
response.Request.URL = r.URL.String()
|
||
|
response.Request.RemoteAddr = r.RemoteAddr
|
||
|
response.Request.RequestURI = r.RequestURI
|
||
|
response.Request.Header = r.Header
|
||
|
response.Request.ContentLength = r.ContentLength
|
||
|
|
||
|
ctx := r.Context()
|
||
|
|
||
|
duration := time.Since(start)
|
||
|
code := http.StatusOK
|
||
|
var message string
|
||
|
|
||
|
if r.ContentLength > 0 {
|
||
|
if body, err := readBody(ctx, r); err != nil {
|
||
|
response.Error = err.Error()
|
||
|
} else {
|
||
|
response.Request.Body = body
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rw.Header().Set("Content-Type", "application/json")
|
||
|
rw.WriteHeader(code)
|
||
|
|
||
|
end := time.Now()
|
||
|
response.ServeDuration = end.Sub(start).String()
|
||
|
encoder := json.NewEncoder(rw)
|
||
|
encoder.SetEscapeHTML(true)
|
||
|
encoder.SetIndent("", " ")
|
||
|
err := encoder.Encode(response)
|
||
|
if err != nil {
|
||
|
log.Errorf(ctx, "json marshal: %s", err.Error())
|
||
|
}
|
||
|
|
||
|
log.WithFields(
|
||
|
log.Field("code", code),
|
||
|
log.Field("duration", duration.String()),
|
||
|
).Info(ctx, message)
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type Response struct {
|
||
|
Error string `json:"error,omitempty"`
|
||
|
ServeDuration string `json:"serveDuration"`
|
||
|
Request ResponseRequest `json:"request"`
|
||
|
}
|
||
|
|
||
|
type MultipartFormFileInfo struct {
|
||
|
MIMEHeader map[string][]string `json:"mimeHeader"`
|
||
|
Filename string `json:"filename"`
|
||
|
Size int64 `json:"size"`
|
||
|
}
|
||
|
|
||
|
type MultipartForm struct {
|
||
|
Values map[string][]string `json:"values"`
|
||
|
Files map[string][]MultipartFormFileInfo `json:"files"`
|
||
|
}
|
||
|
|
||
|
type ResponseRequest struct {
|
||
|
Header http.Header `json:"header"`
|
||
|
Body any `json:"body"`
|
||
|
Proto string `json:"proto"`
|
||
|
Host string `json:"host"`
|
||
|
Method string `json:"method"`
|
||
|
URL string `json:"url"`
|
||
|
RemoteAddr string `json:"remoteAddr"`
|
||
|
RequestURI string `json:"requestUri"`
|
||
|
TransferEncoding []string `json:"transferEncoding"`
|
||
|
ContentLength int64 `json:"contentLength"`
|
||
|
}
|
||
|
|
||
|
func readBody(ctx context.Context, r *http.Request) (any, error) {
|
||
|
contentType := r.Header.Get("Content-Type")
|
||
|
switch {
|
||
|
case contentType == "":
|
||
|
return nil, nil
|
||
|
case strings.HasPrefix(contentType, "application/json"):
|
||
|
b, err := io.ReadAll(r.Body)
|
||
|
if err != nil {
|
||
|
err = fmt.Errorf("read: %w", err)
|
||
|
log.Error(ctx, err.Error())
|
||
|
return nil, err
|
||
|
}
|
||
|
if err := json.Unmarshal(b, new(any)); err != nil {
|
||
|
log.Error(ctx, err.Error())
|
||
|
return nil, err
|
||
|
}
|
||
|
return json.RawMessage(b), nil
|
||
|
case contentType == "application/x-www-form-urlencoded":
|
||
|
fallthrough
|
||
|
case strings.HasPrefix(contentType, "application/xml"):
|
||
|
fallthrough
|
||
|
case strings.HasPrefix(contentType, "text/"):
|
||
|
b, err := io.ReadAll(r.Body)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return string(b), nil
|
||
|
//case :
|
||
|
case strings.HasPrefix(contentType, "multipart/form-data"):
|
||
|
if err := r.ParseMultipartForm(16 * 1024 * 1024); err != nil {
|
||
|
log.Error(ctx, err.Error())
|
||
|
return nil, err
|
||
|
}
|
||
|
body := MultipartForm{
|
||
|
Values: make(map[string][]string),
|
||
|
Files: make(map[string][]MultipartFormFileInfo),
|
||
|
}
|
||
|
body.Values = r.MultipartForm.Value
|
||
|
for k, fs := range r.MultipartForm.File {
|
||
|
for _, f := range fs {
|
||
|
body.Files[k] = append(body.Files[k], MultipartFormFileInfo{
|
||
|
Filename: f.Filename,
|
||
|
MIMEHeader: f.Header,
|
||
|
Size: f.Size,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
return body, nil
|
||
|
}
|
||
|
b, err := io.ReadAll(r.Body)
|
||
|
if err != nil {
|
||
|
err = fmt.Errorf("read: %w", err)
|
||
|
log.Error(ctx, err.Error())
|
||
|
return nil, err
|
||
|
}
|
||
|
if len(b) > 96 {
|
||
|
b = b[:96]
|
||
|
}
|
||
|
return b, nil
|
||
|
}
|