apply clang-tidy modernize-use-override
[folly.git] / folly / io / async / test / AsyncSSLSocketTest2.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <folly/io/async/test/AsyncSSLSocketTest.h>
17
18 #include <pthread.h>
19
20 #include <folly/futures/Promise.h>
21 #include <folly/init/Init.h>
22 #include <folly/io/async/AsyncSSLSocket.h>
23 #include <folly/io/async/EventBase.h>
24 #include <folly/io/async/SSLContext.h>
25 #include <folly/io/async/ScopedEventBaseThread.h>
26 #include <folly/portability/GTest.h>
27
28 using std::string;
29 using std::vector;
30 using std::min;
31 using std::cerr;
32 using std::endl;
33 using std::list;
34
35 namespace folly {
36
37 struct EvbAndContext {
38   EvbAndContext() {
39     ctx_.reset(new SSLContext());
40     ctx_->setOptions(SSL_OP_NO_TICKET);
41     ctx_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
42   }
43
44   std::shared_ptr<AsyncSSLSocket> createSocket() {
45     return AsyncSSLSocket::newSocket(ctx_, getEventBase());
46   }
47
48   EventBase* getEventBase() {
49     return evb_.getEventBase();
50   }
51
52   void attach(AsyncSSLSocket& socket) {
53     socket.attachEventBase(getEventBase());
54     socket.attachSSLContext(ctx_);
55   }
56
57   folly::ScopedEventBaseThread evb_;
58   std::shared_ptr<SSLContext> ctx_;
59 };
60
61 class AttachDetachClient : public AsyncSocket::ConnectCallback,
62                            public AsyncTransportWrapper::WriteCallback,
63                            public AsyncTransportWrapper::ReadCallback {
64  private:
65   // two threads here - we'll create the socket in one, connect
66   // in the other, and then read/write in the initial one
67   EvbAndContext t1_;
68   EvbAndContext t2_;
69   std::shared_ptr<AsyncSSLSocket> sslSocket_;
70   folly::SocketAddress address_;
71   char buf_[128];
72   char readbuf_[128];
73   uint32_t bytesRead_;
74   // promise to fulfill when done
75   folly::Promise<bool> promise_;
76
77   void detach() {
78     sslSocket_->detachEventBase();
79     sslSocket_->detachSSLContext();
80   }
81
82  public:
83   explicit AttachDetachClient(const folly::SocketAddress& address)
84       : address_(address), bytesRead_(0) {}
85
86   Future<bool> getFuture() {
87     return promise_.getFuture();
88   }
89
90   void connect() {
91     // create in one and then move to another
92     auto t1Evb = t1_.getEventBase();
93     t1Evb->runInEventBaseThread([this] {
94       sslSocket_ = t1_.createSocket();
95       // ensure we can detach and reattach the context before connecting
96       for (int i = 0; i < 1000; ++i) {
97         sslSocket_->detachSSLContext();
98         sslSocket_->attachSSLContext(t1_.ctx_);
99       }
100       // detach from t1 and connect in t2
101       detach();
102       auto t2Evb = t2_.getEventBase();
103       t2Evb->runInEventBaseThread([this] {
104         t2_.attach(*sslSocket_);
105         sslSocket_->connect(this, address_);
106       });
107     });
108   }
109
110   void connectSuccess() noexcept override {
111     auto t2Evb = t2_.getEventBase();
112     EXPECT_TRUE(t2Evb->isInEventBaseThread());
113     cerr << "client SSL socket connected" << endl;
114     for (int i = 0; i < 1000; ++i) {
115       sslSocket_->detachSSLContext();
116       sslSocket_->attachSSLContext(t2_.ctx_);
117     }
118
119     // detach from t2 and then read/write in t1
120     t2Evb->runInEventBaseThread([this] {
121       detach();
122       auto t1Evb = t1_.getEventBase();
123       t1Evb->runInEventBaseThread([this] {
124         t1_.attach(*sslSocket_);
125         sslSocket_->write(this, buf_, sizeof(buf_));
126         sslSocket_->setReadCB(this);
127         memset(readbuf_, 'b', sizeof(readbuf_));
128         bytesRead_ = 0;
129       });
130     });
131   }
132
133   void connectErr(const AsyncSocketException& ex) noexcept override
134   {
135     cerr << "AttachDetachClient::connectError: " << ex.what() << endl;
136     sslSocket_.reset();
137   }
138
139   void writeSuccess() noexcept override {
140     cerr << "client write success" << endl;
141   }
142
143   void writeErr(size_t /* bytesWritten */,
144                 const AsyncSocketException& ex) noexcept override {
145     cerr << "client writeError: " << ex.what() << endl;
146   }
147
148   void getReadBuffer(void** bufReturn, size_t* lenReturn) override {
149     *bufReturn = readbuf_ + bytesRead_;
150     *lenReturn = sizeof(readbuf_) - bytesRead_;
151   }
152   void readEOF() noexcept override {
153     cerr << "client readEOF" << endl;
154   }
155
156   void readErr(const AsyncSocketException& ex) noexcept override {
157     cerr << "client readError: " << ex.what() << endl;
158     promise_.setException(ex);
159   }
160
161   void readDataAvailable(size_t len) noexcept override {
162     EXPECT_TRUE(t1_.getEventBase()->isInEventBaseThread());
163     EXPECT_EQ(sslSocket_->getEventBase(), t1_.getEventBase());
164     cerr << "client read data: " << len << endl;
165     bytesRead_ += len;
166     if (len == sizeof(buf_)) {
167       EXPECT_EQ(memcmp(buf_, readbuf_, bytesRead_), 0);
168       sslSocket_->closeNow();
169       sslSocket_.reset();
170       promise_.setValue(true);
171     }
172   }
173 };
174
175 /**
176  * Test passing contexts between threads
177  */
178 TEST(AsyncSSLSocketTest2, AttachDetachSSLContext) {
179   // Start listening on a local port
180   WriteCallbackBase writeCallback;
181   ReadCallback readCallback(&writeCallback);
182   HandshakeCallback handshakeCallback(&readCallback);
183   SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);
184   TestSSLServer server(&acceptCallback);
185
186   std::shared_ptr<AttachDetachClient> client(
187       new AttachDetachClient(server.getAddress()));
188
189   auto f = client->getFuture();
190   client->connect();
191   EXPECT_TRUE(f.within(std::chrono::seconds(3)).get());
192 }
193
194 TEST(AsyncSSLSocketTest2, SSLContextLocks) {
195   SSLContext::initializeOpenSSL();
196 // these are checks based on the locks that are set in the main below
197 #ifdef CRYPTO_LOCK_EVP_PKEY
198   EXPECT_TRUE(SSLContext::isSSLLockDisabled(CRYPTO_LOCK_EVP_PKEY));
199 #endif
200 #ifdef CRYPTO_LOCK_SSL_SESSION
201   EXPECT_FALSE(SSLContext::isSSLLockDisabled(CRYPTO_LOCK_SSL_SESSION));
202 #endif
203 #ifdef CRYPTO_LOCK_ERR
204   EXPECT_FALSE(SSLContext::isSSLLockDisabled(CRYPTO_LOCK_ERR));
205 #endif
206 }
207
208 TEST(AsyncSSLSocketTest2, SSLContextLocksSetAfterInitIgnored) {
209   SSLContext::initializeOpenSSL();
210   SSLContext::setSSLLockTypes({});
211 #ifdef CRYPTO_LOCK_EVP_PKEY
212   EXPECT_TRUE(SSLContext::isSSLLockDisabled(CRYPTO_LOCK_EVP_PKEY));
213 #endif
214 }
215
216 TEST(AsyncSSLSocketTest2, SSLContextSetLocksAndInitialize) {
217   SSLContext::cleanupOpenSSL();
218   SSLContext::setSSLLockTypesAndInitOpenSSL({});
219   EXPECT_DEATH(
220       SSLContext::setSSLLockTypesAndInitOpenSSL({}),
221       "OpenSSL is already initialized");
222
223   SSLContext::cleanupOpenSSL();
224   SSLContext::initializeOpenSSL();
225   EXPECT_DEATH(
226       SSLContext::setSSLLockTypesAndInitOpenSSL({}),
227       "OpenSSL is already initialized");
228 }
229 }  // folly
230
231 int main(int argc, char *argv[]) {
232 #ifdef SIGPIPE
233   signal(SIGPIPE, SIG_IGN);
234 #endif
235   folly::SSLContext::setSSLLockTypes({
236 #ifdef CRYPTO_LOCK_EVP_PKEY
237       {CRYPTO_LOCK_EVP_PKEY, folly::SSLContext::LOCK_NONE},
238 #endif
239 #ifdef CRYPTO_LOCK_SSL_SESSION
240       {CRYPTO_LOCK_SSL_SESSION, folly::SSLContext::LOCK_SPINLOCK},
241 #endif
242 #ifdef CRYPTO_LOCK_SSL_CTX
243       {CRYPTO_LOCK_SSL_CTX, folly::SSLContext::LOCK_NONE}
244 #endif
245   });
246   testing::InitGoogleTest(&argc, argv);
247   folly::init(&argc, &argv);
248   return RUN_ALL_TESTS();
249 }