From eef74af19be78ef65479f1cf8ce39bc9d6582994 Mon Sep 17 00:00:00 2001 From: yhirose Date: Thu, 4 Jul 2013 18:18:52 -0400 Subject: [PATCH] static file support. vc12 support. --- httplib.h | 117 ++++++++++++++++++++++++++---- test/test.cc | 20 +++++ test/test.sln | 28 +++++++ test/test.vc10.sln | 20 ----- test/test.vc10.vcxproj | 88 ---------------------- test/test.vc11.sln | 20 ----- test/test.vc11.vcxproj | 78 -------------------- test/test.vcxproj | 157 ++++++++++++++++++++++++++++++++++++++++ test/www/dir/index.html | 1 + test/www/dir/test.html | 1 + 10 files changed, 308 insertions(+), 222 deletions(-) create mode 100644 test/test.sln delete mode 100644 test/test.vc10.sln delete mode 100644 test/test.vc10.vcxproj delete mode 100644 test/test.vc11.sln delete mode 100644 test/test.vc11.vcxproj create mode 100644 test/test.vcxproj create mode 100644 test/www/dir/index.html create mode 100644 test/www/dir/test.html diff --git a/httplib.h b/httplib.h index 40bc878..53624d2 100644 --- a/httplib.h +++ b/httplib.h @@ -8,7 +8,7 @@ #ifndef _CPPHTTPLIB_HTTPSLIB_H_ #define _CPPHTTPLIB_HTTPSLIB_H_ -#ifdef _WIN32 +#ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE @@ -21,6 +21,11 @@ #ifndef snprintf #define snprintf _snprintf_s #endif +#ifndef getcwd +#define getcwd _getcwd +#endif + +#define S_ISREG(m) (((m)&S_IFREG)==S_IFREG) #include #include @@ -35,10 +40,12 @@ typedef SOCKET socket_t; #include #include #include +#include typedef int socket_t; #endif +#include #include #include #include @@ -93,6 +100,8 @@ public: void get(const char* pattern, Handler handler); void post(const char* pattern, Handler handler); + void set_base_dir(const char* path); + void set_error_handler(Handler handler); void set_logger(Logger logger); @@ -105,13 +114,15 @@ private: void process_request(socket_t sock); bool read_request_line(FILE* fp, Request& req); bool routing(Request& req, Response& res); - bool dispatch_request(Request& req, Response& res, Handlers& handlers); + bool handle_file_request(Request& req, Response& res); + bool dispatch_request(Request& req, Response& res, Handlers& handlers); - socket_t svr_sock_; - Handlers get_handlers_; - Handlers post_handlers_; - Handler error_handler_; - Logger logger_; + socket_t svr_sock_; + std::string base_dir_; + Handlers get_handlers_; + Handlers post_handlers_; + Handler error_handler_; + Logger logger_; }; class Client { @@ -156,7 +167,7 @@ void split(const char* b, const char* e, char d, Fn fn) inline void get_flie_pointers(int fd, FILE*& fp_read, FILE*& fp_write) { -#ifdef _WIN32 +#ifdef _MSC_VER int osfhandle = _open_osfhandle(fd, _O_RDONLY); fp_read = _fdopen(osfhandle, "rb"); fp_write = _fdopen(osfhandle, "wb"); @@ -169,7 +180,7 @@ inline void get_flie_pointers(int fd, FILE*& fp_read, FILE*& fp_write) template socket_t create_socket(const char* host, int port, Fn fn) { -#ifdef _WIN32 +#ifdef _MSC_VER int opt = SO_SYNCHRONOUS_NONALERT; setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&opt, sizeof(opt)); #endif @@ -215,7 +226,7 @@ inline socket_t create_server_socket(const char* host, int port) inline int shutdown_socket(socket_t sock) { -#ifdef _WIN32 +#ifdef _MSC_VER return shutdown(sock, SD_BOTH); #else return shutdown(sock, SHUT_RDWR); @@ -224,7 +235,7 @@ inline int shutdown_socket(socket_t sock) inline int close_socket(socket_t sock) { -#ifdef _WIN32 +#ifdef _MSC_VER return closesocket(sock); #else return close(sock); @@ -241,6 +252,45 @@ inline socket_t create_client_socket(const char* host, int port) }); } +inline bool is_file(const std::string& s) +{ + struct stat st; + if (stat(s.c_str(), &st) < 0) { + return false; + } + return S_ISREG(st.st_mode); +} + +inline void read_file(const std::string& path, std::string& out) +{ + auto fs = std::ifstream(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.assign(size, 0); + fs.read(&out[0], size); +} + +inline std::string get_file_extention(const std::string& path) +{ + std::smatch m; + auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); + auto ret = std::regex_search(path, m, pat); + std::string content_type; + if (ret) { + return m[1].str(); + } + return std::string(); +} + +inline const char* get_content_type_from_file_extention(const std::string& ext) +{ + if (ext == "html") { + return "text/html"; + } + return "text/plain"; +} + inline const char* status_message(int status) { switch (status) { @@ -502,7 +552,7 @@ inline void parse_query_text(const std::string& s, Map& params) }); } -#ifdef _WIN32 +#ifdef _MSC_VER class WSInit { public: WSInit::WSInit() { @@ -573,6 +623,11 @@ inline void Response::set_content(const std::string& s, const char* content_type inline Server::Server() : svr_sock_(-1) { + char curr_dir[FILENAME_MAX]; + if (getcwd(curr_dir, sizeof(curr_dir))) { + curr_dir[sizeof(curr_dir) - 1] = '\0'; + base_dir_ = curr_dir; + } } inline void Server::get(const char* pattern, Handler handler) @@ -585,6 +640,11 @@ inline void Server::post(const char* pattern, Handler handler) post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); } +inline void Server::set_base_dir(const char* path) +{ + base_dir_ = path; +} + inline void Server::set_error_handler(Handler handler) { error_handler_ = handler; @@ -660,8 +720,31 @@ inline bool Server::read_request_line(FILE* fp, Request& req) return false; } +inline bool Server::handle_file_request(Request& req, Response& res) +{ + std::string path = base_dir_ + req.url; + + if (!path.empty() && path.back() == '/') { + path += "index.html"; + } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::get_content_type_from_file_extention(detail::get_file_extention(path)); + res.set_header("Content-Type", type); + res.status = 200; + return true; + } + + return false; +} + inline bool Server::routing(Request& req, Response& res) { + if (req.method == "GET" && handle_file_request(req, res)) { + return true; + } + if (req.method == "GET" || req.method == "HEAD") { return dispatch_request(req, res, get_handlers_); } else if (req.method == "POST") { @@ -723,8 +806,9 @@ inline void Server::process_request(socket_t sock) detail::write_response(fp_write, req, res); fflush(fp_write); - fclose(fp_read); - fclose(fp_write); + // NOTE: The following code causes problem on Windows... + //fclose(fp_read); + //fclose(fp_write); if (logger_) { logger_(req, res); @@ -782,8 +866,9 @@ inline bool Client::send(const Request& req, Response& res) } } - fclose(fp_read); - fclose(fp_write); + // NOTE: The following code causes problem on Windows... + //fclose(fp_read); + //fclose(fp_write); detail::shutdown_socket(sock); detail::close_socket(sock); diff --git a/test/test.cc b/test/test.cc index 0ff8912..5609602 100644 --- a/test/test.cc +++ b/test/test.cc @@ -103,6 +103,8 @@ protected: } virtual void SetUp() { + svr_.set_base_dir("./www"); + svr_.get("/hi", [&](const Request& req, Response& res) { res.set_content("Hello World!", "text/plain"); }); @@ -247,6 +249,24 @@ TEST_F(ServerTest, PostMethod2) ASSERT_EQ("coder", res->body); } +TEST_F(ServerTest, GetMethodDir) +{ + auto res = cli_.get("/dir/"); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(200, res->status); + EXPECT_EQ("text/html", res->get_header_value("Content-Type")); + EXPECT_EQ("index.html", res->body); +} + +TEST_F(ServerTest, GetMethodDirTest) +{ + auto res = cli_.get("/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); +} + #ifdef _WIN32 TEST(CleanupTest, WSACleanup) { diff --git a/test/test.sln b/test/test.sln new file mode 100644 index 0000000..8377dd7 --- /dev/null +++ b/test/test.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.20617.1 PREVIEW +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|Win32.ActiveCfg = Debug|Win32 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|Win32.Build.0 = Debug|Win32 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|x64.ActiveCfg = Debug|x64 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|x64.Build.0 = Debug|x64 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|Win32.ActiveCfg = Release|Win32 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|Win32.Build.0 = Release|Win32 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|x64.ActiveCfg = Release|x64 + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/test.vc10.sln b/test/test.vc10.sln deleted file mode 100644 index fabd3c6..0000000 --- a/test/test.vc10.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test.vc10", "test.vc10.vcxproj", "{7690FEA1-FECA-4EAB-AB33-CC63A4A52CC0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7690FEA1-FECA-4EAB-AB33-CC63A4A52CC0}.Debug|Win32.ActiveCfg = Debug|Win32 - {7690FEA1-FECA-4EAB-AB33-CC63A4A52CC0}.Debug|Win32.Build.0 = Debug|Win32 - {7690FEA1-FECA-4EAB-AB33-CC63A4A52CC0}.Release|Win32.ActiveCfg = Release|Win32 - {7690FEA1-FECA-4EAB-AB33-CC63A4A52CC0}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/test/test.vc10.vcxproj b/test/test.vc10.vcxproj deleted file mode 100644 index 28ba0d6..0000000 --- a/test/test.vc10.vcxproj +++ /dev/null @@ -1,88 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {7690FEA1-FECA-4EAB-AB33-CC63A4A52CC0} - Win32Proj - test - - - - Application - true - Unicode - - - Application - false - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - .;.. - - - Console - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - .;.. - - - Console - true - true - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - \ No newline at end of file diff --git a/test/test.vc11.sln b/test/test.vc11.sln deleted file mode 100644 index 60960e8..0000000 --- a/test/test.vc11.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test.vc11", "test.vc11.vcxproj", "{E5E54389-3E45-4E3D-A004-A1E18A05A9C8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E5E54389-3E45-4E3D-A004-A1E18A05A9C8}.Debug|Win32.ActiveCfg = Debug|Win32 - {E5E54389-3E45-4E3D-A004-A1E18A05A9C8}.Debug|Win32.Build.0 = Debug|Win32 - {E5E54389-3E45-4E3D-A004-A1E18A05A9C8}.Release|Win32.ActiveCfg = Release|Win32 - {E5E54389-3E45-4E3D-A004-A1E18A05A9C8}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/test/test.vc11.vcxproj b/test/test.vc11.vcxproj deleted file mode 100644 index 5ca97e7..0000000 --- a/test/test.vc11.vcxproj +++ /dev/null @@ -1,78 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {E5E54389-3E45-4E3D-A004-A1E18A05A9C8} - test - - - - Application - true - v110 - MultiByte - - - Application - false - v110 - true - MultiByte - - - - - - - - - - - - - - - Level3 - Disabled - .;.. - _MBCS;%(PreprocessorDefinitions);_VARIADIC_MAX=10 - - - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - .;.. - _MBCS;%(PreprocessorDefinitions);_VARIADIC_MAX=10 - - - true - true - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - \ No newline at end of file diff --git a/test/test.vcxproj b/test/test.vcxproj new file mode 100644 index 0000000..5ac1b53 --- /dev/null +++ b/test/test.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26} + Win32Proj + test + + + + Application + true + v120 + Unicode + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + ./;../ + + + Console + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + ./;../ + + + Console + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + ./;../ + + + Console + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + ./;../ + + + Console + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + \ No newline at end of file diff --git a/test/www/dir/index.html b/test/www/dir/index.html new file mode 100644 index 0000000..64233a9 --- /dev/null +++ b/test/www/dir/index.html @@ -0,0 +1 @@ +index.html \ No newline at end of file diff --git a/test/www/dir/test.html b/test/www/dir/test.html new file mode 100644 index 0000000..6d70cd0 --- /dev/null +++ b/test/www/dir/test.html @@ -0,0 +1 @@ +test.html \ No newline at end of file