summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcflip <cflip@cflip.net>2023-03-26 10:21:47 -0600
committercflip <cflip@cflip.net>2023-03-26 10:21:47 -0600
commitf8ac67c943244aa57e5f848ca6b1f66eca32e138 (patch)
tree2bf4f6695393b993ad3a50528304fdb6e6a0b86a
parent83fb0b96c94e7f596f81d5bc346150904457ed64 (diff)
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.
-rw-r--r--.gitignore2
-rw-r--r--cfws.sln31
-rw-r--r--cfws.vcxproj165
-rw-r--r--cfws.vcxproj.filters48
-rw-r--r--cfws.vcxproj.user6
-rw-r--r--src/ClientConnection.cpp23
-rw-r--r--src/ServerConnection.cpp26
-rw-r--r--src/main.cpp18
8 files changed, 308 insertions, 11 deletions
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <Keyword>Win32Proj</Keyword>
+ <ProjectGuid>{fafc8784-5a23-416e-9384-74a9f9d53f0d}</ProjectGuid>
+ <RootNamespace>cfws</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)build\$(Configuration)-$(Platform)\</OutDir>
+ <IntDir>$(SolutionDir)build\$(Configuration)-$(Platform)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)build\$(Configuration)-$(Platform)\</OutDir>
+ <IntDir>$(SolutionDir)build\$(Configuration)-$(Platform)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(SolutionDir)build\$(Configuration)-$(Platform)\</OutDir>
+ <IntDir>$(SolutionDir)build\$(Configuration)-$(Platform)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(SolutionDir)build\$(Configuration)-$(Platform)\</OutDir>
+ <IntDir>$(SolutionDir)build\$(Configuration)-$(Platform)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="src\ClientConnection.h" />
+ <ClInclude Include="src\HttpRequest.h" />
+ <ClInclude Include="src\HttpResponse.h" />
+ <ClInclude Include="src\ServerConnection.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\ClientConnection.cpp" />
+ <ClCompile Include="src\HttpRequest.cpp" />
+ <ClCompile Include="src\HttpResponse.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\ServerConnection.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\ServerConnection.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\ClientConnection.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\HttpRequest.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\HttpResponse.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\ClientConnection.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\HttpRequest.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\HttpResponse.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\ServerConnection.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <ShowAllFiles>false</ShowAllFiles>
+ </PropertyGroup>
+</Project> \ 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 <cstring>
#include <iostream>
#include <sstream>
-#include <unistd.h>
+
+#ifdef _WIN32
+ #include <winsock2.h>
+ #pragma comment(lib, "ws2_32.lib")
+#else
+ #include <unistd.h>
+#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 <arpa/inet.h>
-#include <csignal>
-#include <netdb.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/time.h>
#include <sys/types.h>
-#include <unistd.h>
+
+#ifdef _WIN32
+ #include <winsock2.h>
+ #pragma comment(lib, "ws2_32.lib")
+#else
+ #include <arpa/inet.h>
+ #include <sys/ioctl.h>
+ #include <sys/socket.h>
+ #include <sys/time.h>
+ #include <unistd.h>
+#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 <getopt.h>
+#endif
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
+#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();