Skip to content

Commit 099e42d

Browse files
Xiangyu Bumeta-codesync[bot]
authored andcommitted
Apply server socket options before bind
Summary: Pass configured server socket options into the bind-time listener creation path for both classic Proxygen and proxygen-coro so `PRE_BIND` options such as `TCP_MAXSEG` reach `AsyncServerSocket::bind()` instead of depending on wangle `Acceptor` applying them after `listen()` (which may or may not work depending on when the first accept() happens -- see D107476342). Context is that we need to do mss clamping on manifold http ports until shiv supports large MTU. Before this stack, it relies on wangle Acceptor to apply the socket options which may be too late for PRE_BIND socket options like the mss one. The stack fixes folly and wangle. In this diff we noticed that libproxygen doesn't copy the socket options map to wangle serverbootstrap. Reviewed By: ngoyal Differential Revision: D107599166 fbshipit-source-id: b8e680ae110cb19da64b455c987ff8a29da1a3f3
1 parent c0d03b3 commit 099e42d

8 files changed

Lines changed: 140 additions & 3 deletions

File tree

third-party/proxygen/src/proxygen/httpserver/HTTPServer.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,17 @@ folly::Expected<folly::Unit, std::exception_ptr> HTTPServer::startTcpServer(
167167
bootstrap_[i].setReusePort(true);
168168
}
169169
if (options_->preboundSockets_.size() > i) {
170+
// Intentionally not copying socket options map on prebound sockets
171+
// branch because 1) the caller has freedom to apply them, and 2) wangle
172+
// does not pass it to AsyncServerSocket, and AsyncServerSocket doesn't
173+
// consume it on pre-bound path anyway.
170174
bootstrap_[i].bind(std::move(options_->preboundSockets_[i]));
171175
} else {
176+
// Copy socket options to ServerBootstrap so that wangle can pass them
177+
// to AsyncServerSocket when calling bind(). This way the pre-bind
178+
// options can be applied properly pre-bind.
179+
bootstrap_[i].socketConfig.getSocketOptions() =
180+
accConfig->getSocketOptions();
172181
bootstrap_[i].bind(addresses_[i].address);
173182
}
174183
}

third-party/proxygen/src/proxygen/httpserver/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
proxygen_add_test(TARGET HTTPServerTests
88
SOURCES
99
HTTPServerTest.cpp
10+
HTTPServerTestUtils.cpp
1011
RequestHandlerAdaptorTest.cpp
1112
DEPENDS
1213
codectestutils

third-party/proxygen/src/proxygen/httpserver/tests/HTTPServerTest.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111

1212
#include <folly/FileUtil.h>
1313
#include <folly/executors/IOThreadPoolExecutor.h>
14+
#include <folly/io/SocketOptionMap.h>
1415
#include <folly/io/async/AsyncSSLSocket.h>
1516
#include <folly/io/async/AsyncServerSocket.h>
1617
#include <folly/io/async/EventBaseManager.h>
1718
#include <folly/logging/xlog.h>
1819
#include <folly/portability/GMock.h>
1920
#include <folly/portability/GTest.h>
21+
#include <folly/portability/Sockets.h>
2022
#include <folly/ssl/OpenSSLCertUtils.h>
2123
#include <folly/ssl/OpenSSLPtrTypes.h>
2224
#include <folly/system/HardwareConcurrency.h>
@@ -25,6 +27,7 @@
2527
#include <proxygen/httpserver/HTTPServer.h>
2628
#include <proxygen/httpserver/ResponseBuilder.h>
2729
#include <proxygen/httpserver/ScopedHTTPServer.h>
30+
#include <proxygen/httpserver/tests/HTTPServerTestUtils.h>
2831
#include <proxygen/lib/http/HTTPConnector.h>
2932
#include <proxygen/lib/utils/TestUtils.h>
3033
#include <wangle/acceptor/Acceptor.h>
@@ -39,8 +42,7 @@ using namespace CurlService;
3942
namespace {
4043

4144
const std::string kTestDir = getContainingDirectory(XLOG_FILENAME).str();
42-
43-
}
45+
} // namespace
4446

4547
class ServerThread {
4648
private:
@@ -813,6 +815,33 @@ TEST(GetListenSocket, TestBootstrapWithBinding) {
813815
ASSERT_NE(-1, socketFd);
814816
}
815817

818+
TEST(SocketOptions, AcceptorSocketOptionsApplyTcpMaxSegmentBeforeListen) {
819+
const auto tcpMaxSegment = proxygen::test::getDifferentTcpMaxSegment(
820+
proxygen::test::getDefaultLoopbackTcpMaxSegment());
821+
HTTPServer::IPConfig cfg{folly::SocketAddress("127.0.0.1", 0),
822+
HTTPServer::Protocol::HTTP};
823+
cfg.acceptorSocketOptions = folly::SocketOptionMap{
824+
{{.level = IPPROTO_TCP,
825+
.optname = TCP_MAXSEG,
826+
.applyPos_ = folly::SocketOptionKey::ApplyPos::PRE_BIND},
827+
tcpMaxSegment},
828+
};
829+
830+
HTTPServerOptions options;
831+
options.handlerFactories =
832+
RequestHandlerChain().addThen<TestHandlerFactory>().build();
833+
834+
auto server = std::make_unique<HTTPServer>(std::move(options));
835+
server->bind({cfg});
836+
837+
auto st = std::make_unique<ServerThread>(server.get());
838+
EXPECT_TRUE(st->start());
839+
840+
EXPECT_EQ(proxygen::test::getTcpMaxSegment(
841+
folly::NetworkSocket::fromFd(server->getListenSocket())),
842+
tcpMaxSegment);
843+
}
844+
816845
TEST(UseExistingSocket, TestWithExistingAsyncServerSocket) {
817846
AsyncServerSocket::UniquePtr serverSocket(new folly::AsyncServerSocket);
818847
serverSocket->bind(0);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <proxygen/httpserver/tests/HTTPServerTestUtils.h>
10+
11+
#include <folly/portability/GTest.h>
12+
#include <folly/portability/Sockets.h>
13+
#include <folly/portability/Unistd.h>
14+
15+
namespace proxygen::test {
16+
17+
int getTcpMaxSegment(folly::NetworkSocket fd) {
18+
int mss = 0;
19+
socklen_t mssLength = sizeof(mss);
20+
EXPECT_EQ(::getsockopt(fd.toFd(), IPPROTO_TCP, TCP_MAXSEG, &mss, &mssLength),
21+
0);
22+
return mss;
23+
}
24+
25+
int getDefaultLoopbackTcpMaxSegment() {
26+
auto fd = ::socket(AF_INET, SOCK_STREAM, 0);
27+
EXPECT_NE(fd, -1);
28+
const auto mss = getTcpMaxSegment(folly::NetworkSocket::fromFd(fd));
29+
EXPECT_EQ(::close(fd), 0);
30+
return mss;
31+
}
32+
33+
int getDifferentTcpMaxSegment(int defaultTcpMaxSegment) {
34+
EXPECT_GT(defaultTcpMaxSegment, 100);
35+
return defaultTcpMaxSegment - 100;
36+
}
37+
38+
} // namespace proxygen::test
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <folly/net/NetworkSocket.h>
12+
13+
namespace proxygen::test {
14+
15+
int getTcpMaxSegment(folly::NetworkSocket fd);
16+
17+
int getDefaultLoopbackTcpMaxSegment();
18+
19+
int getDifferentTcpMaxSegment(int defaultTcpMaxSegment);
20+
21+
} // namespace proxygen::test

third-party/proxygen/src/proxygen/lib/http/coro/server/HTTPServer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ void HTTPServer::startTcp(const KeepAliveEventBaseVec& keepAliveEvbs) {
218218
serverSocket->useExistingSocket(
219219
folly::NetworkSocket::fromFd(config_.preboundSocket.value()));
220220
} else {
221-
serverSocket->bind(config_.socketConfig.bindAddress);
221+
serverSocket->bind(config_.socketConfig.bindAddress,
222+
config_.socketConfig.getSocketOptions());
222223
}
223224
serverSocket->listen(config_.socketConfig.acceptBacklog);
224225
serverSocket->startAccepting();

third-party/proxygen/src/proxygen/lib/http/coro/server/HTTPServer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@
2424

2525
namespace proxygen::coro {
2626

27+
namespace test {
28+
class HTTPServerTests;
29+
} // namespace test
30+
2731
using folly::IOThreadPoolExecutor;
2832
using folly::ThreadPoolExecutor;
2933

3034
class HTTPServer : public quic::QuicHandshakeSocketHolder::Callback {
35+
friend class test::HTTPServerTests;
36+
3137
public:
3238
struct QuicConfig {
3339
std::vector<quic::QuicVersion> quicVersions;

third-party/proxygen/src/proxygen/lib/http/coro/server/test/HTTPServerTest.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <folly/logging/xlog.h>
1010
#include <folly/system/HardwareConcurrency.h>
11+
#include <proxygen/httpserver/tests/HTTPServerTestUtils.h>
1112
#include <proxygen/lib/http/codec/test/TestUtils.h>
1213
#include <proxygen/lib/http/coro/HTTPCoroSession.h>
1314
#include <proxygen/lib/http/coro/HTTPFixedSource.h>
@@ -22,6 +23,7 @@
2223

2324
#include <chrono>
2425
#include <folly/coro/GtestHelpers.h>
26+
#include <folly/io/SocketOptionMap.h>
2527
#include <folly/io/async/ScopedEventBaseThread.h>
2628
#include <folly/portability/GMock.h>
2729
#include <folly/portability/GTest.h>
@@ -45,6 +47,8 @@ std::string_view getTestDir() {
4547
return kTestDir;
4648
}
4749

50+
constexpr int kTestTcpMaxSegment = 1200;
51+
4852
struct StatsFactory : public ServerFilterFactory {
4953
std::pair<HTTPSourceFilter*, HTTPSourceFilter*> makeFilters() override {
5054
return StatsFilterUtil::makeFilters(&stats_);
@@ -206,6 +210,11 @@ class HTTPServerTests : public TestWithParam<TransportType> {
206210
std::make_shared<InsecureVerifierDangerousDoNotUseInProduction>());
207211
}
208212

213+
const std::vector<folly::AsyncServerSocket::UniquePtr>& getServerSockets() {
214+
CHECK(server_);
215+
return server_->getServer().serverSockets_;
216+
}
217+
209218
std::string listenAddress_{"127.0.0.1"};
210219
uint16_t listenPort_{0};
211220
std::shared_ptr<HTTPHandler> handler_{std::make_shared<TestHandler>()};
@@ -266,6 +275,29 @@ TEST_P(HTTPServerTests, TestExistingSocket) {
266275
stopServer();
267276
}
268277

278+
TEST_F(HTTPServerTests, SocketConfigAppliesTcpMaxSegmentBeforeListen) {
279+
const auto tcpMaxSegment = proxygen::test::getDifferentTcpMaxSegment(
280+
proxygen::test::getDefaultLoopbackTcpMaxSegment());
281+
serverConfig_.socketConfig.bindAddress.setFromIpPort(listenAddress_,
282+
listenPort_);
283+
serverConfig_.socketConfig.setSocketOptions(folly::SocketOptionMap{
284+
{{.level = IPPROTO_TCP,
285+
.optname = TCP_MAXSEG,
286+
.applyPos_ = folly::SocketOptionKey::ApplyPos::PRE_BIND},
287+
tcpMaxSegment},
288+
});
289+
server_ =
290+
ScopedHTTPServer::start(std::move(serverConfig_), handler_, nullptr);
291+
292+
auto& serverSockets = getServerSockets();
293+
ASSERT_EQ(serverSockets.size(), 1);
294+
EXPECT_EQ(proxygen::test::getTcpMaxSegment(
295+
serverSockets.front()->getNetworkSocket()),
296+
tcpMaxSegment);
297+
298+
stopServer();
299+
}
300+
269301
TEST_P(HTTPServerTests, TestStopMultipleTimes) {
270302
MockServerObserver mockObserver;
271303
serverConfig_.numIOThreads = 4;

0 commit comments

Comments
 (0)