GitHub doesn't seem to support line comments on already-committed code, so I'll just assembled some feedback here:
Why even have a dont-die-on-stale-response? Seems like that should always be true.
dont-cache also seems unnecessary. You could make the decision to cache or not based on whether disk.cache-folder is set. Similarly, dont-seed-cache-from-disk seems unnecessary and unused. Also, as a general naming thing, I'd use "no" instead of "dont" because it doesn't have the ambiguity introduce by eliding the apostrophe.
Some inconsistency about what's exported vs what's not. This is meant to be a command, so probably nothing should be exported. Alternately: Maybe you want to break it into subpackages for better maintenance, and then some things will need to be exported?
Would be good to add more comments, especially at the top of large functions. Easier to do this as you're writing than once it's all done.
As a stress test, it would be fun to seed this with all currently-issued Let's Encrypt certificates. I've been meaning to write an OCSP-aware monitoring daemon- this would effectively be that, if it could log warnings when responses are stale or don't parse or similar. You would probably need to define an alternate way to bring in certificates, and an alternate way to store responses, since 1M files in a directory is bad news. Even if you split among directories, you would have to make sure the host filesystem has enough inodes.
In Entry:
- maybe break out the "request related" and "response related" sections into their own structs?
- don't create an http.Client for each Entry, to save memory. The http client should probably be a property of the
cache
. They're safe for concurrent use.
- mu should be a sync.RWMutex instead of *sync.RWMutex, and doesn't need to be initialized in New (the zero value of mutexes is valid an unlocked).
- do you need to keep the x509.Certificate around for the lifetime? When the number of Entry's is large, the memory savings of discarding it could be large.
Entry.loadCertificate: There should be a cache for issuing certificates. Simple version: in-memory cache. Fancier version: config list of PEM files to load (or wildcard of PEM files to load) containing issuer certificates, pre-seeding in-memory cache.
Proxy URI: It should be possible to list multiple upstream proxies to be randomly selected, like the responders.
NewEntry / FromDef / Init: I'm trying to better understand the breakdown between these. It looks like NewEntry is everything that can't error, FromDef is everything that does file or network I/O, and Init is everything that doesn't do file or network I/O, but can error. Is that right? If so I might recommend merging NewEntry and Init. Also, comments. :-)
Entry.monitor: This seems like a mix of techniques: You have a constant-interval ticker, but you also pick an exact amount of time to sleep based on the response. Probably better to pick one. Recommendation: Define a single ticker on cache
that loops through all entries and refreshes in a goroutine if it's time to do so. That way you only need a single long-running goroutine versus a goroutine per entry, which will make your stack traces easier to read.
HashNameAndPKI: Can't you use x509.MarshalPKIXPublicKey(key)? Also, why do you create an ocsp.Request manually rather than using ocsp.CreateRequest?
cc also @ccppuu