package server import ( "context" "fmt" "net/http" "sort" "strings" handler "git.pyer.club/kingecg/gohttpd/handler" "git.pyer.club/kingecg/gohttpd/model" logger "git.pyer.club/kingecg/gologger" "github.com/nanmu42/gzip" ) type RequestCtxKey string type Route struct { Method string Path string Handler http.Handler matcher *UrlParamMatcher middles *MiddlewareLink } func (route *Route) ServeHTTP(w http.ResponseWriter, r *http.Request) { if route.middles.Len() > 0 && !route.middles.ServeHTTP(w, r) { return } route.Handler.ServeHTTP(w, r) } func (route *Route) Match(r *http.Request) bool { l := logger.GetLogger("Route") l.Debug(fmt.Sprintf("match route: %s %s", r.Method, r.URL.Path)) if route.Method != "" && route.Method != r.Method { l.Debug("method not match") return false } if route.matcher != nil && route.matcher.Reg != nil { params, matched := MatchUrlParam(r.URL.Path, route.matcher) if matched { ctx := r.Context() data := ctx.Value(RequestCtxKey("data")).(map[string]interface{}) for _, p := range route.matcher.Params { data[p] = params[p] } return true } l.Debug("Not match matcher reg") return false } if route.Path == "" { return true } return strings.HasPrefix(r.URL.Path, route.Path) } func (route *Route) Add(m Middleware) { route.middles.Add(m) } // NewRoute 返回一个新的Route实例 // 参数: // - method: 请求方法 // - path: 请求路径 // - handleFn: http.Handler处理函数 // 返回值: // - *Route: 一个指向Route的指针 func NewRoute(method string, path string, handleFn http.Handler) *Route { ret := &Route{ Method: method, Path: path, middles: NewMiddlewareLink(), } p := ParseUrl(path) // 使用handleFn构建handler ret.Handler = handleFn ret.matcher = &p return ret } type Routes []*Route func (rs Routes) Less(i, j int) bool { iPaths := strings.Split(rs[i].Path, "/") jPaths := strings.Split(rs[j].Path, "/") if len(iPaths) > len(jPaths) { return true } if len(iPaths) < len(jPaths) { return false } for k := 0; k < len(iPaths); k++ { if iPaths[k] != jPaths[k] { return iPaths[k] < jPaths[k] } } return false } func (rs Routes) Len() int { return len(rs) } func (rs Routes) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] } // 可以嵌套的Rest http server mux type RestMux struct { Path string routes Routes middlewares *MiddlewareLink } func (mux *RestMux) Use(m Middleware) { mux.middlewares.Add(m) } func (mux *RestMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { c := r.Context() newRequest := r if t := c.Value("data"); t == nil { data := map[string]interface{}{} cn := context.WithValue(c, RequestCtxKey("data"), data) newRequest = r.WithContext(cn) } if mux.middlewares.Len() > 0 && !mux.middlewares.ServeHTTP(w, newRequest) { return } // 根据r 从routes中找到匹配的路由 for _, route := range mux.routes { if route.Match(newRequest) { route.ServeHTTP(w, newRequest) return } } http.NotFound(w, r) } func (mux *RestMux) HandleFunc(method string, path string, f func(http.ResponseWriter, *http.Request)) *Route { r := NewRoute(method, path, http.HandlerFunc(f)) mux.routes = append(mux.routes, r) sort.Sort(mux.routes) return r } func (mux *RestMux) Get(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("GET", path, f) } func (mux *RestMux) Post(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("POST", path, f) } func (mux *RestMux) Put(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("PUT", path, f) } func (mux *RestMux) Delete(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("DELETE", path, f) } func (mux *RestMux) Patch(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("PATCH", path, f) } func (mux *RestMux) Head(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("HEAD", path, f) } func (mux *RestMux) Option(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("OPTION", path, f) } func (mux *RestMux) HandleMux(nmux *RestMux) *Route { p := nmux.Path if !strings.HasSuffix(p, "/") { p = p + "/" } r := NewRoute("", p, nmux) mux.routes = append(mux.routes, r) sort.Sort(mux.routes) return r } func NewRestMux(path string) *RestMux { ret := &RestMux{ Path: path, routes: Routes{}, middlewares: NewMiddlewareLink(), } return ret } type ServerMux struct { http.Handler directiveHandlers *MiddlewareLink handlers map[string]http.Handler paths []string wrappedHandler map[string]http.Handler } func (s *ServerMux) Handle(pattern string, handler http.Handler) { if s.handlers == nil { s.handlers = make(map[string]http.Handler) } s.handlers[pattern] = handler s.paths = append(s.paths, pattern) // 自定义比较函数排序s.paths sort.Slice(s.paths, func(i, j int) bool { return len(s.paths[i]) > len(s.paths[j]) || s.paths[i] > s.paths[j] }) } func (s *ServerMux) AddDirective(directiveStr string) { l := logger.GetLogger("ServerMux") strs := strings.Split(directiveStr, " ") directiveName := strs[0] params := strs[1:] directive, ok := DirectiveMap[directiveName] if ok { l.Debug(fmt.Sprintf("add directive: %s", directiveName)) s.directiveHandlers.Add(directive(params...)) } else { l.Error(fmt.Sprintf("directive not found: %s", directiveName)) } } func (s *ServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { l := logger.GetLogger("Access") data := map[string]interface{}{} c := r.Context() cn := context.WithValue(c, RequestCtxKey("data"), data) newRequest := r.WithContext(cn) l.Info(fmt.Sprintf("From %s-%s %s", r.RemoteAddr, r.Method, r.URL.Path)) for _, p := range s.paths { if strings.HasPrefix(r.URL.Path, p) { l.Info(fmt.Sprintf("match path: %s", p)) s.directiveHandlers.ServeHTTP(w, newRequest) ctx := newRequest.Context() m := ctx.Value(RequestCtxKey("data")).(map[string]interface{}) _, ok := m["Tg"] if ok { wrappedHandler := s.wrappedHandler[p] if wrappedHandler == nil { s.wrappedHandler[p] = gzip.DefaultHandler().WrapHandler(s.handlers[p]) wrappedHandler = s.wrappedHandler[p] } wrappedHandler.ServeHTTP(w, newRequest) } else { s.handlers[p].ServeHTTP(w, newRequest) } return } } l.Error(fmt.Sprintf("404: %s", r.URL.Path)) http.NotFound(w, newRequest) } func NewServeMux(c *model.HttpServerConfig) *ServerMux { l := logger.GetLogger("ServerMux") l.Debug("NewServeMux") s := &ServerMux{ directiveHandlers: NewMiddlewareLink(), handlers: make(map[string]http.Handler), paths: []string{}, wrappedHandler: make(map[string]http.Handler), } for _, directive := range c.Directives { s.AddDirective(string(directive)) } for _, httpPath := range c.Paths { if httpPath.Root != "" { fileHandler := handler.NewFileHandler(handler.FileHandler{ Root: httpPath.Root, Default: httpPath.Default, }) s.Handle(httpPath.Path, fileHandler) } else if len(httpPath.Upstreams) != 0 { proxyHandler := handler.NewProxyHandler(&httpPath) s.Handle(httpPath.Path, proxyHandler) } else { l.Error("Not supportted server path type :", httpPath.Path) } } return s }