github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/httptransport/route_handler.go (about) 1 package httptransport 2 3 import ( 4 "context" 5 "net/http" 6 "reflect" 7 8 "github.com/pkg/errors" 9 10 "github.com/machinefi/w3bstream/pkg/depends/kit/httptransport/httpx" 11 "github.com/machinefi/w3bstream/pkg/depends/kit/httptransport/transformer" 12 "github.com/machinefi/w3bstream/pkg/depends/kit/metax" 13 "github.com/machinefi/w3bstream/pkg/depends/kit/statusx" 14 "github.com/machinefi/w3bstream/pkg/depends/x/contextx" 15 "github.com/machinefi/w3bstream/pkg/depends/x/typesx" 16 ) 17 18 type RouteHandler struct { 19 *RequestTsfmFactory 20 *HttpRouteMeta 21 meta *ServiceMeta 22 tsfms []*RequestTsfm 23 } 24 25 func NewRouteHandler(sm *ServiceMeta, meta *HttpRouteMeta, rtf *RequestTsfmFactory) *RouteHandler { 26 operatorFactories := meta.Metas 27 28 if len(operatorFactories) == 0 { 29 panic(errors.Errorf("missing valid operator")) 30 } 31 32 rts := make([]*RequestTsfm, len(operatorFactories)) 33 34 for i := range operatorFactories { 35 opFactory := operatorFactories[i] 36 rt, err := rtf.NewRequestTsfm(context.Background(), opFactory.Type) 37 if err != nil { 38 panic(err) 39 } 40 rts[i] = rt 41 } 42 43 return &RouteHandler{ 44 RequestTsfmFactory: rtf, 45 HttpRouteMeta: meta, 46 meta: sm, 47 tsfms: rts, 48 } 49 } 50 51 // ServeHTTP impls http.Handler 52 func (hdl *RouteHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 53 rid := hdl.Metas[len(hdl.Metas)-1].ID 54 55 ctx := r.Context() 56 ctx = ContextWithHttpRequest(ctx, r) 57 ctx = ContextWithServiceMeta(ctx, *hdl.meta) 58 ctx = ContextWithRouteMetaID(ctx, rid) 59 60 // trace span, use operator as span name 61 operator := []string{hdl.meta.String() + "/" + rid} 62 ctx = metax.ContextWithMeta(ctx, metax.Meta{"operator": operator}) 63 64 rw.Header().Set("X-Meta", operator[0]) 65 66 ri := httpx.NewRequestInfo(r) 67 68 for i := range hdl.Metas { 69 m := hdl.Metas[i] 70 if m.NoOutput { 71 continue 72 } 73 op := m.New() 74 75 ctx = ContextWithOperatorFactory(ctx, m.OperatorFactory) 76 77 rt := hdl.tsfms[i] 78 if rt != nil { 79 err := rt.DecodeAndValidate(ctx, ri, op) 80 if err != nil { 81 hdl.writeErr(rw, r, err) 82 return 83 } 84 } 85 86 result, err := op.Output(ctx) 87 88 if err != nil { 89 hdl.writeErr(rw, r, err) 90 return 91 } 92 93 if !m.IsLast { 94 if c, ok := result.(context.Context); ok { 95 ctx = c 96 } else { 97 // set result in context with key of operator name 98 ctx = contextx.WithValue(ctx, m.ContextKey, result) 99 } 100 continue 101 } 102 hdl.write(rw, r, result) 103 } 104 } 105 106 // resolve httpx.Encode (httpx.ResolveEncode) 107 func (hdl *RouteHandler) resolve(rsp *httpx.Response) (httpx.Encode, error) { 108 tsfm, err := hdl.Tsfm.NewTransformer( 109 context.Background(), 110 typesx.FromReflectType(reflect.TypeOf(rsp.Value)), 111 transformer.Option{MIME: rsp.ContentType}, 112 ) 113 if err != nil { 114 return nil, err 115 } 116 return tsfm.EncodeTo, nil 117 } 118 119 // write response 120 func (hdl *RouteHandler) write(rw http.ResponseWriter, r *http.Request, rsp interface{}) { 121 err := httpx.ResponseFrom(rsp).WriteTo(rw, r, hdl.resolve) 122 if err != nil { 123 hdl.writeErr(rw, r, err) 124 } 125 } 126 127 func (hdl *RouteHandler) writeErr(rw http.ResponseWriter, r *http.Request, err error) { 128 rsp, ok := err.(*httpx.Response) 129 if !ok { 130 rsp = httpx.ResponseFrom(err) 131 } 132 133 if se, ok := statusx.IsStatusErr(rsp.Unwrap()); ok { 134 err := se.AppendSource(hdl.meta.String()) 135 136 if rwe, ok := rw.(ResponseWithError); ok { 137 rwe.WriteError(err) 138 } 139 140 rsp.Value = err 141 } 142 143 errw := rsp.WriteTo(rw, r, hdl.resolve) 144 if errw != nil { 145 rw.WriteHeader(http.StatusInternalServerError) 146 _, _ = rw.Write([]byte("write err failed:" + errw.Error())) 147 } 148 } 149 150 type ResponseWithError interface { 151 WriteError(err error) 152 }