Skip to content

Commit 35d0054

Browse files
authored
Merge pull request yhirose#49 from sgraham/system-assigned-port
Support system-assigned port via two part listen()
2 parents 0e239a0 + 0515c6a commit 35d0054

File tree

2 files changed

+82
-40
lines changed

2 files changed

+82
-40
lines changed

httplib.h

Lines changed: 75 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ class Server {
196196
void set_error_handler(Handler handler);
197197
void set_logger(Logger logger);
198198

199+
int bind_to_any_port(const char* host, int socket_flags = 0);
200+
bool listen_after_bind();
201+
199202
bool listen(const char* host, int port, int socket_flags = 0);
200203

201204
bool is_running() const;
@@ -210,6 +213,8 @@ class Server {
210213
typedef std::vector<std::pair<std::regex, Handler>> Handlers;
211214

212215
socket_t create_server_socket(const char* host, int port, int socket_flags) const;
216+
int bind_internal(const char* host, int port, int socket_flags);
217+
bool listen_internal();
213218

214219
bool routing(Request& req, Response& res);
215220
bool handle_file_request(Request& req, Response& res);
@@ -1399,49 +1404,20 @@ inline void Server::set_logger(Logger logger)
13991404
logger_ = logger;
14001405
}
14011406

1402-
inline bool Server::listen(const char* host, int port, int socket_flags)
1407+
inline int Server::bind_to_any_port(const char* host, int socket_flags)
14031408
{
1404-
if (!is_valid()) {
1405-
return false;
1406-
}
1407-
1408-
svr_sock_ = create_server_socket(host, port, socket_flags);
1409-
if (svr_sock_ == -1) {
1410-
return false;
1411-
}
1412-
1413-
auto ret = true;
1414-
1415-
for (;;) {
1416-
auto val = detail::select_read(svr_sock_, 0, 100000);
1417-
1418-
if (val == 0) { // Timeout
1419-
if (svr_sock_ == -1) {
1420-
// The server socket was closed by 'stop' method.
1421-
break;
1422-
}
1423-
continue;
1424-
}
1425-
1426-
socket_t sock = accept(svr_sock_, NULL, NULL);
1427-
1428-
if (sock == -1) {
1429-
if (svr_sock_ != -1) {
1430-
detail::close_socket(svr_sock_);
1431-
ret = false;
1432-
} else {
1433-
; // The server socket was closed by user.
1434-
}
1435-
break;
1436-
}
1409+
return bind_internal(host, 0, socket_flags);
1410+
}
14371411

1438-
// TODO: Use thread pool...
1439-
std::thread([=]() {
1440-
read_and_close_socket(sock);
1441-
}).detach();
1442-
}
1412+
inline bool Server::listen_after_bind() {
1413+
return listen_internal();
1414+
}
14431415

1444-
return ret;
1416+
inline bool Server::listen(const char* host, int port, int socket_flags)
1417+
{
1418+
if (bind_internal(host, port, socket_flags) < 0)
1419+
return false;
1420+
return listen_internal();
14451421
}
14461422

14471423
inline bool Server::is_running() const
@@ -1560,6 +1536,65 @@ inline socket_t Server::create_server_socket(const char* host, int port, int soc
15601536
}, socket_flags);
15611537
}
15621538

1539+
inline int Server::bind_internal(const char* host, int port, int socket_flags)
1540+
{
1541+
if (!is_valid()) {
1542+
return -1;
1543+
}
1544+
1545+
svr_sock_ = create_server_socket(host, port, socket_flags);
1546+
if (svr_sock_ == -1) {
1547+
return -1;
1548+
}
1549+
1550+
if (port == 0) {
1551+
struct sockaddr_in sin;
1552+
socklen_t len = sizeof(sin);
1553+
if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&sin), &len) == -1) {
1554+
return -1;
1555+
}
1556+
return ntohs(sin.sin_port);
1557+
} else {
1558+
return port;
1559+
}
1560+
}
1561+
1562+
inline bool Server::listen_internal()
1563+
{
1564+
auto ret = true;
1565+
1566+
for (;;) {
1567+
auto val = detail::select_read(svr_sock_, 0, 100000);
1568+
1569+
if (val == 0) { // Timeout
1570+
if (svr_sock_ == -1) {
1571+
// The server socket was closed by 'stop' method.
1572+
break;
1573+
}
1574+
continue;
1575+
}
1576+
1577+
socket_t sock = accept(svr_sock_, NULL, NULL);
1578+
1579+
if (sock == -1) {
1580+
if (svr_sock_ != -1) {
1581+
detail::close_socket(svr_sock_);
1582+
ret = false;
1583+
} else {
1584+
; // The server socket was closed by user.
1585+
}
1586+
break;
1587+
}
1588+
1589+
// TODO: Use thread pool...
1590+
std::thread([=]() {
1591+
read_and_close_socket(sock);
1592+
}).detach();
1593+
}
1594+
1595+
return ret;
1596+
}
1597+
15631598
inline bool Server::routing(Request& req, Response& res)
15641599
{
15651600
if (req.method == "GET" && handle_file_request(req, res)) {

test/test.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,13 @@ TEST(ConnectionErrorTest, Timeout)
240240
ASSERT_TRUE(res == nullptr);
241241
}
242242

243+
TEST(Server, BindAndListenSeparately) {
244+
Server svr(httplib::HttpVersion::v1_1);
245+
int port = svr.bind_to_any_port("localhost");
246+
ASSERT_TRUE(port > 0);
247+
svr.stop();
248+
}
249+
243250
class ServerTest : public ::testing::Test {
244251
protected:
245252
ServerTest()

0 commit comments

Comments
 (0)