cmd/http-reflect-server/initialization;
parent
8748299a96
commit
4e1fe211cb
@ -0,0 +1,13 @@
|
|||||||
|
module git.blauwelle.com/go/crate/cmd/http-reflect-server
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.blauwelle.com/go/crate/exegroup v0.3.0
|
||||||
|
git.blauwelle.com/go/crate/log v0.4.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.blauwelle.com/go/crate/runtimehelper v0.1.0 // indirect
|
||||||
|
git.blauwelle.com/go/crate/synchelper v0.1.0 // indirect
|
||||||
|
)
|
@ -0,0 +1,8 @@
|
|||||||
|
git.blauwelle.com/go/crate/exegroup v0.3.0 h1:TBLygDztECKc67NeIIBsFDxlA4KcJpbOmafqqRuKRcM=
|
||||||
|
git.blauwelle.com/go/crate/exegroup v0.3.0/go.mod h1:DJoID54YI5WFHGHoTCjBao8oS3HFRzwbWMZW6P57AIQ=
|
||||||
|
git.blauwelle.com/go/crate/log v0.4.0 h1:wK/qwO+a2YE51F6LdC9pZXL2AIARCRW0+AvFIF2Txt8=
|
||||||
|
git.blauwelle.com/go/crate/log v0.4.0/go.mod h1:NfiG7YKQCTnLIcn6fVkaa2qEu+DuYi1Kz783Sc/F3jI=
|
||||||
|
git.blauwelle.com/go/crate/runtimehelper v0.1.0 h1:qNhtnt9YmHXNHKsGRbwD3AZ3pezpOwrbmX1o9Bz532I=
|
||||||
|
git.blauwelle.com/go/crate/runtimehelper v0.1.0/go.mod h1:yVMA0GkO9AS7iuPmalHKeWyv9en0JWj25rY1vpTuHhk=
|
||||||
|
git.blauwelle.com/go/crate/synchelper v0.1.0 h1:4yEXpshkklaws/57P94xN5bA3NmyyKGcZqYmzd6QIK4=
|
||||||
|
git.blauwelle.com/go/crate/synchelper v0.1.0/go.mod h1:2JkfH+7sF0Q0wiIaDOqG42ZLO5JxpcMfSoyy7db4Y2g=
|
@ -0,0 +1,155 @@
|
|||||||
|
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
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.blauwelle.com/go/crate/exegroup"
|
||||||
|
"git.blauwelle.com/go/crate/log"
|
||||||
|
"git.blauwelle.com/go/crate/log/logsdk"
|
||||||
|
"git.blauwelle.com/go/crate/log/logsdk/logjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
var port = flag.Int("port", 8080, "HTTP Port")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
log.Logger().AddProcessor(logsdk.AllLevels, logjson.New())
|
||||||
|
g := exegroup.Default()
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
var handler http.Handler = mux
|
||||||
|
mux.HandleFunc("/", newHandler())
|
||||||
|
g.New().WithGoStop(exegroup.HttpListenAndServe(*port, handler))
|
||||||
|
log.Infof(context.Background(), "listening %d", *port)
|
||||||
|
log.Error(context.Background(), "exit: ", g.Run(context.Background()))
|
||||||
|
}
|
Loading…
Reference in New Issue