...

Source file src/go.mongodb.org/mongo-driver/x/mongo/driver/ocsp/cache.go

Documentation: go.mongodb.org/mongo-driver/x/mongo/driver/ocsp

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package ocsp
     8  
     9  import (
    10  	"crypto"
    11  	"sync"
    12  	"time"
    13  
    14  	"golang.org/x/crypto/ocsp"
    15  )
    16  
    17  type cacheKey struct {
    18  	HashAlgorithm  crypto.Hash
    19  	IssuerNameHash string
    20  	IssuerKeyHash  string
    21  	SerialNumber   string
    22  }
    23  
    24  // Cache represents an OCSP cache.
    25  type Cache interface {
    26  	Update(*ocsp.Request, *ResponseDetails) *ResponseDetails
    27  	Get(request *ocsp.Request) *ResponseDetails
    28  }
    29  
    30  // ConcurrentCache is an implementation of ocsp.Cache that's safe for concurrent use.
    31  type ConcurrentCache struct {
    32  	cache map[cacheKey]*ResponseDetails
    33  	sync.Mutex
    34  }
    35  
    36  var _ Cache = (*ConcurrentCache)(nil)
    37  
    38  // NewCache creates an empty OCSP cache.
    39  func NewCache() *ConcurrentCache {
    40  	return &ConcurrentCache{
    41  		cache: make(map[cacheKey]*ResponseDetails),
    42  	}
    43  }
    44  
    45  // Update updates the cache entry for the provided request. The provided response will only be cached if it has a
    46  // status that is not ocsp.Unknown and has a non-zero NextUpdate time. If there is an existing cache entry for request,
    47  // it will be overwritten by response if response.NextUpdate is further ahead in the future than the existing entry's
    48  // NextUpdate.
    49  //
    50  // This function returns the most up-to-date response corresponding to the request.
    51  func (c *ConcurrentCache) Update(request *ocsp.Request, response *ResponseDetails) *ResponseDetails {
    52  	unknown := response.Status == ocsp.Unknown
    53  	hasUpdateTime := !response.NextUpdate.IsZero()
    54  	canBeCached := !unknown && hasUpdateTime
    55  	key := createCacheKey(request)
    56  
    57  	c.Lock()
    58  	defer c.Unlock()
    59  
    60  	current, ok := c.cache[key]
    61  	if !ok {
    62  		if canBeCached {
    63  			c.cache[key] = response
    64  		}
    65  
    66  		// Return the provided response even though it might not have been cached because it's the most up-to-date
    67  		// response available.
    68  		return response
    69  	}
    70  
    71  	// If the new response is Unknown, we can't cache it. Return the existing cached response.
    72  	if unknown {
    73  		return current
    74  	}
    75  
    76  	// If a response has no nextUpdate set, the responder is telling us that newer information is always available.
    77  	// In this case, remove the existing cache entry because it is stale and return the new response because it is
    78  	// more up-to-date.
    79  	if !hasUpdateTime {
    80  		delete(c.cache, key)
    81  		return response
    82  	}
    83  
    84  	// If we get here, the new response is conclusive and has a non-empty nextUpdate so it can be cached. Overwrite
    85  	// the existing cache entry if the new one will be valid for longer.
    86  	newest := current
    87  	if response.NextUpdate.After(current.NextUpdate) {
    88  		c.cache[key] = response
    89  		newest = response
    90  	}
    91  	return newest
    92  }
    93  
    94  // Get returns the cached response for the request, or nil if there is no cached response. If the cached response has
    95  // expired, it will be removed from the cache and nil will be returned.
    96  func (c *ConcurrentCache) Get(request *ocsp.Request) *ResponseDetails {
    97  	key := createCacheKey(request)
    98  
    99  	c.Lock()
   100  	defer c.Unlock()
   101  
   102  	response, ok := c.cache[key]
   103  	if !ok {
   104  		return nil
   105  	}
   106  
   107  	if time.Now().UTC().Before(response.NextUpdate) {
   108  		return response
   109  	}
   110  	delete(c.cache, key)
   111  	return nil
   112  }
   113  
   114  func createCacheKey(request *ocsp.Request) cacheKey {
   115  	return cacheKey{
   116  		HashAlgorithm:  request.HashAlgorithm,
   117  		IssuerNameHash: string(request.IssuerNameHash),
   118  		IssuerKeyHash:  string(request.IssuerKeyHash),
   119  		SerialNumber:   request.SerialNumber.String(),
   120  	}
   121  }
   122  

View as plain text