Commit fe6985dd authored by Alan Frindell's avatar Alan Frindell Committed by afrind

Move AsyncSocket tests from thrift to folly

Summary: These tests belong with the code that they test.  The old tests had a couple dependencies on TSocket/TSSLSocket, so I wrote a BlockingSocket wrapper for AsyncSocket/AsyncSSLSocket

Test Plan: Ran the tests

Reviewed By: alandau@fb.com

Subscribers: doug, net-systems@, alandau, bmatheny, mshneer, folly-diffs@, yfeldblum, chalfant

FB internal diff: D1959955

Signature: t1:1959955:1427917833:73d334846cf248f8bb215f3eb5b596df7f7cee4f
parent 773ee3cb
......@@ -162,6 +162,8 @@ nobase_follyinclude_HEADERS = \
io/async/Request.h \
io/async/SSLContext.h \
io/async/TimeoutManager.h \
io/async/test/AsyncSSLSocketTest.h \
io/async/test/BlockingSocket.h \
io/async/test/TimeUtil.h \
io/async/test/UndelayedDestruction.h \
io/async/test/Util.h \
......
This diff is collapsed.
This diff is collapsed.
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/io/async/test/AsyncSSLSocketTest.h>
#include <gtest/gtest.h>
#include <pthread.h>
#include <folly/io/async/AsyncSSLSocket.h>
#include <folly/io/async/EventBase.h>
using std::string;
using std::vector;
using std::min;
using std::cerr;
using std::endl;
using std::list;
namespace folly {
class AttachDetachClient : public AsyncSocket::ConnectCallback,
public AsyncTransportWrapper::WriteCallback,
public AsyncTransportWrapper::ReadCallback {
private:
EventBase *eventBase_;
std::shared_ptr<AsyncSSLSocket> sslSocket_;
std::shared_ptr<SSLContext> ctx_;
folly::SocketAddress address_;
char buf_[128];
char readbuf_[128];
uint32_t bytesRead_;
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");
}
void connect() {
sslSocket_ = AsyncSSLSocket::newSocket(ctx_, eventBase_);
sslSocket_->connect(this, address_);
}
void connectSuccess() noexcept override {
cerr << "client SSL socket connected" << endl;
for (int i = 0; i < 1000; ++i) {
sslSocket_->detachSSLContext();
sslSocket_->attachSSLContext(ctx_);
}
EXPECT_EQ(ctx_->getSSLCtx()->references, 2);
sslSocket_->write(this, buf_, sizeof(buf_));
sslSocket_->setReadCB(this);
memset(readbuf_, 'b', sizeof(readbuf_));
bytesRead_ = 0;
}
void connectErr(const AsyncSocketException& ex) noexcept override
{
cerr << "AttachDetachClient::connectError: " << ex.what() << endl;
sslSocket_.reset();
}
void writeSuccess() noexcept override {
cerr << "client write success" << endl;
}
void writeErr(size_t bytesWritten, const AsyncSocketException& ex)
noexcept override {
cerr << "client writeError: " << ex.what() << endl;
}
void getReadBuffer(void** bufReturn, size_t* lenReturn) override {
*bufReturn = readbuf_ + bytesRead_;
*lenReturn = sizeof(readbuf_) - bytesRead_;
}
void readEOF() noexcept override {
cerr << "client readEOF" << endl;
}
void readErr(const AsyncSocketException& ex) noexcept override {
cerr << "client readError: " << ex.what() << endl;
}
void readDataAvailable(size_t len) noexcept override {
cerr << "client read data: " << len << endl;
bytesRead_ += len;
if (len == sizeof(buf_)) {
EXPECT_EQ(memcmp(buf_, readbuf_, bytesRead_), 0);
sslSocket_->closeNow();
}
}
};
/**
* Test passing contexts between threads
*/
TEST(AsyncSSLSocketTest2, AttachDetachSSLContext) {
// Start listening on a local port
WriteCallbackBase writeCallback;
ReadCallback readCallback(&writeCallback);
HandshakeCallback handshakeCallback(&readCallback);
SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);
TestSSLServer server(&acceptCallback);
EventBase eventBase;
EventBaseAborter eba(&eventBase, 3000);
std::shared_ptr<AttachDetachClient> client(
new AttachDetachClient(&eventBase, server.getAddress()));
client->connect();
eventBase.loop();
}
}
///////////////////////////////////////////////////////////////////////////
// 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
This diff is collapsed.
This diff is collapsed.
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/Optional.h>
#include <folly/io/async/SSLContext.h>
#include <folly/io/async/AsyncSocket.h>
#include <folly/io/async/AsyncSSLSocket.h>
class BlockingSocket : public folly::AsyncSocket::ConnectCallback,
public folly::AsyncTransportWrapper::ReadCallback,
public folly::AsyncTransportWrapper::WriteCallback
{
public:
explicit BlockingSocket(int fd)
: sock_(new folly::AsyncSocket(&eventBase_, fd)) {
}
BlockingSocket(folly::SocketAddress address,
std::shared_ptr<folly::SSLContext> sslContext)
: sock_(sslContext ? new folly::AsyncSSLSocket(sslContext, &eventBase_) :
new folly::AsyncSocket(&eventBase_)),
address_(address) {}
void open() {
sock_->connect(this, address_);
eventBase_.loop();
if (err_.hasValue()) {
throw err_.value();
}
}
void close() {
sock_->close();
}
int32_t write(uint8_t const* buf, size_t len) {
sock_->write(this, buf, len);
eventBase_.loop();
if (err_.hasValue()) {
throw err_.value();
}
return len;
}
void flush() {}
int32_t readAll(uint8_t *buf, size_t len) {
return readHelper(buf, len, true);
}
int32_t read(uint8_t *buf, size_t len) {
return readHelper(buf, len, false);
}
int getSocketFD() const {
return sock_->getFd();
}
private:
folly::EventBase eventBase_;
folly::AsyncSocket::UniquePtr sock_;
folly::Optional<folly::AsyncSocketException> err_;
uint8_t *readBuf_{nullptr};
size_t readLen_{0};
folly::SocketAddress address_;
void connectSuccess() noexcept override {}
void connectErr(const folly::AsyncSocketException& ex) noexcept override {
err_ = ex;
}
void getReadBuffer(void** bufReturn, size_t* lenReturn) override {
*bufReturn = readBuf_;
*lenReturn = readLen_;
}
void readDataAvailable(size_t len) noexcept override {
readBuf_ += len;
readLen_ -= len;
if (readLen_ == 0) {
sock_->setReadCB(nullptr);
}
}
void readEOF() noexcept override {
}
void readErr(const folly::AsyncSocketException& ex) noexcept override {
err_ = ex;
}
void writeSuccess() noexcept override {}
void writeErr(size_t bytesWritten,
const folly::AsyncSocketException& ex) noexcept override {
err_ = ex;
}
int32_t readHelper(uint8_t *buf, size_t len, bool all) {
readBuf_ = buf;
readLen_ = len;
sock_->setReadCB(this);
while (!err_ && sock_->good() && readLen_ > 0) {
eventBase_.loop();
if (!all) {
break;
}
}
sock_->setReadCB(nullptr);
if (err_.hasValue()) {
throw err_.value();
}
if (all && readLen_ > 0) {
throw folly::AsyncSocketException(folly::AsyncSocketException::UNKNOWN,
"eof");
}
return len - readLen_;
}
};
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKMZICGWUzawMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAlVTMQ8wDQYDVQQKDAZUaHJpZnQxJTAjBgNVBAMMHFRocmlmdCBDZXJ0aWZp
Y2F0ZSBBdXRob3JpdHkwHhcNMTQwNTE2MjAyODUyWhcNNDExMDAxMjAyODUyWjBF
MQswCQYDVQQGEwJVUzEPMA0GA1UECgwGVGhyaWZ0MSUwIwYDVQQDDBxUaHJpZnQg
Q2VydGlmaWNhdGUgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA1Bx2vUvXZ8PrvEBxwdH5qM1F2Xo7UkeC1jzQ+OLUBEcCiEduyStitSvB
NOAzAGdjt7NmHTP/7OJngp2vzQGjSQzm20XacyTieFUuPBuikUc0Ge3Tf+uQXtiU
zZPh+xn6arHH+zBWtmUCt3cBrpgRqdnWUsbl8eqo5HsczY781FxQbDoT9VP6A+9R
KGTsEhxxKbWJ1C7OngwLKc7Zv4DtTC1JFlFyKd8ryDtxP4s/GgsXJkoK0Hkpputr
cMxMm6OGt77mFvzR2qRY1CpEK/9rjBB6Gqd8GakXsvoOsqL/37k2wVhN/JoS/Pde
12Mp6TZ2rA8NW8vRujfWU0u55gnQnwIDAQABo1AwTjAdBgNVHQ4EFgQUQ00NGVmY
NZ6LJg8UQUOVLZX1Gh8wHwYDVR0jBBgwFoAUQ00NGVmYNZ6LJg8UQUOVLZX1Gh8w
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAdlxt5+z9uXCBr1Wt6r49
4MmOYw9lOnEOG1JPMRo108TLpmwXEWReCAtjQuR7BitRJW0kJtlO1M6t3qoIh6GA
sBkgsjQM1xNY3YEpx71MLt1V+JD+2WtSBKMyysj1TiOmIH66kkvXO3ptXzhjhZyX
G6B+kxLtxrqkn9SJULyN55X8T+dkW28UIBZVLavoREDU+UPrYU9JgZeIVObtGSWi
DvS4RIJZNjgG3vTrT00rfUGEfTlI54Vbcmv0cYvswP/nMsLtDStCdgI7c/ipyJve
dfuI4CedjE240AxK5OFxFg/k/IfnB4a5oojbdIR9hKrTU57TPaUVD50Na9WA1aqX
5Q==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDKzCCAhOgAwIBAgIBCjANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEP
MA0GA1UECgwGVGhyaWZ0MSUwIwYDVQQDDBxUaHJpZnQgQ2VydGlmaWNhdGUgQXV0
aG9yaXR5MB4XDTE0MDUxNjIwMjg1MloXDTQxMTAwMTIwMjg1MlowRjELMAkGA1UE
BhMCVVMxDTALBgNVBAgTBE9oaW8xETAPBgNVBAcTCEhpbGxpYXJkMRUwEwYDVQQD
EwxBc294IENvbXBhbnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz
ZGrJ5XQHAuMYHlBgn32OOc9l0n3RjXccio2ceeWctXkSxDP3vFyZ4kBILF1lsY1g
o8UTjMkqSDYcytCLK0qavrv9BZRLB9FcpqJ9o4V9feaI/HsHa8DYHyEs8qyNTTNG
YQ3i4j+AA9iDSpezIYy/tyAOAjrSquUW1jI4tzKTBh8hk8MAMvR2/NPHPkrp4gI+
EMH6u4vWdr4F9bbriLFWoU04T9mWOMk7G+h8BS9sgINg2+v5cWvl3BC4kLk5L1yJ
FEyuofSSCEEe6dDf7uVh+RPKa4hEkIYo31AEOPFrN56d+pCj/5l67HTWXoQx3rjy
dNXMvgU75urm6TQe8dB5AgMBAAGjJTAjMCEGA1UdEQQaMBiHBH8AAAGHEAAAAAAA
AAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEFBQADggEBAD26XYInaEvlWZJYgtl3yQyC
3NRQc3LG7XxWg4aFdXCxYLPRAL2HLoarKYH8GPFso57t5xnhA8WfP7iJxmgsKdCS
0pNIicOWsMmXvYLib0j9tMCFR+a8rn3f4n+clwnqas4w/vWBJUoMgyxtkP8NNNZO
kIl02JKRhuyiFyPLilVp5tu0e+lmyUER+ak53WjLq2yoytYAlHkzkOpc4MZ/TNt5
UTEtx/WVlZvlrPi3dsi7QikkjQgo1wCnm7owtuAHlPDMAB8wKk4+vvIOjsGM33T/
8ffq/4X1HeYM0w0fM+SVlX1rwkXA1RW/jn48VWFHpWbE10+m196OdiToGfm2OJI=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAs2RqyeV0BwLjGB5QYJ99jjnPZdJ90Y13HIqNnHnlnLV5EsQz
97xcmeJASCxdZbGNYKPFE4zJKkg2HMrQiytKmr67/QWUSwfRXKaifaOFfX3miPx7
B2vA2B8hLPKsjU0zRmEN4uI/gAPYg0qXsyGMv7cgDgI60qrlFtYyOLcykwYfIZPD
ADL0dvzTxz5K6eICPhDB+ruL1na+BfW264ixVqFNOE/ZljjJOxvofAUvbICDYNvr
+XFr5dwQuJC5OS9ciRRMrqH0kghBHunQ3+7lYfkTymuIRJCGKN9QBDjxazeenfqQ
o/+Zeux01l6EMd648nTVzL4FO+bq5uk0HvHQeQIDAQABAoIBAQCSPcBYindF5/Kd
jMjVm+9M7I/IYAo1tG9vkvvSngSy9bWXuN7sjF+pCyqAK7qP1mh8acWVJGYx0+BZ
JHVRnp8Y+3hg0hWL/PmN4EICzjVakjJHZhwddpglF2uCKurD3jV4oFIjrXE6uOfe
UAbO/wCwoWa+RM8TQkGzljYmyiGufCcXlgEKMNA7TIvbJ9TVx3VTCOQy6EjZ13jd
M6X7byV/ZOFpZ2H0QV46LvZraw04riXQ/59gVmzizYdI+BwnxxapsCmalTJoV/Y0
LMI2ylat4PTMVTxPF+ti7Nt+rUkkEx6kuiAgfc+bzE4BSD5X4wy3fdLVLccoxXYw
4N3fOuQhAoGBAOLrMhiSCrzXGjDWTbPrwzxXDO0qm+wURELi3N5SXIkKUdG2/In6
wNdpXdvqblOm7SASgPf9KCwUSADrNw6R6nbfrrir5EHg66YydI/OW42QzJKcBUFh
5Q5na3fvoL/zRhsmh0gEymBg+OIfNel2LY69bl8aAko2y0R1kj7zb8X1AoGBAMph
9hlnkIBSw60+pKHaOqo2t/kihNNMFyfOgJvh8960eFeMDhMIXgxPUR8yaPX0bBMb
bCdEJJ2pmq7zUBPvxVJLedwkGMhywElA8yYVh+S6x4Cg+lYo4spIjrHQ/WTvJkHB
GrDskxdq80lbXjwRd0dPJZkxhKJec1o0n8S03Mn1AoGAGarK5taWGlgmYUHMVj6j
vc6G6si4DFMaiYpJu2gLiYC+Un9lP2I6r+L+N+LjidjG16rgJazf/2Rn5Jq2hpJg
uAODKuZekkkTvp/UaXPJDVFEooy9V3DwTNnL4SwcvbmRw35vLOlFzvMJE+K94WN5
sbyhoGY7vhNGmL7HxREaIoUCgYEAwpteVWFz3yE2ziF9l7FMVh7V23go9zGk1n9I
xhyJL26khbLEWeLi5L1kiTYlHdUSE3F8F2n8N6s+ddq79t/KA29WV6xSNHW7lvUg
mk975CMC8hpZfn5ETjVlGXGYJ/Wa+QGiE9z5ODx8gt6cB/DXnLdrtRqbqrJeA7C0
rScpY/0CgYBCC1QeuAiwWHOqQn3BwsZo9JQBTyT0QvDqLH/F+h9QbXep+4HvyAxG
nTMNDtGyfyKGDaDUn5hyeU7Oxvzq0K9P+eZD3MjQeaMEg/++GPGUPmDUTqyb2UT8
5s0NIUobxfKnTD6IpgOIq7ffvVY6cKBMyuLmu/gSvscsbONHjKti3Q==
-----END RSA PRIVATE KEY-----
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/io/ShutdownSocketSet.h>
#include <atomic>
#include <chrono>
#include <thread>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
using folly::ShutdownSocketSet;
namespace folly { namespace test {
ShutdownSocketSet shutdownSocketSet;
class Server {
public:
Server();
void stop(bool abortive);
void join();
int port() const { return port_; }
int closeClients(bool abortive);
private:
int acceptSocket_;
int port_;
enum StopMode {
NO_STOP,
ORDERLY,
ABORTIVE
};
std::atomic<StopMode> stop_;
std::thread serverThread_;
std::vector<int> fds_;
};
Server::Server()
: acceptSocket_(-1),
port_(0),
stop_(NO_STOP) {
acceptSocket_ = socket(PF_INET, SOCK_STREAM, 0);
CHECK_ERR(acceptSocket_);
shutdownSocketSet.add(acceptSocket_);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = INADDR_ANY;
CHECK_ERR(bind(acceptSocket_,
reinterpret_cast<const sockaddr*>(&addr),
sizeof(addr)));
CHECK_ERR(listen(acceptSocket_, 10));
socklen_t addrLen = sizeof(addr);
CHECK_ERR(getsockname(acceptSocket_,
reinterpret_cast<sockaddr*>(&addr),
&addrLen));
port_ = ntohs(addr.sin_port);
serverThread_ = std::thread([this] {
while (stop_ == NO_STOP) {
sockaddr_in peer;
socklen_t peerLen = sizeof(peer);
int fd = accept(acceptSocket_,
reinterpret_cast<sockaddr*>(&peer),
&peerLen);
if (fd == -1) {
if (errno == EINTR) {
continue;
}
if (errno == EINVAL || errno == ENOTSOCK) { // socket broken
break;
}
}
CHECK_ERR(fd);
shutdownSocketSet.add(fd);
fds_.push_back(fd);
}
if (stop_ != NO_STOP) {
closeClients(stop_ == ABORTIVE);
}
shutdownSocketSet.close(acceptSocket_);
acceptSocket_ = -1;
port_ = 0;
});
}
int Server::closeClients(bool abortive) {
for (int fd : fds_) {
if (abortive) {
struct linger l = {1, 0};
CHECK_ERR(setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)));
}
shutdownSocketSet.close(fd);
}
int n = fds_.size();
fds_.clear();
return n;
}
void Server::stop(bool abortive) {
stop_ = abortive ? ABORTIVE : ORDERLY;
shutdown(acceptSocket_, SHUT_RDWR);
}
void Server::join() {
serverThread_.join();
}
int createConnectedSocket(int port) {
int sock = socket(PF_INET, SOCK_STREAM, 0);
CHECK_ERR(sock);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl((127 << 24) | 1); // XXX
CHECK_ERR(connect(sock,
reinterpret_cast<const sockaddr*>(&addr),
sizeof(addr)));
return sock;
}
void runCloseTest(bool abortive) {
Server server;
int sock = createConnectedSocket(server.port());
std::thread stopper([&server, abortive] {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
server.stop(abortive);
server.join();
});
char c;
int r = read(sock, &c, 1);
if (abortive) {
int e = errno;
EXPECT_EQ(-1, r);
EXPECT_EQ(ECONNRESET, e);
} else {
EXPECT_EQ(0, r);
}
close(sock);
stopper.join();
EXPECT_EQ(0, server.closeClients(false)); // closed by server when it exited
}
TEST(ShutdownSocketSetTest, OrderlyClose) {
runCloseTest(false);
}
TEST(ShutdownSocketSetTest, AbortiveClose) {
runCloseTest(true);
}
void runKillTest(bool abortive) {
Server server;
int sock = createConnectedSocket(server.port());
std::thread killer([&server, abortive] {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
shutdownSocketSet.shutdownAll(abortive);
server.join();
});
char c;
int r = read(sock, &c, 1);
// "abortive" is just a hint for ShutdownSocketSet, so accept both
// behaviors
if (abortive) {
if (r == -1) {
EXPECT_EQ(ECONNRESET, errno);
} else {
EXPECT_EQ(r, 0);
}
} else {
EXPECT_EQ(0, r);
}
close(sock);
killer.join();
// NOT closed by server when it exited
EXPECT_EQ(1, server.closeClients(false));
}
TEST(ShutdownSocketSetTest, OrderlyKill) {
runKillTest(false);
}
TEST(ShutdownSocketSetTest, AbortiveKill) {
runKillTest(true);
}
}} // namespaces
int main(int argc, char *argv[]) {
testing::InitGoogleTest(&argc, argv);
google::ParseCommandLineFlags(&argc, &argv, true);
return RUN_ALL_TESTS();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment