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
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.

=== modified file 'src/cbdata.h'
--- src/cbdata.h	2017-01-01 00:12:22 +0000
+++ src/cbdata.h	2017-06-08 10:44:00 +0000
@@ -354,22 +354,42 @@
  * Use this when you need to pass callback data to a blocking
  * operation, but you don't want to/cannot have that pointer be
  * cbdata itself.
  */
 class generic_cbdata
 {
     CBDATA_CLASS(generic_cbdata);
 
 public:
     generic_cbdata(void *aData) : data(aData) {}
 
     template<typename wrapped_type>void unwrap(wrapped_type **output) {
         *output = static_cast<wrapped_type *>(data);
         delete this;
     }
 
 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 */
 

=== modified file 'src/ssl/helper.cc'
--- src/ssl/helper.cc	2017-01-12 13:26:45 +0000
+++ src/ssl/helper.cc	2017-06-08 10:44:00 +0000
@@ -2,43 +2,88 @@
  * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "../helper.h"
 #include "anyp/PortCfg.h"
 #include "fs_io.h"
 #include "helper/Reply.h"
 #include "SquidConfig.h"
 #include "SquidString.h"
 #include "SquidTime.h"
 #include "ssl/cert_validate_message.h"
 #include "ssl/Config.h"
 #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<GeneratorRequestor> GeneratorRequestors;
+    GeneratorRequestors requestors;
+};
+
+/// Ssl::Helper query:GeneratorRequest map
+typedef std::unordered_map<SBuf, GeneratorRequest*> 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;
     return &sslHelper;
 }
 
 Ssl::Helper::Helper() : ssl_crtd(NULL)
 {
 }
 
 Ssl::Helper::~Helper()
 {
     Shutdown();
 }
 
 void Ssl::Helper::Init()
 {
     assert(ssl_crtd == NULL);
 
     // we need to start ssl_crtd only if some port(s) need to bump SSL *and* generate certificates
@@ -65,48 +110,78 @@
         }
         safe_free(tmp_begin);
     }
     helperOpenServers(ssl_crtd);
 }
 
 void Ssl::Helper::Shutdown()
 {
     if (!ssl_crtd)
         return;
     helperShutdown(ssl_crtd);
     wordlistDestroy(&ssl_crtd->cmdline);
     delete ssl_crtd;
     ssl_crtd = NULL;
 }
 
 void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data)
 {
     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<Ssl::GeneratorRequest> request(static_cast<Ssl::GeneratorRequest*>(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
 
 Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance()
 {
     static Ssl::CertValidationHelper sslHelper;
     if (!Ssl::TheConfig.ssl_crt_validator)
         return NULL;
     return &sslHelper;
 }
 
 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL)
 {
 }
 
 Ssl::CertValidationHelper::~CertValidationHelper()
 {
     Shutdown();
 }
 

_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to