2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <folly/io/async/test/AsyncSSLSocketTest.h>
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>
37 struct EvbAndContext {
39 ctx_.reset(new SSLContext());
40 ctx_->setOptions(SSL_OP_NO_TICKET);
41 ctx_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
44 std::shared_ptr<AsyncSSLSocket> createSocket() {
45 return AsyncSSLSocket::newSocket(ctx_, getEventBase());
48 EventBase* getEventBase() {
49 return evb_.getEventBase();
52 void attach(AsyncSSLSocket& socket) {
53 socket.attachEventBase(getEventBase());
54 socket.attachSSLContext(ctx_);
57 folly::ScopedEventBaseThread evb_;
58 std::shared_ptr<SSLContext> ctx_;
61 class AttachDetachClient : public AsyncSocket::ConnectCallback,
62 public AsyncTransportWrapper::WriteCallback,
63 public AsyncTransportWrapper::ReadCallback {
65 // two threads here - we'll create the socket in one, connect
66 // in the other, and then read/write in the initial one
69 std::shared_ptr<AsyncSSLSocket> sslSocket_;
70 folly::SocketAddress address_;
74 // promise to fulfill when done
75 folly::Promise<bool> promise_;
78 sslSocket_->detachEventBase();
79 sslSocket_->detachSSLContext();
83 explicit AttachDetachClient(const folly::SocketAddress& address)
84 : address_(address), bytesRead_(0) {}
86 Future<bool> getFuture() {
87 return promise_.getFuture();
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_);
100 // detach from t1 and connect in t2
102 auto t2Evb = t2_.getEventBase();
103 t2Evb->runInEventBaseThread([this] {
104 t2_.attach(*sslSocket_);
105 sslSocket_->connect(this, address_);
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_);
119 // detach from t2 and then read/write in t1
120 t2Evb->runInEventBaseThread([this] {
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_));
133 void connectErr(const AsyncSocketException& ex) noexcept override
135 cerr << "AttachDetachClient::connectError: " << ex.what() << endl;
139 void writeSuccess() noexcept override {
140 cerr << "client write success" << endl;
143 void writeErr(size_t /* bytesWritten */,
144 const AsyncSocketException& ex) noexcept override {
145 cerr << "client writeError: " << ex.what() << endl;
148 void getReadBuffer(void** bufReturn, size_t* lenReturn) override {
149 *bufReturn = readbuf_ + bytesRead_;
150 *lenReturn = sizeof(readbuf_) - bytesRead_;
152 void readEOF() noexcept override {
153 cerr << "client readEOF" << endl;
156 void readErr(const AsyncSocketException& ex) noexcept override {
157 cerr << "client readError: " << ex.what() << endl;
158 promise_.setException(ex);
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;
166 if (len == sizeof(buf_)) {
167 EXPECT_EQ(memcmp(buf_, readbuf_, bytesRead_), 0);
168 sslSocket_->closeNow();
170 promise_.setValue(true);
176 * Test passing contexts between threads
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);
186 std::shared_ptr<AttachDetachClient> client(
187 new AttachDetachClient(server.getAddress()));
189 auto f = client->getFuture();
191 EXPECT_TRUE(f.within(std::chrono::seconds(3)).get());
196 int main(int argc, char *argv[]) {
198 signal(SIGPIPE, SIG_IGN);
200 folly::SSLContext::setSSLLockTypes({
201 #ifdef CRYPTO_LOCK_EVP_PKEY
202 {CRYPTO_LOCK_EVP_PKEY, folly::SSLContext::LOCK_NONE},
204 #ifdef CRYPTO_LOCK_SSL_SESSION
205 {CRYPTO_LOCK_SSL_SESSION, folly::SSLContext::LOCK_SPINLOCK},
207 #ifdef CRYPTO_LOCK_SSL_CTX
208 {CRYPTO_LOCK_SSL_CTX, folly::SSLContext::LOCK_NONE}
211 testing::InitGoogleTest(&argc, argv);
212 folly::init(&argc, &argv);
213 return RUN_ALL_TESTS();