github.com/profzone/eden-framework@v1.0.10/pkg/courier/transport_http/serve.go (about) 1 package transport_http 2 3 import ( 4 "context" 5 "fmt" 6 "github.com/profzone/envconfig" 7 "net/http" 8 _ "net/http/pprof" 9 "os" 10 "os/signal" 11 "reflect" 12 "regexp" 13 "sort" 14 "strings" 15 "time" 16 17 "github.com/fatih/color" 18 "github.com/julienschmidt/httprouter" 19 20 "github.com/profzone/eden-framework/pkg/courier" 21 ) 22 23 type ServeHTTP struct { 24 Name string 25 IP string 26 Port int 27 WriteTimeout envconfig.Duration 28 ReadTimeout envconfig.Duration 29 WithCORS bool 30 router *httprouter.Router 31 } 32 33 func (s ServeHTTP) MarshalDefaults(v interface{}) { 34 if h, ok := v.(*ServeHTTP); ok { 35 if h.Name == "" { 36 h.Name = os.Getenv("PROJECT_NAME") 37 } 38 39 if h.Port == 0 { 40 h.Port = 8000 41 } 42 43 if h.ReadTimeout == 0 { 44 h.ReadTimeout = envconfig.Duration(15 * time.Second) 45 } 46 47 if h.WriteTimeout == 0 { 48 h.WriteTimeout = envconfig.Duration(15 * time.Second) 49 } 50 } 51 } 52 53 func (s *ServeHTTP) Serve(router *courier.Router) error { 54 s.MarshalDefaults(s) 55 s.router = s.convertRouterToHttpRouter(router) 56 s.router.GET("/healthz", func(http.ResponseWriter, *http.Request, httprouter.Params) {}) 57 58 srv := &http.Server{ 59 Handler: s, 60 Addr: fmt.Sprintf("%s:%d", s.IP, s.Port), 61 WriteTimeout: time.Duration(s.WriteTimeout), 62 ReadTimeout: time.Duration(s.ReadTimeout), 63 } 64 65 idleConnsClosed := make(chan struct{}) 66 go func() { 67 sigint := make(chan os.Signal, 1) 68 signal.Notify(sigint, os.Interrupt) 69 <-sigint 70 if err := srv.Shutdown(context.Background()); err != nil { 71 fmt.Printf("HTTP server Shutdown: %v", err) 72 } 73 close(idleConnsClosed) 74 }() 75 76 fmt.Printf("[Courier] listen on %s\n", srv.Addr) 77 return srv.ListenAndServe() 78 } 79 80 var RxHttpRouterPath = regexp.MustCompile("/:([^/]+)") 81 82 func (s *ServeHTTP) ServeHTTP(w http.ResponseWriter, req *http.Request) { 83 if s.WithCORS { 84 headers := w.Header() 85 setCORS(&headers) 86 } 87 s.router.ServeHTTP(w, req) 88 } 89 90 func (s *ServeHTTP) convertRouterToHttpRouter(router *courier.Router) *httprouter.Router { 91 routes := router.Routes() 92 93 if len(routes) == 0 { 94 panic(fmt.Sprintf("need to register operation before Listion")) 95 } 96 97 r := httprouter.New() 98 r.Handler(http.MethodGet, "/debug/pprof/:item", http.DefaultServeMux) 99 100 sort.Slice(routes, func(i, j int) bool { 101 return getPath(routes[i]) < getPath(routes[j]) 102 }) 103 104 for _, route := range routes { 105 method := getMethod(route) 106 p := getPath(route) 107 108 finalOperators, operatorTypeNames := route.EffectiveOperators() 109 110 if len(finalOperators) == 0 { 111 panic(fmt.Errorf( 112 "[Courier] No available operator %v", 113 route.Operators, 114 )) 115 } 116 117 if method == "" { 118 panic(fmt.Errorf( 119 "[Courier] Missing method of %s\n", 120 color.CyanString(reflect.TypeOf(finalOperators[len(finalOperators)-1]).Name()), 121 )) 122 } 123 124 lengthOfOperatorTypes := len(operatorTypeNames) 125 126 for i := range operatorTypeNames { 127 if i < lengthOfOperatorTypes-1 { 128 operatorTypeNames[i] = color.MagentaString(operatorTypeNames[i]) 129 } else { 130 operatorTypeNames[i] = color.CyanString(operatorTypeNames[i]) 131 } 132 } 133 134 fmt.Printf( 135 "[Courier] %s %s\n", 136 colorByMethod(method)("%s %s", method[0:3], RxHttpRouterPath.ReplaceAllString(p, "/{$1}")), 137 strings.Join(operatorTypeNames, " "), 138 ) 139 140 r.Handle(method, p, CreateHttpHandler(s, finalOperators...)) 141 } 142 143 return r 144 } 145 146 func getMethod(route *courier.Route) string { 147 if withHttpMethod, ok := route.Operators[len(route.Operators)-1].(IMethod); ok { 148 return string(withHttpMethod.Method()) 149 } 150 return "" 151 } 152 153 func getPath(route *courier.Route) string { 154 p := "/" 155 for _, operator := range route.Operators { 156 if WithHttpPath, ok := operator.(IPath); ok { 157 p += WithHttpPath.Path() 158 } 159 } 160 return httprouter.CleanPath(p) 161 } 162 163 func colorByMethod(method string) func(f string, args ...interface{}) string { 164 switch method { 165 case http.MethodGet: 166 return color.BlueString 167 case http.MethodPost: 168 return color.GreenString 169 case http.MethodPut: 170 return color.YellowString 171 case http.MethodDelete: 172 return color.RedString 173 case http.MethodHead: 174 return color.WhiteString 175 default: 176 return color.BlackString 177 } 178 }