Browse Source

Added url::Get interface

yhirose 5 years ago
parent
commit
da26b517a3
6 changed files with 153 additions and 6 deletions
  1. 1 0
      .gitignore
  2. 5 2
      example/Makefile
  3. 1 1
      example/hello.cc
  4. 33 0
      example/simplecli.cc
  5. 59 2
      httplib.h
  6. 54 1
      test/test.cc

+ 1 - 0
.gitignore

@@ -3,6 +3,7 @@ tags
 example/server
 example/server
 example/client
 example/client
 example/hello
 example/hello
+example/simplecli
 example/simplesvr
 example/simplesvr
 example/benchmark
 example/benchmark
 example/redirect
 example/redirect

+ 5 - 2
example/Makefile

@@ -5,7 +5,7 @@ OPENSSL_DIR = /usr/local/opt/openssl
 OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
 OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
 ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
 ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
 
 
-all: server client hello simplesvr upload redirect sse benchmark
+all: server client hello simplecli simplesvr upload redirect sse benchmark
 
 
 server : server.cc ../httplib.h Makefile
 server : server.cc ../httplib.h Makefile
 	$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
 	$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
@@ -16,6 +16,9 @@ client : client.cc ../httplib.h Makefile
 hello : hello.cc ../httplib.h Makefile
 hello : hello.cc ../httplib.h Makefile
 	$(CXX) -o hello $(CXXFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
 	$(CXX) -o hello $(CXXFLAGS) hello.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
 
 
+simplecli : simplecli.cc ../httplib.h Makefile
+	$(CXX) -o simplecli $(CXXFLAGS) simplecli.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
+
 simplesvr : simplesvr.cc ../httplib.h Makefile
 simplesvr : simplesvr.cc ../httplib.h Makefile
 	$(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
 	$(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
 
 
@@ -36,4 +39,4 @@ pem:
 	openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
 	openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
 
 
 clean:
 clean:
-	rm server client hello simplesvr upload redirect sse benchmark *.pem
+	rm server client hello simplecli simplesvr upload redirect sse benchmark *.pem

+ 1 - 1
example/hello.cc

@@ -15,5 +15,5 @@ int main(void) {
     res.set_content("Hello World!", "text/plain");
     res.set_content("Hello World!", "text/plain");
   });
   });
 
 
-  svr.listen("localhost", 1234);
+  svr.listen("localhost", 8080);
 }
 }

+ 33 - 0
example/simplecli.cc

@@ -0,0 +1,33 @@
+//
+//  simplecli.cc
+//
+//  Copyright (c) 2019 Yuji Hirose. All rights reserved.
+//  MIT License
+//
+
+#include <httplib.h>
+#include <iostream>
+
+#define CA_CERT_FILE "./ca-bundle.crt"
+
+using namespace std;
+
+int main(void) {
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+  httplib::url::Options options;
+  options.ca_cert_file_path = CA_CERT_FILE;
+  // options.server_certificate_verification = true;
+
+  auto res = httplib::url::Get("https://localhost:8080/hi", options);
+#else
+  auto res = httplib::url::Get("http://localhost:8080/hi");
+#endif
+
+  if (res) {
+    cout << res->status << endl;
+    cout << res->get_header_value("Content-Type") << endl;
+    cout << res->body << endl;
+  }
+
+  return 0;
+}

+ 59 - 2
httplib.h

@@ -3885,10 +3885,10 @@ inline bool Client::redirect(const Request &req, Response &res) {
   if (location.empty()) { return false; }
   if (location.empty()) { return false; }
 
 
   const static std::regex re(
   const static std::regex re(
-      R"(^(?:(https?):)?(?://([^/?#]*)(?:(:\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
+      R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
 
 
   std::smatch m;
   std::smatch m;
-  if (!regex_match(location, m, re)) { return false; }
+  if (!std::regex_match(location, m, re)) { return false; }
 
 
   auto scheme = is_ssl() ? "https" : "http";
   auto scheme = is_ssl() ? "https" : "http";
 
 
@@ -4967,6 +4967,63 @@ inline bool SSLClient::check_host_name(const char *pattern,
 }
 }
 #endif
 #endif
 
 
+namespace url {
+
+struct Options {
+  // TODO: support more options...
+  bool follow_location = false;
+  std::string client_cert_path;
+  std::string client_key_path;
+
+  std::string ca_cert_file_path;
+  std::string ca_cert_dir_path;
+  bool server_certificate_verification = false;
+};
+
+inline std::shared_ptr<Response> Get(const char *url, Options &options) {
+  const static std::regex re(
+      R"(^(https?)://([^:/?#]+)(?::(\d+))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
+
+  std::cmatch m;
+  if (!std::regex_match(url, m, re)) { return nullptr; }
+
+  auto next_scheme = m[1].str();
+  auto next_host = m[2].str();
+  auto port_str = m[3].str();
+  auto next_path = m[4].str();
+
+  auto next_port = !port_str.empty() ? std::stoi(port_str)
+                                     : (next_scheme == "https" ? 443 : 80);
+
+  if (next_path.empty()) { next_path = "/"; }
+
+  if (next_scheme == "https") {
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+    SSLClient cli(next_host.c_str(), next_port, options.client_cert_path,
+                  options.client_key_path);
+    cli.set_follow_location(options.follow_location);
+    cli.set_ca_cert_path(options.ca_cert_file_path.c_str(), options.ca_cert_dir_path.c_str());
+    cli.enable_server_certificate_verification(
+        options.server_certificate_verification);
+    return cli.Get(next_path.c_str());
+#else
+    return nullptr;
+#endif
+  } else {
+    Client cli(next_host.c_str(), next_port, options.client_cert_path,
+               options.client_key_path);
+    cli.set_follow_location(options.follow_location);
+    return cli.Get(next_path.c_str());
+  }
+}
+
+inline std::shared_ptr<Response> Get(const char *url) {
+  Options options;
+  return Get(url, options);
+}
+
+} // namespace url
+
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 
 
 } // namespace httplib
 } // namespace httplib

+ 54 - 1
test/test.cc

@@ -654,7 +654,7 @@ TEST(YahooRedirectTestWithURL, Redirect) {
   ASSERT_TRUE(res != nullptr);
   ASSERT_TRUE(res != nullptr);
   EXPECT_EQ(301, res->status);
   EXPECT_EQ(301, res->status);
 
 
-  httplib::url::options options;
+  httplib::url::Options options;
   options.follow_location = true;
   options.follow_location = true;
 
 
   res = httplib::url::Get("http://yahoo.com", options);
   res = httplib::url::Get("http://yahoo.com", options);
@@ -669,6 +669,59 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
       cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
       cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
   ASSERT_TRUE(res != nullptr);
   ASSERT_TRUE(res != nullptr);
 }
 }
+
+TEST(HttpsToHttpRedirectTestWithURL, Redirect) {
+  httplib::url::Options options;
+  options.follow_location = true;
+
+  auto res = httplib::url::Get(
+      "https://httpbin.org/"
+      "redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
+  ASSERT_TRUE(res != nullptr);
+}
+
+TEST(RedirectToDifferentPort, Redirect) {
+  Server svr8080;
+  Server svr8081;
+
+  svr8080.Get("/1", [&](const Request & /*req*/, Response &res) {
+    res.set_redirect("http://localhost:8081/2");
+  });
+
+  svr8081.Get("/2", [&](const Request & /*req*/, Response &res) {
+    res.set_content("Hello World!", "text/plain");
+  });
+
+  auto thread8080 = std::thread([&]() {
+      svr8080.listen("localhost", 8080);
+  });
+
+  auto thread8081 = std::thread([&]() {
+      svr8081.listen("localhost", 8081);
+  });
+
+  while (!svr8080.is_running() || !svr8081.is_running()) {
+    std::this_thread::sleep_for(std::chrono::milliseconds(1));
+  }
+
+  // Give GET time to get a few messages.
+  std::this_thread::sleep_for(std::chrono::seconds(1));
+
+  Client cli("localhost", 8080);
+  cli.set_follow_location(true);
+
+  auto res = cli.Get("/1");
+  ASSERT_TRUE(res != nullptr);
+  EXPECT_EQ(200, res->status);
+  EXPECT_EQ(res->body, "Hello World!");
+
+  svr8080.stop();
+  svr8081.stop();
+  thread8080.join();
+  thread8081.join();
+  ASSERT_FALSE(svr8080.is_running());
+  ASSERT_FALSE(svr8081.is_running());
+}
 #endif
 #endif
 
 
 TEST(Server, BindAndListenSeparately) {
 TEST(Server, BindAndListenSeparately) {