From f8ac67c943244aa57e5f848ca6b1f66eca32e138 Mon Sep 17 00:00:00 2001 From: cflip Date: Sun, 26 Mar 2023 10:21:47 -0600 Subject: Add support for building on Windows This makes it possible to compile cfws with Visual Studio. Since winsock and POSIX use very similar APIs, porting is mostly just a matter of placing ifdefs around #includes and functions with slightly different names. However, CGI scripts and command line arguments are not available in this port yet, since they used the Unix-exclusive getopt.h and popen. --- .gitignore | 2 + cfws.sln | 31 +++++++++ cfws.vcxproj | 165 +++++++++++++++++++++++++++++++++++++++++++++++ cfws.vcxproj.filters | 48 ++++++++++++++ cfws.vcxproj.user | 6 ++ src/ClientConnection.cpp | 23 ++++++- src/ServerConnection.cpp | 26 ++++++-- src/main.cpp | 18 +++++- 8 files changed, 308 insertions(+), 11 deletions(-) create mode 100644 cfws.sln create mode 100644 cfws.vcxproj create mode 100644 cfws.vcxproj.filters create mode 100644 cfws.vcxproj.user diff --git a/.gitignore b/.gitignore index 3bfe699..468ae6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Ignore IDE and editor project files .vscode +.vs # Ignore build output +build/ cfws *.o diff --git a/cfws.sln b/cfws.sln new file mode 100644 index 0000000..568c33e --- /dev/null +++ b/cfws.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cfws", "cfws.vcxproj", "{FAFC8784-5A23-416E-9384-74A9F9D53F0D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FAFC8784-5A23-416E-9384-74A9F9D53F0D}.Debug|x64.ActiveCfg = Debug|x64 + {FAFC8784-5A23-416E-9384-74A9F9D53F0D}.Debug|x64.Build.0 = Debug|x64 + {FAFC8784-5A23-416E-9384-74A9F9D53F0D}.Debug|x86.ActiveCfg = Debug|Win32 + {FAFC8784-5A23-416E-9384-74A9F9D53F0D}.Debug|x86.Build.0 = Debug|Win32 + {FAFC8784-5A23-416E-9384-74A9F9D53F0D}.Release|x64.ActiveCfg = Release|x64 + {FAFC8784-5A23-416E-9384-74A9F9D53F0D}.Release|x64.Build.0 = Release|x64 + {FAFC8784-5A23-416E-9384-74A9F9D53F0D}.Release|x86.ActiveCfg = Release|Win32 + {FAFC8784-5A23-416E-9384-74A9F9D53F0D}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7D5025E0-100E-4F05-97C2-24FFFD3E487D} + EndGlobalSection +EndGlobal diff --git a/cfws.vcxproj b/cfws.vcxproj new file mode 100644 index 0000000..e10f31a --- /dev/null +++ b/cfws.vcxproj @@ -0,0 +1,165 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {fafc8784-5a23-416e-9384-74a9f9d53f0d} + cfws + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)build\$(Configuration)-$(Platform)\ + $(SolutionDir)build\$(Configuration)-$(Platform)\ + + + $(SolutionDir)build\$(Configuration)-$(Platform)\ + $(SolutionDir)build\$(Configuration)-$(Platform)\ + + + $(SolutionDir)build\$(Configuration)-$(Platform)\ + $(SolutionDir)build\$(Configuration)-$(Platform)\ + + + $(SolutionDir)build\$(Configuration)-$(Platform)\ + $(SolutionDir)build\$(Configuration)-$(Platform)\ + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cfws.vcxproj.filters b/cfws.vcxproj.filters new file mode 100644 index 0000000..bd4ed7f --- /dev/null +++ b/cfws.vcxproj.filters @@ -0,0 +1,48 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/cfws.vcxproj.user b/cfws.vcxproj.user new file mode 100644 index 0000000..dc63f8a --- /dev/null +++ b/cfws.vcxproj.user @@ -0,0 +1,6 @@ + + + + false + + \ No newline at end of file diff --git a/src/ClientConnection.cpp b/src/ClientConnection.cpp index c434212..e8e6692 100644 --- a/src/ClientConnection.cpp +++ b/src/ClientConnection.cpp @@ -3,7 +3,13 @@ #include #include #include -#include + +#ifdef _WIN32 + #include + #pragma comment(lib, "ws2_32.lib") +#else + #include +#endif ClientConnection::ClientConnection(int socket) : m_socket_fd(socket) @@ -19,7 +25,12 @@ HttpRequest ClientConnection::read_request() const int n = 0; memset(buffer, 0, BUFFER_SIZE); - while ((n = read(m_socket_fd, buffer, BUFFER_SIZE - 1)) > 0) { +#ifdef _WIN32 + n = recv(m_socket_fd, buffer, BUFFER_SIZE - 1, 0); +#else + n = read(m_socket_fd, buffer, BUFFER_SIZE - 1); +#endif + while (n > 0) { if (buffer[n - 1] == '\n') break; @@ -35,7 +46,11 @@ bool ClientConnection::send(const HttpResponse& response) const return false; std::string result = response.to_string(); +#ifdef _WIN32 + ::send(m_socket_fd, result.c_str(), result.length(), 0); +#else write(m_socket_fd, result.c_str(), result.length()); +#endif return true; } @@ -43,5 +58,9 @@ bool ClientConnection::send(const HttpResponse& response) const void ClientConnection::close_connection() { m_is_open = false; +#ifdef _WIN32 + closesocket(m_socket_fd); +#else close(m_socket_fd); +#endif } diff --git a/src/ServerConnection.cpp b/src/ServerConnection.cpp index 64692b6..0a47dc3 100644 --- a/src/ServerConnection.cpp +++ b/src/ServerConnection.cpp @@ -1,13 +1,17 @@ #include "ServerConnection.h" -#include -#include -#include -#include -#include -#include #include -#include + +#ifdef _WIN32 + #include + #pragma comment(lib, "ws2_32.lib") +#else + #include + #include + #include + #include + #include +#endif #include "ClientConnection.h" @@ -19,14 +23,22 @@ static void error_and_die(const char* message) ServerConnection::ServerConnection(int port) { +#ifdef _WIN32 + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + error_and_die("Failed to initialize Winsock"); +#endif + sockaddr_in address {}; int socket_options = 1; if ((m_socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) error_and_die("Failed to create socket"); +#ifndef _WIN32 if (setsockopt(m_socket_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &socket_options, sizeof(socket_options)) != 0) error_and_die("setsockopt"); +#endif address.sin_family = AF_INET; address.sin_addr.s_addr = htonl(INADDR_ANY); diff --git a/src/main.cpp b/src/main.cpp index c0c9bb3..4937f0d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,15 @@ +#ifndef _WIN32 #include +#endif #include #include #include #include +#ifndef _WIN32 #include "CGIScript.h" +#endif #include "ClientConnection.h" #include "HttpRequest.h" #include "HttpResponse.h" @@ -66,6 +70,7 @@ static HttpResponse serve_from_filesystem(const HttpRequest& request) return response; } +#ifndef _WIN32 static HttpResponse serve_from_cgi(const std::string& script_path, const HttpRequest& request) { HttpResponse response; @@ -99,11 +104,14 @@ static HttpResponse serve_from_cgi(const std::string& script_path, const HttpReq response.add_headers_and_content(script.read_output()); return response; } +#endif +#ifndef _WIN32 static option long_options[] = { { "cgi", required_argument, nullptr, 'c' }, { "port", required_argument, nullptr, 'p' }, }; +#endif int main(int argc, char** argv) { @@ -111,6 +119,8 @@ int main(int argc, char** argv) bool in_cgi_mode = false; std::string cgi_program_name; + // TODO: getopt.h is not available on Windows, so this part needs to be rewritten. +#ifndef _WIN32 int c = 0; int option_index = 0; while ((c = getopt_long(argc, argv, "c:p:", long_options, &option_index)) != -1) { @@ -135,6 +145,7 @@ int main(int argc, char** argv) // script before attempting to start the server. if (in_cgi_mode) CGIScript::validate_path(cgi_program_name); +#endif ServerConnection server(port); std::cout << "Serving a " << (in_cgi_mode ? "CGI script" : "directory") << " on port " << port << std::endl; @@ -144,11 +155,14 @@ int main(int argc, char** argv) HttpRequest request = client.read_request(); HttpResponse response; + // TODO: Support running CGI executables on Windows +#ifndef _WIN32 if (in_cgi_mode) { response = serve_from_cgi(cgi_program_name, request); - } else { + } else +#endif response = serve_from_filesystem(request); - } + client.send(response); client.close_connection(); -- cgit v1.2.3