helpers.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. // ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
  2. // 🤖 Github Repository: https://github.com/gofiber/fiber
  3. // 📌 API Documentation: https://docs.gofiber.io
  4. package fiber
  5. import (
  6. "bytes"
  7. "crypto/tls"
  8. "fmt"
  9. "hash/crc32"
  10. "io"
  11. "net"
  12. "os"
  13. "path/filepath"
  14. "reflect"
  15. "strings"
  16. "time"
  17. "unsafe"
  18. "github.com/gofiber/fiber/v2/log"
  19. "github.com/gofiber/fiber/v2/utils"
  20. "github.com/valyala/bytebufferpool"
  21. "github.com/valyala/fasthttp"
  22. )
  23. // acceptType is a struct that holds the parsed value of an Accept header
  24. // along with quality, specificity, and order.
  25. // used for sorting accept headers.
  26. type acceptedType struct {
  27. spec string
  28. quality float64
  29. specificity int
  30. order int
  31. }
  32. // getTLSConfig returns a net listener's tls config
  33. func getTLSConfig(ln net.Listener) *tls.Config {
  34. // Get listener type
  35. pointer := reflect.ValueOf(ln)
  36. // Is it a tls.listener?
  37. if pointer.String() == "<*tls.listener Value>" {
  38. // Copy value from pointer
  39. if val := reflect.Indirect(pointer); val.Type() != nil {
  40. // Get private field from value
  41. if field := val.FieldByName("config"); field.Type() != nil {
  42. // Copy value from pointer field (unsafe)
  43. newval := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())) //nolint:gosec // Probably the only way to extract the *tls.Config from a net.Listener. TODO: Verify there really is no easier way without using unsafe.
  44. if newval.Type() != nil {
  45. // Get element from pointer
  46. if elem := newval.Elem(); elem.Type() != nil {
  47. // Cast value to *tls.Config
  48. c, ok := elem.Interface().(*tls.Config)
  49. if !ok {
  50. panic(fmt.Errorf("failed to type-assert to *tls.Config"))
  51. }
  52. return c
  53. }
  54. }
  55. }
  56. }
  57. }
  58. return nil
  59. }
  60. // readContent opens a named file and read content from it
  61. func readContent(rf io.ReaderFrom, name string) (int64, error) {
  62. // Read file
  63. f, err := os.Open(filepath.Clean(name))
  64. if err != nil {
  65. return 0, fmt.Errorf("failed to open: %w", err)
  66. }
  67. defer func() {
  68. if err = f.Close(); err != nil {
  69. log.Errorf("Error closing file: %s", err)
  70. }
  71. }()
  72. if n, err := rf.ReadFrom(f); err != nil {
  73. return n, fmt.Errorf("failed to read: %w", err)
  74. }
  75. return 0, nil
  76. }
  77. // quoteString escape special characters in a given string
  78. func (app *App) quoteString(raw string) string {
  79. bb := bytebufferpool.Get()
  80. // quoted := string(fasthttp.AppendQuotedArg(bb.B, getBytes(raw)))
  81. quoted := app.getString(fasthttp.AppendQuotedArg(bb.B, app.getBytes(raw)))
  82. bytebufferpool.Put(bb)
  83. return quoted
  84. }
  85. // Scan stack if other methods match the request
  86. func (app *App) methodExist(ctx *Ctx) bool {
  87. var exists bool
  88. methods := app.config.RequestMethods
  89. for i := 0; i < len(methods); i++ {
  90. // Skip original method
  91. if ctx.methodINT == i {
  92. continue
  93. }
  94. // Reset stack index
  95. indexRoute := -1
  96. tree, ok := ctx.app.treeStack[i][ctx.treePath]
  97. if !ok {
  98. tree = ctx.app.treeStack[i][""]
  99. }
  100. // Get stack length
  101. lenr := len(tree) - 1
  102. // Loop over the route stack starting from previous index
  103. for indexRoute < lenr {
  104. // Increment route index
  105. indexRoute++
  106. // Get *Route
  107. route := tree[indexRoute]
  108. // Skip use routes
  109. if route.use {
  110. continue
  111. }
  112. // Check if it matches the request path
  113. match := route.match(ctx.detectionPath, ctx.path, &ctx.values)
  114. // No match, next route
  115. if match {
  116. // We matched
  117. exists = true
  118. // Add method to Allow header
  119. ctx.Append(HeaderAllow, methods[i])
  120. // Break stack loop
  121. break
  122. }
  123. }
  124. }
  125. return exists
  126. }
  127. // uniqueRouteStack drop all not unique routes from the slice
  128. func uniqueRouteStack(stack []*Route) []*Route {
  129. var unique []*Route
  130. m := make(map[*Route]int)
  131. for _, v := range stack {
  132. if _, ok := m[v]; !ok {
  133. // Unique key found. Record position and collect
  134. // in result.
  135. m[v] = len(unique)
  136. unique = append(unique, v)
  137. }
  138. }
  139. return unique
  140. }
  141. // defaultString returns the value or a default value if it is set
  142. func defaultString(value string, defaultValue []string) string {
  143. if len(value) == 0 && len(defaultValue) > 0 {
  144. return defaultValue[0]
  145. }
  146. return value
  147. }
  148. const normalizedHeaderETag = "Etag"
  149. // Generate and set ETag header to response
  150. func setETag(c *Ctx, weak bool) { //nolint: revive // Accepting a bool param is fine here
  151. // Don't generate ETags for invalid responses
  152. if c.fasthttp.Response.StatusCode() != StatusOK {
  153. return
  154. }
  155. body := c.fasthttp.Response.Body()
  156. // Skips ETag if no response body is present
  157. if len(body) == 0 {
  158. return
  159. }
  160. // Get ETag header from request
  161. clientEtag := c.Get(HeaderIfNoneMatch)
  162. // Generate ETag for response
  163. const pol = 0xD5828281
  164. crc32q := crc32.MakeTable(pol)
  165. etag := fmt.Sprintf("\"%d-%v\"", len(body), crc32.Checksum(body, crc32q))
  166. // Enable weak tag
  167. if weak {
  168. etag = "W/" + etag
  169. }
  170. // Check if client's ETag is weak
  171. if strings.HasPrefix(clientEtag, "W/") {
  172. // Check if server's ETag is weak
  173. if clientEtag[2:] == etag || clientEtag[2:] == etag[2:] {
  174. // W/1 == 1 || W/1 == W/1
  175. if err := c.SendStatus(StatusNotModified); err != nil {
  176. log.Errorf("setETag: failed to SendStatus: %v", err)
  177. }
  178. c.fasthttp.ResetBody()
  179. return
  180. }
  181. // W/1 != W/2 || W/1 != 2
  182. c.setCanonical(normalizedHeaderETag, etag)
  183. return
  184. }
  185. if strings.Contains(clientEtag, etag) {
  186. // 1 == 1
  187. if err := c.SendStatus(StatusNotModified); err != nil {
  188. log.Errorf("setETag: failed to SendStatus: %v", err)
  189. }
  190. c.fasthttp.ResetBody()
  191. return
  192. }
  193. // 1 != 2
  194. c.setCanonical(normalizedHeaderETag, etag)
  195. }
  196. func getGroupPath(prefix, path string) string {
  197. if len(path) == 0 {
  198. return prefix
  199. }
  200. if path[0] != '/' {
  201. path = "/" + path
  202. }
  203. return utils.TrimRight(prefix, '/') + path
  204. }
  205. // acceptsOffer This function determines if an offer matches a given specification.
  206. // It checks if the specification ends with a '*' or if the offer has the prefix of the specification.
  207. // Returns true if the offer matches the specification, false otherwise.
  208. func acceptsOffer(spec, offer string) bool {
  209. if len(spec) >= 1 && spec[len(spec)-1] == '*' {
  210. return true
  211. } else if strings.HasPrefix(spec, offer) {
  212. return true
  213. }
  214. return false
  215. }
  216. // acceptsOfferType This function determines if an offer type matches a given specification.
  217. // It checks if the specification is equal to */* (i.e., all types are accepted).
  218. // It gets the MIME type of the offer (either from the offer itself or by its file extension).
  219. // It checks if the offer MIME type matches the specification MIME type or if the specification is of the form <MIME_type>/* and the offer MIME type has the same MIME type.
  220. // Returns true if the offer type matches the specification, false otherwise.
  221. func acceptsOfferType(spec, offerType string) bool {
  222. // Accept: */*
  223. if spec == "*/*" {
  224. return true
  225. }
  226. var mimetype string
  227. if strings.IndexByte(offerType, '/') != -1 {
  228. mimetype = offerType // MIME type
  229. } else {
  230. mimetype = utils.GetMIME(offerType) // extension
  231. }
  232. if spec == mimetype {
  233. // Accept: <MIME_type>/<MIME_subtype>
  234. return true
  235. }
  236. s := strings.IndexByte(mimetype, '/')
  237. // Accept: <MIME_type>/*
  238. if strings.HasPrefix(spec, mimetype[:s]) && (spec[s:] == "/*" || mimetype[s:] == "/*") {
  239. return true
  240. }
  241. return false
  242. }
  243. // getSplicedStrList function takes a string and a string slice as an argument, divides the string into different
  244. // elements divided by ',' and stores these elements in the string slice.
  245. // It returns the populated string slice as an output.
  246. //
  247. // If the given slice hasn't enough space, it will allocate more and return.
  248. func getSplicedStrList(headerValue string, dst []string) []string {
  249. if headerValue == "" {
  250. return nil
  251. }
  252. var (
  253. index int
  254. character rune
  255. lastElementEndsAt uint8
  256. insertIndex int
  257. )
  258. for index, character = range headerValue + "$" {
  259. if character == ',' || index == len(headerValue) {
  260. if insertIndex >= len(dst) {
  261. oldSlice := dst
  262. dst = make([]string, len(dst)+(len(dst)>>1)+2)
  263. copy(dst, oldSlice)
  264. }
  265. dst[insertIndex] = utils.TrimLeft(headerValue[lastElementEndsAt:index], ' ')
  266. lastElementEndsAt = uint8(index + 1)
  267. insertIndex++
  268. }
  269. }
  270. if len(dst) > insertIndex {
  271. dst = dst[:insertIndex]
  272. }
  273. return dst
  274. }
  275. // getOffer return valid offer for header negotiation
  276. func getOffer(header string, isAccepted func(spec, offer string) bool, offers ...string) string {
  277. if len(offers) == 0 {
  278. return ""
  279. }
  280. if header == "" {
  281. return offers[0]
  282. }
  283. // Parse header and get accepted types with their quality and specificity
  284. // See: https://www.rfc-editor.org/rfc/rfc9110#name-content-negotiation-fields
  285. spec, commaPos, order := "", 0, 0
  286. acceptedTypes := make([]acceptedType, 0, 20)
  287. for len(header) > 0 {
  288. order++
  289. // Skip spaces
  290. header = utils.TrimLeft(header, ' ')
  291. // Get spec
  292. commaPos = strings.IndexByte(header, ',')
  293. if commaPos != -1 {
  294. spec = utils.Trim(header[:commaPos], ' ')
  295. } else {
  296. spec = utils.TrimLeft(header, ' ')
  297. }
  298. // Get quality
  299. quality := 1.0
  300. if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 {
  301. factor := utils.Trim(spec[factorSign+1:], ' ')
  302. if strings.HasPrefix(factor, "q=") {
  303. if q, err := fasthttp.ParseUfloat(utils.UnsafeBytes(factor[2:])); err == nil {
  304. quality = q
  305. }
  306. }
  307. spec = spec[:factorSign]
  308. }
  309. // Skip if quality is 0.0
  310. // See: https://www.rfc-editor.org/rfc/rfc9110#quality.values
  311. if quality == 0.0 {
  312. if commaPos != -1 {
  313. header = header[commaPos+1:]
  314. } else {
  315. break
  316. }
  317. continue
  318. }
  319. // Get specificity
  320. specificity := 0
  321. // check for wildcard this could be a mime */* or a wildcard character *
  322. if spec == "*/*" || spec == "*" {
  323. specificity = 1
  324. } else if strings.HasSuffix(spec, "/*") {
  325. specificity = 2
  326. } else if strings.IndexByte(spec, '/') != -1 {
  327. specificity = 3
  328. } else {
  329. specificity = 4
  330. }
  331. // Add to accepted types
  332. acceptedTypes = append(acceptedTypes, acceptedType{spec, quality, specificity, order})
  333. // Next
  334. if commaPos != -1 {
  335. header = header[commaPos+1:]
  336. } else {
  337. break
  338. }
  339. }
  340. if len(acceptedTypes) > 1 {
  341. // Sort accepted types by quality and specificity, preserving order of equal elements
  342. sortAcceptedTypes(&acceptedTypes)
  343. }
  344. // Find the first offer that matches the accepted types
  345. for _, acceptedType := range acceptedTypes {
  346. for _, offer := range offers {
  347. if len(offer) == 0 {
  348. continue
  349. }
  350. if isAccepted(acceptedType.spec, offer) {
  351. return offer
  352. }
  353. }
  354. }
  355. return ""
  356. }
  357. // sortAcceptedTypes sorts accepted types by quality and specificity, preserving order of equal elements
  358. //
  359. // Parameters are not supported, they are ignored when sorting by specificity.
  360. //
  361. // See: https://www.rfc-editor.org/rfc/rfc9110#name-content-negotiation-fields
  362. func sortAcceptedTypes(at *[]acceptedType) {
  363. if at == nil || len(*at) < 2 {
  364. return
  365. }
  366. acceptedTypes := *at
  367. for i := 1; i < len(acceptedTypes); i++ {
  368. lo, hi := 0, i-1
  369. for lo <= hi {
  370. mid := (lo + hi) / 2
  371. if acceptedTypes[i].quality < acceptedTypes[mid].quality ||
  372. (acceptedTypes[i].quality == acceptedTypes[mid].quality && acceptedTypes[i].specificity < acceptedTypes[mid].specificity) ||
  373. (acceptedTypes[i].quality == acceptedTypes[mid].quality && acceptedTypes[i].specificity == acceptedTypes[mid].specificity && acceptedTypes[i].order > acceptedTypes[mid].order) {
  374. lo = mid + 1
  375. } else {
  376. hi = mid - 1
  377. }
  378. }
  379. for j := i; j > lo; j-- {
  380. acceptedTypes[j-1], acceptedTypes[j] = acceptedTypes[j], acceptedTypes[j-1]
  381. }
  382. }
  383. }
  384. func matchEtag(s, etag string) bool {
  385. if s == etag || s == "W/"+etag || "W/"+s == etag {
  386. return true
  387. }
  388. return false
  389. }
  390. func (app *App) isEtagStale(etag string, noneMatchBytes []byte) bool {
  391. var start, end int
  392. // Adapted from:
  393. // https://github.com/jshttp/fresh/blob/10e0471669dbbfbfd8de65bc6efac2ddd0bfa057/index.js#L110
  394. for i := range noneMatchBytes {
  395. switch noneMatchBytes[i] {
  396. case 0x20:
  397. if start == end {
  398. start = i + 1
  399. end = i + 1
  400. }
  401. case 0x2c:
  402. if matchEtag(app.getString(noneMatchBytes[start:end]), etag) {
  403. return false
  404. }
  405. start = i + 1
  406. end = i + 1
  407. default:
  408. end = i + 1
  409. }
  410. }
  411. return !matchEtag(app.getString(noneMatchBytes[start:end]), etag)
  412. }
  413. func parseAddr(raw string) (string, string) { //nolint:revive // Returns (host, port)
  414. if i := strings.LastIndex(raw, ":"); i != -1 {
  415. return raw[:i], raw[i+1:]
  416. }
  417. return raw, ""
  418. }
  419. const noCacheValue = "no-cache"
  420. // isNoCache checks if the cacheControl header value is a `no-cache`.
  421. func isNoCache(cacheControl string) bool {
  422. i := strings.Index(cacheControl, noCacheValue)
  423. if i == -1 {
  424. return false
  425. }
  426. // Xno-cache
  427. if i > 0 && !(cacheControl[i-1] == ' ' || cacheControl[i-1] == ',') {
  428. return false
  429. }
  430. // bla bla, no-cache
  431. if i+len(noCacheValue) == len(cacheControl) {
  432. return true
  433. }
  434. // bla bla, no-cacheX
  435. if cacheControl[i+len(noCacheValue)] != ',' {
  436. return false
  437. }
  438. // OK
  439. return true
  440. }
  441. type testConn struct {
  442. r bytes.Buffer
  443. w bytes.Buffer
  444. }
  445. func (c *testConn) Read(b []byte) (int, error) { return c.r.Read(b) } //nolint:wrapcheck // This must not be wrapped
  446. func (c *testConn) Write(b []byte) (int, error) { return c.w.Write(b) } //nolint:wrapcheck // This must not be wrapped
  447. func (*testConn) Close() error { return nil }
  448. func (*testConn) LocalAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} }
  449. func (*testConn) RemoteAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} }
  450. func (*testConn) SetDeadline(_ time.Time) error { return nil }
  451. func (*testConn) SetReadDeadline(_ time.Time) error { return nil }
  452. func (*testConn) SetWriteDeadline(_ time.Time) error { return nil }
  453. func getStringImmutable(b []byte) string {
  454. return string(b)
  455. }
  456. func getBytesImmutable(s string) []byte {
  457. return []byte(s)
  458. }
  459. // HTTP methods and their unique INTs
  460. func (app *App) methodInt(s string) int {
  461. // For better performance
  462. if len(app.configured.RequestMethods) == 0 {
  463. // TODO: Use iota instead
  464. switch s {
  465. case MethodGet:
  466. return 0
  467. case MethodHead:
  468. return 1
  469. case MethodPost:
  470. return 2
  471. case MethodPut:
  472. return 3
  473. case MethodDelete:
  474. return 4
  475. case MethodConnect:
  476. return 5
  477. case MethodOptions:
  478. return 6
  479. case MethodTrace:
  480. return 7
  481. case MethodPatch:
  482. return 8
  483. default:
  484. return -1
  485. }
  486. }
  487. // For method customization
  488. for i, v := range app.config.RequestMethods {
  489. if s == v {
  490. return i
  491. }
  492. }
  493. return -1
  494. }
  495. // IsMethodSafe reports whether the HTTP method is considered safe.
  496. // See https://datatracker.ietf.org/doc/html/rfc9110#section-9.2.1
  497. func IsMethodSafe(m string) bool {
  498. switch m {
  499. case MethodGet,
  500. MethodHead,
  501. MethodOptions,
  502. MethodTrace:
  503. return true
  504. default:
  505. return false
  506. }
  507. }
  508. // IsMethodIdempotent reports whether the HTTP method is considered idempotent.
  509. // See https://datatracker.ietf.org/doc/html/rfc9110#section-9.2.2
  510. func IsMethodIdempotent(m string) bool {
  511. if IsMethodSafe(m) {
  512. return true
  513. }
  514. switch m {
  515. case MethodPut, MethodDelete:
  516. return true
  517. default:
  518. return false
  519. }
  520. }
  521. // HTTP methods were copied from net/http.
  522. const (
  523. MethodGet = "GET" // RFC 7231, 4.3.1
  524. MethodHead = "HEAD" // RFC 7231, 4.3.2
  525. MethodPost = "POST" // RFC 7231, 4.3.3
  526. MethodPut = "PUT" // RFC 7231, 4.3.4
  527. MethodPatch = "PATCH" // RFC 5789
  528. MethodDelete = "DELETE" // RFC 7231, 4.3.5
  529. MethodConnect = "CONNECT" // RFC 7231, 4.3.6
  530. MethodOptions = "OPTIONS" // RFC 7231, 4.3.7
  531. MethodTrace = "TRACE" // RFC 7231, 4.3.8
  532. methodUse = "USE"
  533. )
  534. // MIME types that are commonly used
  535. const (
  536. MIMETextXML = "text/xml"
  537. MIMETextHTML = "text/html"
  538. MIMETextPlain = "text/plain"
  539. MIMETextJavaScript = "text/javascript"
  540. MIMEApplicationXML = "application/xml"
  541. MIMEApplicationJSON = "application/json"
  542. // Deprecated: use MIMETextJavaScript instead
  543. MIMEApplicationJavaScript = "application/javascript"
  544. MIMEApplicationForm = "application/x-www-form-urlencoded"
  545. MIMEOctetStream = "application/octet-stream"
  546. MIMEMultipartForm = "multipart/form-data"
  547. MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8"
  548. MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8"
  549. MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8"
  550. MIMETextJavaScriptCharsetUTF8 = "text/javascript; charset=utf-8"
  551. MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8"
  552. MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8"
  553. // Deprecated: use MIMETextJavaScriptCharsetUTF8 instead
  554. MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=utf-8"
  555. )
  556. // HTTP status codes were copied from net/http with the following updates:
  557. // - Rename StatusNonAuthoritativeInfo to StatusNonAuthoritativeInformation
  558. // - Add StatusSwitchProxy (306)
  559. // NOTE: Keep this list in sync with statusMessage
  560. const (
  561. StatusContinue = 100 // RFC 9110, 15.2.1
  562. StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2
  563. StatusProcessing = 102 // RFC 2518, 10.1
  564. StatusEarlyHints = 103 // RFC 8297
  565. StatusOK = 200 // RFC 9110, 15.3.1
  566. StatusCreated = 201 // RFC 9110, 15.3.2
  567. StatusAccepted = 202 // RFC 9110, 15.3.3
  568. StatusNonAuthoritativeInformation = 203 // RFC 9110, 15.3.4
  569. StatusNoContent = 204 // RFC 9110, 15.3.5
  570. StatusResetContent = 205 // RFC 9110, 15.3.6
  571. StatusPartialContent = 206 // RFC 9110, 15.3.7
  572. StatusMultiStatus = 207 // RFC 4918, 11.1
  573. StatusAlreadyReported = 208 // RFC 5842, 7.1
  574. StatusIMUsed = 226 // RFC 3229, 10.4.1
  575. StatusMultipleChoices = 300 // RFC 9110, 15.4.1
  576. StatusMovedPermanently = 301 // RFC 9110, 15.4.2
  577. StatusFound = 302 // RFC 9110, 15.4.3
  578. StatusSeeOther = 303 // RFC 9110, 15.4.4
  579. StatusNotModified = 304 // RFC 9110, 15.4.5
  580. StatusUseProxy = 305 // RFC 9110, 15.4.6
  581. StatusSwitchProxy = 306 // RFC 9110, 15.4.7 (Unused)
  582. StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8
  583. StatusPermanentRedirect = 308 // RFC 9110, 15.4.9
  584. StatusBadRequest = 400 // RFC 9110, 15.5.1
  585. StatusUnauthorized = 401 // RFC 9110, 15.5.2
  586. StatusPaymentRequired = 402 // RFC 9110, 15.5.3
  587. StatusForbidden = 403 // RFC 9110, 15.5.4
  588. StatusNotFound = 404 // RFC 9110, 15.5.5
  589. StatusMethodNotAllowed = 405 // RFC 9110, 15.5.6
  590. StatusNotAcceptable = 406 // RFC 9110, 15.5.7
  591. StatusProxyAuthRequired = 407 // RFC 9110, 15.5.8
  592. StatusRequestTimeout = 408 // RFC 9110, 15.5.9
  593. StatusConflict = 409 // RFC 9110, 15.5.10
  594. StatusGone = 410 // RFC 9110, 15.5.11
  595. StatusLengthRequired = 411 // RFC 9110, 15.5.12
  596. StatusPreconditionFailed = 412 // RFC 9110, 15.5.13
  597. StatusRequestEntityTooLarge = 413 // RFC 9110, 15.5.14
  598. StatusRequestURITooLong = 414 // RFC 9110, 15.5.15
  599. StatusUnsupportedMediaType = 415 // RFC 9110, 15.5.16
  600. StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17
  601. StatusExpectationFailed = 417 // RFC 9110, 15.5.18
  602. StatusTeapot = 418 // RFC 9110, 15.5.19 (Unused)
  603. StatusMisdirectedRequest = 421 // RFC 9110, 15.5.20
  604. StatusUnprocessableEntity = 422 // RFC 9110, 15.5.21
  605. StatusLocked = 423 // RFC 4918, 11.3
  606. StatusFailedDependency = 424 // RFC 4918, 11.4
  607. StatusTooEarly = 425 // RFC 8470, 5.2.
  608. StatusUpgradeRequired = 426 // RFC 9110, 15.5.22
  609. StatusPreconditionRequired = 428 // RFC 6585, 3
  610. StatusTooManyRequests = 429 // RFC 6585, 4
  611. StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5
  612. StatusUnavailableForLegalReasons = 451 // RFC 7725, 3
  613. StatusInternalServerError = 500 // RFC 9110, 15.6.1
  614. StatusNotImplemented = 501 // RFC 9110, 15.6.2
  615. StatusBadGateway = 502 // RFC 9110, 15.6.3
  616. StatusServiceUnavailable = 503 // RFC 9110, 15.6.4
  617. StatusGatewayTimeout = 504 // RFC 9110, 15.6.5
  618. StatusHTTPVersionNotSupported = 505 // RFC 9110, 15.6.6
  619. StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1
  620. StatusInsufficientStorage = 507 // RFC 4918, 11.5
  621. StatusLoopDetected = 508 // RFC 5842, 7.2
  622. StatusNotExtended = 510 // RFC 2774, 7
  623. StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
  624. )
  625. // Errors
  626. var (
  627. ErrBadRequest = NewError(StatusBadRequest) // 400
  628. ErrUnauthorized = NewError(StatusUnauthorized) // 401
  629. ErrPaymentRequired = NewError(StatusPaymentRequired) // 402
  630. ErrForbidden = NewError(StatusForbidden) // 403
  631. ErrNotFound = NewError(StatusNotFound) // 404
  632. ErrMethodNotAllowed = NewError(StatusMethodNotAllowed) // 405
  633. ErrNotAcceptable = NewError(StatusNotAcceptable) // 406
  634. ErrProxyAuthRequired = NewError(StatusProxyAuthRequired) // 407
  635. ErrRequestTimeout = NewError(StatusRequestTimeout) // 408
  636. ErrConflict = NewError(StatusConflict) // 409
  637. ErrGone = NewError(StatusGone) // 410
  638. ErrLengthRequired = NewError(StatusLengthRequired) // 411
  639. ErrPreconditionFailed = NewError(StatusPreconditionFailed) // 412
  640. ErrRequestEntityTooLarge = NewError(StatusRequestEntityTooLarge) // 413
  641. ErrRequestURITooLong = NewError(StatusRequestURITooLong) // 414
  642. ErrUnsupportedMediaType = NewError(StatusUnsupportedMediaType) // 415
  643. ErrRequestedRangeNotSatisfiable = NewError(StatusRequestedRangeNotSatisfiable) // 416
  644. ErrExpectationFailed = NewError(StatusExpectationFailed) // 417
  645. ErrTeapot = NewError(StatusTeapot) // 418
  646. ErrMisdirectedRequest = NewError(StatusMisdirectedRequest) // 421
  647. ErrUnprocessableEntity = NewError(StatusUnprocessableEntity) // 422
  648. ErrLocked = NewError(StatusLocked) // 423
  649. ErrFailedDependency = NewError(StatusFailedDependency) // 424
  650. ErrTooEarly = NewError(StatusTooEarly) // 425
  651. ErrUpgradeRequired = NewError(StatusUpgradeRequired) // 426
  652. ErrPreconditionRequired = NewError(StatusPreconditionRequired) // 428
  653. ErrTooManyRequests = NewError(StatusTooManyRequests) // 429
  654. ErrRequestHeaderFieldsTooLarge = NewError(StatusRequestHeaderFieldsTooLarge) // 431
  655. ErrUnavailableForLegalReasons = NewError(StatusUnavailableForLegalReasons) // 451
  656. ErrInternalServerError = NewError(StatusInternalServerError) // 500
  657. ErrNotImplemented = NewError(StatusNotImplemented) // 501
  658. ErrBadGateway = NewError(StatusBadGateway) // 502
  659. ErrServiceUnavailable = NewError(StatusServiceUnavailable) // 503
  660. ErrGatewayTimeout = NewError(StatusGatewayTimeout) // 504
  661. ErrHTTPVersionNotSupported = NewError(StatusHTTPVersionNotSupported) // 505
  662. ErrVariantAlsoNegotiates = NewError(StatusVariantAlsoNegotiates) // 506
  663. ErrInsufficientStorage = NewError(StatusInsufficientStorage) // 507
  664. ErrLoopDetected = NewError(StatusLoopDetected) // 508
  665. ErrNotExtended = NewError(StatusNotExtended) // 510
  666. ErrNetworkAuthenticationRequired = NewError(StatusNetworkAuthenticationRequired) // 511
  667. )
  668. // HTTP Headers were copied from net/http.
  669. const (
  670. HeaderAuthorization = "Authorization"
  671. HeaderProxyAuthenticate = "Proxy-Authenticate"
  672. HeaderProxyAuthorization = "Proxy-Authorization"
  673. HeaderWWWAuthenticate = "WWW-Authenticate"
  674. HeaderAge = "Age"
  675. HeaderCacheControl = "Cache-Control"
  676. HeaderClearSiteData = "Clear-Site-Data"
  677. HeaderExpires = "Expires"
  678. HeaderPragma = "Pragma"
  679. HeaderWarning = "Warning"
  680. HeaderAcceptCH = "Accept-CH"
  681. HeaderAcceptCHLifetime = "Accept-CH-Lifetime"
  682. HeaderContentDPR = "Content-DPR"
  683. HeaderDPR = "DPR"
  684. HeaderEarlyData = "Early-Data"
  685. HeaderSaveData = "Save-Data"
  686. HeaderViewportWidth = "Viewport-Width"
  687. HeaderWidth = "Width"
  688. HeaderETag = "ETag"
  689. HeaderIfMatch = "If-Match"
  690. HeaderIfModifiedSince = "If-Modified-Since"
  691. HeaderIfNoneMatch = "If-None-Match"
  692. HeaderIfUnmodifiedSince = "If-Unmodified-Since"
  693. HeaderLastModified = "Last-Modified"
  694. HeaderVary = "Vary"
  695. HeaderConnection = "Connection"
  696. HeaderKeepAlive = "Keep-Alive"
  697. HeaderAccept = "Accept"
  698. HeaderAcceptCharset = "Accept-Charset"
  699. HeaderAcceptEncoding = "Accept-Encoding"
  700. HeaderAcceptLanguage = "Accept-Language"
  701. HeaderCookie = "Cookie"
  702. HeaderExpect = "Expect"
  703. HeaderMaxForwards = "Max-Forwards"
  704. HeaderSetCookie = "Set-Cookie"
  705. HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
  706. HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
  707. HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
  708. HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
  709. HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
  710. HeaderAccessControlMaxAge = "Access-Control-Max-Age"
  711. HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
  712. HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
  713. HeaderOrigin = "Origin"
  714. HeaderTimingAllowOrigin = "Timing-Allow-Origin"
  715. HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies"
  716. HeaderDNT = "DNT"
  717. HeaderTk = "Tk"
  718. HeaderContentDisposition = "Content-Disposition"
  719. HeaderContentEncoding = "Content-Encoding"
  720. HeaderContentLanguage = "Content-Language"
  721. HeaderContentLength = "Content-Length"
  722. HeaderContentLocation = "Content-Location"
  723. HeaderContentType = "Content-Type"
  724. HeaderForwarded = "Forwarded"
  725. HeaderVia = "Via"
  726. HeaderXForwardedFor = "X-Forwarded-For"
  727. HeaderXForwardedHost = "X-Forwarded-Host"
  728. HeaderXForwardedProto = "X-Forwarded-Proto"
  729. HeaderXForwardedProtocol = "X-Forwarded-Protocol"
  730. HeaderXForwardedSsl = "X-Forwarded-Ssl"
  731. HeaderXUrlScheme = "X-Url-Scheme"
  732. HeaderLocation = "Location"
  733. HeaderFrom = "From"
  734. HeaderHost = "Host"
  735. HeaderReferer = "Referer"
  736. HeaderReferrerPolicy = "Referrer-Policy"
  737. HeaderUserAgent = "User-Agent"
  738. HeaderAllow = "Allow"
  739. HeaderServer = "Server"
  740. HeaderAcceptRanges = "Accept-Ranges"
  741. HeaderContentRange = "Content-Range"
  742. HeaderIfRange = "If-Range"
  743. HeaderRange = "Range"
  744. HeaderContentSecurityPolicy = "Content-Security-Policy"
  745. HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
  746. HeaderCrossOriginResourcePolicy = "Cross-Origin-Resource-Policy"
  747. HeaderExpectCT = "Expect-CT"
  748. // Deprecated: use HeaderPermissionsPolicy instead
  749. HeaderFeaturePolicy = "Feature-Policy"
  750. HeaderPermissionsPolicy = "Permissions-Policy"
  751. HeaderPublicKeyPins = "Public-Key-Pins"
  752. HeaderPublicKeyPinsReportOnly = "Public-Key-Pins-Report-Only"
  753. HeaderStrictTransportSecurity = "Strict-Transport-Security"
  754. HeaderUpgradeInsecureRequests = "Upgrade-Insecure-Requests"
  755. HeaderXContentTypeOptions = "X-Content-Type-Options"
  756. HeaderXDownloadOptions = "X-Download-Options"
  757. HeaderXFrameOptions = "X-Frame-Options"
  758. HeaderXPoweredBy = "X-Powered-By"
  759. HeaderXXSSProtection = "X-XSS-Protection"
  760. HeaderLastEventID = "Last-Event-ID"
  761. HeaderNEL = "NEL"
  762. HeaderPingFrom = "Ping-From"
  763. HeaderPingTo = "Ping-To"
  764. HeaderReportTo = "Report-To"
  765. HeaderTE = "TE"
  766. HeaderTrailer = "Trailer"
  767. HeaderTransferEncoding = "Transfer-Encoding"
  768. HeaderSecWebSocketAccept = "Sec-WebSocket-Accept"
  769. HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions"
  770. HeaderSecWebSocketKey = "Sec-WebSocket-Key"
  771. HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol"
  772. HeaderSecWebSocketVersion = "Sec-WebSocket-Version"
  773. HeaderAcceptPatch = "Accept-Patch"
  774. HeaderAcceptPushPolicy = "Accept-Push-Policy"
  775. HeaderAcceptSignature = "Accept-Signature"
  776. HeaderAltSvc = "Alt-Svc"
  777. HeaderDate = "Date"
  778. HeaderIndex = "Index"
  779. HeaderLargeAllocation = "Large-Allocation"
  780. HeaderLink = "Link"
  781. HeaderPushPolicy = "Push-Policy"
  782. HeaderRetryAfter = "Retry-After"
  783. HeaderServerTiming = "Server-Timing"
  784. HeaderSignature = "Signature"
  785. HeaderSignedHeaders = "Signed-Headers"
  786. HeaderSourceMap = "SourceMap"
  787. HeaderUpgrade = "Upgrade"
  788. HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control"
  789. HeaderXPingback = "X-Pingback"
  790. HeaderXRequestID = "X-Request-ID"
  791. HeaderXRequestedWith = "X-Requested-With"
  792. HeaderXRobotsTag = "X-Robots-Tag"
  793. HeaderXUACompatible = "X-UA-Compatible"
  794. )
  795. // Network types that are commonly used
  796. const (
  797. NetworkTCP = "tcp"
  798. NetworkTCP4 = "tcp4"
  799. NetworkTCP6 = "tcp6"
  800. )
  801. // Compression types
  802. const (
  803. StrGzip = "gzip"
  804. StrBr = "br"
  805. StrDeflate = "deflate"
  806. StrBrotli = "brotli"
  807. )
  808. // Cookie SameSite
  809. // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7
  810. const (
  811. CookieSameSiteDisabled = "disabled" // not in RFC, just control "SameSite" attribute will not be set.
  812. CookieSameSiteLaxMode = "lax"
  813. CookieSameSiteStrictMode = "strict"
  814. CookieSameSiteNoneMode = "none"
  815. )
  816. // Route Constraints
  817. const (
  818. ConstraintInt = "int"
  819. ConstraintBool = "bool"
  820. ConstraintFloat = "float"
  821. ConstraintAlpha = "alpha"
  822. ConstraintGuid = "guid" //nolint:revive,stylecheck // TODO: Rename to "ConstraintGUID" in v3
  823. ConstraintMinLen = "minLen"
  824. ConstraintMaxLen = "maxLen"
  825. ConstraintLen = "len"
  826. ConstraintBetweenLen = "betweenLen"
  827. ConstraintMinLenLower = "minlen"
  828. ConstraintMaxLenLower = "maxlen"
  829. ConstraintBetweenLenLower = "betweenlen"
  830. ConstraintMin = "min"
  831. ConstraintMax = "max"
  832. ConstraintRange = "range"
  833. ConstraintDatetime = "datetime"
  834. ConstraintRegex = "regex"
  835. )
  836. func IndexRune(str string, needle int32) bool {
  837. for _, b := range str {
  838. if b == needle {
  839. return true
  840. }
  841. }
  842. return false
  843. }