DestructorGuard dg(this);
- if (handshakeCallback_) {
- AsyncSocketException ex(AsyncSocketException::END_OF_FILE,
- "SSL connection closed locally");
- HandshakeCB* callback = handshakeCallback_;
- handshakeCallback_ = nullptr;
- callback->handshakeErr(this, ex);
- }
+ invokeHandshakeErr(
+ AsyncSocketException(
+ AsyncSocketException::END_OF_FILE,
+ "SSL connection closed locally"));
if (ssl_ != nullptr) {
SSL_free(ssl_);
AsyncSocketException ex(AsyncSocketException::INVALID_STATE,
"sslAccept() called with socket in invalid state");
+ handshakeEndTime_ = std::chrono::steady_clock::now();
if (callback) {
callback->handshakeErr(this, ex);
}
handshakeCallback_ != nullptr) {
return invalidState(callback);
}
+ handshakeStartTime_ = std::chrono::steady_clock::now();
+ // Make end time at least >= start time.
+ handshakeEndTime_ = handshakeStartTime_;
sslState_ = STATE_ACCEPTING;
handshakeCallback_ = callback;
void AsyncSSLSocket::failHandshake(const char* fn,
const AsyncSocketException& ex) {
startFail();
-
if (handshakeTimeout_.isScheduled()) {
handshakeTimeout_.cancelTimeout();
}
+ invokeHandshakeErr(ex);
+ finishFail();
+}
+
+void AsyncSSLSocket::invokeHandshakeErr(const AsyncSocketException& ex) {
+ handshakeEndTime_ = std::chrono::steady_clock::now();
if (handshakeCallback_ != nullptr) {
HandshakeCB* callback = handshakeCallback_;
handshakeCallback_ = nullptr;
callback->handshakeErr(this, ex);
}
-
- finishFail();
}
void AsyncSSLSocket::invokeHandshakeCB() {
+ handshakeEndTime_ = std::chrono::steady_clock::now();
if (handshakeTimeout_.isScheduled()) {
handshakeTimeout_.cancelTimeout();
}
return invalidState(callback);
}
+ handshakeStartTime_ = std::chrono::steady_clock::now();
+ // Make end time at least >= start time.
+ handshakeEndTime_ = handshakeStartTime_;
+
sslState_ = STATE_CONNECTING;
handshakeCallback_ = callback;
return clientHelloInfo_.get();
}
+ /**
+ * Returns the time taken to complete a handshake.
+ */
+ std::chrono::nanoseconds getHandshakeTime() const {
+ return handshakeEndTime_ - handshakeStartTime_;
+ }
+
void setMinWriteSize(size_t minWriteSize) {
minWriteSize_ = minWriteSize;
}
// Inherit error handling methods from AsyncSocket, plus the following.
void failHandshake(const char* fn, const AsyncSocketException& ex);
+ void invokeHandshakeErr(const AsyncSocketException& ex);
void invokeHandshakeCB();
static void sslInfoCallback(const SSL *ssl, int type, int val);
bool parseClientHello_{false};
std::unique_ptr<ClientHelloInfo> clientHelloInfo_;
+
+ // Time taken to complete the ssl handshake.
+ std::chrono::steady_clock::time_point handshakeStartTime_;
+ std::chrono::steady_clock::time_point handshakeEndTime_;
};
} // namespace
return invalidState(callback);
}
+ connectStartTime_ = std::chrono::steady_clock::now();
+ // Make connect end time at least >= connectStartTime.
+ connectEndTime_ = connectStartTime_;
+
assert(fd_ == -1);
state_ = StateEnum::CONNECTING;
connectCallback_ = callback;
assert(readCallback_ == nullptr);
assert(writeReqHead_ == nullptr);
state_ = StateEnum::ESTABLISHED;
- if (callback) {
- connectCallback_ = nullptr;
- callback->connectSuccess();
- }
+ invokeConnectSuccess();
}
void AsyncSocket::connect(ConnectCallback* callback,
doClose();
}
- if (connectCallback_) {
- ConnectCallback* callback = connectCallback_;
- connectCallback_ = nullptr;
- callback->connectErr(socketClosedLocallyEx);
- }
+ invokeConnectErr(socketClosedLocallyEx);
failAllWrites(socketClosedLocallyEx);
// callbacks (since the callbacks may call detachEventBase()).
EventBase* originalEventBase = eventBase_;
- // Call the connect callback.
- if (connectCallback_) {
- ConnectCallback* callback = connectCallback_;
- connectCallback_ = nullptr;
- callback->connectSuccess();
- }
-
+ invokeConnectSuccess();
// Note that the connect callback may have changed our state.
// (set or unset the read callback, called write(), closed the socket, etc.)
// The following code needs to handle these situations correctly.
AsyncSocketException ex(AsyncSocketException::INTERNAL_ERROR,
withAddr("socket closing after error"));
- if (connectCallback_) {
- ConnectCallback* callback = connectCallback_;
- connectCallback_ = nullptr;
- callback->connectErr(ex);
- }
-
+ invokeConnectErr(ex);
failAllWrites(ex);
if (readCallback_) {
<< ex.what();
startFail();
- if (connectCallback_ != nullptr) {
- ConnectCallback* callback = connectCallback_;
- connectCallback_ = nullptr;
- callback->connectErr(ex);
- }
-
+ invokeConnectErr(ex);
finishFail();
}
AsyncSocketException ex(AsyncSocketException::ALREADY_OPEN,
"connect() called with socket in invalid state");
+ connectEndTime_ = std::chrono::steady_clock::now();
if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) {
if (callback) {
callback->connectErr(ex);
}
}
+void AsyncSocket::invokeConnectErr(const AsyncSocketException& ex) {
+ connectEndTime_ = std::chrono::steady_clock::now();
+ if (connectCallback_) {
+ ConnectCallback* callback = connectCallback_;
+ connectCallback_ = nullptr;
+ callback->connectErr(ex);
+ }
+}
+
+void AsyncSocket::invokeConnectSuccess() {
+ connectEndTime_ = std::chrono::steady_clock::now();
+ if (connectCallback_) {
+ ConnectCallback* callback = connectCallback_;
+ connectCallback_ = nullptr;
+ callback->connectSuccess();
+ }
+}
+
void AsyncSocket::invalidState(ReadCallback* callback) {
VLOG(4) << "AsyncSocket(this=" << this << ", fd=" << fd_
<< "): setReadCallback(" << callback
#include <folly/io/async/EventHandler.h>
#include <folly/io/async/DelayedDestruction.h>
+#include <chrono>
#include <memory>
#include <map>
return getAppBytesReceived();
}
+ std::chrono::nanoseconds getConnectTime() const {
+ return connectEndTime_ - connectStartTime_;
+ }
+
// Methods controlling socket options
/**
const AsyncSocketException& ex);
void failWrite(const char* fn, const AsyncSocketException& ex);
void failAllWrites(const AsyncSocketException& ex);
+ void invokeConnectErr(const AsyncSocketException& ex);
+ void invokeConnectSuccess();
void invalidState(ConnectCallback* callback);
void invalidState(ReadCallback* callback);
void invalidState(WriteCallback* callback);
bool peek_{false}; // Peek bytes.
int8_t readErr_{READ_NO_ERROR}; ///< The read error encountered, if any.
+
+ std::chrono::steady_clock::time_point connectStartTime_;
+ std::chrono::steady_clock::time_point connectEndTime_;
};
EXPECT_TRUE(client.handshakeVerify_);
EXPECT_TRUE(client.handshakeSuccess_);
EXPECT_TRUE(!client.handshakeError_);
+ EXPECT_LE(0, client.handshakeTime.count());
EXPECT_TRUE(!server.handshakeVerify_);
EXPECT_TRUE(server.handshakeSuccess_);
EXPECT_TRUE(!server.handshakeError_);
+ EXPECT_LE(0, server.handshakeTime.count());
}
/**
EXPECT_TRUE(client.handshakeVerify_);
EXPECT_TRUE(!client.handshakeSuccess_);
EXPECT_TRUE(client.handshakeError_);
+ EXPECT_LE(0, client.handshakeTime.count());
EXPECT_TRUE(!server.handshakeVerify_);
EXPECT_TRUE(!server.handshakeSuccess_);
EXPECT_TRUE(server.handshakeError_);
+ EXPECT_LE(0, server.handshakeTime.count());
}
/**
EXPECT_TRUE(!client.handshakeVerify_);
EXPECT_TRUE(client.handshakeSuccess_);
EXPECT_TRUE(!client.handshakeError_);
+ EXPECT_LE(0, client.handshakeTime.count());
EXPECT_TRUE(!server.handshakeVerify_);
EXPECT_TRUE(server.handshakeSuccess_);
EXPECT_TRUE(!server.handshakeError_);
+ EXPECT_LE(0, server.handshakeTime.count());
}
/**
EXPECT_TRUE(client.handshakeVerify_);
EXPECT_TRUE(client.handshakeSuccess_);
EXPECT_FALSE(client.handshakeError_);
+ EXPECT_LE(0, client.handshakeTime.count());
EXPECT_TRUE(server.handshakeVerify_);
EXPECT_TRUE(server.handshakeSuccess_);
EXPECT_FALSE(server.handshakeError_);
+ EXPECT_LE(0, server.handshakeTime.count());
}
/**
EXPECT_TRUE(client.handshakeVerify_);
EXPECT_TRUE(client.handshakeSuccess_);
EXPECT_TRUE(!client.handshakeError_);
+ EXPECT_LE(0, client.handshakeTime.count());
EXPECT_TRUE(!server.handshakeVerify_);
EXPECT_TRUE(server.handshakeSuccess_);
EXPECT_TRUE(!server.handshakeError_);
+ EXPECT_LE(0, server.handshakeTime.count());
}
/**
EXPECT_TRUE(!client.handshakeVerify_);
EXPECT_TRUE(client.handshakeSuccess_);
EXPECT_TRUE(!client.handshakeError_);
+ EXPECT_LE(0, client.handshakeTime.count());
EXPECT_TRUE(!server.handshakeVerify_);
EXPECT_TRUE(server.handshakeSuccess_);
EXPECT_TRUE(!server.handshakeError_);
+ EXPECT_LE(0, server.handshakeTime.count());
}
/**
EXPECT_TRUE(client.handshakeVerify_);
EXPECT_TRUE(client.handshakeSuccess_);
EXPECT_FALSE(client.handshakeError_);
+ EXPECT_LE(0, client.handshakeTime.count());
EXPECT_TRUE(server.handshakeVerify_);
EXPECT_TRUE(server.handshakeSuccess_);
EXPECT_FALSE(server.handshakeError_);
+ EXPECT_LE(0, server.handshakeTime.count());
}
EXPECT_FALSE(server.handshakeVerify_);
EXPECT_FALSE(server.handshakeSuccess_);
EXPECT_TRUE(server.handshakeError_);
+ EXPECT_LE(0, client.handshakeTime.count());
+ EXPECT_LE(0, server.handshakeTime.count());
}
TEST(AsyncSSLSocketTest, MinWriteSizeTest) {
bool handshakeVerify_;
bool handshakeSuccess_;
bool handshakeError_;
+ std::chrono::nanoseconds handshakeTime;
protected:
AsyncSSLSocket::UniquePtr socket_;
void handshakeSuc(AsyncSSLSocket*) noexcept override {
handshakeSuccess_ = true;
+ handshakeTime = socket_->getHandshakeTime();
}
void handshakeErr(
AsyncSSLSocket*,
const AsyncSocketException& ex) noexcept override {
handshakeError_ = true;
+ handshakeTime = socket_->getHandshakeTime();
}
// WriteCallback
evb.loop();
CHECK_EQ(cb.state, STATE_SUCCEEDED);
+ EXPECT_LE(0, socket->getConnectTime().count());
}
/**
CHECK_EQ(cb.state, STATE_FAILED);
CHECK_EQ(cb.exception.getType(), AsyncSocketException::NOT_OPEN);
+ EXPECT_LE(0, socket->getConnectTime().count());
}
/**
folly::SocketAddress peer;
socket->getPeerAddress(&peer);
CHECK_EQ(peer, addr);
+ EXPECT_LE(0, socket->getConnectTime().count());
}
/**