From ce53ad1d60a2774cfc56b78251d37f255ab75418 Mon Sep 17 00:00:00 2001 From: agungdwiprasetyo Date: Fri, 17 Mar 2023 16:23:05 +0700 Subject: [PATCH] improve root middleware gql & rest server --- candishared/http_handler.go | 38 ----------- codebase/app/graphql_server/graphql_server.go | 6 +- codebase/app/graphql_server/option.go | 11 ++-- codebase/app/graphql_server/tracer.go | 4 +- codebase/app/rest_server/option.go | 11 ++-- codebase/app/rest_server/rest_server.go | 4 +- .../appfactory/setup_graphql_server.go | 4 -- .../factory/appfactory/setup_rest_server.go | 4 -- init.go | 2 +- wrapper/http_handler.go | 65 +++++++++++++++++-- wrapper/http_response_writer.go | 8 +-- 11 files changed, 86 insertions(+), 71 deletions(-) delete mode 100644 candishared/http_handler.go diff --git a/candishared/http_handler.go b/candishared/http_handler.go deleted file mode 100644 index 4ccdb89a..00000000 --- a/candishared/http_handler.go +++ /dev/null @@ -1,38 +0,0 @@ -package candishared - -import ( - "encoding/json" - "fmt" - "net/http" - "runtime" - "time" -) - -// HTTPRoot http handler -func HTTPRoot(serviceName, buildNumber string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - payload := map[string]string{ - "message": fmt.Sprintf("Service %s up and running", serviceName), - "timestamp": time.Now().Format(time.RFC3339Nano), - } - if buildNumber != "" { - payload["build_number"] = buildNumber - } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(payload) - } -} - -// HTTPMemstatsHandler calculate runtime statistic -func HTTPMemstatsHandler(w http.ResponseWriter, r *http.Request) { - var m runtime.MemStats - runtime.ReadMemStats(&m) - data := struct { - NumGoroutine int `json:"num_goroutine"` - Memstats interface{} `json:"memstats"` - }{ - runtime.NumGoroutine(), m, - } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(data) -} diff --git a/codebase/app/graphql_server/graphql_server.go b/codebase/app/graphql_server/graphql_server.go index b375ae23..66799c90 100644 --- a/codebase/app/graphql_server/graphql_server.go +++ b/codebase/app/graphql_server/graphql_server.go @@ -20,6 +20,7 @@ import ( "github.com/golangid/candi/codebase/factory/types" "github.com/golangid/candi/logger" "github.com/golangid/candi/tracer" + "github.com/golangid/candi/wrapper" graphql "github.com/golangid/graphql-go" gqltypes "github.com/golangid/graphql-go/types" @@ -44,6 +45,7 @@ func NewServer(service factory.ServiceFactory, opts ...OptionFunc) factory.AppSe httpEngine := new(http.Server) server := &graphqlServer{ httpEngine: httpEngine, + opt: getDefaultOption(), } for _, opt := range opts { opt(&server.opt) @@ -53,7 +55,7 @@ func NewServer(service factory.ServiceFactory, opts ...OptionFunc) factory.AppSe mux := http.NewServeMux() mux.Handle("/", server.opt.rootHandler) - mux.HandleFunc("/memstats", candishared.HTTPMemstatsHandler) + mux.Handle("/memstats", service.GetDependency().GetMiddleware().HTTPBasicAuth(http.HandlerFunc(wrapper.HTTPHandlerMemstats))) mux.HandleFunc(server.opt.rootPath+rootGraphQLPath, httpHandler.ServeGraphQL()) mux.HandleFunc(server.opt.rootPath+rootGraphQLPlayground, httpHandler.ServePlayground) mux.HandleFunc(server.opt.rootPath+rootGraphQLVoyager, httpHandler.ServeVoyager) @@ -67,7 +69,7 @@ func NewServer(service factory.ServiceFactory, opts ...OptionFunc) factory.AppSe fmt.Printf("\x1b[34;1m⇨ GraphQL HTTP server run at port [::]%s\x1b[0m\n\n", httpEngine.Addr) if server.opt.sharedListener != nil { - server.listener = server.opt.sharedListener.Match(cmux.HTTP1Fast()) + server.listener = server.opt.sharedListener.Match(cmux.HTTP1Fast(http.MethodPatch)) } return server diff --git a/codebase/app/graphql_server/option.go b/codebase/app/graphql_server/option.go index da08741b..de02b052 100644 --- a/codebase/app/graphql_server/option.go +++ b/codebase/app/graphql_server/option.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" + "github.com/golangid/candi/wrapper" "github.com/golangid/graphql-go/types" "github.com/soheilhy/cmux" ) @@ -29,12 +30,10 @@ type ( func getDefaultOption() Option { return Option{ - httpPort: ":8000", - rootPath: "", - debugMode: true, - rootHandler: http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte("REST Server up and running")) - }), + httpPort: ":8000", + rootPath: "", + debugMode: true, + rootHandler: http.HandlerFunc(wrapper.HTTPHandlerDefaultRoot), } } diff --git a/codebase/app/graphql_server/tracer.go b/codebase/app/graphql_server/tracer.go index 7df99d7f..584051eb 100644 --- a/codebase/app/graphql_server/tracer.go +++ b/codebase/app/graphql_server/tracer.go @@ -43,7 +43,9 @@ func (t *graphqlTracer) TraceQuery(ctx context.Context, queryString string, oper trace, ctx := tracer.StartTraceFromHeader(ctx, strings.TrimSuffix(fmt.Sprintf("GraphQL-Root:%s", operationName), ":"), header) - trace.SetTag("graphql.operationName", operationName) + if operationName != "" { + trace.SetTag("graphql.operationName", operationName) + } if len(headers) > 0 { trace.Log("http.headers", headers) } diff --git a/codebase/app/rest_server/option.go b/codebase/app/rest_server/option.go index e7a626bf..511010d5 100644 --- a/codebase/app/rest_server/option.go +++ b/codebase/app/rest_server/option.go @@ -42,12 +42,13 @@ func getDefaultOption() option { env.BaseEnv().CORSAllowMethods, env.BaseEnv().CORSAllowHeaders, env.BaseEnv().CORSAllowOrigins, nil, env.BaseEnv().CORSAllowCredential, )), - EchoWrapMiddleware(wrapper.HTTPMiddlewareTracer(env.BaseEnv().JaegerMaxPacketSize)), + EchoWrapMiddleware(wrapper.HTTPMiddlewareTracer(wrapper.HTTPMiddlewareTracerConfig{ + MaxLogSize: env.BaseEnv().JaegerMaxPacketSize, + ExcludePath: map[string]struct{}{"/": {}, "/graphql": {}}, + })), EchoLoggerMiddleware(env.BaseEnv().DebugMode, os.Stdout), }, - rootHandler: http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte("REST Server up and running")) - }), + rootHandler: http.HandlerFunc(wrapper.HTTPHandlerDefaultRoot), errorHandler: CustomHTTPErrorHandler, } } @@ -115,7 +116,7 @@ func SetRootMiddlewares(middlewares ...echo.MiddlewareFunc) OptionFunc { } } -// AddRootMiddlewares option func +// AddRootMiddlewares option func, overide root middleware func AddRootMiddlewares(middlewares ...echo.MiddlewareFunc) OptionFunc { return func(o *option) { o.rootMiddlewares = append(o.rootMiddlewares, middlewares...) diff --git a/codebase/app/rest_server/rest_server.go b/codebase/app/rest_server/rest_server.go index 519eb872..424c4447 100644 --- a/codebase/app/rest_server/rest_server.go +++ b/codebase/app/rest_server/rest_server.go @@ -13,11 +13,11 @@ import ( "github.com/soheilhy/cmux" "github.com/golangid/candi/candihelper" - "github.com/golangid/candi/candishared" graphqlserver "github.com/golangid/candi/codebase/app/graphql_server" "github.com/golangid/candi/codebase/factory" "github.com/golangid/candi/codebase/factory/types" "github.com/golangid/candi/logger" + "github.com/golangid/candi/wrapper" ) type restServer struct { @@ -51,7 +51,7 @@ func NewServer(service factory.ServiceFactory, opts ...OptionFunc) factory.AppSe server.serverEngine.GET("/", echo.WrapHandler(server.opt.rootHandler)) server.serverEngine.GET("/memstats", - echo.WrapHandler(http.HandlerFunc(candishared.HTTPMemstatsHandler)), + echo.WrapHandler(http.HandlerFunc(wrapper.HTTPHandlerMemstats)), echo.WrapMiddleware(service.GetDependency().GetMiddleware().HTTPBasicAuth), ) diff --git a/codebase/factory/appfactory/setup_graphql_server.go b/codebase/factory/appfactory/setup_graphql_server.go index 3daa5903..58371b10 100644 --- a/codebase/factory/appfactory/setup_graphql_server.go +++ b/codebase/factory/appfactory/setup_graphql_server.go @@ -1,9 +1,6 @@ package appfactory import ( - "net/http" - - "github.com/golangid/candi/candishared" graphqlserver "github.com/golangid/candi/codebase/app/graphql_server" "github.com/golangid/candi/codebase/factory" "github.com/golangid/candi/config/env" @@ -15,7 +12,6 @@ func SetupGraphQLServer(service factory.ServiceFactory, opts ...graphqlserver.Op graphqlserver.SetHTTPPort(env.BaseEnv().HTTPPort), graphqlserver.SetRootPath(env.BaseEnv().HTTPRootPath), graphqlserver.SetDisableIntrospection(env.BaseEnv().GraphQLDisableIntrospection), - graphqlserver.SetRootHTTPHandler(http.HandlerFunc(candishared.HTTPRoot(string(service.Name()), env.BaseEnv().BuildNumber))), graphqlserver.SetSharedListener(service.GetConfig().SharedListener), graphqlserver.SetDebugMode(env.BaseEnv().DebugMode), graphqlserver.SetJaegerMaxPacketSize(env.BaseEnv().JaegerMaxPacketSize), diff --git a/codebase/factory/appfactory/setup_rest_server.go b/codebase/factory/appfactory/setup_rest_server.go index 13bbe622..bff66dc4 100644 --- a/codebase/factory/appfactory/setup_rest_server.go +++ b/codebase/factory/appfactory/setup_rest_server.go @@ -1,9 +1,6 @@ package appfactory import ( - "net/http" - - "github.com/golangid/candi/candishared" restserver "github.com/golangid/candi/codebase/app/rest_server" "github.com/golangid/candi/codebase/factory" "github.com/golangid/candi/config/env" @@ -15,7 +12,6 @@ func SetupRESTServer(service factory.ServiceFactory, opts ...restserver.OptionFu restserver.SetHTTPPort(env.BaseEnv().HTTPPort), restserver.SetRootPath(env.BaseEnv().HTTPRootPath), restserver.SetIncludeGraphQL(env.BaseEnv().UseGraphQL), - restserver.SetRootHTTPHandler(http.HandlerFunc(candishared.HTTPRoot(string(service.Name()), env.BaseEnv().BuildNumber))), restserver.SetSharedListener(service.GetConfig().SharedListener), restserver.SetDebugMode(env.BaseEnv().DebugMode), restserver.SetJaegerMaxPacketSize(env.BaseEnv().JaegerMaxPacketSize), diff --git a/init.go b/init.go index 9c5e04f8..161517f3 100644 --- a/init.go +++ b/init.go @@ -2,5 +2,5 @@ package candi const ( // Version of this library - Version = "v1.14.2" + Version = "v1.14.3" ) diff --git a/wrapper/http_handler.go b/wrapper/http_handler.go index e91f24c5..9b99ffb7 100644 --- a/wrapper/http_handler.go +++ b/wrapper/http_handler.go @@ -2,25 +2,39 @@ package wrapper import ( "bytes" + "encoding/json" "fmt" "io" "net/http" "net/http/httputil" + "os" + "runtime" "strconv" "strings" + "time" "github.com/golangid/candi/candihelper" + "github.com/golangid/candi/config/env" "github.com/golangid/candi/logger" "github.com/golangid/candi/tracer" ) +type HTTPMiddlewareTracerConfig struct { + MaxLogSize int + ExcludePath map[string]struct{} +} + // HTTPMiddlewareTracer middleware wrapper for tracer -func HTTPMiddlewareTracer(maxLogSize int) func(http.Handler) http.Handler { +func HTTPMiddlewareTracer(cfg HTTPMiddlewareTracerConfig) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + if _, isExcludePath := cfg.ExcludePath[req.URL.Path]; isExcludePath { + next.ServeHTTP(rw, req) + return + } isDisableTrace, _ := strconv.ParseBool(req.Header.Get(candihelper.HeaderDisableTrace)) - if isDisableTrace || req.URL.Path == "/" { + if isDisableTrace { next.ServeHTTP(rw, req.WithContext(tracer.SkipTraceContext(req.Context()))) return } @@ -45,7 +59,7 @@ func HTTPMiddlewareTracer(maxLogSize int) func(http.Handler) http.Handler { trace.Log("http.request", httpDump) body, _ := io.ReadAll(req.Body) - if len(body) < maxLogSize { + if len(body) < cfg.MaxLogSize { trace.Log("request.body", body) } else { trace.Log("request.body.size", len(body)) @@ -62,7 +76,7 @@ func HTTPMiddlewareTracer(maxLogSize int) func(http.Handler) http.Handler { trace.SetError(fmt.Errorf("resp.code:%d", respWriter.statusCode)) } - if resBody.Len() < maxLogSize { + if resBody.Len() < cfg.MaxLogSize { trace.Log("response.body", resBody.String()) } else { trace.Log("response.body.size", resBody.Len()) @@ -134,3 +148,46 @@ func HTTPMiddlewareCORS( }) } } + +// HTTPHandlerDefaultRoot default root http handler +func HTTPHandlerDefaultRoot(w http.ResponseWriter, r *http.Request) { + now := time.Now() + payload := struct { + BuildNumber string `json:"build_number,omitempty"` + Message string `json:"message,omitempty"` + Hostname string `json:"hostname,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + StartAt string `json:"start_at,omitempty"` + Uptime string `json:"uptime,omitempty"` + }{ + Message: fmt.Sprintf("Service %s up and running", env.BaseEnv().ServiceName), + Timestamp: now.Format(time.RFC3339Nano), + } + + if startAt, err := time.Parse(time.RFC3339, env.BaseEnv().StartAt); err == nil { + payload.StartAt = env.BaseEnv().StartAt + payload.Uptime = now.Sub(startAt).String() + } + if env.BaseEnv().BuildNumber != "" { + payload.BuildNumber = env.BaseEnv().BuildNumber + } + if hostname, err := os.Hostname(); err == nil { + payload.Hostname = hostname + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(payload) +} + +// HTTPHandlerMemstats calculate runtime statistic +func HTTPHandlerMemstats(w http.ResponseWriter, r *http.Request) { + var m runtime.MemStats + runtime.ReadMemStats(&m) + data := struct { + NumGoroutine int `json:"num_goroutine"` + Memstats interface{} `json:"memstats"` + }{ + runtime.NumGoroutine(), m, + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(data) +} diff --git a/wrapper/http_response_writer.go b/wrapper/http_response_writer.go index 12ddfa84..f502994f 100644 --- a/wrapper/http_response_writer.go +++ b/wrapper/http_response_writer.go @@ -9,13 +9,13 @@ import ( type WrapHTTPResponseWriter struct { statusCode int writer io.Writer - rw http.ResponseWriter + http.ResponseWriter } // NewWrapHTTPResponseWriter init new wrapper for http response writter func NewWrapHTTPResponseWriter(w io.Writer, httpResponseWriter http.ResponseWriter) *WrapHTTPResponseWriter { // Default the status code to 200 - return &WrapHTTPResponseWriter{statusCode: http.StatusOK, writer: io.MultiWriter(w, httpResponseWriter), rw: httpResponseWriter} + return &WrapHTTPResponseWriter{statusCode: http.StatusOK, writer: io.MultiWriter(w, httpResponseWriter), ResponseWriter: httpResponseWriter} } // StatusCode give a way to get the Code @@ -25,7 +25,7 @@ func (w *WrapHTTPResponseWriter) StatusCode() int { // Header Satisfy the http.ResponseWriter interface func (w *WrapHTTPResponseWriter) Header() http.Header { - return w.rw.Header() + return w.ResponseWriter.Header() } func (w *WrapHTTPResponseWriter) Write(data []byte) (int, error) { @@ -39,5 +39,5 @@ func (w *WrapHTTPResponseWriter) WriteHeader(statusCode int) { w.statusCode = statusCode // Write the status code onward. - w.rw.WriteHeader(statusCode) + w.ResponseWriter.WriteHeader(statusCode) }