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, &params)
    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  }