Initial win32 webserver
This commit is contained in:
parent
39422838b7
commit
af50eea208
13 changed files with 661 additions and 5 deletions
|
@ -23,6 +23,8 @@ list(APPEND mesh_LIB_INCLUDES
|
||||||
MeshPrimitives.h
|
MeshPrimitives.h
|
||||||
MeshBuilder.cpp
|
MeshBuilder.cpp
|
||||||
MeshBuilder.h
|
MeshBuilder.h
|
||||||
|
MeshObjWriter.h
|
||||||
|
MeshObjWriter.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
32
src/mesh/MeshObjWriter.cpp
Normal file
32
src/mesh/MeshObjWriter.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include "MeshObjWriter.h"
|
||||||
|
|
||||||
|
#include "File.h"
|
||||||
|
#include "TriMesh.h"
|
||||||
|
#include "AbstractFace.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
std::string MeshObjWriter::serialize(TriMesh* mesh)
|
||||||
|
{
|
||||||
|
std::stringstream output;
|
||||||
|
for (const auto& node : mesh->getNodes())
|
||||||
|
{
|
||||||
|
const auto x = node->getPoint().getX();
|
||||||
|
const auto y = node->getPoint().getY();
|
||||||
|
const auto z = node->getPoint().getZ();
|
||||||
|
output << "v "<< x << " " << y << " " << z << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& face : mesh->getFaces())
|
||||||
|
{
|
||||||
|
auto ids = face->getNodeIds();
|
||||||
|
output << "f " << 1 + ids[0] << " " << 1 + ids[1] << " " << 1 + ids[2] << "\n";
|
||||||
|
}
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshObjWriter::write(const Path& path, TriMesh* mesh)
|
||||||
|
{
|
||||||
|
File file(path);
|
||||||
|
file.writeText(serialize(mesh));
|
||||||
|
}
|
14
src/mesh/MeshObjWriter.h
Normal file
14
src/mesh/MeshObjWriter.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
using Path = std::filesystem::path;
|
||||||
|
class TriMesh;
|
||||||
|
|
||||||
|
class MeshObjWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string serialize(TriMesh* mesh);
|
||||||
|
static void write(const Path& path, TriMesh* mesh);
|
||||||
|
};
|
|
@ -1,7 +1,14 @@
|
||||||
set(platform_INCLUDES)
|
set(platform_INCLUDES)
|
||||||
|
set(platform_LIBS)
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
list(APPEND platform_INCLUDES
|
list(APPEND platform_INCLUDES
|
||||||
sockets/UnixSocketInterface.cpp)
|
sockets/UnixSocketInterface.cpp)
|
||||||
|
else()
|
||||||
|
list(APPEND platform_INCLUDES
|
||||||
|
server/win32/Win32WebServer.h
|
||||||
|
server/win32/Win32WebServer.cpp)
|
||||||
|
list(APPEND platform_LIBS Httpapi.lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(APPEND network_HEADERS
|
list(APPEND network_HEADERS
|
||||||
|
@ -9,6 +16,7 @@ list(APPEND network_HEADERS
|
||||||
sockets/Socket.h
|
sockets/Socket.h
|
||||||
sockets/SocketInterface.h
|
sockets/SocketInterface.h
|
||||||
sockets/ISocketMessageHandler.h
|
sockets/ISocketMessageHandler.h
|
||||||
|
web/HttpMessageHandler.h
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND network_LIB_INCLUDES
|
list(APPEND network_LIB_INCLUDES
|
||||||
|
@ -20,11 +28,12 @@ list(APPEND network_LIB_INCLUDES
|
||||||
add_library(network SHARED ${network_LIB_INCLUDES} ${platform_INCLUDES} ${network_HEADERS})
|
add_library(network SHARED ${network_LIB_INCLUDES} ${platform_INCLUDES} ${network_HEADERS})
|
||||||
|
|
||||||
target_include_directories(network PUBLIC
|
target_include_directories(network PUBLIC
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/sockets"
|
${CMAKE_CURRENT_SOURCE_DIR}/sockets
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/web"
|
${CMAKE_CURRENT_SOURCE_DIR}/web
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/server/win32
|
||||||
)
|
)
|
||||||
set_target_properties( network PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
set_target_properties( network PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
||||||
target_link_libraries( network PUBLIC core)
|
target_link_libraries( network PUBLIC core ${platform_LIBS})
|
||||||
|
|
||||||
set_property(TARGET network PROPERTY FOLDER src)
|
set_property(TARGET network PROPERTY FOLDER src)
|
|
@ -1,6 +1,8 @@
|
||||||
#include "NetworkManager.h"
|
#include "NetworkManager.h"
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include "UnixSocketInterface.h"
|
#include "UnixSocketInterface.h"
|
||||||
|
#else
|
||||||
|
#include "Win32WebServer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -31,6 +33,10 @@ void NetworkManager::Initialize()
|
||||||
|
|
||||||
void NetworkManager::RunHttpServer()
|
void NetworkManager::RunHttpServer()
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
Win32WebServer server;
|
||||||
|
server.initialize();
|
||||||
|
#else
|
||||||
if (!mSocketInterface)
|
if (!mSocketInterface)
|
||||||
{
|
{
|
||||||
Initialize();
|
Initialize();
|
||||||
|
@ -40,6 +46,7 @@ void NetworkManager::RunHttpServer()
|
||||||
mSocketInterface->InitializeSocket(socket);
|
mSocketInterface->InitializeSocket(socket);
|
||||||
mSocketInterface->Listen(socket);
|
mSocketInterface->Listen(socket);
|
||||||
mSocketInterface->Run(socket);
|
mSocketInterface->Run(socket);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::RunHttpClient()
|
void NetworkManager::RunHttpClient()
|
||||||
|
|
|
@ -27,7 +27,6 @@ public:
|
||||||
private:
|
private:
|
||||||
std::vector<SocketPtr> mActiveSockets;
|
std::vector<SocketPtr> mActiveSockets;
|
||||||
ISocketInterfaceUPtr mSocketInterface;
|
ISocketInterfaceUPtr mSocketInterface;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using NetworkManagerUPtr = std::unique_ptr<NetworkManager>;
|
using NetworkManagerUPtr = std::unique_ptr<NetworkManager>;
|
||||||
|
|
504
src/network/server/win32/Win32WebServer.cpp
Normal file
504
src/network/server/win32/Win32WebServer.cpp
Normal file
|
@ -0,0 +1,504 @@
|
||||||
|
#include "Win32WebServer.h"
|
||||||
|
|
||||||
|
#include "FileLogger.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
|
Win32WebServer::Win32WebServer()
|
||||||
|
: mListenUrl("http://localhost:80/49152")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Win32WebServer::~Win32WebServer()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32WebServer::clear()
|
||||||
|
{
|
||||||
|
// Remove url
|
||||||
|
::HttpRemoveUrl(mWorkingQueue, StringUtils::convert(mListenUrl).c_str());
|
||||||
|
|
||||||
|
// Close the Request Queue handle.
|
||||||
|
if (mWorkingQueue)
|
||||||
|
{
|
||||||
|
::CloseHandle(mWorkingQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call HttpTerminate.
|
||||||
|
::HttpTerminate(HTTP_INITIALIZE_SERVER, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32WebServer::initialize()
|
||||||
|
{
|
||||||
|
HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;
|
||||||
|
|
||||||
|
auto retCode = ::HttpInitialize(HttpApiVersion, HTTP_INITIALIZE_SERVER, nullptr);
|
||||||
|
if (FAILED(retCode))
|
||||||
|
{
|
||||||
|
MLOG_ERROR("Failed to initialize win32 http server.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
retCode = ::HttpCreateHttpHandle(&mWorkingQueue, 0);
|
||||||
|
if (FAILED(retCode))
|
||||||
|
{
|
||||||
|
MLOG_ERROR("Failed to create request queue handle.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
retCode = ::HttpAddUrl(mWorkingQueue, StringUtils::convert(mListenUrl).c_str(), nullptr);
|
||||||
|
if (FAILED(retCode))
|
||||||
|
{
|
||||||
|
MLOG_ERROR("Failed to register queue to url: " << mListenUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32WebServer::onRequest(PHTTP_REQUEST request)
|
||||||
|
{
|
||||||
|
const std::wstring wide_url = request->CookedUrl.pFullUrl;
|
||||||
|
const auto url = StringUtils::convert(wide_url);
|
||||||
|
|
||||||
|
switch (request->Verb)
|
||||||
|
{
|
||||||
|
case HttpVerbGET:
|
||||||
|
MLOG_INFO("Got a GET request for: " << url);
|
||||||
|
auto result = sendHttpResponse(request, 200, "OK", "Hey! You hit the server \r\n");
|
||||||
|
break;
|
||||||
|
case HttpVerbPOST:
|
||||||
|
MLOG_INFO("Got a POST request for: " << url);
|
||||||
|
result = sendHttpPostResponse(request);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
MLOG_INFO("Got an unknown request for: " << url);
|
||||||
|
result = sendHttpResponse(request, 503, "Not Implemented");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32WebServer::initializeHttpResponse(HTTP_RESPONSE& response, USHORT StatusCode, const std::string& reason)
|
||||||
|
{
|
||||||
|
RtlZeroMemory((&response), sizeof(*(&response)));
|
||||||
|
response.StatusCode = StatusCode;
|
||||||
|
response.pReason = reason.c_str();
|
||||||
|
response.ReasonLength = (USHORT)strlen(reason.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
|
||||||
|
|
||||||
|
DWORD Win32WebServer::sendHttpPostResponse(PHTTP_REQUEST pRequest)
|
||||||
|
{
|
||||||
|
DWORD result;
|
||||||
|
auto hTempFile = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
// Allocate space for an entity buffer. Buffer can be increased on demand.
|
||||||
|
ULONG EntityBufferLength = 2048;
|
||||||
|
PUCHAR pEntityBuffer = reinterpret_cast<PUCHAR>(::HeapAlloc(::GetProcessHeap(), 0, (EntityBufferLength)));
|
||||||
|
if (pEntityBuffer == nullptr)
|
||||||
|
{
|
||||||
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
MLOG_ERROR("Insufficient resources");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP_RESPONSE response;
|
||||||
|
initializeHttpResponse(response, 200, "OK");
|
||||||
|
|
||||||
|
if (pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
|
||||||
|
{
|
||||||
|
// The entity body is sent over multiple calls. Collect these in a file and send back. Create a temporary file.
|
||||||
|
TCHAR szTempName[MAX_PATH + 1];
|
||||||
|
if (::GetTempFileName(L".", L"New", 0, szTempName) == 0)
|
||||||
|
{
|
||||||
|
result = ::GetLastError();
|
||||||
|
MLOG_ERROR("Failed to set up temp file for buffer with: " << result);
|
||||||
|
if (pEntityBuffer)
|
||||||
|
{
|
||||||
|
HeapFree(GetProcessHeap(), 0, (pEntityBuffer));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
hTempFile = CreateFile(szTempName, GENERIC_READ | GENERIC_WRITE,
|
||||||
|
0, // Do not share.
|
||||||
|
NULL, // No security descriptor.
|
||||||
|
CREATE_ALWAYS, // Overrwrite existing.
|
||||||
|
FILE_ATTRIBUTE_NORMAL, // Normal file.
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
if (hTempFile == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
result = GetLastError();
|
||||||
|
MLOG_ERROR("Failed to create temp file for buffer with: " << result);
|
||||||
|
if (pEntityBuffer)
|
||||||
|
{
|
||||||
|
HeapFree(GetProcessHeap(), 0, (pEntityBuffer));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD bytesSent;
|
||||||
|
ULONG TempFileBytesWritten;
|
||||||
|
CHAR szContentLength[MAX_ULONG_STR];
|
||||||
|
HTTP_DATA_CHUNK dataChunk;
|
||||||
|
|
||||||
|
ULONG TotalBytesRead = 0;
|
||||||
|
ULONG BytesRead = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Read the entity chunk from the request.
|
||||||
|
BytesRead = 0;
|
||||||
|
result = ::HttpReceiveRequestEntityBody(mWorkingQueue, pRequest->RequestId, 0,
|
||||||
|
pEntityBuffer, EntityBufferLength, &BytesRead, NULL);
|
||||||
|
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case NO_ERROR:
|
||||||
|
if (BytesRead != 0)
|
||||||
|
{
|
||||||
|
TotalBytesRead += BytesRead;
|
||||||
|
::WriteFile(hTempFile, pEntityBuffer, BytesRead, &TempFileBytesWritten, NULL);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ERROR_HANDLE_EOF:
|
||||||
|
|
||||||
|
//
|
||||||
|
// The last request entity body has been read.
|
||||||
|
// Send back a response.
|
||||||
|
//
|
||||||
|
// To illustrate entity sends via
|
||||||
|
// HttpSendResponseEntityBody, the response will
|
||||||
|
// be sent over multiple calls. To do this,
|
||||||
|
// pass the HTTP_SEND_RESPONSE_FLAG_MORE_DATA
|
||||||
|
// flag.
|
||||||
|
|
||||||
|
if (BytesRead != 0)
|
||||||
|
{
|
||||||
|
TotalBytesRead += BytesRead;
|
||||||
|
WriteFile(
|
||||||
|
hTempFile,
|
||||||
|
pEntityBuffer,
|
||||||
|
BytesRead,
|
||||||
|
&TempFileBytesWritten,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Because the response is sent over multiple
|
||||||
|
// API calls, add a content-length.
|
||||||
|
//
|
||||||
|
// Alternatively, the response could have been
|
||||||
|
// sent using chunked transfer encoding, by
|
||||||
|
// passimg "Transfer-Encoding: Chunked".
|
||||||
|
//
|
||||||
|
|
||||||
|
// NOTE: Because the TotalBytesread in a ULONG
|
||||||
|
// are accumulated, this will not work
|
||||||
|
// for entity bodies larger than 4 GB.
|
||||||
|
// For support of large entity bodies,
|
||||||
|
// use a ULONGLONG.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
sprintf_s(szContentLength, MAX_ULONG_STR, "%lu", TotalBytesRead);
|
||||||
|
|
||||||
|
ADD_KNOWN_HEADER(
|
||||||
|
response,
|
||||||
|
HttpHeaderContentLength,
|
||||||
|
szContentLength
|
||||||
|
);
|
||||||
|
|
||||||
|
result =
|
||||||
|
HttpSendHttpResponse(
|
||||||
|
hReqQueue, // ReqQueueHandle
|
||||||
|
pRequest->RequestId, // Request ID
|
||||||
|
HTTP_SEND_RESPONSE_FLAG_MORE_DATA,
|
||||||
|
&response, // HTTP response
|
||||||
|
NULL, // pReserved1
|
||||||
|
&bytesSent, // bytes sent-optional
|
||||||
|
NULL, // pReserved2
|
||||||
|
0, // Reserved3
|
||||||
|
NULL, // LPOVERLAPPED
|
||||||
|
NULL // pReserved4
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
wprintf(
|
||||||
|
L"HttpSendHttpResponse failed with %lu \n",
|
||||||
|
result
|
||||||
|
);
|
||||||
|
goto Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send entity body from a file handle.
|
||||||
|
//
|
||||||
|
dataChunk.DataChunkType =
|
||||||
|
HttpDataChunkFromFileHandle;
|
||||||
|
|
||||||
|
dataChunk.FromFileHandle.
|
||||||
|
ByteRange.StartingOffset.QuadPart = 0;
|
||||||
|
|
||||||
|
dataChunk.FromFileHandle.
|
||||||
|
ByteRange.Length.QuadPart =
|
||||||
|
HTTP_BYTE_RANGE_TO_EOF;
|
||||||
|
|
||||||
|
dataChunk.FromFileHandle.FileHandle = hTempFile;
|
||||||
|
|
||||||
|
result = HttpSendResponseEntityBody(
|
||||||
|
hReqQueue,
|
||||||
|
pRequest->RequestId,
|
||||||
|
0, // This is the last send.
|
||||||
|
1, // Entity Chunk Count.
|
||||||
|
&dataChunk,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
wprintf(
|
||||||
|
L"HttpSendResponseEntityBody failed %lu\n",
|
||||||
|
result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto Done;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
wprintf(
|
||||||
|
L"HttpReceiveRequestEntityBody failed with %lu \n",
|
||||||
|
result);
|
||||||
|
goto Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (TRUE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This request does not have an entity body.
|
||||||
|
//
|
||||||
|
|
||||||
|
result = HttpSendHttpResponse(
|
||||||
|
hReqQueue, // ReqQueueHandle
|
||||||
|
pRequest->RequestId, // Request ID
|
||||||
|
0,
|
||||||
|
&response, // HTTP response
|
||||||
|
NULL, // pReserved1
|
||||||
|
&bytesSent, // bytes sent (optional)
|
||||||
|
NULL, // pReserved2
|
||||||
|
0, // Reserved3
|
||||||
|
NULL, // LPOVERLAPPED
|
||||||
|
NULL // pReserved4
|
||||||
|
);
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
wprintf(L"HttpSendHttpResponse failed with %lu \n",
|
||||||
|
result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Done:
|
||||||
|
|
||||||
|
if (pEntityBuffer)
|
||||||
|
{
|
||||||
|
FREE_MEM(pEntityBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (INVALID_HANDLE_VALUE != hTempFile)
|
||||||
|
{
|
||||||
|
CloseHandle(hTempFile);
|
||||||
|
DeleteFile(szTempName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD Win32WebServer::sendHttpResponse(PHTTP_REQUEST pRequest, USHORT StatusCode, const std::string& reason, const std::string& message)
|
||||||
|
{
|
||||||
|
HTTP_RESPONSE response;
|
||||||
|
RtlZeroMemory((&response), sizeof(*(&response)));
|
||||||
|
response.StatusCode = StatusCode;
|
||||||
|
response.pReason = reason.c_str();
|
||||||
|
response.ReasonLength = (USHORT)strlen(reason.c_str());
|
||||||
|
|
||||||
|
const std::string content_type = "text / html";
|
||||||
|
response.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = content_type.c_str();
|
||||||
|
response.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)strlen(content_type.c_str());
|
||||||
|
|
||||||
|
if (!message.empty())
|
||||||
|
{
|
||||||
|
auto entity_string = message;
|
||||||
|
HTTP_DATA_CHUNK dataChunk;
|
||||||
|
dataChunk.DataChunkType = HttpDataChunkFromMemory;
|
||||||
|
dataChunk.FromMemory.pBuffer = entity_string.data();
|
||||||
|
dataChunk.FromMemory.BufferLength = (ULONG)strlen(entity_string.data());
|
||||||
|
|
||||||
|
response.EntityChunkCount = 1;
|
||||||
|
response.pEntityChunks = &dataChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD bytesSent;
|
||||||
|
DWORD result = ::HttpSendHttpResponse(
|
||||||
|
mWorkingQueue, // ReqQueueHandle
|
||||||
|
pRequest->RequestId, // Request ID
|
||||||
|
0, // Flags
|
||||||
|
&response, // HTTP response
|
||||||
|
NULL, // pReserved1
|
||||||
|
&bytesSent, // bytes sent (OPTIONAL)
|
||||||
|
NULL, // pReserved2 (must be NULL)
|
||||||
|
0, // Reserved3 (must be 0)
|
||||||
|
NULL, // LPOVERLAPPED(OPTIONAL)
|
||||||
|
NULL // pReserved4 (must be NULL)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
MLOG_ERROR("Http response failed with error: " << result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32WebServer::run()
|
||||||
|
{
|
||||||
|
// Allocate a 2 KB buffer. This size should work for most
|
||||||
|
// requests. The buffer size can be increased if required. Space
|
||||||
|
// is also required for an HTTP_REQUEST structure.
|
||||||
|
ULONG RequestBufferLength = sizeof(HTTP_REQUEST) + 2048;
|
||||||
|
PCHAR pRequestBuffer = reinterpret_cast<PCHAR>(::HeapAlloc(::GetProcessHeap(), 0, (RequestBufferLength)));
|
||||||
|
if (pRequestBuffer == nullptr)
|
||||||
|
{
|
||||||
|
MLOG_ERROR("Failed to allocate http request buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PHTTP_REQUEST pRequest = (PHTTP_REQUEST)pRequestBuffer;
|
||||||
|
|
||||||
|
// Wait for a new request. This is indicated by a NULL
|
||||||
|
// request ID.
|
||||||
|
HTTP_REQUEST_ID requestId;
|
||||||
|
HTTP_SET_NULL_ID(&requestId);
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
RtlZeroMemory(pRequest, RequestBufferLength);
|
||||||
|
|
||||||
|
DWORD bytesRead;
|
||||||
|
ULONG result = ::HttpReceiveHttpRequest(
|
||||||
|
mWorkingQueue, // Req Queue
|
||||||
|
requestId, // Req ID
|
||||||
|
0, // Flags
|
||||||
|
pRequest, // HTTP request buffer
|
||||||
|
RequestBufferLength,// req buffer length
|
||||||
|
&bytesRead, // bytes received
|
||||||
|
nullptr // LPOVERLAPPED
|
||||||
|
);
|
||||||
|
|
||||||
|
if (NO_ERROR == result)
|
||||||
|
{
|
||||||
|
switch (pRequest->Verb)
|
||||||
|
{
|
||||||
|
case HttpVerbGET:
|
||||||
|
wprintf(L"Got a GET request for %ws \n",
|
||||||
|
pRequest->CookedUrl.pFullUrl);
|
||||||
|
|
||||||
|
result = SendHttpResponse(
|
||||||
|
hReqQueue,
|
||||||
|
pRequest,
|
||||||
|
200,
|
||||||
|
"OK",
|
||||||
|
"Hey! You hit the server \r\n"
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HttpVerbPOST:
|
||||||
|
|
||||||
|
wprintf(L"Got a POST request for %ws \n",
|
||||||
|
pRequest->CookedUrl.pFullUrl);
|
||||||
|
|
||||||
|
result = SendHttpPostResponse(hReqQueue, pRequest);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
wprintf(L"Got a unknown request for %ws \n",
|
||||||
|
pRequest->CookedUrl.pFullUrl);
|
||||||
|
|
||||||
|
result = SendHttpResponse(
|
||||||
|
hReqQueue,
|
||||||
|
pRequest,
|
||||||
|
503,
|
||||||
|
"Not Implemented",
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Reset the Request ID to handle the next request.
|
||||||
|
//
|
||||||
|
HTTP_SET_NULL_ID(&requestId);
|
||||||
|
}
|
||||||
|
else if (result == ERROR_MORE_DATA)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// The input buffer was too small to hold the request
|
||||||
|
// headers. Increase the buffer size and call the
|
||||||
|
// API again.
|
||||||
|
//
|
||||||
|
// When calling the API again, handle the request
|
||||||
|
// that failed by passing a RequestID.
|
||||||
|
//
|
||||||
|
// This RequestID is read from the old buffer.
|
||||||
|
//
|
||||||
|
requestId = pRequest->RequestId;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Free the old buffer and allocate a new buffer.
|
||||||
|
//
|
||||||
|
RequestBufferLength = bytesRead;
|
||||||
|
FREE_MEM(pRequestBuffer);
|
||||||
|
pRequestBuffer = (PCHAR)ALLOC_MEM(RequestBufferLength);
|
||||||
|
|
||||||
|
if (pRequestBuffer == NULL)
|
||||||
|
{
|
||||||
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pRequest = (PHTTP_REQUEST)pRequestBuffer;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ERROR_CONNECTION_INVALID == result &&
|
||||||
|
!HTTP_IS_NULL_ID(&requestId))
|
||||||
|
{
|
||||||
|
// The TCP connection was corrupted by the peer when
|
||||||
|
// attempting to handle a request with more buffer.
|
||||||
|
// Continue to the next request.
|
||||||
|
|
||||||
|
HTTP_SET_NULL_ID(&requestId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pRequestBuffer)
|
||||||
|
{
|
||||||
|
FREE_MEM(pRequestBuffer);
|
||||||
|
}
|
||||||
|
}
|
42
src/network/server/win32/Win32WebServer.h
Normal file
42
src/network/server/win32/Win32WebServer.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef UNICODE
|
||||||
|
#define UNICODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32_WINNT
|
||||||
|
#define _WIN32_WINNT 0x0600
|
||||||
|
#endif
|
||||||
|
#include <http.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Win32WebServer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Win32WebServer();
|
||||||
|
|
||||||
|
~Win32WebServer();
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onRequest(PHTTP_REQUEST request);
|
||||||
|
|
||||||
|
DWORD sendHttpResponse(PHTTP_REQUEST pRequest, USHORT StatusCode, const std::string& reason, const std::string& message = {});
|
||||||
|
DWORD sendHttpPostResponse(PHTTP_REQUEST pRequest);
|
||||||
|
|
||||||
|
void initializeHttpResponse(HTTP_RESPONSE& response, USHORT StatusCode, const std::string& reason);
|
||||||
|
|
||||||
|
HANDLE mWorkingQueue{ 0 };
|
||||||
|
std::string mListenUrl;
|
||||||
|
};
|
|
@ -13,6 +13,7 @@ set(TEST_MODULES
|
||||||
image
|
image
|
||||||
ipc
|
ipc
|
||||||
network
|
network
|
||||||
|
mesh
|
||||||
publishing
|
publishing
|
||||||
video
|
video
|
||||||
web
|
web
|
||||||
|
|
11
test/mesh/CMakeLists.txt
Normal file
11
test/mesh/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
set(MESH_UNIT_TEST_FILES
|
||||||
|
mesh/TestMeshObjWriter.cpp
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
set(MESH_UNIT_TEST_DEPENDENCIES
|
||||||
|
mesh
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
15
test/mesh/TestMeshObjWriter.cpp
Normal file
15
test/mesh/TestMeshObjWriter.cpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include "MeshObjWriter.h"
|
||||||
|
|
||||||
|
#include "TestFramework.h"
|
||||||
|
#include "TestUtils.h"
|
||||||
|
|
||||||
|
#include "MeshPrimitives.h"
|
||||||
|
|
||||||
|
TEST_CASE(TestMeshObjWriter, "mesh")
|
||||||
|
{
|
||||||
|
auto mesh = MeshPrimitives::buildRectangleAsTriMesh();
|
||||||
|
|
||||||
|
MeshObjWriter writer;
|
||||||
|
writer.write(TestUtils::getTestOutputDir(__FILE__) / "out.obj", mesh.get());
|
||||||
|
|
||||||
|
};
|
|
@ -3,8 +3,18 @@ set(NETWORK_UNIT_TEST_FILES
|
||||||
network/TestNetworkManagerServer.cpp
|
network/TestNetworkManagerServer.cpp
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(NETWORK_INTEGRATION_TEST_FILES
|
||||||
|
network/TestWin32WebServer.cpp
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
|
||||||
set(NETWORK_UNIT_TEST_DEPENDENCIES
|
set(NETWORK_UNIT_TEST_DEPENDENCIES
|
||||||
network
|
network
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
|
||||||
|
set(NETWORK_INTEGRATION_TEST_DEPENDENCIES
|
||||||
|
network
|
||||||
|
PARENT_SCOPE
|
||||||
)
|
)
|
10
test/network/TestWin32WebServer.cpp
Normal file
10
test/network/TestWin32WebServer.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include "NetworkManager.h"
|
||||||
|
|
||||||
|
#include "TestFramework.h"
|
||||||
|
|
||||||
|
TEST_CASE(TestWin32WebServer, "network")
|
||||||
|
{
|
||||||
|
auto network_manager = NetworkManager::Create();
|
||||||
|
|
||||||
|
network_manager->RunHttpServer();
|
||||||
|
}
|
Loading…
Reference in a new issue