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