From: Christos Tsantilas Date: Mon, 12 Jun 2017 16:05:59 +0000 (+0300) Subject: Collapse security_file_certgen requests. X-Git-Tag: M-staged-PR71~126 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=58fa3f51e51a82c1aecb586f408d3c30e413802e;p=thirdparty%2Fsquid.git Collapse security_file_certgen requests. Concurrent identical same-worker security_file_certgen (a.k.a. ssl_crtd) requests are collapsed: The first such request goes through to one of the helpers while others wait for that first request to complete, successfully or otherwise. This optimization helps dealing with flash crowds that suddenly send a large number of HTTPS requests to a small group of origin servers. Two certificate generation requests are considered identical if their on-the-wire images are identical. This simple and fast approach covers all certificate generation parameters, including all mimicked certificate properties, and avoids hash collisions and poisoning. Compared to collision- or poisoning-sensitive approaches that store raw certificates and compare their signatures or fingerprints, storing helper queries costs a few extra KB per pending helper request. That extra RAM cost is worth the advantages and will be eliminated when helper code switches from c-strings to SBufs. This is a Measurement Factory project. --- diff --git a/src/cbdata.h b/src/cbdata.h index 4d7a570174..508e557707 100644 --- a/src/cbdata.h +++ b/src/cbdata.h @@ -371,5 +371,25 @@ private: void *data; }; +// Discouraged: Use CbcPointer<> and asynchronous calls instead if possible. +/// an old-style void* callback parameter +class CallbackData +{ +public: + CallbackData(): data_(nullptr) {} + CallbackData(void *data): data_(cbdataReference(data)) {} + CallbackData(const CallbackData &other): data_(cbdataReference(other.data_)) {} + CallbackData(CallbackData &&other): data_(other.data_) { other.data_ = nullptr; } + ~CallbackData() { cbdataReferenceDone(data_); } + + // implement if needed + CallbackData &operator =(const CallbackData &other) = delete; + + void *validDone() { void *result; return cbdataReferenceValidDone(data_, &result) ? result : nullptr; } + +private: + void *data_; ///< raw callback data, maybe invalid +}; + #endif /* SQUID_CBDATA_H */ diff --git a/src/ssl/helper.cc b/src/ssl/helper.cc index 01efccea93..e0e343c95a 100644 --- a/src/ssl/helper.cc +++ b/src/ssl/helper.cc @@ -19,9 +19,54 @@ #include "ssl/helper.h" #include "wordlist.h" +#if USE_SSL_CRTD + +namespace Ssl { + +/// Initiator of an Ssl::Helper query. +class GeneratorRequestor { +public: + GeneratorRequestor(HLPCB *aCallback, void *aData): callback(aCallback), data(aData) {} + HLPCB *callback; + CallbackData data; +}; + +/// A pending Ssl::Helper request, combining the original and collapsed queries. +class GeneratorRequest { + CBDATA_CLASS(GeneratorRequest); + +public: + /// adds a GeneratorRequestor + void emplace(HLPCB *callback, void *data) { requestors.emplace_back(callback, data); } + + SBuf query; ///< Ssl::Helper request message (GeneratorRequests key) + + /// Ssl::Helper request initiators waiting for the same answer (FIFO). + typedef std::vector GeneratorRequestors; + GeneratorRequestors requestors; +}; + +/// Ssl::Helper query:GeneratorRequest map +typedef std::unordered_map GeneratorRequests; + +static void HandleGeneratorReply(void *data, const ::Helper::Reply &reply); + +} // namespace Ssl + +CBDATA_NAMESPACED_CLASS_INIT(Ssl, GeneratorRequest); + +/// prints Ssl::GeneratorRequest for debugging +static std::ostream & +operator <<(std::ostream &os, const Ssl::GeneratorRequest &gr) +{ + return os << "crtGenRq" << gr.query.id.value << "/" << gr.requestors.size(); +} + +/// pending Ssl::Helper requests (to all certificate generator helpers combined) +static Ssl::GeneratorRequests TheGeneratorRequests; + Ssl::CertValidationHelper::LruCache *Ssl::CertValidationHelper::HelperCache = nullptr; -#if USE_SSL_CRTD Ssl::Helper * Ssl::Helper::GetInstance() { static Ssl::Helper sslHelper; @@ -82,14 +127,44 @@ void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void { assert(ssl_crtd); - std::string msg = message.compose(); - msg += '\n'; - if (!ssl_crtd->trySubmit(msg.c_str(), callback, data)) { - ::Helper::Reply failReply(::Helper::BrokenHelper); - failReply.notes.add("message", "error 45 Temporary network problem, please retry later"); - callback(data, failReply); + SBuf rawMessage(message.compose().c_str()); // XXX: helpers cannot use SBuf + rawMessage.append("\n", 1); + + const auto pending = TheGeneratorRequests.find(rawMessage); + if (pending != TheGeneratorRequests.end()) { + pending->second->emplace(callback, data); + debugs(83, 5, "collapsed request from " << data << " onto " << *pending->second); return; } + + GeneratorRequest *request = new GeneratorRequest; + request->query = rawMessage; + request->emplace(callback, data); + TheGeneratorRequests.emplace(request->query, request); + debugs(83, 5, "request from " << data << " as " << *request); + if (ssl_crtd->trySubmit(request->query.c_str(), HandleGeneratorReply, request)) + return; + + ::Helper::Reply failReply(::Helper::BrokenHelper); + failReply.notes.add("message", "error 45 Temporary network problem, please retry later"); + HandleGeneratorReply(request, failReply); +} + +/// receives helper response +static void +Ssl::HandleGeneratorReply(void *data, const ::Helper::Reply &reply) +{ + const std::unique_ptr request(static_cast(data)); + assert(request); + const auto erased = TheGeneratorRequests.erase(request->query); + assert(erased); + + for (auto &requestor: request->requestors) { + if (void *cbdata = requestor.data.validDone()) { + debugs(83, 5, "to " << cbdata << " in " << *request); + requestor.callback(cbdata, reply); + } + } } #endif //USE_SSL_CRTD