Browse Source

Merge pull request #290 from yhirose/interface

Fix #285. Added set_interface method on client
yhirose 6 years ago
parent
commit
82a5ac735f
3 changed files with 72 additions and 5 deletions
  1. 6 0
      README.md
  2. 64 3
      httplib.h
  3. 2 2
      test/test.cc

+ 6 - 0
README.md

@@ -392,6 +392,12 @@ res = cli.Get("/");
 res->status; // 200
 res->status; // 200
 ```
 ```
 
 
+### Use a specitic network interface
+
+```cpp
+cli.set_interface("eth0"); // Interface name, IP address or host name
+```
+
 OpenSSL Support
 OpenSSL Support
 ---------------
 ---------------
 
 

+ 64 - 3
httplib.h

@@ -114,6 +114,7 @@ using socket_t = SOCKET;
 
 
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <cstring>
 #include <cstring>
+#include <ifaddrs.h>
 #include <netdb.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 #ifdef CPPHTTPLIB_USE_POLL
 #ifdef CPPHTTPLIB_USE_POLL
@@ -743,6 +744,8 @@ public:
 
 
   void set_compress(bool on);
   void set_compress(bool on);
 
 
+  void set_interface(const char *intf);
+
 protected:
 protected:
   bool process_request(Stream &strm, const Request &req, Response &res,
   bool process_request(Stream &strm, const Request &req, Response &res,
                        bool last_connection, bool &connection_close);
                        bool last_connection, bool &connection_close);
@@ -758,6 +761,7 @@ protected:
   std::string username_;
   std::string username_;
   std::string password_;
   std::string password_;
   bool compress_;
   bool compress_;
+  std::string interface_;
 
 
 private:
 private:
   socket_t create_client_socket() const;
   socket_t create_client_socket() const;
@@ -1348,10 +1352,62 @@ inline bool is_connection_error() {
 #endif
 #endif
 }
 }
 
 
+inline bool bind_ip_address(socket_t sock, const char *host) {
+  struct addrinfo hints;
+  struct addrinfo *result;
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = 0;
+
+  if (getaddrinfo(host, "0", &hints, &result)) { return false; }
+
+  bool ret = false;
+  for (auto rp = result; rp; rp = rp->ai_next) {
+    const auto &ai = *rp;
+    if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
+      ret = true;
+      break;
+    }
+  }
+
+  freeaddrinfo(result);
+  return ret;
+}
+
+inline std::string if2ip(const std::string &ifn) {
+#ifndef _WIN32
+  struct ifaddrs *ifap;
+  getifaddrs(&ifap);
+  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
+    if (ifa->ifa_addr && ifn == ifa->ifa_name) {
+      if (ifa->ifa_addr->sa_family == AF_INET) {
+        auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
+        char buf[INET_ADDRSTRLEN];
+        if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
+          freeifaddrs(ifap);
+          return std::string(buf, INET_ADDRSTRLEN);
+        }
+      }
+    }
+  }
+  freeifaddrs(ifap);
+#endif
+  return std::string();
+}
+
 inline socket_t create_client_socket(const char *host, int port,
 inline socket_t create_client_socket(const char *host, int port,
-                                     time_t timeout_sec) {
+                                     time_t timeout_sec,
+                                     const std::string &intf) {
   return create_socket(
   return create_socket(
-      host, port, [=](socket_t sock, struct addrinfo &ai) -> bool {
+      host, port, [&](socket_t sock, struct addrinfo &ai) -> bool {
+        if (!intf.empty()) {
+          auto ip = if2ip(intf);
+          if (ip.empty()) { ip = intf; }
+          if (!bind_ip_address(sock, ip.c_str())) { return false; }
+        }
+
         set_nonblocking(sock, true);
         set_nonblocking(sock, true);
 
 
         auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
         auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
@@ -3312,7 +3368,8 @@ inline Client::~Client() {}
 inline bool Client::is_valid() const { return true; }
 inline bool Client::is_valid() const { return true; }
 
 
 inline socket_t Client::create_client_socket() const {
 inline socket_t Client::create_client_socket() const {
-  return detail::create_client_socket(host_.c_str(), port_, timeout_sec_);
+  return detail::create_client_socket(host_.c_str(), port_, timeout_sec_,
+                                      interface_);
 }
 }
 
 
 inline bool Client::read_response_line(Stream &strm, Response &res) {
 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; }
 
 
 inline void Client::set_compress(bool on) { compress_ = on; }
 inline void Client::set_compress(bool on) { compress_ = on; }
 
 
+inline void Client::set_interface(const char *intf) {
+  interface_ = intf;
+}
+
 /*
 /*
  * SSL Implementation
  * SSL Implementation
  */
  */

+ 2 - 2
test/test.cc

@@ -1817,8 +1817,8 @@ TEST_F(ServerTest, MultipartFormDataGzip) {
 
 
 // Sends a raw request to a server listening at HOST:PORT.
 // Sends a raw request to a server listening at HOST:PORT.
 static bool send_request(time_t read_timeout_sec, const std::string &req) {
 static bool send_request(time_t read_timeout_sec, const std::string &req) {
-  auto client_sock =
-      detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5);
+  auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
+                                                  std::string());
 
 
   if (client_sock == INVALID_SOCKET) { return false; }
   if (client_sock == INVALID_SOCKET) { return false; }