yhirose 6 years ago
parent
commit
295e4d58aa
6 changed files with 86 additions and 24 deletions
  1. 31 21
      httplib.h
  2. 37 3
      test/test.cc
  3. 8 0
      test/www2/dir/index.html
  4. 1 0
      test/www2/dir/test.html
  5. 8 0
      test/www3/dir/index.html
  6. 1 0
      test/www3/dir/test.html

+ 31 - 21
httplib.h

@@ -129,8 +129,9 @@ using socket_t = int;
 #define INVALID_SOCKET (-1)
 #endif //_WIN32
 
-#include <cassert>
+#include <array>
 #include <atomic>
+#include <cassert>
 #include <condition_variable>
 #include <errno.h>
 #include <fcntl.h>
@@ -145,7 +146,6 @@ using socket_t = int;
 #include <string>
 #include <sys/stat.h>
 #include <thread>
-#include <array>
 
 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
 #include <openssl/err.h>
@@ -507,7 +507,7 @@ public:
   Server &Delete(const char *pattern, Handler handler);
   Server &Options(const char *pattern, Handler handler);
 
-  bool set_base_dir(const char *path);
+  bool set_base_dir(const char *dir, const char *mount_point = nullptr);
   void set_file_request_handler(Handler handler);
 
   void set_error_handler(Handler handler);
@@ -570,7 +570,7 @@ private:
 
   std::atomic<bool> is_running_;
   std::atomic<socket_t> svr_sock_;
-  std::string base_dir_;
+  std::vector<std::pair<std::string, std::string>> base_dirs_;
   Handler file_request_handler_;
   Handlers get_handlers_;
   Handlers post_handlers_;
@@ -2408,10 +2408,13 @@ inline Server &Server::Options(const char *pattern, Handler handler) {
   return *this;
 }
 
-inline bool Server::set_base_dir(const char *path) {
-  if (detail::is_dir(path)) {
-    base_dir_ = path;
-    return true;
+inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
+  if (detail::is_dir(dir)) {
+    std::string mnt = mount_point ? mount_point : "/";
+    if (!mnt.empty() && mnt[0] == '/') {
+      base_dirs_.emplace_back(mnt, dir);
+      return true;
+    }
   }
   return false;
 }
@@ -2684,21 +2687,28 @@ Server::read_content_with_content_receiver(Stream &strm, bool last_connection,
 }
 
 inline bool Server::handle_file_request(Request &req, Response &res) {
-  if (!base_dir_.empty() && detail::is_valid_path(req.path)) {
-    std::string path = base_dir_ + req.path;
-
-    if (!path.empty() && path.back() == '/') { path += "index.html"; }
-
-    if (detail::is_file(path)) {
-      detail::read_file(path, res.body);
-      auto type = detail::find_content_type(path);
-      if (type) { res.set_header("Content-Type", type); }
-      res.status = 200;
-      if (file_request_handler_) { file_request_handler_(req, res); }
-      return true;
+  for (const auto& kv: base_dirs_) {
+    const auto& mount_point = kv.first;
+    const auto& base_dir = kv.second;
+
+    // Prefix match
+    if (!req.path.find(mount_point)) {
+      std::string sub_path = "/" + req.path.substr(mount_point.size());
+      if (detail::is_valid_path(sub_path)) {
+        auto path = base_dir + sub_path;
+        if (path.back() == '/') { path += "index.html"; }
+
+        if (detail::is_file(path)) {
+          detail::read_file(path, res.body);
+          auto type = detail::find_content_type(path);
+          if (type) { res.set_header("Content-Type", type); }
+          res.status = 200;
+          if (file_request_handler_) { file_request_handler_(req, res); }
+          return true;
+        }
+      }
     }
   }
-
   return false;
 }
 

+ 37 - 3
test/test.cc

@@ -567,6 +567,7 @@ protected:
 
   virtual void SetUp() {
     svr_.set_base_dir("./www");
+    svr_.set_base_dir("./www2", "/mount");
 
     svr_.Get("/hi",
              [&](const Request & /*req*/, Response &res) {
@@ -1003,9 +1004,42 @@ TEST_F(ServerTest, GetMethodOutOfBaseDir2) {
   EXPECT_EQ(404, res->status);
 }
 
-TEST_F(ServerTest, InvalidBaseDir) {
-  EXPECT_EQ(false, svr_.set_base_dir("invalid_dir"));
-  EXPECT_EQ(true, svr_.set_base_dir("."));
+TEST_F(ServerTest, GetMethodDirMountTest) {
+  auto res = cli_.Get("/mount/dir/test.html");
+  ASSERT_TRUE(res != nullptr);
+  EXPECT_EQ(200, res->status);
+  EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
+  EXPECT_EQ("test.html", res->body);
+}
+
+TEST_F(ServerTest, GetMethodDirMountTestWithDoubleDots) {
+  auto res = cli_.Get("/mount/dir/../dir/test.html");
+  ASSERT_TRUE(res != nullptr);
+  EXPECT_EQ(200, res->status);
+  EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
+  EXPECT_EQ("test.html", res->body);
+}
+
+TEST_F(ServerTest, GetMethodInvalidMountPath) {
+  auto res = cli_.Get("/mount/dir/../test.html");
+  ASSERT_TRUE(res != nullptr);
+  EXPECT_EQ(404, res->status);
+}
+
+TEST_F(ServerTest, GetMethodOutOfBaseDirMount) {
+  auto res = cli_.Get("/mount/../www2/dir/test.html");
+  ASSERT_TRUE(res != nullptr);
+  EXPECT_EQ(404, res->status);
+}
+
+TEST_F(ServerTest, GetMethodOutOfBaseDirMount2) {
+  auto res = cli_.Get("/mount/dir/../../www2/dir/test.html");
+  ASSERT_TRUE(res != nullptr);
+  EXPECT_EQ(404, res->status);
+}
+
+TEST_F(ServerTest, InvalidBaseDirMount) {
+  EXPECT_EQ(false, svr_.set_base_dir("./www3", "invalid_mount_point"));
 }
 
 TEST_F(ServerTest, EmptyRequest) {

+ 8 - 0
test/www2/dir/index.html

@@ -0,0 +1,8 @@
+<html>
+<head>
+</head>
+<body>
+  <a href="/dir/test.html">Test</a>
+  <a href="/hi">hi</a>
+</body>
+</html>

+ 1 - 0
test/www2/dir/test.html

@@ -0,0 +1 @@
+test.html

+ 8 - 0
test/www3/dir/index.html

@@ -0,0 +1,8 @@
+<html>
+<head>
+</head>
+<body>
+  <a href="/dir/test.html">Test</a>
+  <a href="/hi">hi</a>
+</body>
+</html>

+ 1 - 0
test/www3/dir/test.html

@@ -0,0 +1 @@
+test.html