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