github.com/gofiber/fiber/v2@v2.47.0/middleware/etag/etag.go (about) 1 package etag 2 3 import ( 4 "bytes" 5 "hash/crc32" 6 7 "github.com/gofiber/fiber/v2" 8 9 "github.com/valyala/bytebufferpool" 10 ) 11 12 // New creates a new middleware handler 13 func New(config ...Config) fiber.Handler { 14 // Set default config 15 cfg := configDefault(config...) 16 17 var ( 18 normalizedHeaderETag = []byte("Etag") 19 weakPrefix = []byte("W/") 20 ) 21 22 const crcPol = 0xD5828281 23 crc32q := crc32.MakeTable(crcPol) 24 25 // Return new handler 26 return func(c *fiber.Ctx) error { 27 // Don't execute middleware if Next returns true 28 if cfg.Next != nil && cfg.Next(c) { 29 return c.Next() 30 } 31 32 // Return err if next handler returns one 33 if err := c.Next(); err != nil { 34 return err 35 } 36 37 // Don't generate ETags for invalid responses 38 if c.Response().StatusCode() != fiber.StatusOK { 39 return nil 40 } 41 body := c.Response().Body() 42 // Skips ETag if no response body is present 43 if len(body) == 0 { 44 return nil 45 } 46 // Skip ETag if header is already present 47 if c.Response().Header.PeekBytes(normalizedHeaderETag) != nil { 48 return nil 49 } 50 51 // Generate ETag for response 52 bb := bytebufferpool.Get() 53 defer bytebufferpool.Put(bb) 54 55 // Enable weak tag 56 if cfg.Weak { 57 _, _ = bb.Write(weakPrefix) //nolint:errcheck // This will never fail 58 } 59 60 _ = bb.WriteByte('"') //nolint:errcheck // This will never fail 61 bb.B = appendUint(bb.Bytes(), uint32(len(body))) 62 _ = bb.WriteByte('-') //nolint:errcheck // This will never fail 63 bb.B = appendUint(bb.Bytes(), crc32.Checksum(body, crc32q)) 64 _ = bb.WriteByte('"') //nolint:errcheck // This will never fail 65 66 etag := bb.Bytes() 67 68 // Get ETag header from request 69 clientEtag := c.Request().Header.Peek(fiber.HeaderIfNoneMatch) 70 71 // Check if client's ETag is weak 72 if bytes.HasPrefix(clientEtag, weakPrefix) { 73 // Check if server's ETag is weak 74 if bytes.Equal(clientEtag[2:], etag) || bytes.Equal(clientEtag[2:], etag[2:]) { 75 // W/1 == 1 || W/1 == W/1 76 c.Context().ResetBody() 77 78 return c.SendStatus(fiber.StatusNotModified) 79 } 80 // W/1 != W/2 || W/1 != 2 81 c.Response().Header.SetCanonical(normalizedHeaderETag, etag) 82 83 return nil 84 } 85 86 if bytes.Contains(clientEtag, etag) { 87 // 1 == 1 88 c.Context().ResetBody() 89 90 return c.SendStatus(fiber.StatusNotModified) 91 } 92 // 1 != 2 93 c.Response().Header.SetCanonical(normalizedHeaderETag, etag) 94 95 return nil 96 } 97 } 98 99 // appendUint appends n to dst and returns the extended dst. 100 func appendUint(dst []byte, n uint32) []byte { 101 var b [20]byte 102 buf := b[:] 103 i := len(buf) 104 var q uint32 105 for n >= 10 { 106 i-- 107 q = n / 10 108 buf[i] = '0' + byte(n-q*10) 109 n = q 110 } 111 i-- 112 buf[i] = '0' + byte(n) 113 114 dst = append(dst, buf[i:]...) 115 return dst 116 }