]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Collapse security_file_certgen requests.
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Mon, 12 Jun 2017 16:05:59 +0000 (19:05 +0300)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Mon, 12 Jun 2017 16:05:59 +0000 (19:05 +0300)
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.

src/cbdata.h
src/ssl/helper.cc

index 4d7a570174d8b32607ff6727d8553d900ea02000..508e557707abd4eac880286f35c2c1c7a032468d 100644 (file)
@@ -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 */
 
index 01efccea931bf1ca1fcf64ef03c258142c425de6..e0e343c95aee171a272bc4d0faea9693c9a8ee4a 100644 (file)
 #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;
@@ -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<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