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  }