github.com/profzone/eden-framework@v1.0.10/pkg/courier/transport_http/transport.go (about) 1 package transport_http 2 3 import ( 4 "bufio" 5 "context" 6 "net" 7 "net/http" 8 "os" 9 10 "github.com/google/uuid" 11 "github.com/gorilla/websocket" 12 "github.com/julienschmidt/httprouter" 13 "github.com/sirupsen/logrus" 14 15 logContext "github.com/profzone/eden-framework/pkg/context" 16 "github.com/profzone/eden-framework/pkg/courier" 17 "github.com/profzone/eden-framework/pkg/courier/httpx" 18 "github.com/profzone/eden-framework/pkg/duration" 19 ) 20 21 func CreateHttpHandler(s *ServeHTTP, ops ...courier.IOperator) httprouter.Handle { 22 operatorMetas := courier.ToOperatorMetaList(ops...) 23 24 return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) { 25 var err error 26 ctx := r.Context() 27 ctx = ContextWithServiceName(ctx, s.Name) 28 ctx = ContextWithOperators(ctx, ops...) 29 ctx = ContextWithRequest(ctx, r) 30 31 codeWriter := &ResponseWriter{ 32 ResponseWriter: w, 33 Code: 0, 34 written: -1, 35 } 36 37 w.Header().Set("X-Reversion", ProjectRef) 38 39 reqID := r.Header.Get(httpx.HeaderRequestID) 40 41 if reqID == "" { 42 reqID = uuid.New().String() 43 } 44 45 logContext.SetLogID(reqID) 46 defer logContext.Close() 47 48 d := duration.NewDuration() 49 50 defer func() { 51 if r.URL.Path == "/healthz" { 52 return 53 } 54 fields := logrus.Fields{ 55 "tag": "access", 56 "log_id": reqID, 57 "remote_ip": GetClientIP(r), 58 "method": r.Method, 59 "pathname": r.URL.Path, 60 "user_agent": r.Header.Get(httpx.HeaderUserAgent), 61 } 62 63 fields["status"] = codeWriter.Code 64 fields["request_time"] = d.Get() 65 66 logger := logrus.WithFields(fields) 67 68 if err != nil { 69 if codeWriter.Code >= http.StatusInternalServerError { 70 logger.Errorf(err.Error()) 71 } else { 72 logger.Warnf(err.Error()) 73 } 74 } else { 75 logger.Infof("") 76 } 77 }() 78 79 opDecode := createHttpRequestDecoder(r, ¶ms) 80 81 for _, opMeta := range operatorMetas { 82 op, decodeErr := courier.NewOperatorBy(opMeta.Type, opMeta.Operator, opDecode) 83 if decodeErr != nil { 84 err = encodeHttpError(ctx, codeWriter, r, decodeErr) 85 return 86 } 87 88 response, endpointErr := op.Output(ctx) 89 if canCookie, ok := op.(httpx.ICookie); ok { 90 cookie := canCookie.Cookies() 91 if cookie != nil { 92 http.SetCookie(w, cookie) 93 } 94 } 95 96 if endpointErr != nil { 97 err = encodeHttpError(ctx, codeWriter, r, endpointErr) 98 return 99 } 100 101 if !opMeta.IsLast { 102 // set result in context with key of operator name 103 ctx = context.WithValue(ctx, opMeta.ContextKey, response) 104 continue 105 } 106 107 if ws, ok := response.(IWebSocket); ok { 108 conn, errForUpgrade := (&websocket.Upgrader{}).Upgrade(codeWriter, r, nil) 109 if errForUpgrade != nil { 110 err = errForUpgrade 111 return 112 } 113 ws.SubscribeOn(conn) 114 return 115 } 116 117 encodeErr := encodeHttpResponse(ctx, codeWriter, r, response) 118 if encodeErr != nil { 119 err = encodeHttpError(ctx, codeWriter, r, encodeErr) 120 return 121 } 122 } 123 } 124 } 125 126 var ProjectRef = os.Getenv("PROJECT_REF") 127 128 var ( 129 ContextKeyServerName = uuid.New().String() 130 ContextKeyRequest = uuid.New().String() 131 ContextKeyOperators = uuid.New().String() 132 ) 133 134 func ContextWithServiceName(ctx context.Context, serverName string) context.Context { 135 return context.WithValue(ctx, ContextKeyServerName, serverName) 136 } 137 138 func ContextWithOperators(ctx context.Context, ops ...courier.IOperator) context.Context { 139 return context.WithValue(ctx, ContextKeyOperators, ops) 140 } 141 142 func GetOperators(ctx context.Context) []courier.IOperator { 143 return ctx.Value(ContextKeyOperators).([]courier.IOperator) 144 } 145 146 func ContextWithRequest(ctx context.Context, req *http.Request) context.Context { 147 return context.WithValue(ctx, ContextKeyRequest, req) 148 } 149 150 func GetRequest(ctx context.Context) *http.Request { 151 return ctx.Value(ContextKeyRequest).(*http.Request) 152 } 153 154 type ResponseWriter struct { 155 http.ResponseWriter 156 Code int 157 written int64 158 } 159 160 func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 161 return w.ResponseWriter.(http.Hijacker).Hijack() 162 } 163 164 func (w *ResponseWriter) WriteHeader(code int) { 165 w.Code = code 166 w.ResponseWriter.WriteHeader(code) 167 } 168 169 func (w *ResponseWriter) Write(p []byte) (int, error) { 170 n, err := w.ResponseWriter.Write(p) 171 w.written += int64(n) 172 return n, err 173 }