Clean up win32 server example.
This commit is contained in:
parent
5362b694e0
commit
2c825adc1d
23 changed files with 337 additions and 156 deletions
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "SvgConverter.h"
|
#include "SvgConverter.h"
|
||||||
#include "SvgWriter.h"
|
#include "SvgWriter.h"
|
||||||
|
#include "SvgDocument.h"
|
||||||
|
|
||||||
#include "File.h"
|
#include "File.h"
|
||||||
|
|
||||||
|
|
28
src/console/BasicWebApp.cpp
Normal file
28
src/console/BasicWebApp.cpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#include "BasicWebApp.h"
|
||||||
|
|
||||||
|
#include "FileLogger.h"
|
||||||
|
|
||||||
|
HttpResponse BasicWebApp::onHttpRequest(const HttpRequest& request)
|
||||||
|
{
|
||||||
|
const auto url = request.getPath();
|
||||||
|
|
||||||
|
HttpResponse response;
|
||||||
|
if (request.getVerb() == HttpRequest::Verb::GET)
|
||||||
|
{
|
||||||
|
MLOG_INFO("Got a GET request for: " << url);
|
||||||
|
response.setBody("Hey! You hit the server \r\n");
|
||||||
|
response.setStatusCode(200);
|
||||||
|
response.setResponseReason("OK");
|
||||||
|
}
|
||||||
|
else if (request.getVerb() == HttpRequest::Verb::POST)
|
||||||
|
{
|
||||||
|
MLOG_INFO("Got a POST request for: " << url);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MLOG_INFO("Got an unknown request for: " << url);
|
||||||
|
response.setStatusCode(503);
|
||||||
|
response.setResponseReason("Not Implemented");
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
11
src/console/BasicWebApp.h
Normal file
11
src/console/BasicWebApp.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AbstractWebApp.h"
|
||||||
|
#include "HttpRequest.h"
|
||||||
|
#include "HttpResponse.h"
|
||||||
|
|
||||||
|
class BasicWebApp : public AbstractWebApp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HttpResponse onHttpRequest(const HttpRequest& request) override;
|
||||||
|
};
|
|
@ -1,6 +1,10 @@
|
||||||
list(APPEND console_HEADERS MainApplication.h)
|
list(APPEND console_HEADERS
|
||||||
|
MainApplication.h
|
||||||
|
BasicWebApp.h)
|
||||||
|
|
||||||
list(APPEND console_LIB_INCLUDES MainApplication.cpp)
|
list(APPEND console_LIB_INCLUDES
|
||||||
|
BasicWebApp.cpp
|
||||||
|
MainApplication.cpp)
|
||||||
|
|
||||||
add_library(console SHARED ${console_LIB_INCLUDES} ${console_HEADERS})
|
add_library(console SHARED ${console_LIB_INCLUDES} ${console_HEADERS})
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include "DiscretePoint.h"
|
#include "DiscretePoint.h"
|
||||||
#include "Scene.h"
|
#include "Scene.h"
|
||||||
|
|
||||||
|
#include "BasicWebApp.h"
|
||||||
|
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
@ -118,7 +120,8 @@ void MainApplication::run()
|
||||||
|
|
||||||
void MainApplication::runServer()
|
void MainApplication::runServer()
|
||||||
{
|
{
|
||||||
mNetworkManager->RunHttpServer();
|
BasicWebApp web_app;
|
||||||
|
mNetworkManager->runHttpServer(&web_app);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainApplication::playAudio()
|
void MainApplication::playAudio()
|
||||||
|
@ -151,7 +154,7 @@ void MainApplication::shutDown()
|
||||||
|
|
||||||
if (mNetworkManager)
|
if (mNetworkManager)
|
||||||
{
|
{
|
||||||
mNetworkManager->ShutDown();
|
mNetworkManager->shutDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
MLOG_INFO("Shut down");
|
MLOG_INFO("Shut down");
|
||||||
|
|
|
@ -14,6 +14,8 @@ list(APPEND core_HEADERS
|
||||||
file_utilities/PathUtils.h
|
file_utilities/PathUtils.h
|
||||||
StringUtils.h
|
StringUtils.h
|
||||||
http/HttpResponse.h
|
http/HttpResponse.h
|
||||||
|
http/HttpHeader.h
|
||||||
|
http/HttpRequest.h
|
||||||
serializers/TomlReader.h
|
serializers/TomlReader.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,23 @@
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
|
|
||||||
|
HttpHeader::HttpHeader()
|
||||||
|
: mContentType("text / html"),
|
||||||
|
mHttpVersion("1.1")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HttpHeader::getContentType() const
|
||||||
|
{
|
||||||
|
return mContentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HttpHeader::getHttpVersion() const
|
||||||
|
{
|
||||||
|
return mHttpVersion;
|
||||||
|
}
|
||||||
|
|
||||||
void HttpHeader::parse(const std::vector<std::string >& message)
|
void HttpHeader::parse(const std::vector<std::string >& message)
|
||||||
{
|
{
|
||||||
std::string tag;
|
std::string tag;
|
||||||
|
|
|
@ -7,10 +7,16 @@
|
||||||
class HttpHeader
|
class HttpHeader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
HttpHeader();
|
||||||
void parse(const std::vector<std::string >& message);
|
void parse(const std::vector<std::string >& message);
|
||||||
|
|
||||||
private:
|
std::string getContentType() const;
|
||||||
|
|
||||||
|
std::string getHttpVersion() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mHttpVersion;
|
||||||
|
std::string mContentType;
|
||||||
std::string mHost;
|
std::string mHost;
|
||||||
std::string mUserAgent;
|
std::string mUserAgent;
|
||||||
std::string mAccept;
|
std::string mAccept;
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
#include "HttpResponse.h"
|
#include "HttpResponse.h"
|
||||||
|
|
||||||
HttpResponse::HttpResponse()
|
HttpResponse::HttpResponse()
|
||||||
: mHttpVersion("1.1"),
|
: mStatusCode(200),
|
||||||
mStatusCode(200),
|
|
||||||
mResponseReason("OK"),
|
mResponseReason("OK"),
|
||||||
mContentType("text/plain"),
|
|
||||||
mBody()
|
mBody()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -15,6 +13,11 @@ HttpResponse::~HttpResponse()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HttpHeader& HttpResponse::getHeader() const
|
||||||
|
{
|
||||||
|
return mHeader;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned short HttpResponse::getStatusCode() const
|
unsigned short HttpResponse::getStatusCode() const
|
||||||
{
|
{
|
||||||
return mStatusCode;
|
return mStatusCode;
|
||||||
|
@ -37,13 +40,13 @@ unsigned HttpResponse::getBodyLength() const
|
||||||
|
|
||||||
std::string HttpResponse::getHeaderString() const
|
std::string HttpResponse::getHeaderString() const
|
||||||
{
|
{
|
||||||
std::string header = "HTTP/" + mHttpVersion + " " + std::to_string(mStatusCode) + " " + mResponseReason + "\n";
|
std::string header = "HTTP/" + mHeader.getHttpVersion() + " " + std::to_string(mStatusCode) + " " + mResponseReason + "\n";
|
||||||
header += "Content-Type: " + mContentType + "\n";
|
header += "Content-Type: " + mHeader.getContentType() + "\n";
|
||||||
header += "Content-Length: " + std::to_string(getBodyLength()) + "\n";
|
header += "Content-Length: " + std::to_string(getBodyLength()) + "\n";
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HttpResponse::getResponseReason() const
|
const std::string& HttpResponse::getResponseReason() const
|
||||||
{
|
{
|
||||||
return mResponseReason;
|
return mResponseReason;
|
||||||
}
|
}
|
||||||
|
@ -52,3 +55,13 @@ std::string HttpResponse::toString() const
|
||||||
{
|
{
|
||||||
return getHeaderString() + "\n\n" + mBody;
|
return getHeaderString() + "\n\n" + mBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpResponse::setStatusCode(unsigned short code)
|
||||||
|
{
|
||||||
|
mStatusCode = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpResponse::setResponseReason(const std::string& reason)
|
||||||
|
{
|
||||||
|
mResponseReason = reason;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "HttpHeader.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class HttpResponse
|
class HttpResponse
|
||||||
|
@ -9,25 +11,29 @@ public:
|
||||||
|
|
||||||
~HttpResponse();
|
~HttpResponse();
|
||||||
|
|
||||||
void setBody(const std::string& body);
|
|
||||||
|
|
||||||
unsigned getBodyLength() const;
|
unsigned getBodyLength() const;
|
||||||
|
|
||||||
const std::string& getBody() const;
|
const std::string& getBody() const;
|
||||||
|
|
||||||
|
const HttpHeader& getHeader() const;
|
||||||
|
|
||||||
std::string getHeaderString() const;
|
std::string getHeaderString() const;
|
||||||
|
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
|
||||||
unsigned short getStatusCode() const;
|
unsigned short getStatusCode() const;
|
||||||
|
|
||||||
std::string getResponseReason() const;
|
const std::string& getResponseReason() const;
|
||||||
|
|
||||||
|
void setStatusCode(unsigned short code);
|
||||||
|
|
||||||
|
void setResponseReason(const std::string& reason);
|
||||||
|
|
||||||
|
void setBody(const std::string& body);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
HttpHeader mHeader;
|
||||||
unsigned short mStatusCode{ 200 };
|
unsigned short mStatusCode{ 200 };
|
||||||
std::string mResponseReason{ };
|
std::string mResponseReason{ };
|
||||||
|
|
||||||
std::string mHttpVersion;
|
|
||||||
std::string mContentType;
|
|
||||||
std::string mBody;
|
std::string mBody;
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,18 +24,19 @@ std::unique_ptr<NetworkManager> NetworkManager::Create()
|
||||||
return std::make_unique<NetworkManager>();
|
return std::make_unique<NetworkManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::Initialize()
|
void NetworkManager::initialize()
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
mSocketInterface = UnixSocketInterface::Create();
|
mSocketInterface = UnixSocketInterface::Create();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::RunHttpServer()
|
void NetworkManager::runHttpServer(AbstractWebApp* webApp)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Win32WebServer server;
|
Win32WebServer server(webApp);
|
||||||
server.initialize();
|
server.initialize();
|
||||||
|
server.run();
|
||||||
#else
|
#else
|
||||||
if (!mSocketInterface)
|
if (!mSocketInterface)
|
||||||
{
|
{
|
||||||
|
@ -49,11 +50,11 @@ void NetworkManager::RunHttpServer()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::RunHttpClient()
|
void NetworkManager::runHttpClient()
|
||||||
{
|
{
|
||||||
if (!mSocketInterface)
|
if (!mSocketInterface)
|
||||||
{
|
{
|
||||||
Initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mSocketInterface)
|
if (!mSocketInterface)
|
||||||
|
@ -66,7 +67,7 @@ void NetworkManager::RunHttpClient()
|
||||||
mSocketInterface->Write(socket, "Hello Friend");
|
mSocketInterface->Write(socket, "Hello Friend");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::ShutDown()
|
void NetworkManager::shutDown()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class AbstractWebApp;
|
||||||
|
|
||||||
class NetworkManager
|
class NetworkManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -16,13 +18,13 @@ public:
|
||||||
|
|
||||||
static std::unique_ptr<NetworkManager> Create();
|
static std::unique_ptr<NetworkManager> Create();
|
||||||
|
|
||||||
void Initialize();
|
void initialize();
|
||||||
|
|
||||||
void RunHttpServer();
|
void runHttpServer(AbstractWebApp* webApp);
|
||||||
|
|
||||||
void RunHttpClient();
|
void runHttpClient();
|
||||||
|
|
||||||
void ShutDown();
|
void shutDown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<SocketPtr> mActiveSockets;
|
std::vector<SocketPtr> mActiveSockets;
|
||||||
|
|
|
@ -21,9 +21,9 @@ Win32Buffer::~Win32Buffer()
|
||||||
clearBuffer();
|
clearBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
PUCHAR Win32Buffer::getBufferAsUChar() const
|
unsigned char* Win32Buffer::getBufferAsUChar() const
|
||||||
{
|
{
|
||||||
return reinterpret_cast<PUCHAR>(mBuffer);
|
return reinterpret_cast<unsigned char*>(mBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Win32Buffer::getBuffer() const
|
void* Win32Buffer::getBuffer() const
|
||||||
|
|
|
@ -9,7 +9,7 @@ public:
|
||||||
|
|
||||||
void* getBuffer() const;
|
void* getBuffer() const;
|
||||||
|
|
||||||
PUCHAR getBufferAsUChar() const;
|
unsigned char* getBufferAsUChar() const;
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
#include "HttpRequest.h"
|
#include "HttpRequest.h"
|
||||||
|
#include "FileLogger.h"
|
||||||
|
|
||||||
|
#include "Win32TempFile.h"
|
||||||
|
|
||||||
Win32WebRequest::Win32WebRequest()
|
Win32WebRequest::Win32WebRequest()
|
||||||
: mBuffer(DEFAULT_BUFFER_SIZE)
|
: mBuffer(DEFAULT_BUFFER_SIZE)
|
||||||
|
@ -55,8 +58,10 @@ HttpRequest Win32WebRequest::getRequest() const
|
||||||
{
|
{
|
||||||
case HttpVerbGET:
|
case HttpVerbGET:
|
||||||
verb = HttpRequest::Verb::GET;
|
verb = HttpRequest::Verb::GET;
|
||||||
|
break;
|
||||||
case HttpVerbPOST:
|
case HttpVerbPOST:
|
||||||
verb = HttpRequest::Verb::POST;
|
verb = HttpRequest::Verb::POST;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
verb = HttpRequest::Verb::UNKNOWN;
|
verb = HttpRequest::Verb::UNKNOWN;
|
||||||
break;
|
break;
|
||||||
|
@ -65,3 +70,47 @@ HttpRequest Win32WebRequest::getRequest() const
|
||||||
HttpRequest request(verb, getUrlFromRequest());
|
HttpRequest request(verb, getUrlFromRequest());
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Win32WebRequest::startReceiveEntity()
|
||||||
|
{
|
||||||
|
mEntityBuffer = std::make_unique<Win32Buffer>(ENTITY_BUFFER_SIZE);
|
||||||
|
if (!mEntityBuffer->isValid())
|
||||||
|
{
|
||||||
|
MLOG_ERROR("Failed to allocate entity buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTempFile = std::make_unique<Win32TempFile>();
|
||||||
|
if (!mTempFile->isValid())
|
||||||
|
{
|
||||||
|
MLOG_ERROR("Failed to create tempfile");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Win32Buffer* Win32WebRequest::getEntityBuffer() const
|
||||||
|
{
|
||||||
|
return mEntityBuffer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Win32WebRequest::getEntityBufferSize() const
|
||||||
|
{
|
||||||
|
return ENTITY_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Win32TempFile* Win32WebRequest::getTempFile() const
|
||||||
|
{
|
||||||
|
return mTempFile.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32WebRequest::flushEntityBuffer(unsigned numBytes)
|
||||||
|
{
|
||||||
|
mEntityTotalSize += numBytes;
|
||||||
|
mTempFile->write(mEntityBuffer->getBufferAsUChar(), numBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Win32WebRequest::getEntityTotalSize() const
|
||||||
|
{
|
||||||
|
return mEntityTotalSize;
|
||||||
|
}
|
|
@ -14,31 +14,49 @@
|
||||||
#include <http.h>
|
#include <http.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "Win32Buffer.h"
|
#include "Win32Buffer.h"
|
||||||
|
|
||||||
class HttpRequest;
|
class HttpRequest;
|
||||||
|
class Win32TempFile;
|
||||||
|
|
||||||
class Win32WebRequest
|
class Win32WebRequest
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Win32WebRequest();
|
Win32WebRequest();
|
||||||
|
|
||||||
bool isValid() const;
|
HttpRequest getRequest() const;
|
||||||
|
|
||||||
PHTTP_REQUEST getHandle() const;
|
PHTTP_REQUEST getHandle() const;
|
||||||
|
|
||||||
unsigned getBufferSize() const;
|
unsigned getBufferSize() const;
|
||||||
|
|
||||||
|
Win32Buffer* getEntityBuffer() const;
|
||||||
|
|
||||||
|
unsigned getEntityBufferSize() const;
|
||||||
|
|
||||||
|
unsigned getEntityTotalSize() const;
|
||||||
|
|
||||||
|
void flushEntityBuffer(unsigned numBytes);
|
||||||
|
|
||||||
|
Win32TempFile* getTempFile() const;
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
void resizeBuffer(unsigned size);
|
void resizeBuffer(unsigned size);
|
||||||
|
|
||||||
void zeroBufferMemory();
|
void zeroBufferMemory();
|
||||||
|
|
||||||
HttpRequest getRequest() const;
|
bool startReceiveEntity();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string getUrlFromRequest() const;
|
std::string getUrlFromRequest() const;
|
||||||
|
|
||||||
|
static const unsigned ENTITY_BUFFER_SIZE{ 2048 };
|
||||||
|
std::unique_ptr<Win32Buffer> mEntityBuffer;
|
||||||
|
std::unique_ptr<Win32TempFile> mTempFile;
|
||||||
|
unsigned mEntityTotalSize{ 0 };
|
||||||
|
|
||||||
static const unsigned DEFAULT_BUFFER_SIZE{ sizeof(HTTP_REQUEST) + 2048 };
|
static const unsigned DEFAULT_BUFFER_SIZE{ sizeof(HTTP_REQUEST) + 2048 };
|
||||||
Win32Buffer mBuffer;
|
Win32Buffer mBuffer;
|
||||||
|
|
|
@ -1,34 +1,60 @@
|
||||||
#include "Win32WebResponse.h"
|
#include "Win32WebResponse.h"
|
||||||
|
|
||||||
#include "HttpResponse.h"
|
#include "HttpResponse.h"
|
||||||
|
#include "Win32WebRequest.h"
|
||||||
|
|
||||||
|
#include "FileLogger.h"
|
||||||
|
|
||||||
Win32WebResponse::Win32WebResponse(const HttpResponse& response)
|
Win32WebResponse::Win32WebResponse(const HttpResponse& response)
|
||||||
{
|
{
|
||||||
RtlZeroMemory((&mResponse), sizeof(*(&mResponse)));
|
RtlZeroMemory((&mResponse), sizeof(*(&mResponse)));
|
||||||
|
|
||||||
|
|
||||||
mResponse.StatusCode = response.getStatusCode();
|
mResponse.StatusCode = response.getStatusCode();
|
||||||
|
|
||||||
std::string reason = response.getResponseReason();
|
mResponseReason = response.getResponseReason();
|
||||||
mResponse.pReason = reason.c_str();
|
mResponse.pReason = mResponseReason.c_str();
|
||||||
mResponse.ReasonLength = (USHORT)strlen(reason.c_str());
|
mResponse.ReasonLength = (USHORT)strlen(mResponseReason.c_str());
|
||||||
|
|
||||||
const std::string content_type = "text / html";
|
populateHeader(response);
|
||||||
mResponse.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = content_type.c_str();
|
|
||||||
mResponse.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)strlen(content_type.c_str());
|
|
||||||
|
|
||||||
auto body = response.getBody();
|
populateBody(response);
|
||||||
if (!body.empty())
|
|
||||||
{
|
|
||||||
HTTP_DATA_CHUNK dataChunk;
|
|
||||||
dataChunk.DataChunkType = HttpDataChunkFromMemory;
|
|
||||||
dataChunk.FromMemory.pBuffer = body.data();
|
|
||||||
dataChunk.FromMemory.BufferLength = (ULONG)strlen(body.data());
|
|
||||||
|
|
||||||
mResponse.EntityChunkCount = 1;
|
|
||||||
mResponse.pEntityChunks = &dataChunk;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_RESPONSE& Win32WebResponse::getResponse()
|
HTTP_RESPONSE& Win32WebResponse::getResponse()
|
||||||
{
|
{
|
||||||
return mResponse;
|
return mResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Win32WebResponse::populateBody(const HttpResponse& response)
|
||||||
|
{
|
||||||
|
mBody = response.getBody();
|
||||||
|
if (!mBody.empty())
|
||||||
|
{
|
||||||
|
mContentLength = std::to_string(mBody.size());
|
||||||
|
mResponse.Headers.KnownHeaders[HttpHeaderContentLength].pRawValue = mContentLength.c_str();
|
||||||
|
mResponse.Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength = (USHORT)strlen(mContentLength.c_str());
|
||||||
|
|
||||||
|
HTTP_DATA_CHUNK dataChunk;
|
||||||
|
dataChunk.DataChunkType = HttpDataChunkFromMemory;
|
||||||
|
dataChunk.FromMemory.pBuffer = mBody.data();
|
||||||
|
dataChunk.FromMemory.BufferLength = (ULONG)strlen(mBody.data());
|
||||||
|
|
||||||
|
mResponse.EntityChunkCount = 1;
|
||||||
|
mResponse.pEntityChunks = &dataChunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32WebResponse::populateHeader(const HttpResponse& response)
|
||||||
|
{
|
||||||
|
mContentType = response.getHeader().getContentType();
|
||||||
|
mResponse.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = mContentType.c_str();
|
||||||
|
mResponse.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)strlen(mContentType.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32WebResponse::addRequestContentLength(const Win32WebRequest& request)
|
||||||
|
{
|
||||||
|
mContentLength = std::to_string(request.getEntityTotalSize());
|
||||||
|
mResponse.Headers.KnownHeaders[HttpHeaderContentLength].pRawValue = mContentLength.c_str();
|
||||||
|
mResponse.Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength = (USHORT)strlen(mContentLength.c_str());
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class HttpResponse;
|
class HttpResponse;
|
||||||
|
class Win32WebRequest;
|
||||||
|
|
||||||
class Win32WebResponse
|
class Win32WebResponse
|
||||||
{
|
{
|
||||||
|
@ -25,6 +26,16 @@ public:
|
||||||
|
|
||||||
HTTP_RESPONSE& getResponse();
|
HTTP_RESPONSE& getResponse();
|
||||||
|
|
||||||
|
void addRequestContentLength(const Win32WebRequest& request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void populateBody(const HttpResponse& response);
|
||||||
|
void populateHeader(const HttpResponse& response);
|
||||||
|
|
||||||
|
std::string mResponseReason;
|
||||||
|
std::string mContentType;
|
||||||
|
std::string mBody;
|
||||||
|
|
||||||
|
std::string mContentLength;
|
||||||
HTTP_RESPONSE mResponse;
|
HTTP_RESPONSE mResponse;
|
||||||
};
|
};
|
|
@ -12,7 +12,7 @@
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
Win32WebServer::Win32WebServer(AbstractWebApp* webApp)
|
Win32WebServer::Win32WebServer(AbstractWebApp* webApp)
|
||||||
: mListenUrl("http://localhost:80/49152"),
|
: mListenUrl("http://localhost:49153/"),
|
||||||
mWebApp(webApp)
|
mWebApp(webApp)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -64,19 +64,66 @@ void Win32WebServer::initialize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD Win32WebServer::onRequest(const Win32WebRequest& request)
|
bool Win32WebServer::onPostRequest(Win32WebRequest& request)
|
||||||
{
|
{
|
||||||
auto app_request = request.getRequest();
|
if (request.getHandle()->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
|
||||||
auto response = mWebApp->onHttpRequest(app_request);
|
{
|
||||||
|
if (!request.startReceiveEntity())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
DWORD result;
|
ULONG bytes_read = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bytes_read = 0;
|
||||||
|
auto result = ::HttpReceiveRequestEntityBody(mWorkingQueue, request.getHandle()->RequestId, 0, request.getEntityBuffer()->getBuffer(),
|
||||||
|
request.getEntityBufferSize(), &bytes_read, nullptr);
|
||||||
|
|
||||||
|
if (result == NO_ERROR || result == ERROR_HANDLE_EOF)
|
||||||
|
{
|
||||||
|
request.flushEntityBuffer(bytes_read);
|
||||||
|
if (result == ERROR_HANDLE_EOF)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MLOG_ERROR("HttpReceiveRequestEntityBody failed with: " << result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Win32WebServer::onRequest(Win32WebRequest& request)
|
||||||
|
{
|
||||||
|
if (request.getHandle()->Verb == HttpVerbPOST)
|
||||||
|
{
|
||||||
|
if (!onPostRequest(request))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto app_request = request.getRequest();
|
||||||
|
const auto response = mWebApp->onHttpRequest(app_request);
|
||||||
if (app_request.getVerb() == HttpRequest::Verb::POST)
|
if (app_request.getVerb() == HttpRequest::Verb::POST)
|
||||||
{
|
{
|
||||||
result = sendHttpPostResponse(request.getHandle());
|
if (!sendHttpPostResponse(request, response))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = sendHttpResponse(request.getHandle(), response);
|
if (!sendHttpResponse(request, response))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -94,123 +141,57 @@ DWORD Win32WebServer::onRequest(const Win32WebRequest& request)
|
||||||
break;
|
break;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return result;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD Win32WebServer::sendHttpResponse(PHTTP_REQUEST pRequest, const HttpResponse& appResponse)
|
bool Win32WebServer::sendHttpResponse(const Win32WebRequest& request, const HttpResponse& appResponse)
|
||||||
{
|
{
|
||||||
Win32WebResponse response(appResponse);
|
Win32WebResponse response(appResponse);
|
||||||
|
|
||||||
DWORD bytesSent;
|
DWORD bytesSent;
|
||||||
const auto result = ::HttpSendHttpResponse(mWorkingQueue, pRequest->RequestId, 0, &(response.getResponse()), NULL, &bytesSent, NULL, 0, NULL, NULL);
|
const auto result = ::HttpSendHttpResponse(mWorkingQueue, request.getHandle()->RequestId, 0, &(response.getResponse()), NULL, &bytesSent, NULL, 0, NULL, NULL);
|
||||||
if (result != NO_ERROR)
|
if (result != NO_ERROR)
|
||||||
{
|
{
|
||||||
MLOG_ERROR("Http response failed with error: " << result);
|
MLOG_ERROR("Http response failed with error: " << result);
|
||||||
}
|
}
|
||||||
return result;
|
return result == NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
|
bool Win32WebServer::sendHttpPostResponse(const Win32WebRequest& request, const HttpResponse& appResponse)
|
||||||
|
|
||||||
DWORD Win32WebServer::sendHttpPostResponse(PHTTP_REQUEST pRequest)
|
|
||||||
{
|
{
|
||||||
// Allocate space for an entity buffer. Buffer can be increased on demand.
|
|
||||||
const ULONG entity_buffer_length = 2048;
|
|
||||||
|
|
||||||
Win32Buffer entity_buffer(entity_buffer_length);
|
|
||||||
if (!entity_buffer.isValid())
|
|
||||||
{
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTP_RESPONSE response;
|
|
||||||
initializeHttpResponse(response, 200, "OK");
|
|
||||||
|
|
||||||
DWORD result{ 0 };
|
DWORD result{ 0 };
|
||||||
if (pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
|
if (request.getEntityBuffer())
|
||||||
{
|
{
|
||||||
Win32TempFile temp_file;
|
Win32WebResponse response(appResponse);
|
||||||
if (!temp_file.isValid())
|
response.addRequestContentLength(request);
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG total_bytes_read = 0;
|
|
||||||
ULONG bytes_read = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// Read the entity chunk from the request.
|
|
||||||
bytes_read = 0;
|
|
||||||
result = ::HttpReceiveRequestEntityBody(mWorkingQueue, pRequest->RequestId, 0, entity_buffer.getBuffer(), entity_buffer_length, &bytes_read, nullptr);
|
|
||||||
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case NO_ERROR:
|
|
||||||
if (bytes_read != 0)
|
|
||||||
{
|
|
||||||
total_bytes_read += bytes_read;
|
|
||||||
temp_file.write(entity_buffer.getBufferAsUChar(), bytes_read);
|
|
||||||
}
|
|
||||||
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 (bytes_read != 0)
|
|
||||||
{
|
|
||||||
total_bytes_read += bytes_read;
|
|
||||||
temp_file.write(entity_buffer.getBufferAsUChar(), bytes_read);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
CHAR szContentLength[MAX_ULONG_STR];
|
|
||||||
sprintf_s(szContentLength, MAX_ULONG_STR, "%lu", total_bytes_read);
|
|
||||||
response.Headers.KnownHeaders[HttpHeaderContentLength].pRawValue = szContentLength;
|
|
||||||
response.Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength = (USHORT)strlen(szContentLength);
|
|
||||||
|
|
||||||
DWORD bytesSent;
|
|
||||||
result = ::HttpSendHttpResponse(mWorkingQueue, pRequest->RequestId, HTTP_SEND_RESPONSE_FLAG_MORE_DATA, &response, NULL, &bytesSent, NULL, 0, NULL, NULL);
|
|
||||||
if (result != NO_ERROR)
|
|
||||||
{
|
|
||||||
MLOG_ERROR("HttpSendHttpResponse failed with: " << result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send entity body from a file handle.
|
|
||||||
HTTP_DATA_CHUNK dataChunk;
|
|
||||||
dataChunk.DataChunkType = HttpDataChunkFromFileHandle;
|
|
||||||
dataChunk.FromFileHandle.ByteRange.StartingOffset.QuadPart = 0;
|
|
||||||
dataChunk.FromFileHandle.ByteRange.Length.QuadPart = HTTP_BYTE_RANGE_TO_EOF;
|
|
||||||
dataChunk.FromFileHandle.FileHandle = temp_file.getHandle();
|
|
||||||
|
|
||||||
result = ::HttpSendResponseEntityBody(mWorkingQueue, pRequest->RequestId, 0, 1, &dataChunk, NULL, NULL, 0, NULL, NULL);
|
|
||||||
if (result != NO_ERROR)
|
|
||||||
{
|
|
||||||
MLOG_ERROR("HttpSendResponseEntityBody failed" << result);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MLOG_ERROR("HttpReceiveRequestEntityBody failed with: " << result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DWORD bytesSent;
|
DWORD bytesSent;
|
||||||
result = ::HttpSendHttpResponse(mWorkingQueue, pRequest->RequestId, 0, &response, NULL, &bytesSent, NULL, 0, NULL, NULL );
|
result = ::HttpSendHttpResponse(mWorkingQueue, request.getHandle()->RequestId, HTTP_SEND_RESPONSE_FLAG_MORE_DATA, &(response.getResponse()), NULL, &bytesSent, NULL, 0, NULL, NULL);
|
||||||
if (result != NO_ERROR)
|
if (result != NO_ERROR)
|
||||||
{
|
{
|
||||||
MLOG_ERROR("HttpSendHttpResponse failed with: " << result);
|
MLOG_ERROR("HttpSendHttpResponse failed with: " << result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send entity body from a file handle.
|
||||||
|
HTTP_DATA_CHUNK dataChunk;
|
||||||
|
dataChunk.DataChunkType = HttpDataChunkFromFileHandle;
|
||||||
|
dataChunk.FromFileHandle.ByteRange.StartingOffset.QuadPart = 0;
|
||||||
|
dataChunk.FromFileHandle.ByteRange.Length.QuadPart = HTTP_BYTE_RANGE_TO_EOF;
|
||||||
|
dataChunk.FromFileHandle.FileHandle = request.getTempFile()->getHandle();
|
||||||
|
|
||||||
|
result = ::HttpSendResponseEntityBody(mWorkingQueue, request.getHandle()->RequestId, 0, 1, &dataChunk, NULL, NULL, 0, NULL, NULL);
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
MLOG_ERROR("HttpSendResponseEntityBody failed" << result);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
else
|
||||||
|
{
|
||||||
|
result = sendHttpResponse(request, appResponse);
|
||||||
|
}
|
||||||
|
return result == NO_ERROR;;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Win32WebServer::run()
|
void Win32WebServer::run()
|
||||||
|
@ -232,8 +213,7 @@ void Win32WebServer::run()
|
||||||
auto result = ::HttpReceiveHttpRequest(mWorkingQueue, request_id, 0, request.getHandle(), request.getBufferSize(), &bytes_read, nullptr);
|
auto result = ::HttpReceiveHttpRequest(mWorkingQueue, request_id, 0, request.getHandle(), request.getBufferSize(), &bytes_read, nullptr);
|
||||||
if (NO_ERROR == result)
|
if (NO_ERROR == result)
|
||||||
{
|
{
|
||||||
result = onRequest(request);
|
if (!onRequest(request))
|
||||||
if (result != NO_ERROR)
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
class AbstractWebApp;
|
class AbstractWebApp;
|
||||||
class Win32WebRequest;
|
class Win32WebRequest;
|
||||||
|
|
||||||
class HttpResponse;
|
class HttpResponse;
|
||||||
|
|
||||||
class Win32WebServer
|
class Win32WebServer
|
||||||
|
@ -35,10 +34,11 @@ public:
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DWORD onRequest(const Win32WebRequest& request);
|
bool onRequest(Win32WebRequest& request);
|
||||||
|
bool onPostRequest(Win32WebRequest& request);
|
||||||
|
|
||||||
DWORD sendHttpResponse(PHTTP_REQUEST pRequest, const HttpResponse& response);
|
bool sendHttpResponse(const Win32WebRequest& request, const HttpResponse& response);
|
||||||
DWORD sendHttpPostResponse(PHTTP_REQUEST pRequest);
|
bool sendHttpPostResponse(const Win32WebRequest& request, const HttpResponse& response);
|
||||||
|
|
||||||
AbstractWebApp* mWebApp{ nullptr };
|
AbstractWebApp* mWebApp{ nullptr };
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ std::string HttpMessageHandler::onMessage(const std::string& message)
|
||||||
request.parseMessage(message);
|
request.parseMessage(message);
|
||||||
|
|
||||||
HttpResponse response;
|
HttpResponse response;
|
||||||
response.SetBody("Hello world!");
|
response.setBody("Hello world!");
|
||||||
|
|
||||||
const auto response_message = response.ToString();
|
const auto response_message = response.toString();
|
||||||
|
|
||||||
return response_message;
|
return response_message;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,5 @@ TEST_CASE(TestNetworkManagerClient, "network")
|
||||||
{
|
{
|
||||||
auto network_manager = NetworkManager::Create();
|
auto network_manager = NetworkManager::Create();
|
||||||
|
|
||||||
network_manager->RunHttpClient();
|
network_manager->runHttpClient();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
|
|
||||||
#include "TestFramework.h"
|
#include "TestFramework.h"
|
||||||
|
|
||||||
|
#include "BasicWebApp.h"
|
||||||
|
|
||||||
TEST_CASE(TestWin32WebServer, "network")
|
TEST_CASE(TestWin32WebServer, "network")
|
||||||
{
|
{
|
||||||
auto network_manager = NetworkManager::Create();
|
auto network_manager = NetworkManager::Create();
|
||||||
|
|
||||||
network_manager->RunHttpServer();
|
BasicWebApp web_app;
|
||||||
|
network_manager->runHttpServer(&web_app);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue