123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- package xweb
- import (
- "crypto/tls"
- "fmt"
- "net"
- "net/http"
- "net/http/pprof"
- "os"
- "path"
- "regexp"
- "runtime"
- runtimePprof "runtime/pprof"
- "strconv"
- "strings"
- "time"
- "app.yhyue.com/moapp/jybase/endless"
- "app.yhyue.com/moapp/jybase/go-xweb/httpsession"
- "app.yhyue.com/moapp/jybase/go-xweb/log"
- )
- // ServerConfig is configuration for server objects.
- type ServerConfig struct {
- Addr string
- Port int
- RecoverPanic bool
- Profiler bool
- EnableGzip bool
- StaticExtensionsToGzip []string
- Url string
- UrlPrefix string
- UrlSuffix string
- StaticHtmlDir string
- SessionTimeout time.Duration
- }
- var ServerNumber uint = 0
- // Server represents a xweb server.
- type Server struct {
- Config *ServerConfig
- Apps map[string]*App
- AppsNamePath map[string]string
- Name string
- SessionManager *httpsession.Manager
- RootApp *App
- Logger *log.Logger
- Env map[string]interface{}
- //save the listener so it can be closed
- l net.Listener
- }
- func NewServer(args ...string) *Server {
- name := ""
- if len(args) == 1 {
- name = args[0]
- } else {
- name = fmt.Sprintf("Server%d", ServerNumber)
- ServerNumber++
- }
- s := &Server{
- Config: Config,
- Env: map[string]interface{}{},
- Apps: map[string]*App{},
- AppsNamePath: map[string]string{},
- Name: name,
- }
- Servers[s.Name] = s
- s.SetLogger(log.New(os.Stdout, "", log.Ldefault()))
- app := NewApp("/", "root")
- s.AddApp(app)
- return s
- }
- func (s *Server) AddApp(a *App) {
- a.BasePath = strings.TrimRight(a.BasePath, "/") + "/"
- s.Apps[a.BasePath] = a
- if a.Name != "" {
- s.AppsNamePath[a.Name] = a.BasePath
- }
- a.Server = s
- a.Logger = s.Logger
- if a.BasePath == "/" {
- s.RootApp = a
- }
- }
- func (s *Server) AddAction(cs ...interface{}) {
- s.RootApp.AddAction(cs...)
- }
- func (s *Server) AutoAction(c ...interface{}) {
- s.RootApp.AutoAction(c...)
- }
- func (s *Server) AddRouter(url string, c interface{}) {
- s.RootApp.AddRouter(url, c)
- }
- func (s *Server) AddTmplVar(name string, varOrFun interface{}) {
- s.RootApp.AddTmplVar(name, varOrFun)
- }
- func (s *Server) AddTmplVars(t *T) {
- s.RootApp.AddTmplVars(t)
- }
- func (s *Server) AddFilter(filter Filter) {
- s.RootApp.AddFilter(filter)
- }
- func (s *Server) AddConfig(name string, value interface{}) {
- s.RootApp.SetConfig(name, value)
- }
- func (s *Server) SetConfig(name string, value interface{}) {
- s.RootApp.SetConfig(name, value)
- }
- func (s *Server) GetConfig(name string) interface{} {
- return s.RootApp.GetConfig(name)
- }
- func (s *Server) error(w http.ResponseWriter, status int, content string) error {
- return s.RootApp.error(w, status, content)
- }
- func (s *Server) initServer() {
- if s.Config == nil {
- s.Config = &ServerConfig{}
- s.Config.Profiler = true
- }
- for _, app := range s.Apps {
- app.initApp()
- }
- }
- // ServeHTTP is the interface method for Go's http server package
- func (s *Server) ServeHTTP(c http.ResponseWriter, req *http.Request) {
- s.Process(c, req)
- }
- // Process invokes the routing system for server s
- // non-root app's route will override root app's if there is same path
- func (s *Server) Process(w http.ResponseWriter, req *http.Request) {
- var result bool = true
- _, _ = XHook.Call("BeforeProcess", &result, s, w, req)
- if !result {
- return
- }
- if s.Config.UrlSuffix != "" && strings.HasSuffix(req.URL.Path, s.Config.UrlSuffix) {
- req.URL.Path = strings.TrimSuffix(req.URL.Path, s.Config.UrlSuffix)
- }
- if s.Config.UrlPrefix != "" && strings.HasPrefix(req.URL.Path, "/"+s.Config.UrlPrefix) {
- req.URL.Path = strings.TrimPrefix(req.URL.Path, "/"+s.Config.UrlPrefix)
- }
- if req.URL.Path[0] != '/' {
- req.URL.Path = "/" + req.URL.Path
- }
- for _, app := range s.Apps {
- if app != s.RootApp && strings.HasPrefix(req.URL.Path, app.BasePath) {
- app.routeHandler(req, w)
- return
- }
- }
- s.RootApp.routeHandler(req, w)
- _, _ = XHook.Call("AfterProcess", &result, s, w, req)
- }
- // Run starts the web application and serves HTTP requests for s
- func (s *Server) RunBase(addr string, mux *http.ServeMux) {
- addrs := strings.Split(addr, ":")
- s.Config.Addr = addrs[0]
- s.Config.Port, _ = strconv.Atoi(addrs[1])
- s.initServer()
- //mux := http.NewServeMux()
- if s.Config.Profiler {
- mux.Handle("/debug/pprof", http.HandlerFunc(pprof.Index))
- mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
- mux.Handle("/debug/pprof/block", pprof.Handler("block"))
- mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
- mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
- mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
- mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
- mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
- mux.Handle("/debug/pprof/startcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- StartCPUProfile()
- }))
- mux.Handle("/debug/pprof/stopcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- StopCPUProfile()
- }))
- mux.Handle("/debug/pprof/memprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- runtime.GC()
- runtimePprof.WriteHeapProfile(rw)
- }))
- mux.Handle("/debug/pprof/gc", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- PrintGCSummary(rw)
- }))
- }
- if c, err := XHook.Call("MuxHandle", mux); err == nil {
- if ret := XHook.Value(c, 0); ret != nil {
- mux = ret.(*http.ServeMux)
- }
- }
- mux.Handle("/", s)
- s.Logger.Infof("http server is listening %s", addr)
- err := endless.ListenAndServe(addr, s.Handler(mux), func() {})
- if err != nil {
- s.Logger.Error("ListenAndServe:", err)
- }
- /*l, err := net.Listen("tcp", addr)
- if err != nil {
- s.Logger.Error("ListenAndServe:", err)
- }
- s.l = l
- err = http.Serve(s.l, mux)
- s.l.Close()*/
- }
- func (s *Server) Run(addr string) {
- addrs := strings.Split(addr, ":")
- s.Config.Addr = addrs[0]
- s.Config.Port, _ = strconv.Atoi(addrs[1])
- s.initServer()
- mux := http.NewServeMux()
- if s.Config.Profiler {
- mux.Handle("/debug/pprof", http.HandlerFunc(pprof.Index))
- mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
- mux.Handle("/debug/pprof/block", pprof.Handler("block"))
- mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
- mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
- mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
- mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
- mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
- mux.Handle("/debug/pprof/startcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- StartCPUProfile()
- }))
- mux.Handle("/debug/pprof/stopcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- StopCPUProfile()
- }))
- mux.Handle("/debug/pprof/memprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- runtime.GC()
- runtimePprof.WriteHeapProfile(rw)
- }))
- mux.Handle("/debug/pprof/gc", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- PrintGCSummary(rw)
- }))
- }
- if c, err := XHook.Call("MuxHandle", mux); err == nil {
- if ret := XHook.Value(c, 0); ret != nil {
- mux = ret.(*http.ServeMux)
- }
- }
- mux.Handle("/", s)
- s.Logger.Infof("http server is listening %s", addr)
- err := endless.ListenAndServe(addr, s.Handler(mux), func() {})
- if err != nil {
- s.Logger.Error("ListenAndServe:", func() {})
- }
- /*l, err := net.Listen("tcp", addr)
- if err != nil {
- s.Logger.Error("ListenAndServe:", err)
- }
- s.l = l
- err = http.Serve(s.l, mux)
- s.l.Close()*/
- }
- // RunFcgi starts the web application and serves FastCGI requests for s.
- func (s *Server) RunFcgi(addr string) {
- s.initServer()
- s.Logger.Infof("fcgi server is listening %s", addr)
- s.listenAndServeFcgi(addr)
- }
- // RunScgi starts the web application and serves SCGI requests for s.
- func (s *Server) RunScgi(addr string) {
- s.initServer()
- s.Logger.Infof("scgi server is listening %s", addr)
- s.listenAndServeScgi(addr)
- }
- // RunTLS starts the web application and serves HTTPS requests for s.
- func (s *Server) RunTLS(addr string, config *tls.Config) error {
- s.initServer()
- mux := http.NewServeMux()
- mux.Handle("/", s)
- l, err := tls.Listen("tcp", addr, config)
- if err != nil {
- s.Logger.Errorf("Listen: %v", err)
- return err
- }
- s.l = l
- s.Logger.Infof("https server is listening %s", addr)
- return http.Serve(s.l, mux)
- }
- // Close stops server s.
- func (s *Server) Close() {
- if s.l != nil {
- s.l.Close()
- }
- }
- // SetLogger sets the logger for server s
- func (s *Server) SetLogger(logger *log.Logger) {
- s.Logger = logger
- s.Logger.SetPrefix("[" + s.Name + "] ")
- if s.RootApp != nil {
- s.RootApp.Logger = s.Logger
- }
- }
- func (s *Server) InitSession() {
- if s.SessionManager == nil {
- s.SessionManager = httpsession.Default()
- }
- if s.Config.SessionTimeout > time.Second {
- s.SessionManager.SetMaxAge(s.Config.SessionTimeout)
- }
- s.SessionManager.Run()
- if s.RootApp != nil {
- s.RootApp.SessionManager = s.SessionManager
- }
- }
- func (s *Server) SetTemplateDir(path string) {
- s.RootApp.SetTemplateDir(path)
- }
- func (s *Server) SetStaticDir(path string) {
- s.RootApp.SetStaticDir(path)
- }
- func (s *Server) App(name string) *App {
- path, ok := s.AppsNamePath[name]
- if ok {
- return s.Apps[path]
- }
- return nil
- }
- func (s *Server) Handler(mux *http.ServeMux) http.Handler {
- keepOriginalUrl, _ := s.GetConfig("keepOriginalUrl").(string)
- if keepOriginalUrl == "" {
- return mux
- }
- keepOriginalUrlReg := regexp.MustCompile(keepOriginalUrl)
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if keepOriginalUrlReg.MatchString(r.URL.Path) {
- r.URL.Path = path.Clean(r.URL.Path)
- }
- mux.ServeHTTP(w, r)
- })
- }
|