From 6ba2117b74f6b4ebec8d1b9ab771d77e35a252e4 Mon Sep 17 00:00:00 2001 From: Yedidya Feldblum Date: Mon, 15 Aug 2016 19:39:44 -0700 Subject: [PATCH] ScopedBoundPort Summary: [Folly] `ScopedBoundPort`. An RAII class for `bind`ing, but not `listen`ing, an ephemral port. Useful when there is a need to test server-down cases - in such cases, a guaranteed-not-listening port is required. Extracted from a unit-test in Thrift; switches the Thrift test to use this extracted implementation instead. Reviewed By: djwatson Differential Revision: D3714293 fbshipit-source-id: 649fba1a0b7f0519b8297a3183d03c5dde23ddc6 --- folly/Makefile.am | 2 + folly/io/async/AsyncServerSocket.h | 11 +++++ folly/io/async/test/ScopedBoundPort.cpp | 40 +++++++++++++++++ folly/io/async/test/ScopedBoundPort.h | 60 +++++++++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 folly/io/async/test/ScopedBoundPort.cpp create mode 100644 folly/io/async/test/ScopedBoundPort.h diff --git a/folly/Makefile.am b/folly/Makefile.am index 328f96a3..116ef90e 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -227,6 +227,7 @@ nobase_follyinclude_HEADERS = \ io/async/test/MockAsyncTransport.h \ io/async/test/MockAsyncUDPSocket.h \ io/async/test/MockTimeoutManager.h \ + io/async/test/ScopedBoundPort.h \ io/async/test/SocketPair.h \ io/async/test/TimeUtil.h \ io/async/test/UndelayedDestruction.h \ @@ -415,6 +416,7 @@ libfolly_la_SOURCES = \ io/async/SSLContext.cpp \ io/async/ScopedEventBaseThread.cpp \ io/async/HHWheelTimer.cpp \ + io/async/test/ScopedBoundPort.cpp \ io/async/test/SocketPair.cpp \ io/async/test/TimeUtil.cpp \ io/async/ssl/OpenSSLUtils.cpp \ diff --git a/folly/io/async/AsyncServerSocket.h b/folly/io/async/AsyncServerSocket.h index d2f5002b..83ef058c 100644 --- a/folly/io/async/AsyncServerSocket.h +++ b/folly/io/async/AsyncServerSocket.h @@ -351,6 +351,17 @@ class AsyncServerSocket : public DelayedDestruction */ void getAddress(SocketAddress* addressReturn) const; + /** + * Get the local address to which the socket is bound. + * + * Throws TTransportException on error. + */ + SocketAddress getAddress() const { + SocketAddress ret; + getAddress(&ret); + return ret; + } + /** * Get all the local addresses to which the socket is bound. * diff --git a/folly/io/async/test/ScopedBoundPort.cpp b/folly/io/async/test/ScopedBoundPort.cpp new file mode 100644 index 00000000..b3ba946d --- /dev/null +++ b/folly/io/async/test/ScopedBoundPort.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2016 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 + +#include +#include +#include + +namespace folly { + +ScopedBoundPort::ScopedBoundPort(IPAddress host) { + ebth_ = folly::make_unique(); + ebth_->getEventBase()->runInEventBaseThreadAndWait([&] { + sock_ = AsyncServerSocket::newSocket(ebth_->getEventBase()); + sock_->bind(SocketAddress(host, 0)); + }); +} + +ScopedBoundPort::~ScopedBoundPort() { + ebth_->getEventBase()->runInEventBaseThread([sock = std::move(sock_)]{}); +} + +SocketAddress ScopedBoundPort::getAddress() const { + return sock_->getAddress(); +} +} diff --git a/folly/io/async/test/ScopedBoundPort.h b/folly/io/async/test/ScopedBoundPort.h new file mode 100644 index 00000000..8dc90eda --- /dev/null +++ b/folly/io/async/test/ScopedBoundPort.h @@ -0,0 +1,60 @@ +/* + * Copyright 2016 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 +#include + +#include +#include +#include + +namespace folly { + +class AsyncServerSocket; +class ScopedEventBaseThread; + +/*** + * ScopedBoundPort + * + * Binds to an ephemeral port in the ctor but does not listen. Unbinds from the + * port in the dtor. + * + * While an instance is in scope, we know at least one port which is guaranteed + * not to be listening - the port the instance binds but does not listen. + * + * Useful for testing server-down cases. + * + * Example: + * + * TEST(MyClient, WhenTheServerIsDown_ThrowsServerDownException) { + * folly::ScopedBoundPort bound; + * MyClient client(bound.getAddress(), 100ms); + * EXPECT_THROW(client.getData(), ServerDownException); + * } + */ +class ScopedBoundPort { + public: + explicit ScopedBoundPort(IPAddress host = IPAddressV6("::1")); + ~ScopedBoundPort(); + SocketAddress getAddress() const; + + private: + std::unique_ptr ebth_; + std::shared_ptr sock_; +}; +} -- 2.34.1