1// Copyright 2021 The Prometheus Authors 2// This code is partly borrowed from Caddy: 3// Copyright 2015 Matthew Holt and The Caddy Authors 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15 16package web 17 18import ( 19 weakrand "math/rand" 20 "sync" 21 "time" 22) 23 24var cacheSize = 100 25 26func init() { 27 weakrand.Seed(time.Now().UnixNano()) 28} 29 30type cache struct { 31 cache map[string]bool 32 mtx sync.Mutex 33} 34 35// newCache returns a cache that contains a mapping of plaintext passwords 36// to their hashes (with random eviction). This can greatly improve the 37// performance of traffic-heavy servers that use secure password hashing 38// algorithms, with the downside that plaintext passwords will be stored in 39// memory for a longer time (this should not be a problem as long as your 40// machine is not compromised, at which point all bets are off, since basicauth 41// necessitates plaintext passwords being received over the wire anyway). 42func newCache() *cache { 43 return &cache{ 44 cache: make(map[string]bool), 45 } 46} 47 48func (c *cache) get(key string) (bool, bool) { 49 c.mtx.Lock() 50 defer c.mtx.Unlock() 51 v, ok := c.cache[key] 52 return v, ok 53} 54 55func (c *cache) set(key string, value bool) { 56 c.mtx.Lock() 57 defer c.mtx.Unlock() 58 c.makeRoom() 59 c.cache[key] = value 60} 61 62func (c *cache) makeRoom() { 63 if len(c.cache) < cacheSize { 64 return 65 } 66 // We delete more than just 1 entry so that we don't have 67 // to do this on every request; assuming the capacity of 68 // the cache is on a long tail, we can save a lot of CPU 69 // time by doing a whole bunch of deletions now and then 70 // we won't have to do them again for a while. 71 numToDelete := len(c.cache) / 10 72 if numToDelete < 1 { 73 numToDelete = 1 74 } 75 for deleted := 0; deleted <= numToDelete; deleted++ { 76 // Go maps are "nondeterministic" not actually random, 77 // so although we could just chop off the "front" of the 78 // map with less code, this is a heavily skewed eviction 79 // strategy; generating random numbers is cheap and 80 // ensures a much better distribution. 81 rnd := weakrand.Intn(len(c.cache)) 82 i := 0 83 for key := range c.cache { 84 if i == rnd { 85 delete(c.cache, key) 86 break 87 } 88 i++ 89 } 90 } 91} 92