github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/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 "github.com/johnnyeven/libtools/courier" 16 "github.com/johnnyeven/libtools/courier/httpx" 17 "github.com/johnnyeven/libtools/duration" 18 logContext "github.com/johnnyeven/libtools/log/context" 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 fields := logrus.Fields{ 52 "tag": "access", 53 "log_id": reqID, 54 "remote_ip": GetClientIP(r), 55 "method": r.Method, 56 "pathname": r.URL.Path, 57 "user_agent": r.Header.Get(httpx.HeaderUserAgent), 58 } 59 60 fields["status"] = codeWriter.Code 61 fields["request_time"] = d.Get() 62 63 logger := logrus.WithFields(fields) 64 65 if err != nil { 66 if codeWriter.Code >= http.StatusInternalServerError { 67 logger.Errorf(err.Error()) 68 } else { 69 logger.Warnf(err.Error()) 70 } 71 } else { 72 logger.Infof("") 73 } 74 }() 75 76 opDecode := createHttpRequestDecoder(r, ¶ms) 77 78 for _, opMeta := range operatorMetas { 79 op, decodeErr := courier.NewOperatorBy(opMeta.Type, opMeta.Operator, opDecode) 80 if decodeErr != nil { 81 err = encodeHttpError(ctx, codeWriter, r, decodeErr) 82 return 83 } 84 85 response, endpointErr := op.Output(ctx) 86 if canCookie, ok := op.(httpx.ICookie); ok { 87 cookie := canCookie.Cookies() 88 if cookie != nil { 89 http.SetCookie(w, cookie) 90 } 91 } 92 93 if endpointErr != nil { 94 err = encodeHttpError(ctx, codeWriter, r, endpointErr) 95 return 96 } 97 98 if !opMeta.IsLast { 99 // set result in context with key of operator name 100 ctx = context.WithValue(ctx, opMeta.ContextKey, response) 101 continue 102 } 103 104 if ws, ok := response.(IWebSocket); ok { 105 conn, errForUpgrade := (&websocket.Upgrader{}).Upgrade(codeWriter, r, nil) 106 if errForUpgrade != nil { 107 err = errForUpgrade 108 return 109 } 110 ws.SubscribeOn(conn) 111 return 112 } 113 114 encodeErr := encodeHttpResponse(ctx, codeWriter, r, response) 115 if encodeErr != nil { 116 err = encodeHttpError(ctx, codeWriter, r, encodeErr) 117 return 118 } 119 } 120 } 121 } 122 123 var ProjectRef = os.Getenv("PROJECT_REF") 124 125 var ( 126 ContextKeyServerName = uuid.New().String() 127 ContextKeyRequest = uuid.New().String() 128 ContextKeyOperators = uuid.New().String() 129 ) 130 131 func ContextWithServiceName(ctx context.Context, serverName string) context.Context { 132 return context.WithValue(ctx, ContextKeyServerName, serverName) 133 } 134 135 func ContextWithOperators(ctx context.Context, ops ...courier.IOperator) context.Context { 136 return context.WithValue(ctx, ContextKeyOperators, ops) 137 } 138 139 func GetOperators(ctx context.Context) []courier.IOperator { 140 return ctx.Value(ContextKeyOperators).([]courier.IOperator) 141 } 142 143 func ContextWithRequest(ctx context.Context, req *http.Request) context.Context { 144 return context.WithValue(ctx, ContextKeyRequest, req) 145 } 146 147 func GetRequest(ctx context.Context) *http.Request { 148 return ctx.Value(ContextKeyRequest).(*http.Request) 149 } 150 151 type ResponseWriter struct { 152 http.ResponseWriter 153 Code int 154 written int64 155 } 156 157 func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 158 return w.ResponseWriter.(http.Hijacker).Hijack() 159 } 160 161 func (w *ResponseWriter) WriteHeader(code int) { 162 w.Code = code 163 w.ResponseWriter.WriteHeader(code) 164 } 165 166 func (w *ResponseWriter) Write(p []byte) (int, error) { 167 n, err := w.ResponseWriter.Write(p) 168 w.written += int64(n) 169 return n, err 170 }