cmd/http-reflect-server/initialization;

develop
Ge Song 2 years ago
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…
Cancel
Save