123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- package api
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "os"
- "time"
- "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/errs"
- "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/sse"
- context "github.com/kataras/iris/v12/context"
- "go.mongodb.org/mongo-driver/bson/primitive"
- )
- type DebugStage struct {
- DebugEvent `json:",inline"`
- Events []*DebugEvent `json:"events"`
- }
- type DebugEvent struct {
- ID string `json:"id"`
- Type string `json:"type"`
- Status string `json:"status"`
- Created int64 `json:"created"`
- Error *errs.Error `json:"error"`
- Data interface{} `json:"data"`
- }
- type Request struct {
- ID string `json:"id"`
- Status string `json:"status"`
- Created int64 `json:"created"`
- }
- type DebugTaks struct {
- ID string `json:"id"`
- Status string `json:"status"`
- Created int64 `json:"created"`
- Stages []*DebugStage `json:"stages"`
- CurrentStage *DebugStage `json:"-"`
- Debug *Debugger `json:"-"`
- Request interface{} `json:"request"`
- }
- func NewDebugTaks() *DebugTaks {
- return &DebugTaks{
- Stages: []*DebugStage{},
- CurrentStage: &DebugStage{},
- }
- }
- func (debug *DebugTaks) Stage(id string) *DebugStage {
- stage := &DebugStage{}
- stage.ID = id
- stage.Events = []*DebugEvent{}
- debug.Stages = append(debug.Stages, stage)
- debug.CurrentStage = stage
- return stage
- }
- func (stage *DebugStage) PushEvent(event *DebugEvent) {
- stage.Events = append(stage.Events, event)
- }
- func (debug *DebugTaks) Event(eventType, eventId string) *DebugEvent {
- event := &DebugEvent{
- ID: eventId,
- Type: eventType,
- Created: time.Now().Unix(),
- }
- debug.CurrentStage.PushEvent(event)
- return event
- }
- func (task *DebugTaks) Finalize() {
- var (
- debug = task.Debug
- out []byte
- err error
- )
- for _, stage := range task.Stages {
- for _, event := range stage.Events {
- if event.Error != nil {
- event.Status = "error"
- stage.Status = "error"
- task.Status = "error"
- goto end
- }
- }
- }
- // task.Event()
- end:
- if out, err = json.Marshal(task); err != nil {
- debug.Emit(&sse.Event{
- Kind: "debugger.error",
- Payload: err.Error(),
- })
- } else {
- debug.Emit(&sse.Event{
- Kind: "request",
- Payload: string(out),
- })
- }
- }
- type Debugger struct {
- ChannelID string
- Tasks []*DebugTaks
- Hub *sse.SSEHub
- }
- func (debug *Debugger) Handler() func(context.Context) {
- return func(ctx context.Context) {
- var err error
- task := debug.CreateTask()
- if task.Request, err = parseRequest(ctx.Request()); err != nil {
- fmt.Println("Request debug error", err.Error())
- }
- ctx.Values().Set("#debug", task)
- ctx.Next()
- }
- }
- func (debug *Debugger) CreateTask() *DebugTaks {
- task := &DebugTaks{
- ID: primitive.NewObjectID().Hex(),
- Status: "",
- Created: time.Now().Unix(),
- Debug: debug,
- }
- debug.Tasks = append([]*DebugTaks{task}, debug.Tasks...)
- return task
- }
- func (debug *Debugger) Emit(events ...*sse.Event) {
- channel := debug.Hub.GetChannel(debug.ChannelID)
- for _, event := range events {
- channel.Emit(event)
- }
- }
- func (debug *Debugger) EventStream() func(context.Context) (interface{}, *errs.Error) {
- return func(ctx context.Context) (resp interface{}, err *errs.Error) {
- go func() {
- if len(debug.Tasks) > 0 {
- time.Sleep(time.Second)
- fmt.Println("initialize debug with ", len(debug.Tasks))
- for _, task := range debug.Tasks[:10] {
- task.Finalize()
- }
- debug.Tasks = debug.Tasks[:10]
- }
- }()
- if err = debug.Hub.UpgradeConnection(
- ctx,
- debug.ChannelID,
- ); err != nil {
- return
- }
- resp = true
- return
- }
- }
- func NewDebug() *Debugger {
- return &Debugger{
- ChannelID: "debug",
- Tasks: []*DebugTaks{},
- Hub: sse.NewSSEHub(&sse.SSEOptions{
- URI: os.Getenv("REDIS_URI"),
- Password: os.Getenv("REDIS_PASSWD"),
- ChannelCollection: "debugger",
- }),
- }
- }
- func parseRequest(req *http.Request) (result map[string]interface{}, err error) {
- save := req.Body
- defer func() {
- req.Body = save
- }()
- result = map[string]interface{}{}
- // if !body || req.Body == nil {
- // req.Body = nil
- // } else {
- // save, req.Body, err = drainBody(req.Body)
- // if err != nil {
- // return nil, err
- // }
- // }
- var (
- reqURI string
- reqMethod string
- )
- if reqURI = req.RequestURI; reqURI == "" {
- reqURI = req.URL.RequestURI()
- }
- if reqMethod = req.Method; req.Method == "" {
- reqMethod = "GET"
- }
- result["URI"] = reqURI
- result["URL"] = map[string]interface{}{
- "Scheme": req.URL.Scheme,
- "Host": req.URL.Hostname(),
- "IsAbs": req.URL.IsAbs(),
- "EscapedPath": req.URL.EscapedPath(),
- "Port": req.URL.Port(),
- "Uri": req.URL.String(),
- "query": req.URL.Query(),
- }
- result["Method"] = reqMethod
- result["ProtoMajor"] = req.ProtoMajor
- result["ProtoMinor"] = req.ProtoMinor
- result["Proto"] = req.Proto
- result["RemoteAddr"] = req.RemoteAddr
- result["Header"] = req.Header
- result["Host"] = req.Host
- result["Close"] = req.Close
- result["ContentLength"] = req.ContentLength
- // for name, value := range req.Header {
- // fmt.Printf("%v: %v\n", name, value)
- // }
- // fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
- // reqURI, req.ProtoMajor, req.ProtoMinor)
- // absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://")
- // if !absRequestURI {
- // host := req.Host
- // if host == "" && req.URL != nil {
- // host = req.URL.Host
- // }
- // if host != "" {
- // fmt.Fprintf(&b, "Host: %s\r\n", host)
- // }
- // }
- // chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked"
- // if len(req.TransferEncoding) > 0 {
- // fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ","))
- // }
- // if req.Close {
- // fmt.Fprintf(&b, "Connection: close\r\n")
- // }
- // err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
- // if err != nil {
- // return nil, err
- // }
- // io.WriteString(&b, "\r\n")
- // if req.Body != nil {
- // var dest io.Writer = &b
- // if chunked {
- // dest = NewChunkedWriter(dest)
- // }
- // _, err = io.Copy(dest, req.Body)
- // if chunked {
- // dest.(io.Closer).Close()
- // io.WriteString(&b, "\r\n")
- // }
- // }
- // req.Body = save
- // if err != nil {
- // return nil, err
- // }
- // return b.Bytes(), nil
- return
- }
|