Fixes to always call Singleton's registrationComplete()/initFacebook()
[folly.git] / folly / io / async / test / AsyncSSLSocketTest2.cpp
index 5f4818ee973c69a0c9bc53c0376186f8ad755386..06b6db7df29d6415efda906e9c01b762d897309e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 #include <folly/io/async/test/AsyncSSLSocketTest.h>
 
-#include <gtest/gtest.h>
 #include <pthread.h>
 
+#include <folly/futures/Promise.h>
+#include <folly/init/Init.h>
 #include <folly/io/async/AsyncSSLSocket.h>
 #include <folly/io/async/EventBase.h>
+#include <folly/io/async/SSLContext.h>
+#include <folly/io/async/ScopedEventBaseThread.h>
+#include <folly/portability/GTest.h>
 
 using std::string;
 using std::vector;
@@ -30,44 +34,100 @@ using std::list;
 
 namespace folly {
 
+struct EvbAndContext {
+  EvbAndContext() {
+    ctx_.reset(new SSLContext());
+    ctx_->setOptions(SSL_OP_NO_TICKET);
+    ctx_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+  }
+
+  std::shared_ptr<AsyncSSLSocket> createSocket() {
+    return AsyncSSLSocket::newSocket(ctx_, getEventBase());
+  }
+
+  EventBase* getEventBase() {
+    return evb_.getEventBase();
+  }
+
+  void attach(AsyncSSLSocket& socket) {
+    socket.attachEventBase(getEventBase());
+    socket.attachSSLContext(ctx_);
+  }
+
+  folly::ScopedEventBaseThread evb_;
+  std::shared_ptr<SSLContext> ctx_;
+};
+
 class AttachDetachClient : public AsyncSocket::ConnectCallback,
                            public AsyncTransportWrapper::WriteCallback,
                            public AsyncTransportWrapper::ReadCallback {
  private:
-  EventBase *eventBase_;
+  // two threads here - we'll create the socket in one, connect
+  // in the other, and then read/write in the initial one
+  EvbAndContext t1_;
+  EvbAndContext t2_;
   std::shared_ptr<AsyncSSLSocket> sslSocket_;
-  std::shared_ptr<SSLContext> ctx_;
   folly::SocketAddress address_;
   char buf_[128];
   char readbuf_[128];
   uint32_t bytesRead_;
+  // promise to fulfill when done
+  folly::Promise<bool> promise_;
+
+  void detach() {
+    sslSocket_->detachEventBase();
+    sslSocket_->detachSSLContext();
+  }
+
  public:
-  AttachDetachClient(EventBase *eventBase, const folly::SocketAddress& address)
-      : eventBase_(eventBase), address_(address), bytesRead_(0) {
-    ctx_.reset(new SSLContext());
-    ctx_->setOptions(SSL_OP_NO_TICKET);
-    ctx_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+  explicit AttachDetachClient(const folly::SocketAddress& address)
+      : address_(address), bytesRead_(0) {}
+
+  Future<bool> getFuture() {
+    return promise_.getFuture();
   }
 
   void connect() {
-    sslSocket_ = AsyncSSLSocket::newSocket(ctx_, eventBase_);
-    sslSocket_->connect(this, address_);
+    // create in one and then move to another
+    auto t1Evb = t1_.getEventBase();
+    t1Evb->runInEventBaseThread([this] {
+      sslSocket_ = t1_.createSocket();
+      // ensure we can detach and reattach the context before connecting
+      for (int i = 0; i < 1000; ++i) {
+        sslSocket_->detachSSLContext();
+        sslSocket_->attachSSLContext(t1_.ctx_);
+      }
+      // detach from t1 and connect in t2
+      detach();
+      auto t2Evb = t2_.getEventBase();
+      t2Evb->runInEventBaseThread([this] {
+        t2_.attach(*sslSocket_);
+        sslSocket_->connect(this, address_);
+      });
+    });
   }
 
   void connectSuccess() noexcept override {
+    auto t2Evb = t2_.getEventBase();
+    EXPECT_TRUE(t2Evb->isInEventBaseThread());
     cerr << "client SSL socket connected" << endl;
-
     for (int i = 0; i < 1000; ++i) {
       sslSocket_->detachSSLContext();
-      sslSocket_->attachSSLContext(ctx_);
+      sslSocket_->attachSSLContext(t2_.ctx_);
     }
 
-    EXPECT_EQ(ctx_->getSSLCtx()->references, 2);
-
-    sslSocket_->write(this, buf_, sizeof(buf_));
-    sslSocket_->setReadCB(this);
-    memset(readbuf_, 'b', sizeof(readbuf_));
-    bytesRead_ = 0;
+    // detach from t2 and then read/write in t1
+    t2Evb->runInEventBaseThread([this] {
+      detach();
+      auto t1Evb = t1_.getEventBase();
+      t1Evb->runInEventBaseThread([this] {
+        t1_.attach(*sslSocket_);
+        sslSocket_->write(this, buf_, sizeof(buf_));
+        sslSocket_->setReadCB(this);
+        memset(readbuf_, 'b', sizeof(readbuf_));
+        bytesRead_ = 0;
+      });
+    });
   }
 
   void connectErr(const AsyncSocketException& ex) noexcept override
@@ -80,8 +140,8 @@ class AttachDetachClient : public AsyncSocket::ConnectCallback,
     cerr << "client write success" << endl;
   }
 
-  void writeErr(size_t bytesWritten, const AsyncSocketException& ex)
-    noexcept override {
+  void writeErr(size_t /* bytesWritten */,
+                const AsyncSocketException& ex) noexcept override {
     cerr << "client writeError: " << ex.what() << endl;
   }
 
@@ -95,14 +155,19 @@ class AttachDetachClient : public AsyncSocket::ConnectCallback,
 
   void readErr(const AsyncSocketException& ex) noexcept override {
     cerr << "client readError: " << ex.what() << endl;
+    promise_.setException(ex);
   }
 
   void readDataAvailable(size_t len) noexcept override {
+    EXPECT_TRUE(t1_.getEventBase()->isInEventBaseThread());
+    EXPECT_EQ(sslSocket_->getEventBase(), t1_.getEventBase());
     cerr << "client read data: " << len << endl;
     bytesRead_ += len;
     if (len == sizeof(buf_)) {
       EXPECT_EQ(memcmp(buf_, readbuf_, bytesRead_), 0);
       sslSocket_->closeNow();
+      sslSocket_.reset();
+      promise_.setValue(true);
     }
   }
 };
@@ -118,30 +183,32 @@ TEST(AsyncSSLSocketTest2, AttachDetachSSLContext) {
   SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);
   TestSSLServer server(&acceptCallback);
 
-  EventBase eventBase;
-  EventBaseAborter eba(&eventBase, 3000);
   std::shared_ptr<AttachDetachClient> client(
-    new AttachDetachClient(&eventBase, server.getAddress()));
+      new AttachDetachClient(server.getAddress()));
 
+  auto f = client->getFuture();
   client->connect();
-  eventBase.loop();
+  EXPECT_TRUE(f.within(std::chrono::seconds(3)).get());
 }
 
+}  // folly
+
+int main(int argc, char *argv[]) {
+#ifdef SIGPIPE
+  signal(SIGPIPE, SIG_IGN);
+#endif
+  folly::SSLContext::setSSLLockTypes({
+#ifdef CRYPTO_LOCK_EVP_PKEY
+      {CRYPTO_LOCK_EVP_PKEY, folly::SSLContext::LOCK_NONE},
+#endif
+#ifdef CRYPTO_LOCK_SSL_SESSION
+      {CRYPTO_LOCK_SSL_SESSION, folly::SSLContext::LOCK_SPINLOCK},
+#endif
+#ifdef CRYPTO_LOCK_SSL_CTX
+      {CRYPTO_LOCK_SSL_CTX, folly::SSLContext::LOCK_NONE}
+#endif
+  });
+  testing::InitGoogleTest(&argc, argv);
+  folly::init(&argc, &argv);
+  return RUN_ALL_TESTS();
 }
-///////////////////////////////////////////////////////////////////////////
-// init_unit_test_suite
-///////////////////////////////////////////////////////////////////////////
-
-namespace {
-using folly::SSLContext;
-struct Initializer {
-  Initializer() {
-    signal(SIGPIPE, SIG_IGN);
-    SSLContext::setSSLLockTypes({
-        {CRYPTO_LOCK_EVP_PKEY, SSLContext::LOCK_NONE},
-        {CRYPTO_LOCK_SSL_SESSION, SSLContext::LOCK_SPINLOCK},
-        {CRYPTO_LOCK_SSL_CTX, SSLContext::LOCK_NONE}});
-  }
-};
-Initializer initializer;
-} // anonymous