Skip to content

Commit 82a5ac7

Browse files
authored
Merge pull request yhirose#290 from yhirose/interface
Fix yhirose#285. Added set_interface method on client
2 parents 10759f0 + 08bf806 commit 82a5ac7

File tree

3 files changed

+72
-5
lines changed

3 files changed

+72
-5
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,12 @@ res = cli.Get("/");
392392
res->status; // 200
393393
```
394394
395+
### Use a specitic network interface
396+
397+
```cpp
398+
cli.set_interface("eth0"); // Interface name, IP address or host name
399+
```
400+
395401
OpenSSL Support
396402
---------------
397403

httplib.h

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ using socket_t = SOCKET;
114114

115115
#include <arpa/inet.h>
116116
#include <cstring>
117+
#include <ifaddrs.h>
117118
#include <netdb.h>
118119
#include <netinet/in.h>
119120
#ifdef CPPHTTPLIB_USE_POLL
@@ -743,6 +744,8 @@ class Client {
743744

744745
void set_compress(bool on);
745746

747+
void set_interface(const char *intf);
748+
746749
protected:
747750
bool process_request(Stream &strm, const Request &req, Response &res,
748751
bool last_connection, bool &connection_close);
@@ -758,6 +761,7 @@ class Client {
758761
std::string username_;
759762
std::string password_;
760763
bool compress_;
764+
std::string interface_;
761765

762766
private:
763767
socket_t create_client_socket() const;
@@ -1348,10 +1352,62 @@ inline bool is_connection_error() {
13481352
#endif
13491353
}
13501354

1355+
inline bool bind_ip_address(socket_t sock, const char *host) {
1356+
struct addrinfo hints;
1357+
struct addrinfo *result;
1358+
1359+
memset(&hints, 0, sizeof(struct addrinfo));
1360+
hints.ai_family = AF_UNSPEC;
1361+
hints.ai_socktype = SOCK_STREAM;
1362+
hints.ai_protocol = 0;
1363+
1364+
if (getaddrinfo(host, "0", &hints, &result)) { return false; }
1365+
1366+
bool ret = false;
1367+
for (auto rp = result; rp; rp = rp->ai_next) {
1368+
const auto &ai = *rp;
1369+
if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
1370+
ret = true;
1371+
break;
1372+
}
1373+
}
1374+
1375+
freeaddrinfo(result);
1376+
return ret;
1377+
}
1378+
1379+
inline std::string if2ip(const std::string &ifn) {
1380+
#ifndef _WIN32
1381+
struct ifaddrs *ifap;
1382+
getifaddrs(&ifap);
1383+
for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
1384+
if (ifa->ifa_addr && ifn == ifa->ifa_name) {
1385+
if (ifa->ifa_addr->sa_family == AF_INET) {
1386+
auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
1387+
char buf[INET_ADDRSTRLEN];
1388+
if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
1389+
freeifaddrs(ifap);
1390+
return std::string(buf, INET_ADDRSTRLEN);
1391+
}
1392+
}
1393+
}
1394+
}
1395+
freeifaddrs(ifap);
1396+
#endif
1397+
return std::string();
1398+
}
1399+
13511400
inline socket_t create_client_socket(const char *host, int port,
1352-
time_t timeout_sec) {
1401+
time_t timeout_sec,
1402+
const std::string &intf) {
13531403
return create_socket(
1354-
host, port, [=](socket_t sock, struct addrinfo &ai) -> bool {
1404+
host, port, [&](socket_t sock, struct addrinfo &ai) -> bool {
1405+
if (!intf.empty()) {
1406+
auto ip = if2ip(intf);
1407+
if (ip.empty()) { ip = intf; }
1408+
if (!bind_ip_address(sock, ip.c_str())) { return false; }
1409+
}
1410+
13551411
set_nonblocking(sock, true);
13561412

13571413
auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
@@ -3312,7 +3368,8 @@ inline Client::~Client() {}
33123368
inline bool Client::is_valid() const { return true; }
33133369

33143370
inline socket_t Client::create_client_socket() const {
3315-
return detail::create_client_socket(host_.c_str(), port_, timeout_sec_);
3371+
return detail::create_client_socket(host_.c_str(), port_, timeout_sec_,
3372+
interface_);
33163373
}
33173374

33183375
inline bool Client::read_response_line(Stream &strm, Response &res) {
@@ -3942,6 +3999,10 @@ inline void Client::set_follow_location(bool on) { follow_location_ = on; }
39423999

39434000
inline void Client::set_compress(bool on) { compress_ = on; }
39444001

4002+
inline void Client::set_interface(const char *intf) {
4003+
interface_ = intf;
4004+
}
4005+
39454006
/*
39464007
* SSL Implementation
39474008
*/

test/test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,8 +1817,8 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
18171817

18181818
// Sends a raw request to a server listening at HOST:PORT.
18191819
static bool send_request(time_t read_timeout_sec, const std::string &req) {
1820-
auto client_sock =
1821-
detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5);
1820+
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
1821+
std::string());
18221822

18231823
if (client_sock == INVALID_SOCKET) { return false; }
18241824

0 commit comments

Comments
 (0)