server.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. package xweb
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "net"
  6. "net/http"
  7. "net/http/pprof"
  8. "os"
  9. "path"
  10. "regexp"
  11. "runtime"
  12. runtimePprof "runtime/pprof"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "app.yhyue.com/moapp/jybase/endless"
  17. "app.yhyue.com/moapp/jybase/go-xweb/httpsession"
  18. "app.yhyue.com/moapp/jybase/go-xweb/log"
  19. )
  20. // ServerConfig is configuration for server objects.
  21. type ServerConfig struct {
  22. Addr string
  23. Port int
  24. RecoverPanic bool
  25. Profiler bool
  26. EnableGzip bool
  27. StaticExtensionsToGzip []string
  28. Url string
  29. UrlPrefix string
  30. UrlSuffix string
  31. StaticHtmlDir string
  32. SessionTimeout time.Duration
  33. }
  34. var ServerNumber uint = 0
  35. // Server represents a xweb server.
  36. type Server struct {
  37. Config *ServerConfig
  38. Apps map[string]*App
  39. AppsNamePath map[string]string
  40. Name string
  41. SessionManager *httpsession.Manager
  42. RootApp *App
  43. Logger *log.Logger
  44. Env map[string]interface{}
  45. //save the listener so it can be closed
  46. l net.Listener
  47. }
  48. func NewServer(args ...string) *Server {
  49. name := ""
  50. if len(args) == 1 {
  51. name = args[0]
  52. } else {
  53. name = fmt.Sprintf("Server%d", ServerNumber)
  54. ServerNumber++
  55. }
  56. s := &Server{
  57. Config: Config,
  58. Env: map[string]interface{}{},
  59. Apps: map[string]*App{},
  60. AppsNamePath: map[string]string{},
  61. Name: name,
  62. }
  63. Servers[s.Name] = s
  64. s.SetLogger(log.New(os.Stdout, "", log.Ldefault()))
  65. app := NewApp("/", "root")
  66. s.AddApp(app)
  67. return s
  68. }
  69. func (s *Server) AddApp(a *App) {
  70. a.BasePath = strings.TrimRight(a.BasePath, "/") + "/"
  71. s.Apps[a.BasePath] = a
  72. if a.Name != "" {
  73. s.AppsNamePath[a.Name] = a.BasePath
  74. }
  75. a.Server = s
  76. a.Logger = s.Logger
  77. if a.BasePath == "/" {
  78. s.RootApp = a
  79. }
  80. }
  81. func (s *Server) AddAction(cs ...interface{}) {
  82. s.RootApp.AddAction(cs...)
  83. }
  84. func (s *Server) AutoAction(c ...interface{}) {
  85. s.RootApp.AutoAction(c...)
  86. }
  87. func (s *Server) AddRouter(url string, c interface{}) {
  88. s.RootApp.AddRouter(url, c)
  89. }
  90. func (s *Server) AddTmplVar(name string, varOrFun interface{}) {
  91. s.RootApp.AddTmplVar(name, varOrFun)
  92. }
  93. func (s *Server) AddTmplVars(t *T) {
  94. s.RootApp.AddTmplVars(t)
  95. }
  96. func (s *Server) AddFilter(filter Filter) {
  97. s.RootApp.AddFilter(filter)
  98. }
  99. func (s *Server) AddConfig(name string, value interface{}) {
  100. s.RootApp.SetConfig(name, value)
  101. }
  102. func (s *Server) SetConfig(name string, value interface{}) {
  103. s.RootApp.SetConfig(name, value)
  104. }
  105. func (s *Server) GetConfig(name string) interface{} {
  106. return s.RootApp.GetConfig(name)
  107. }
  108. func (s *Server) error(w http.ResponseWriter, status int, content string) error {
  109. return s.RootApp.error(w, status, content)
  110. }
  111. func (s *Server) initServer() {
  112. if s.Config == nil {
  113. s.Config = &ServerConfig{}
  114. s.Config.Profiler = true
  115. }
  116. for _, app := range s.Apps {
  117. app.initApp()
  118. }
  119. }
  120. // ServeHTTP is the interface method for Go's http server package
  121. func (s *Server) ServeHTTP(c http.ResponseWriter, req *http.Request) {
  122. s.Process(c, req)
  123. }
  124. // Process invokes the routing system for server s
  125. // non-root app's route will override root app's if there is same path
  126. func (s *Server) Process(w http.ResponseWriter, req *http.Request) {
  127. var result bool = true
  128. _, _ = XHook.Call("BeforeProcess", &result, s, w, req)
  129. if !result {
  130. return
  131. }
  132. if s.Config.UrlSuffix != "" && strings.HasSuffix(req.URL.Path, s.Config.UrlSuffix) {
  133. req.URL.Path = strings.TrimSuffix(req.URL.Path, s.Config.UrlSuffix)
  134. }
  135. if s.Config.UrlPrefix != "" && strings.HasPrefix(req.URL.Path, "/"+s.Config.UrlPrefix) {
  136. req.URL.Path = strings.TrimPrefix(req.URL.Path, "/"+s.Config.UrlPrefix)
  137. }
  138. if req.URL.Path[0] != '/' {
  139. req.URL.Path = "/" + req.URL.Path
  140. }
  141. for _, app := range s.Apps {
  142. if app != s.RootApp && strings.HasPrefix(req.URL.Path, app.BasePath) {
  143. app.routeHandler(req, w)
  144. return
  145. }
  146. }
  147. s.RootApp.routeHandler(req, w)
  148. _, _ = XHook.Call("AfterProcess", &result, s, w, req)
  149. }
  150. // Run starts the web application and serves HTTP requests for s
  151. func (s *Server) RunBase(addr string, mux *http.ServeMux) {
  152. addrs := strings.Split(addr, ":")
  153. s.Config.Addr = addrs[0]
  154. s.Config.Port, _ = strconv.Atoi(addrs[1])
  155. s.initServer()
  156. //mux := http.NewServeMux()
  157. if s.Config.Profiler {
  158. mux.Handle("/debug/pprof", http.HandlerFunc(pprof.Index))
  159. mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
  160. mux.Handle("/debug/pprof/block", pprof.Handler("block"))
  161. mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
  162. mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
  163. mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
  164. mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
  165. mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
  166. mux.Handle("/debug/pprof/startcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  167. StartCPUProfile()
  168. }))
  169. mux.Handle("/debug/pprof/stopcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  170. StopCPUProfile()
  171. }))
  172. mux.Handle("/debug/pprof/memprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  173. runtime.GC()
  174. runtimePprof.WriteHeapProfile(rw)
  175. }))
  176. mux.Handle("/debug/pprof/gc", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  177. PrintGCSummary(rw)
  178. }))
  179. }
  180. if c, err := XHook.Call("MuxHandle", mux); err == nil {
  181. if ret := XHook.Value(c, 0); ret != nil {
  182. mux = ret.(*http.ServeMux)
  183. }
  184. }
  185. mux.Handle("/", s)
  186. s.Logger.Infof("http server is listening %s", addr)
  187. err := endless.ListenAndServe(addr, s.Handler(mux), func() {})
  188. if err != nil {
  189. s.Logger.Error("ListenAndServe:", err)
  190. }
  191. /*l, err := net.Listen("tcp", addr)
  192. if err != nil {
  193. s.Logger.Error("ListenAndServe:", err)
  194. }
  195. s.l = l
  196. err = http.Serve(s.l, mux)
  197. s.l.Close()*/
  198. }
  199. func (s *Server) Run(addr string) {
  200. addrs := strings.Split(addr, ":")
  201. s.Config.Addr = addrs[0]
  202. s.Config.Port, _ = strconv.Atoi(addrs[1])
  203. s.initServer()
  204. mux := http.NewServeMux()
  205. if s.Config.Profiler {
  206. mux.Handle("/debug/pprof", http.HandlerFunc(pprof.Index))
  207. mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
  208. mux.Handle("/debug/pprof/block", pprof.Handler("block"))
  209. mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
  210. mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
  211. mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
  212. mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
  213. mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
  214. mux.Handle("/debug/pprof/startcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  215. StartCPUProfile()
  216. }))
  217. mux.Handle("/debug/pprof/stopcpuprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  218. StopCPUProfile()
  219. }))
  220. mux.Handle("/debug/pprof/memprof", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  221. runtime.GC()
  222. runtimePprof.WriteHeapProfile(rw)
  223. }))
  224. mux.Handle("/debug/pprof/gc", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  225. PrintGCSummary(rw)
  226. }))
  227. }
  228. if c, err := XHook.Call("MuxHandle", mux); err == nil {
  229. if ret := XHook.Value(c, 0); ret != nil {
  230. mux = ret.(*http.ServeMux)
  231. }
  232. }
  233. mux.Handle("/", s)
  234. s.Logger.Infof("http server is listening %s", addr)
  235. err := endless.ListenAndServe(addr, s.Handler(mux), func() {})
  236. if err != nil {
  237. s.Logger.Error("ListenAndServe:", func() {})
  238. }
  239. /*l, err := net.Listen("tcp", addr)
  240. if err != nil {
  241. s.Logger.Error("ListenAndServe:", err)
  242. }
  243. s.l = l
  244. err = http.Serve(s.l, mux)
  245. s.l.Close()*/
  246. }
  247. // RunFcgi starts the web application and serves FastCGI requests for s.
  248. func (s *Server) RunFcgi(addr string) {
  249. s.initServer()
  250. s.Logger.Infof("fcgi server is listening %s", addr)
  251. s.listenAndServeFcgi(addr)
  252. }
  253. // RunScgi starts the web application and serves SCGI requests for s.
  254. func (s *Server) RunScgi(addr string) {
  255. s.initServer()
  256. s.Logger.Infof("scgi server is listening %s", addr)
  257. s.listenAndServeScgi(addr)
  258. }
  259. // RunTLS starts the web application and serves HTTPS requests for s.
  260. func (s *Server) RunTLS(addr string, config *tls.Config) error {
  261. s.initServer()
  262. mux := http.NewServeMux()
  263. mux.Handle("/", s)
  264. l, err := tls.Listen("tcp", addr, config)
  265. if err != nil {
  266. s.Logger.Errorf("Listen: %v", err)
  267. return err
  268. }
  269. s.l = l
  270. s.Logger.Infof("https server is listening %s", addr)
  271. return http.Serve(s.l, mux)
  272. }
  273. // Close stops server s.
  274. func (s *Server) Close() {
  275. if s.l != nil {
  276. s.l.Close()
  277. }
  278. }
  279. // SetLogger sets the logger for server s
  280. func (s *Server) SetLogger(logger *log.Logger) {
  281. s.Logger = logger
  282. s.Logger.SetPrefix("[" + s.Name + "] ")
  283. if s.RootApp != nil {
  284. s.RootApp.Logger = s.Logger
  285. }
  286. }
  287. func (s *Server) InitSession() {
  288. if s.SessionManager == nil {
  289. s.SessionManager = httpsession.Default()
  290. }
  291. if s.Config.SessionTimeout > time.Second {
  292. s.SessionManager.SetMaxAge(s.Config.SessionTimeout)
  293. }
  294. s.SessionManager.Run()
  295. if s.RootApp != nil {
  296. s.RootApp.SessionManager = s.SessionManager
  297. }
  298. }
  299. func (s *Server) SetTemplateDir(path string) {
  300. s.RootApp.SetTemplateDir(path)
  301. }
  302. func (s *Server) SetStaticDir(path string) {
  303. s.RootApp.SetStaticDir(path)
  304. }
  305. func (s *Server) App(name string) *App {
  306. path, ok := s.AppsNamePath[name]
  307. if ok {
  308. return s.Apps[path]
  309. }
  310. return nil
  311. }
  312. func (s *Server) Handler(mux *http.ServeMux) http.Handler {
  313. keepOriginalUrl, _ := s.GetConfig("keepOriginalUrl").(string)
  314. if keepOriginalUrl == "" {
  315. return mux
  316. }
  317. keepOriginalUrlReg := regexp.MustCompile(keepOriginalUrl)
  318. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  319. if keepOriginalUrlReg.MatchString(r.URL.Path) {
  320. r.URL.Path = path.Clean(r.URL.Path)
  321. }
  322. mux.ServeHTTP(w, r)
  323. })
  324. }