gin.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
  2. // Use of this source code is governed by a MIT style
  3. // license that can be found in the LICENSE file.
  4. package gin
  5. import (
  6. "fmt"
  7. "html/template"
  8. "net"
  9. "net/http"
  10. "os"
  11. "path"
  12. "reflect"
  13. "strings"
  14. "sync"
  15. "github.com/gin-gonic/gin/internal/bytesconv"
  16. "github.com/gin-gonic/gin/render"
  17. )
  18. const defaultMultipartMemory = 32 << 20 // 32 MB
  19. var (
  20. default404Body = []byte("404 page not found")
  21. default405Body = []byte("405 method not allowed")
  22. )
  23. var defaultPlatform string
  24. var defaultTrustedCIDRs = []*net.IPNet{{IP: net.IP{0x0, 0x0, 0x0, 0x0}, Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}}} // 0.0.0.0/0
  25. // HandlerFunc defines the handler used by gin middleware as return value.
  26. type HandlerFunc func(*Context)
  27. // HandlersChain defines a HandlerFunc array.
  28. type HandlersChain []HandlerFunc
  29. // Last returns the last handler in the chain. ie. the last handler is the main one.
  30. func (c HandlersChain) Last() HandlerFunc {
  31. if length := len(c); length > 0 {
  32. return c[length-1]
  33. }
  34. return nil
  35. }
  36. // RouteInfo represents a request route's specification which contains method and path and its handler.
  37. type RouteInfo struct {
  38. Method string
  39. Path string
  40. Handler string
  41. HandlerFunc HandlerFunc
  42. }
  43. // RoutesInfo defines a RouteInfo array.
  44. type RoutesInfo []RouteInfo
  45. // Trusted platforms
  46. const (
  47. // When running on Google App Engine. Trust X-Appengine-Remote-Addr
  48. // for determining the client's IP
  49. PlatformGoogleAppEngine = "X-Appengine-Remote-Addr"
  50. // When using Cloudflare's CDN. Trust CF-Connecting-IP for determining
  51. // the client's IP
  52. PlatformCloudflare = "CF-Connecting-IP"
  53. )
  54. // Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
  55. // Create an instance of Engine, by using New() or Default()
  56. type Engine struct {
  57. RouterGroup
  58. // Enables automatic redirection if the current route can't be matched but a
  59. // handler for the path with (without) the trailing slash exists.
  60. // For example if /foo/ is requested but a route only exists for /foo, the
  61. // client is redirected to /foo with http status code 301 for GET requests
  62. // and 307 for all other request methods.
  63. RedirectTrailingSlash bool
  64. // If enabled, the router tries to fix the current request path, if no
  65. // handle is registered for it.
  66. // First superfluous path elements like ../ or // are removed.
  67. // Afterwards the router does a case-insensitive lookup of the cleaned path.
  68. // If a handle can be found for this route, the router makes a redirection
  69. // to the corrected path with status code 301 for GET requests and 307 for
  70. // all other request methods.
  71. // For example /FOO and /..//Foo could be redirected to /foo.
  72. // RedirectTrailingSlash is independent of this option.
  73. RedirectFixedPath bool
  74. // If enabled, the router checks if another method is allowed for the
  75. // current route, if the current request can not be routed.
  76. // If this is the case, the request is answered with 'Method Not Allowed'
  77. // and HTTP status code 405.
  78. // If no other Method is allowed, the request is delegated to the NotFound
  79. // handler.
  80. HandleMethodNotAllowed bool
  81. // If enabled, client IP will be parsed from the request's headers that
  82. // match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
  83. // fetched, it falls back to the IP obtained from
  84. // `(*gin.Context).Request.RemoteAddr`.
  85. ForwardedByClientIP bool
  86. // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.GoogleAppEngine` INSTEAD
  87. // #726 #755 If enabled, it will trust some headers starting with
  88. // 'X-AppEngine...' for better integration with that PaaS.
  89. AppEngine bool
  90. // If enabled, the url.RawPath will be used to find parameters.
  91. UseRawPath bool
  92. // If true, the path value will be unescaped.
  93. // If UseRawPath is false (by default), the UnescapePathValues effectively is true,
  94. // as url.Path gonna be used, which is already unescaped.
  95. UnescapePathValues bool
  96. // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
  97. // See the PR #1817 and issue #1644
  98. RemoveExtraSlash bool
  99. // List of headers used to obtain the client IP when
  100. // `(*gin.Engine).ForwardedByClientIP` is `true` and
  101. // `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
  102. // network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
  103. RemoteIPHeaders []string
  104. // If set to a constant of value gin.Platform*, trusts the headers set by
  105. // that platform, for example to determine the client IP
  106. TrustedPlatform string
  107. // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
  108. // method call.
  109. MaxMultipartMemory int64
  110. delims render.Delims
  111. secureJSONPrefix string
  112. HTMLRender render.HTMLRender
  113. FuncMap template.FuncMap
  114. allNoRoute HandlersChain
  115. allNoMethod HandlersChain
  116. noRoute HandlersChain
  117. noMethod HandlersChain
  118. pool sync.Pool
  119. trees methodTrees
  120. maxParams uint16
  121. maxSections uint16
  122. trustedProxies []string
  123. trustedCIDRs []*net.IPNet
  124. }
  125. var _ IRouter = &Engine{}
  126. // New returns a new blank Engine instance without any middleware attached.
  127. // By default the configuration is:
  128. // - RedirectTrailingSlash: true
  129. // - RedirectFixedPath: false
  130. // - HandleMethodNotAllowed: false
  131. // - ForwardedByClientIP: true
  132. // - UseRawPath: false
  133. // - UnescapePathValues: true
  134. func New() *Engine {
  135. debugPrintWARNINGNew()
  136. engine := &Engine{
  137. RouterGroup: RouterGroup{
  138. Handlers: nil,
  139. basePath: "/",
  140. root: true,
  141. },
  142. FuncMap: template.FuncMap{},
  143. RedirectTrailingSlash: true,
  144. RedirectFixedPath: false,
  145. HandleMethodNotAllowed: false,
  146. ForwardedByClientIP: true,
  147. RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
  148. TrustedPlatform: defaultPlatform,
  149. UseRawPath: false,
  150. RemoveExtraSlash: false,
  151. UnescapePathValues: true,
  152. MaxMultipartMemory: defaultMultipartMemory,
  153. trees: make(methodTrees, 0, 9),
  154. delims: render.Delims{Left: "{{", Right: "}}"},
  155. secureJSONPrefix: "while(1);",
  156. trustedProxies: []string{"0.0.0.0/0"},
  157. trustedCIDRs: defaultTrustedCIDRs,
  158. }
  159. engine.RouterGroup.engine = engine
  160. engine.pool.New = func() interface{} {
  161. return engine.allocateContext()
  162. }
  163. return engine
  164. }
  165. // Default returns an Engine instance with the Logger and Recovery middleware already attached.
  166. func Default() *Engine {
  167. debugPrintWARNINGDefault()
  168. engine := New()
  169. engine.Use(Logger(), Recovery())
  170. return engine
  171. }
  172. func (engine *Engine) allocateContext() *Context {
  173. v := make(Params, 0, engine.maxParams)
  174. skippedNodes := make([]skippedNode, 0, engine.maxSections)
  175. return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes}
  176. }
  177. // Delims sets template left and right delims and returns a Engine instance.
  178. func (engine *Engine) Delims(left, right string) *Engine {
  179. engine.delims = render.Delims{Left: left, Right: right}
  180. return engine
  181. }
  182. // SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
  183. func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
  184. engine.secureJSONPrefix = prefix
  185. return engine
  186. }
  187. // LoadHTMLGlob loads HTML files identified by glob pattern
  188. // and associates the result with HTML renderer.
  189. func (engine *Engine) LoadHTMLGlob(pattern string) {
  190. left := engine.delims.Left
  191. right := engine.delims.Right
  192. templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
  193. if IsDebugging() {
  194. debugPrintLoadTemplate(templ)
  195. engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
  196. return
  197. }
  198. engine.SetHTMLTemplate(templ)
  199. }
  200. // LoadHTMLFiles loads a slice of HTML files
  201. // and associates the result with HTML renderer.
  202. func (engine *Engine) LoadHTMLFiles(files ...string) {
  203. if IsDebugging() {
  204. engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
  205. return
  206. }
  207. templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
  208. engine.SetHTMLTemplate(templ)
  209. }
  210. // SetHTMLTemplate associate a template with HTML renderer.
  211. func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
  212. if len(engine.trees) > 0 {
  213. debugPrintWARNINGSetHTMLTemplate()
  214. }
  215. engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
  216. }
  217. // SetFuncMap sets the FuncMap used for template.FuncMap.
  218. func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
  219. engine.FuncMap = funcMap
  220. }
  221. // NoRoute adds handlers for NoRoute. It return a 404 code by default.
  222. func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
  223. engine.noRoute = handlers
  224. engine.rebuild404Handlers()
  225. }
  226. // NoMethod sets the handlers called when Engine.HandleMethodNotAllowed = true.
  227. func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
  228. engine.noMethod = handlers
  229. engine.rebuild405Handlers()
  230. }
  231. // Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
  232. // included in the handlers chain for every single request. Even 404, 405, static files...
  233. // For example, this is the right place for a logger or error management middleware.
  234. func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
  235. engine.RouterGroup.Use(middleware...)
  236. engine.rebuild404Handlers()
  237. engine.rebuild405Handlers()
  238. return engine
  239. }
  240. func (engine *Engine) rebuild404Handlers() {
  241. engine.allNoRoute = engine.combineHandlers(engine.noRoute)
  242. }
  243. func (engine *Engine) rebuild405Handlers() {
  244. engine.allNoMethod = engine.combineHandlers(engine.noMethod)
  245. }
  246. func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
  247. assert1(path[0] == '/', "path must begin with '/'")
  248. assert1(method != "", "HTTP method can not be empty")
  249. assert1(len(handlers) > 0, "there must be at least one handler")
  250. debugPrintRoute(method, path, handlers)
  251. root := engine.trees.get(method)
  252. if root == nil {
  253. root = new(node)
  254. root.fullPath = "/"
  255. engine.trees = append(engine.trees, methodTree{method: method, root: root})
  256. }
  257. root.addRoute(path, handlers)
  258. // Update maxParams
  259. if paramsCount := countParams(path); paramsCount > engine.maxParams {
  260. engine.maxParams = paramsCount
  261. }
  262. if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
  263. engine.maxSections = sectionsCount
  264. }
  265. }
  266. // Routes returns a slice of registered routes, including some useful information, such as:
  267. // the http method, path and the handler name.
  268. func (engine *Engine) Routes() (routes RoutesInfo) {
  269. for _, tree := range engine.trees {
  270. routes = iterate("", tree.method, routes, tree.root)
  271. }
  272. return routes
  273. }
  274. func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
  275. path += root.path
  276. if len(root.handlers) > 0 {
  277. handlerFunc := root.handlers.Last()
  278. routes = append(routes, RouteInfo{
  279. Method: method,
  280. Path: path,
  281. Handler: nameOfFunction(handlerFunc),
  282. HandlerFunc: handlerFunc,
  283. })
  284. }
  285. for _, child := range root.children {
  286. routes = iterate(path, method, routes, child)
  287. }
  288. return routes
  289. }
  290. // Run attaches the router to a http.Server and starts listening and serving HTTP requests.
  291. // It is a shortcut for http.ListenAndServe(addr, router)
  292. // Note: this method will block the calling goroutine indefinitely unless an error happens.
  293. func (engine *Engine) Run(addr ...string) (err error) {
  294. defer func() { debugPrintError(err) }()
  295. if engine.isUnsafeTrustedProxies() {
  296. debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  297. "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  298. }
  299. address := resolveAddress(addr)
  300. debugPrint("Listening and serving HTTP on %s\n", address)
  301. err = http.ListenAndServe(address, engine)
  302. return
  303. }
  304. func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
  305. if engine.trustedProxies == nil {
  306. return nil, nil
  307. }
  308. cidr := make([]*net.IPNet, 0, len(engine.trustedProxies))
  309. for _, trustedProxy := range engine.trustedProxies {
  310. if !strings.Contains(trustedProxy, "/") {
  311. ip := parseIP(trustedProxy)
  312. if ip == nil {
  313. return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy}
  314. }
  315. switch len(ip) {
  316. case net.IPv4len:
  317. trustedProxy += "/32"
  318. case net.IPv6len:
  319. trustedProxy += "/128"
  320. }
  321. }
  322. _, cidrNet, err := net.ParseCIDR(trustedProxy)
  323. if err != nil {
  324. return cidr, err
  325. }
  326. cidr = append(cidr, cidrNet)
  327. }
  328. return cidr, nil
  329. }
  330. // SetTrustedProxies set a list of network origins (IPv4 addresses,
  331. // IPv4 CIDRs, IPv6 addresses or IPv6 CIDRs) from which to trust
  332. // request's headers that contain alternative client IP when
  333. // `(*gin.Engine).ForwardedByClientIP` is `true`. `TrustedProxies`
  334. // feature is enabled by default, and it also trusts all proxies
  335. // by default. If you want to disable this feature, use
  336. // Engine.SetTrustedProxies(nil), then Context.ClientIP() will
  337. // return the remote address directly.
  338. func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
  339. engine.trustedProxies = trustedProxies
  340. return engine.parseTrustedProxies()
  341. }
  342. // isUnsafeTrustedProxies compares Engine.trustedCIDRs and defaultTrustedCIDRs, it's not safe if equal (returns true)
  343. func (engine *Engine) isUnsafeTrustedProxies() bool {
  344. return reflect.DeepEqual(engine.trustedCIDRs, defaultTrustedCIDRs)
  345. }
  346. // parseTrustedProxies parse Engine.trustedProxies to Engine.trustedCIDRs
  347. func (engine *Engine) parseTrustedProxies() error {
  348. trustedCIDRs, err := engine.prepareTrustedCIDRs()
  349. engine.trustedCIDRs = trustedCIDRs
  350. return err
  351. }
  352. // parseIP parse a string representation of an IP and returns a net.IP with the
  353. // minimum byte representation or nil if input is invalid.
  354. func parseIP(ip string) net.IP {
  355. parsedIP := net.ParseIP(ip)
  356. if ipv4 := parsedIP.To4(); ipv4 != nil {
  357. // return ip in a 4-byte representation
  358. return ipv4
  359. }
  360. // return ip in a 16-byte representation or nil
  361. return parsedIP
  362. }
  363. // RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
  364. // It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
  365. // Note: this method will block the calling goroutine indefinitely unless an error happens.
  366. func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
  367. debugPrint("Listening and serving HTTPS on %s\n", addr)
  368. defer func() { debugPrintError(err) }()
  369. if engine.isUnsafeTrustedProxies() {
  370. debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  371. "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  372. }
  373. err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
  374. return
  375. }
  376. // RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
  377. // through the specified unix socket (ie. a file).
  378. // Note: this method will block the calling goroutine indefinitely unless an error happens.
  379. func (engine *Engine) RunUnix(file string) (err error) {
  380. debugPrint("Listening and serving HTTP on unix:/%s", file)
  381. defer func() { debugPrintError(err) }()
  382. if engine.isUnsafeTrustedProxies() {
  383. debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  384. "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  385. }
  386. listener, err := net.Listen("unix", file)
  387. if err != nil {
  388. return
  389. }
  390. defer listener.Close()
  391. defer os.Remove(file)
  392. err = http.Serve(listener, engine)
  393. return
  394. }
  395. // RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
  396. // through the specified file descriptor.
  397. // Note: this method will block the calling goroutine indefinitely unless an error happens.
  398. func (engine *Engine) RunFd(fd int) (err error) {
  399. debugPrint("Listening and serving HTTP on fd@%d", fd)
  400. defer func() { debugPrintError(err) }()
  401. if engine.isUnsafeTrustedProxies() {
  402. debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  403. "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  404. }
  405. f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
  406. listener, err := net.FileListener(f)
  407. if err != nil {
  408. return
  409. }
  410. defer listener.Close()
  411. err = engine.RunListener(listener)
  412. return
  413. }
  414. // RunListener attaches the router to a http.Server and starts listening and serving HTTP requests
  415. // through the specified net.Listener
  416. func (engine *Engine) RunListener(listener net.Listener) (err error) {
  417. debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr())
  418. defer func() { debugPrintError(err) }()
  419. if engine.isUnsafeTrustedProxies() {
  420. debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  421. "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  422. }
  423. err = http.Serve(listener, engine)
  424. return
  425. }
  426. // ServeHTTP conforms to the http.Handler interface.
  427. func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  428. c := engine.pool.Get().(*Context)
  429. c.writermem.reset(w)
  430. c.Request = req
  431. c.reset()
  432. engine.handleHTTPRequest(c)
  433. engine.pool.Put(c)
  434. }
  435. // HandleContext re-enter a context that has been rewritten.
  436. // This can be done by setting c.Request.URL.Path to your new target.
  437. // Disclaimer: You can loop yourself to death with this, use wisely.
  438. func (engine *Engine) HandleContext(c *Context) {
  439. oldIndexValue := c.index
  440. c.reset()
  441. engine.handleHTTPRequest(c)
  442. c.index = oldIndexValue
  443. }
  444. func (engine *Engine) handleHTTPRequest(c *Context) {
  445. httpMethod := c.Request.Method
  446. rPath := c.Request.URL.Path
  447. unescape := false
  448. if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
  449. rPath = c.Request.URL.RawPath
  450. unescape = engine.UnescapePathValues
  451. }
  452. if engine.RemoveExtraSlash {
  453. rPath = cleanPath(rPath)
  454. }
  455. // Find root of the tree for the given HTTP method
  456. t := engine.trees
  457. for i, tl := 0, len(t); i < tl; i++ {
  458. if t[i].method != httpMethod {
  459. continue
  460. }
  461. root := t[i].root
  462. // Find route in tree
  463. value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
  464. if value.params != nil {
  465. c.Params = *value.params
  466. }
  467. if value.handlers != nil {
  468. c.handlers = value.handlers
  469. c.fullPath = value.fullPath
  470. c.Next()
  471. c.writermem.WriteHeaderNow()
  472. return
  473. }
  474. if httpMethod != "CONNECT" && rPath != "/" {
  475. if value.tsr && engine.RedirectTrailingSlash {
  476. redirectTrailingSlash(c)
  477. return
  478. }
  479. if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
  480. return
  481. }
  482. }
  483. break
  484. }
  485. if engine.HandleMethodNotAllowed {
  486. for _, tree := range engine.trees {
  487. if tree.method == httpMethod {
  488. continue
  489. }
  490. if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
  491. c.handlers = engine.allNoMethod
  492. serveError(c, http.StatusMethodNotAllowed, default405Body)
  493. return
  494. }
  495. }
  496. }
  497. c.handlers = engine.allNoRoute
  498. serveError(c, http.StatusNotFound, default404Body)
  499. }
  500. var mimePlain = []string{MIMEPlain}
  501. func serveError(c *Context, code int, defaultMessage []byte) {
  502. c.writermem.status = code
  503. c.Next()
  504. if c.writermem.Written() {
  505. return
  506. }
  507. if c.writermem.Status() == code {
  508. c.writermem.Header()["Content-Type"] = mimePlain
  509. _, err := c.Writer.Write(defaultMessage)
  510. if err != nil {
  511. debugPrint("cannot write message to writer during serve error: %v", err)
  512. }
  513. return
  514. }
  515. c.writermem.WriteHeaderNow()
  516. }
  517. func redirectTrailingSlash(c *Context) {
  518. req := c.Request
  519. p := req.URL.Path
  520. if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
  521. p = prefix + "/" + req.URL.Path
  522. }
  523. req.URL.Path = p + "/"
  524. if length := len(p); length > 1 && p[length-1] == '/' {
  525. req.URL.Path = p[:length-1]
  526. }
  527. redirectRequest(c)
  528. }
  529. func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
  530. req := c.Request
  531. rPath := req.URL.Path
  532. if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok {
  533. req.URL.Path = bytesconv.BytesToString(fixedPath)
  534. redirectRequest(c)
  535. return true
  536. }
  537. return false
  538. }
  539. func redirectRequest(c *Context) {
  540. req := c.Request
  541. rPath := req.URL.Path
  542. rURL := req.URL.String()
  543. code := http.StatusMovedPermanently // Permanent redirect, request with GET method
  544. if req.Method != http.MethodGet {
  545. code = http.StatusTemporaryRedirect
  546. }
  547. debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL)
  548. http.Redirect(c.Writer, req, rURL, code)
  549. c.writermem.WriteHeaderNow()
  550. }