ghttp_server_handler.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package ghttp
  7. import (
  8. "net/http"
  9. "os"
  10. "sort"
  11. "strings"
  12. "github.com/gogf/gf/text/gstr"
  13. "github.com/gogf/gf/errors/gerror"
  14. "github.com/gogf/gf/os/gres"
  15. "github.com/gogf/gf/encoding/ghtml"
  16. "github.com/gogf/gf/os/gfile"
  17. "github.com/gogf/gf/os/gspath"
  18. "github.com/gogf/gf/os/gtime"
  19. )
  20. // ServeHTTP is the default handler for http request.
  21. // It should not create new goroutine handling the request as
  22. // it's called by am already created new goroutine from http.Server.
  23. //
  24. // This function also make serve implementing the interface of http.Handler.
  25. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  26. // Max body size limit.
  27. if s.config.ClientMaxBodySize > 0 {
  28. r.Body = http.MaxBytesReader(w, r.Body, s.config.ClientMaxBodySize)
  29. }
  30. // Rewrite feature checks.
  31. if len(s.config.Rewrites) > 0 {
  32. if rewrite, ok := s.config.Rewrites[r.URL.Path]; ok {
  33. r.URL.Path = rewrite
  34. }
  35. }
  36. // Remove char '/' in the tail of URI.
  37. if r.URL.Path != "/" {
  38. for len(r.URL.Path) > 0 && r.URL.Path[len(r.URL.Path)-1] == '/' {
  39. r.URL.Path = r.URL.Path[:len(r.URL.Path)-1]
  40. }
  41. }
  42. // Default URI value if it's empty.
  43. if r.URL.Path == "" {
  44. r.URL.Path = "/"
  45. }
  46. // Create a new request object.
  47. request := newRequest(s, r, w)
  48. defer func() {
  49. request.LeaveTime = gtime.TimestampMilli()
  50. // error log handling.
  51. if request.error != nil {
  52. s.handleErrorLog(request.error, request)
  53. } else {
  54. if exception := recover(); exception != nil {
  55. request.Response.WriteStatus(http.StatusInternalServerError)
  56. if err, ok := exception.(error); ok {
  57. s.handleErrorLog(gerror.Wrap(err, ""), request)
  58. } else {
  59. s.handleErrorLog(gerror.Newf("%v", exception), request)
  60. }
  61. }
  62. }
  63. // access log handling.
  64. s.handleAccessLog(request)
  65. // Close the session, which automatically update the TTL
  66. // of the session if it exists.
  67. request.Session.Close()
  68. // Close the request and response body
  69. // to release the file descriptor in time.
  70. request.Request.Body.Close()
  71. if request.Request.Response != nil {
  72. request.Request.Response.Body.Close()
  73. }
  74. }()
  75. // ============================================================
  76. // Priority:
  77. // Static File > Dynamic Service > Static Directory
  78. // ============================================================
  79. // Search the static file with most high priority,
  80. // which also handle the index files feature.
  81. if s.config.FileServerEnabled {
  82. request.StaticFile = s.searchStaticFile(r.URL.Path)
  83. if request.StaticFile != nil {
  84. request.isFileRequest = true
  85. }
  86. }
  87. // Search the dynamic service handler.
  88. request.handlers, request.hasHookHandler, request.hasServeHandler = s.getHandlersWithCache(request)
  89. // Check the service type static or dynamic for current request.
  90. if request.StaticFile != nil && request.StaticFile.IsDir && request.hasServeHandler {
  91. request.isFileRequest = false
  92. }
  93. // HOOK - BeforeServe
  94. s.callHookHandler(HookBeforeServe, request)
  95. // Core serving handling.
  96. if !request.IsExited() {
  97. if request.isFileRequest {
  98. // Static file service.
  99. s.serveFile(request, request.StaticFile)
  100. } else {
  101. if len(request.handlers) > 0 {
  102. // Dynamic service.
  103. request.Middleware.Next()
  104. } else {
  105. if request.StaticFile != nil && request.StaticFile.IsDir {
  106. // Serve the directory.
  107. s.serveFile(request, request.StaticFile)
  108. } else {
  109. if len(request.Response.Header()) == 0 &&
  110. request.Response.Status == 0 &&
  111. request.Response.BufferLength() == 0 {
  112. request.Response.WriteHeader(http.StatusNotFound)
  113. }
  114. }
  115. }
  116. }
  117. }
  118. // HOOK - AfterServe
  119. if !request.IsExited() {
  120. s.callHookHandler(HookAfterServe, request)
  121. }
  122. // HOOK - BeforeOutput
  123. if !request.IsExited() {
  124. s.callHookHandler(HookBeforeOutput, request)
  125. }
  126. // HTTP status checking.
  127. if request.Response.Status == 0 {
  128. if request.StaticFile != nil || request.Middleware.served || request.Response.buffer.Len() > 0 {
  129. request.Response.WriteHeader(http.StatusOK)
  130. } else {
  131. request.Response.WriteHeader(http.StatusNotFound)
  132. }
  133. }
  134. // HTTP status handler.
  135. if request.Response.Status != http.StatusOK {
  136. statusFuncArray := s.getStatusHandler(request.Response.Status, request)
  137. for _, f := range statusFuncArray {
  138. // Call custom status handler.
  139. niceCallFunc(func() {
  140. f(request)
  141. })
  142. if request.IsExited() {
  143. break
  144. }
  145. }
  146. }
  147. // Automatically set the session id to cookie
  148. // if it creates a new session id in this request.
  149. if s.config.SessionCookieOutput &&
  150. request.Session.IsDirty() &&
  151. request.Session.Id() != request.GetSessionId() {
  152. request.Cookie.SetSessionId(request.Session.Id())
  153. }
  154. // Output the cookie content to client.
  155. request.Cookie.Flush()
  156. // Output the buffer content to client.
  157. request.Response.Flush()
  158. // HOOK - AfterOutput
  159. if !request.IsExited() {
  160. s.callHookHandler(HookAfterOutput, request)
  161. }
  162. }
  163. // searchStaticFile searches the file with given URI.
  164. // It returns a file struct specifying the file information.
  165. func (s *Server) searchStaticFile(uri string) *StaticFile {
  166. var file *gres.File
  167. var path string
  168. var dir bool
  169. // Firstly search the StaticPaths mapping.
  170. if len(s.config.StaticPaths) > 0 {
  171. for _, item := range s.config.StaticPaths {
  172. if len(uri) >= len(item.prefix) && strings.EqualFold(item.prefix, uri[0:len(item.prefix)]) {
  173. // To avoid case like: /static/style -> /static/style.css
  174. if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' {
  175. continue
  176. }
  177. file = gres.GetWithIndex(item.path+uri[len(item.prefix):], s.config.IndexFiles)
  178. if file != nil {
  179. return &StaticFile{
  180. File: file,
  181. IsDir: file.FileInfo().IsDir(),
  182. }
  183. }
  184. path, dir = gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...)
  185. if path != "" {
  186. return &StaticFile{
  187. Path: path,
  188. IsDir: dir,
  189. }
  190. }
  191. }
  192. }
  193. }
  194. // Secondly search the root and searching paths.
  195. if len(s.config.SearchPaths) > 0 {
  196. for _, p := range s.config.SearchPaths {
  197. file = gres.GetWithIndex(p+uri, s.config.IndexFiles)
  198. if file != nil {
  199. return &StaticFile{
  200. File: file,
  201. IsDir: file.FileInfo().IsDir(),
  202. }
  203. }
  204. if path, dir = gspath.Search(p, uri, s.config.IndexFiles...); path != "" {
  205. return &StaticFile{
  206. Path: path,
  207. IsDir: dir,
  208. }
  209. }
  210. }
  211. }
  212. // Lastly search the resource manager.
  213. if len(s.config.StaticPaths) == 0 && len(s.config.SearchPaths) == 0 {
  214. if file = gres.GetWithIndex(uri, s.config.IndexFiles); file != nil {
  215. return &StaticFile{
  216. File: file,
  217. IsDir: file.FileInfo().IsDir(),
  218. }
  219. }
  220. }
  221. return nil
  222. }
  223. // serveFile serves the static file for client.
  224. // The optional parameter <allowIndex> specifies if allowing directory listing if <f> is directory.
  225. func (s *Server) serveFile(r *Request, f *StaticFile, allowIndex ...bool) {
  226. // Use resource file from memory.
  227. if f.File != nil {
  228. if f.IsDir {
  229. if s.config.IndexFolder || (len(allowIndex) > 0 && allowIndex[0]) {
  230. s.listDir(r, f.File)
  231. } else {
  232. r.Response.WriteStatus(http.StatusForbidden)
  233. }
  234. } else {
  235. info := f.File.FileInfo()
  236. r.Response.wroteHeader = true
  237. http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), f.File)
  238. }
  239. return
  240. }
  241. // Use file from dist.
  242. file, err := os.Open(f.Path)
  243. if err != nil {
  244. r.Response.WriteStatus(http.StatusForbidden)
  245. return
  246. }
  247. defer file.Close()
  248. // Clear the response buffer before file serving.
  249. // It ignores all custom buffer content and uses the file content.
  250. r.Response.ClearBuffer()
  251. info, _ := file.Stat()
  252. if info.IsDir() {
  253. if s.config.IndexFolder || (len(allowIndex) > 0 && allowIndex[0]) {
  254. s.listDir(r, file)
  255. } else {
  256. r.Response.WriteStatus(http.StatusForbidden)
  257. }
  258. } else {
  259. r.Response.wroteHeader = true
  260. http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), file)
  261. }
  262. }
  263. // listDir lists the sub files of specified directory as HTML content to client.
  264. func (s *Server) listDir(r *Request, f http.File) {
  265. files, err := f.Readdir(-1)
  266. if err != nil {
  267. r.Response.WriteStatus(http.StatusInternalServerError, "Error reading directory")
  268. return
  269. }
  270. // The folder type has the most priority than file.
  271. sort.Slice(files, func(i, j int) bool {
  272. if files[i].IsDir() && !files[j].IsDir() {
  273. return true
  274. }
  275. if !files[i].IsDir() && files[j].IsDir() {
  276. return false
  277. }
  278. return files[i].Name() < files[j].Name()
  279. })
  280. if r.Response.Header().Get("Content-Type") == "" {
  281. r.Response.Header().Set("Content-Type", "text/html; charset=utf-8")
  282. }
  283. r.Response.Write(`<html>`)
  284. r.Response.Write(`<head>`)
  285. r.Response.Write(`<style>`)
  286. r.Response.Write(`body {font-family:Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;}`)
  287. r.Response.Write(`</style>`)
  288. r.Response.Write(`</head>`)
  289. r.Response.Write(`<body>`)
  290. r.Response.Writef(`<h1>Index of %s</h1>`, r.URL.Path)
  291. r.Response.Writef(`<hr />`)
  292. r.Response.Write(`<table>`)
  293. if r.URL.Path != "/" {
  294. r.Response.Write(`<tr>`)
  295. r.Response.Writef(`<td><a href="%s">..</a></td>`, gfile.Dir(r.URL.Path))
  296. r.Response.Write(`</tr>`)
  297. }
  298. name := ""
  299. size := ""
  300. prefix := gstr.TrimRight(r.URL.Path, "/")
  301. for _, file := range files {
  302. name = file.Name()
  303. size = gfile.FormatSize(file.Size())
  304. if file.IsDir() {
  305. name += "/"
  306. size = "-"
  307. }
  308. r.Response.Write(`<tr>`)
  309. r.Response.Writef(`<td><a href="%s/%s">%s</a></td>`, prefix, name, ghtml.SpecialChars(name))
  310. r.Response.Writef(`<td style="width:300px;text-align:center;">%s</td>`, gtime.New(file.ModTime()).ISO8601())
  311. r.Response.Writef(`<td style="width:80px;text-align:right;">%s</td>`, size)
  312. r.Response.Write(`</tr>`)
  313. }
  314. r.Response.Write(`</table>`)
  315. r.Response.Write(`</body>`)
  316. r.Response.Write(`</html>`)
  317. }