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  }