Initial quantum compute and cleaning up win server.

This commit is contained in:
jmsgrogan 2023-01-09 17:31:13 +00:00
parent af50eea208
commit 5362b694e0
45 changed files with 884 additions and 429 deletions

View file

@ -1,4 +1,5 @@
add_subdirectory(circuits) add_subdirectory(circuits)
add_subdirectory(math)
add_subdirectory(machine_learning) add_subdirectory(machine_learning)
add_subdirectory(physics_simulation) add_subdirectory(physics_simulation)
add_subdirectory(quantum_computing) add_subdirectory(quantum_computing)

View file

@ -0,0 +1,12 @@
set(PLUGIN_NAME ntk_math)
list(APPEND math_HEADERS ComplexNumber.h)
list(APPEND math_LIB_INCLUDES ComplexNumber.cpp)
add_library(${PLUGIN_NAME} SHARED ${math_LIB_INCLUDES} ${math_HEADERS})
target_include_directories(${PLUGIN_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${PLUGIN_NAME} PUBLIC core)
set_property(TARGET ${PLUGIN_NAME} PROPERTY FOLDER plugins)
set_target_properties( ${PLUGIN_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )

View file

@ -0,0 +1,28 @@
#include "ComplexNumber.h"
ComplexNumber::ComplexNumber(double real, double imaginary)
: mReal(real),
mImaginary(imaginary)
{
}
double ComplexNumber::getReal() const
{
return mReal;
}
double ComplexNumber::getImaginary() const
{
return mImaginary;
}
void ComplexNumber::setReal(double value)
{
mReal = value;
}
void ComplexNumber::setImaginary(double value)
{
mImaginary = value;
}

View file

@ -0,0 +1,19 @@
#pragma once
class ComplexNumber
{
public:
ComplexNumber(double real = 0.0, double imaginary = 0.0);
double getReal() const;
double getImaginary() const;
void setReal(double value);
void setImaginary(double value);
private:
double mReal{ 0.0 };
double mImaginary{ 0.0 };
};

View file

@ -1,15 +1,2 @@
set(PLUGIN_NAME quantum_computing) add_subdirectory(src)
add_subdirectory(test)
list(APPEND client_HEADERS
QuantumCircuit.h)
list(APPEND client_LIB_INCLUDES
QuantumCircuit.cpp)
add_library(${PLUGIN_NAME} SHARED ${client_LIB_INCLUDES})
target_include_directories(${PLUGIN_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(${PLUGIN_NAME} PUBLIC core)
set_property(TARGET ${PLUGIN_NAME} PROPERTY FOLDER plugins)

View file

@ -0,0 +1,12 @@
#include "BlochSphere.h"
BlochSphere::BlochSphere(const Qubit& state)
: mState(state)
{
}
const Qubit& BlochSphere::getState() const
{
return mState;
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "Qubit.h"
class BlochSphere
{
public:
BlochSphere(const Qubit& state);
const Qubit& getState() const;
private:
Qubit mState;
};

View file

@ -0,0 +1,30 @@
set(PLUGIN_NAME quantum_computing)
list(APPEND quantum_computing_HEADERS
QuantumCircuit.h
BlochSphere.h
QuantumState.h
Qubit.h
QuantumGate.h
QuantumOperator.h
visuals/BlochSphereNode.h
)
list(APPEND quantum_computing_LIB_INCLUDES
QuantumCircuit.cpp
BlochSphere.cpp
QuantumState.cpp
Qubit.cpp
QuantumGate.cpp
QuantumOperator.cpp
visuals/BlochSphereNode.cpp
)
add_library(${PLUGIN_NAME} SHARED ${quantum_computing_LIB_INCLUDES} ${quantum_computing_HEADERS})
target_include_directories(${PLUGIN_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/visuals
)
target_link_libraries(${PLUGIN_NAME} PUBLIC core visual_elements ntk_math)
set_property(TARGET ${PLUGIN_NAME} PROPERTY FOLDER plugins)

View file

@ -0,0 +1,18 @@
#include "Qubit.h"
Qubit::Qubit(const ComplexNumber& alpha, const ComplexNumber& beta)
: mAlpha(alpha),
mBeta(beta)
{
}
const ComplexNumber& Qubit::getAlpha() const
{
return mAlpha;
}
const ComplexNumber& Qubit::getBeta() const
{
return mBeta;
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "ComplexNumber.h"
class Qubit
{
public:
Qubit(const ComplexNumber& alpha, const ComplexNumber& beta);
const ComplexNumber& getAlpha() const;
const ComplexNumber& getBeta() const;
private:
ComplexNumber mAlpha;
ComplexNumber mBeta;
};

View file

@ -0,0 +1,40 @@
#include "BlochSphereNode.h"
#include "CircleNode.h"
BlochSphereNode::BlochSphereNode(const DiscretePoint& location)
: AbstractVisualNode(location, "BlochSphereNode")
{
}
void BlochSphereNode::setContent(BlochSphere* content)
{
mContent = content;
mContentDirty = true;
}
void BlochSphereNode::setSize(double size)
{
if (size != mSize)
{
mSize = size;
mContentDirty = true;
}
}
void BlochSphereNode::update(FontsManager* fontsManager)
{
if (!mContentDirty)
{
return;
}
mChildren.clear();
auto loc = DiscretePoint(mSize / 2.0, mSize / 2.0);
mOuterCircle = std::make_unique<CircleNode>(loc, mSize/2.0);
addChild(mOuterCircle.get());
}

View file

@ -0,0 +1,34 @@
#pragma once
#include "AbstractVisualNode.h"
#include <memory>
class BlochSphere;
class CircleNode;
class LineNode;
class BlochSphereNode : public AbstractVisualNode
{
public:
BlochSphereNode(const DiscretePoint& location);
void setContent(BlochSphere* content);
void update(FontsManager* fontsManager) override;
void setSize(double size);
private:
double mSize{ 1.0 };
bool mContentDirty{ true };
BlochSphere* mContent{ nullptr };
std::unique_ptr<CircleNode> mOuterCircle;
std::unique_ptr<CircleNode> mInnerCircle;
std::unique_ptr<CircleNode> mCentreCircle;
std::unique_ptr<CircleNode> mStateMarkerCircle;
std::unique_ptr<LineNode> mXAxis;
std::unique_ptr<LineNode> mYAxis;
std::unique_ptr<LineNode> mZAxis;
std::unique_ptr<LineNode> mStateVector;
};

View file

@ -0,0 +1,8 @@
list(APPEND UNIT_TEST_FILES
TestBlochSphereNode.cpp
)
add_executable(quantum_computing_unit_tests ${CMAKE_SOURCE_DIR}/test/test_runner.cpp ${UNIT_TEST_FILES})
target_link_libraries(quantum_computing_unit_tests PUBLIC test_utils quantum_computing)
set_property(TARGET quantum_computing_unit_tests PROPERTY FOLDER plugins)

View file

@ -0,0 +1,35 @@
#include "TestFramework.h"
#include "TestUtils.h"
#include "BlochSphereNode.h"
#include "BlochSphere.h"
#include "Scene.h"
#include "SvgConverter.h"
#include "SvgWriter.h"
#include "File.h"
TEST_CASE(TestBlochSphereNode, "quantum_computing")
{
auto node = std::make_unique<BlochSphereNode>();
Qubit state({ 1.0, 0.0 }, { 0.0, 0.0 });
auto bloch_sphere = std::make_unique<BlochSphere>(state);
node->setContent(bloch_sphere.get());
auto scene = std::make_unique<Scene>();
scene->addNode(node.get());
scene->update();
SvgConverter converter;
auto svg_document = converter.convert(scene.get());
SvgWriter writer;
auto svg_content = writer.toString(svg_document.get());
File svg_file(TestUtils::getTestOutputDir(__FILE__) / "bloch_sphere.svg");
svg_file.writeText(svg_content);
}

12
src/core/AbstractWebApp.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include "AbstractApp.h"
class HttpRequest;
class HttpResponse;
class AbstractWebApp : public AbstractApp
{
public:
virtual HttpResponse onHttpRequest(const HttpRequest& request) = 0;
};

View file

@ -2,6 +2,7 @@ set(MODULE_NAME core)
list(APPEND core_HEADERS list(APPEND core_HEADERS
AbstractApp.h AbstractApp.h
AbstractWebApp.h
Dictionary.h Dictionary.h
Event.h Event.h
Color.h Color.h

View file

@ -4,6 +4,23 @@
#include <sstream> #include <sstream>
HttpRequest::HttpRequest(Verb verb, const std::string& path)
: mVerb(verb),
mPath(path)
{
}
HttpRequest::Verb HttpRequest::getVerb() const
{
return mVerb;
}
std::string HttpRequest::getPath() const
{
return mPath;
}
void HttpRequest::parseMessage(const std::string& message) void HttpRequest::parseMessage(const std::string& message)
{ {
std::stringstream ss(message); std::stringstream ss(message);

View file

@ -7,13 +7,31 @@
class HttpRequest class HttpRequest
{ {
public: public:
enum class Verb
{
GET,
PUT,
POST,
PATCH,
_DELETE,
UNKNOWN
};
HttpRequest() = default; HttpRequest() = default;
HttpRequest(Verb verb, const std::string& path);
Verb getVerb() const;
std::string getPath() const;
void parseMessage(const std::string& message); void parseMessage(const std::string& message);
private: private:
void parseFirstLine(const std::string& line); void parseFirstLine(const std::string& line);
Verb mVerb = Verb::UNKNOWN;
HttpHeader mHeader; HttpHeader mHeader;
std::string mMethod; std::string mMethod;
std::string mPath; std::string mPath;

View file

@ -2,7 +2,8 @@
HttpResponse::HttpResponse() HttpResponse::HttpResponse()
: mHttpVersion("1.1"), : mHttpVersion("1.1"),
mResponseCode("200 OK"), mStatusCode(200),
mResponseReason("OK"),
mContentType("text/plain"), mContentType("text/plain"),
mBody() mBody()
{ {
@ -14,25 +15,40 @@ HttpResponse::~HttpResponse()
} }
void HttpResponse::SetBody(const std::string& body) unsigned short HttpResponse::getStatusCode() const
{
return mStatusCode;
}
const std::string& HttpResponse::getBody() const
{
return mBody;
}
void HttpResponse::setBody(const std::string& body)
{ {
mBody = body; mBody = body;
} }
unsigned HttpResponse::GetBodyLength() const unsigned HttpResponse::getBodyLength() const
{ {
return unsigned(mBody.length()); return unsigned(mBody.length());
} }
std::string HttpResponse::GetHeaderString() const std::string HttpResponse::getHeaderString() const
{ {
std::string header = "HTTP/" + mHttpVersion + " " + mResponseCode + "\n"; std::string header = "HTTP/" + mHttpVersion + " " + std::to_string(mStatusCode) + " " + mResponseReason + "\n";
header += "Content-Type: " + mContentType + "\n"; header += "Content-Type: " + mContentType + "\n";
header += "Content-Length: " + std::to_string(GetBodyLength()) + "\n"; header += "Content-Length: " + std::to_string(getBodyLength()) + "\n";
return header; return header;
} }
std::string HttpResponse::ToString() const std::string HttpResponse::getResponseReason() const
{ {
return GetHeaderString() + "\n\n" + mBody; return mResponseReason;
}
std::string HttpResponse::toString() const
{
return getHeaderString() + "\n\n" + mBody;
} }

View file

@ -9,17 +9,25 @@ public:
~HttpResponse(); ~HttpResponse();
void SetBody(const std::string& body); void setBody(const std::string& body);
unsigned GetBodyLength() const; unsigned getBodyLength() const;
std::string GetHeaderString() const; const std::string& getBody() const;
std::string ToString() const; std::string getHeaderString() const;
std::string toString() const;
unsigned short getStatusCode() const;
std::string getResponseReason() const;
private: private:
unsigned short mStatusCode{ 200 };
std::string mResponseReason{ };
std::string mHttpVersion; std::string mHttpVersion;
std::string mResponseCode;
std::string mContentType; std::string mContentType;
std::string mBody; std::string mBody;
}; };

View file

@ -11,6 +11,8 @@ list(APPEND geometry_LIB_INCLUDES
Linalg.cpp Linalg.cpp
Line.cpp Line.cpp
LineSegment.cpp LineSegment.cpp
Matrix.h
Matrix.cpp
Path.h Path.h
Path.cpp Path.cpp
Point.h Point.h
@ -28,8 +30,7 @@ list(APPEND geometry_LIB_INCLUDES
# add the library # add the library
add_library(geometry SHARED ${geometry_LIB_INCLUDES}) add_library(geometry SHARED ${geometry_LIB_INCLUDES})
target_include_directories(geometry PUBLIC target_include_directories(geometry PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
"${CMAKE_CURRENT_SOURCE_DIR}")
set_target_properties( geometry PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) set_target_properties( geometry PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
set_property(TARGET geometry PROPERTY FOLDER src) set_property(TARGET geometry PROPERTY FOLDER src)

0
src/geometry/Matrix.cpp Normal file
View file

0
src/geometry/Matrix.h Normal file
View file

View file

@ -7,7 +7,16 @@ list(APPEND platform_INCLUDES
else() else()
list(APPEND platform_INCLUDES list(APPEND platform_INCLUDES
server/win32/Win32WebServer.h server/win32/Win32WebServer.h
server/win32/Win32WebServer.cpp) server/win32/Win32WebServer.cpp
server/win32/Win32TempFile.h
server/win32/Win32TempFile.cpp
server/win32/Win32WebRequest.h
server/win32/Win32WebRequest.cpp
server/win32/Win32WebResponse.h
server/win32/Win32WebResponse.cpp
server/win32/Win32Buffer.h
server/win32/Win32Buffer.cpp
)
list(APPEND platform_LIBS Httpapi.lib) list(APPEND platform_LIBS Httpapi.lib)
endif() endif()

View file

@ -0,0 +1,63 @@
#include "Win32Buffer.h"
#include "FileLogger.h"
#ifndef UNICODE
#define UNICODE
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
Win32Buffer::Win32Buffer(unsigned size)
{
createBuffer(size);
}
Win32Buffer::~Win32Buffer()
{
clearBuffer();
}
PUCHAR Win32Buffer::getBufferAsUChar() const
{
return reinterpret_cast<PUCHAR>(mBuffer);
}
void* Win32Buffer::getBuffer() const
{
return mBuffer;
}
bool Win32Buffer::isValid() const
{
return mIsValid;
}
void Win32Buffer::createBuffer(unsigned size)
{
mIsValid = false;
mBuffer = ::HeapAlloc(::GetProcessHeap(), 0, size);
if (mBuffer == nullptr)
{
MLOG_ERROR("Insufficient resources");
return;
}
mIsValid = true;
}
void Win32Buffer::clearBuffer()
{
if (mBuffer)
{
::HeapFree(::GetProcessHeap(), 0, mBuffer);
}
}
void Win32Buffer::reset(unsigned size)
{
clearBuffer();
createBuffer(size);
}

View file

@ -0,0 +1,24 @@
#pragma once
class Win32Buffer
{
public:
Win32Buffer(unsigned size);
~Win32Buffer();
void* getBuffer() const;
PUCHAR getBufferAsUChar() const;
bool isValid() const;
void reset(unsigned size);
private:
void createBuffer(unsigned size);
void clearBuffer();
bool mIsValid{ false };
void* mBuffer{ nullptr };
};

View file

@ -0,0 +1,53 @@
#include "Win32TempFile.h"
#include "FileLogger.h"
Win32TempFile::Win32TempFile()
{
if (::GetTempFileName(L".", L"New", 0, &mName[0]) == 0)
{
auto result = ::GetLastError();
MLOG_ERROR("Failed to set up temp file for buffer with: " << result);
return;
}
mHandle = ::CreateFile(&mName[0], GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (mHandle == INVALID_HANDLE_VALUE)
{
auto result = ::GetLastError();
MLOG_ERROR("Failed to create temp file for buffer with: " << result);
return;
}
mIsValid = true;
return;
}
Win32TempFile::~Win32TempFile()
{
if (INVALID_HANDLE_VALUE != mHandle)
{
::CloseHandle(mHandle);
::DeleteFile(&mName[0]);
}
}
void Win32TempFile::write(PUCHAR buffer, ULONG size)
{
if (!mIsValid)
{
return;
}
ULONG temp_file_bytes_written = 0;
::WriteFile(mHandle, buffer, size, &temp_file_bytes_written, nullptr);
}
HANDLE Win32TempFile::getHandle() const
{
return mHandle;
}
bool Win32TempFile::isValid() const
{
return mIsValid;
}

View file

@ -0,0 +1,31 @@
#pragma once
#ifndef UNICODE
#define UNICODE
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <string>
class Win32TempFile
{
public:
Win32TempFile();
~Win32TempFile();
bool isValid() const;
HANDLE getHandle() const;
void write(PUCHAR buffer, ULONG size);
private:
bool mIsValid{ false };
std::wstring mName;
HANDLE mHandle{ 0 };
};

View file

@ -0,0 +1,67 @@
#include "Win32WebRequest.h"
#include "StringUtils.h"
#include "HttpRequest.h"
Win32WebRequest::Win32WebRequest()
: mBuffer(DEFAULT_BUFFER_SIZE)
{
mIsValid = mBuffer.isValid();
mHandle = reinterpret_cast<PHTTP_REQUEST>(mBuffer.getBuffer());
}
bool Win32WebRequest::isValid() const
{
return mIsValid;
}
void Win32WebRequest::zeroBufferMemory()
{
::RtlZeroMemory(mHandle, mWorkingBufferSize);
}
PHTTP_REQUEST Win32WebRequest::getHandle() const
{
return mHandle;
}
unsigned Win32WebRequest::getBufferSize() const
{
return mWorkingBufferSize;
}
void Win32WebRequest::resizeBuffer(unsigned size)
{
mWorkingBufferSize = size;
mBuffer.reset(mWorkingBufferSize);
if (!mBuffer.isValid())
{
mIsValid = false;
return;
}
mHandle = reinterpret_cast<PHTTP_REQUEST>(mBuffer.getBuffer());
}
std::string Win32WebRequest::getUrlFromRequest() const
{
const std::wstring wide_url = mHandle->CookedUrl.pFullUrl;
return StringUtils::convert(wide_url);
}
HttpRequest Win32WebRequest::getRequest() const
{
HttpRequest::Verb verb;
switch (mHandle->Verb)
{
case HttpVerbGET:
verb = HttpRequest::Verb::GET;
case HttpVerbPOST:
verb = HttpRequest::Verb::POST;
default:
verb = HttpRequest::Verb::UNKNOWN;
break;
}
HttpRequest request(verb, getUrlFromRequest());
return request;
}

View file

@ -0,0 +1,50 @@
#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>
#include "Win32Buffer.h"
class HttpRequest;
class Win32WebRequest
{
public:
Win32WebRequest();
bool isValid() const;
PHTTP_REQUEST getHandle() const;
unsigned getBufferSize() const;
void resizeBuffer(unsigned size);
void zeroBufferMemory();
HttpRequest getRequest() const;
private:
std::string getUrlFromRequest() const;
static const unsigned DEFAULT_BUFFER_SIZE{ sizeof(HTTP_REQUEST) + 2048 };
Win32Buffer mBuffer;
bool mIsValid{ false };
unsigned mWorkingBufferSize{ DEFAULT_BUFFER_SIZE };
PHTTP_REQUEST mHandle{ nullptr };
};

View file

@ -0,0 +1,34 @@
#include "Win32WebResponse.h"
#include "HttpResponse.h"
Win32WebResponse::Win32WebResponse(const HttpResponse& response)
{
RtlZeroMemory((&mResponse), sizeof(*(&mResponse)));
mResponse.StatusCode = response.getStatusCode();
std::string reason = response.getResponseReason();
mResponse.pReason = reason.c_str();
mResponse.ReasonLength = (USHORT)strlen(reason.c_str());
const std::string content_type = "text / html";
mResponse.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = content_type.c_str();
mResponse.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)strlen(content_type.c_str());
auto body = response.getBody();
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()
{
return mResponse;
}

View file

@ -0,0 +1,30 @@
#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 HttpResponse;
class Win32WebResponse
{
public:
Win32WebResponse(const HttpResponse& response);
HTTP_RESPONSE& getResponse();
private:
HTTP_RESPONSE mResponse;
};

View file

@ -1,10 +1,19 @@
#include "Win32WebServer.h" #include "Win32WebServer.h"
#include "Win32TempFile.h"
#include "Win32Buffer.h"
#include "Win32WebResponse.h"
#include "Win32WebRequest.h"
#include "AbstractWebApp.h"
#include "HttpRequest.h"
#include "HttpResponse.h"
#include "FileLogger.h" #include "FileLogger.h"
#include "StringUtils.h" #include "StringUtils.h"
Win32WebServer::Win32WebServer() Win32WebServer::Win32WebServer(AbstractWebApp* webApp)
: mListenUrl("http://localhost:80/49152") : mListenUrl("http://localhost:80/49152"),
mWebApp(webApp)
{ {
} }
@ -55,313 +64,45 @@ void Win32WebServer::initialize()
} }
} }
void Win32WebServer::onRequest(PHTTP_REQUEST request) DWORD Win32WebServer::onRequest(const Win32WebRequest& request)
{ {
const std::wstring wide_url = request->CookedUrl.pFullUrl; auto app_request = request.getRequest();
const auto url = StringUtils::convert(wide_url); auto response = mWebApp->onHttpRequest(app_request);
switch (request->Verb) DWORD result;
if (app_request.getVerb() == HttpRequest::Verb::POST)
{ {
result = sendHttpPostResponse(request.getHandle());
}
else
{
result = sendHttpResponse(request.getHandle(), response);
}
/*
case HttpVerbGET: case HttpVerbGET:
MLOG_INFO("Got a GET request for: " << url); MLOG_INFO("Got a GET request for: " << url);
auto result = sendHttpResponse(request, 200, "OK", "Hey! You hit the server \r\n"); result = sendHttpResponse(request, 200, "OK", "Hey! You hit the server \r\n");
break; break;
case HttpVerbPOST: case HttpVerbPOST:
MLOG_INFO("Got a POST request for: " << url); MLOG_INFO("Got a POST request for: " << url);
result = sendHttpPostResponse(request); result = sendHttpPostResponse(request);
break; break;
default: default:
MLOG_INFO("Got an unknown request for: " << url); MLOG_INFO("Got an unknown request for: " << url);
result = sendHttpResponse(request, 503, "Not Implemented"); result = sendHttpResponse(request, 503, "Not Implemented");
break; 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; return result;
} }
HTTP_RESPONSE response; DWORD Win32WebServer::sendHttpResponse(PHTTP_REQUEST pRequest, const HttpResponse& appResponse)
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. Win32WebResponse response(appResponse);
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; DWORD bytesSent;
ULONG TempFileBytesWritten; const auto result = ::HttpSendHttpResponse(mWorkingQueue, pRequest->RequestId, 0, &(response.getResponse()), NULL, &bytesSent, NULL, 0, NULL, NULL);
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) if (result != NO_ERROR)
{ {
MLOG_ERROR("Http response failed with error: " << result); MLOG_ERROR("Http response failed with error: " << result);
@ -369,136 +110,155 @@ DWORD Win32WebServer::sendHttpResponse(PHTTP_REQUEST pRequest, USHORT StatusCode
return result; return result;
} }
#define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
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 };
if (pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
{
Win32TempFile temp_file;
if (!temp_file.isValid())
{
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;
result = ::HttpSendHttpResponse(mWorkingQueue, pRequest->RequestId, 0, &response, NULL, &bytesSent, NULL, 0, NULL, NULL );
if (result != NO_ERROR)
{
MLOG_ERROR("HttpSendHttpResponse failed with: " << result);
}
}
return result;
}
void Win32WebServer::run() void Win32WebServer::run()
{ {
// Allocate a 2 KB buffer. This size should work for most Win32WebRequest request;
// requests. The buffer size can be increased if required. Space if (!request.isValid())
// 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"); MLOG_ERROR("Failed to allocate http request buffer");
return; return;
} }
PHTTP_REQUEST pRequest = (PHTTP_REQUEST)pRequestBuffer;
// Wait for a new request. This is indicated by a NULL HTTP_REQUEST_ID request_id;
// request ID. HTTP_SET_NULL_ID(&request_id);
HTTP_REQUEST_ID requestId;
HTTP_SET_NULL_ID(&requestId);
while(true) while(true)
{ {
RtlZeroMemory(pRequest, RequestBufferLength); request.zeroBufferMemory();
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
);
DWORD bytes_read;
auto result = ::HttpReceiveHttpRequest(mWorkingQueue, request_id, 0, request.getHandle(), request.getBufferSize(), &bytes_read, nullptr);
if (NO_ERROR == result) if (NO_ERROR == result)
{ {
switch (pRequest->Verb) result = onRequest(request);
{
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) if (result != NO_ERROR)
{ {
break; break;
} }
HTTP_SET_NULL_ID(&request_id);
//
// Reset the Request ID to handle the next request.
//
HTTP_SET_NULL_ID(&requestId);
} }
else if (result == ERROR_MORE_DATA) 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.
// The input buffer was too small to hold the request request_id = request.getHandle()->RequestId;
// 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;
// request.resizeBuffer(bytes_read);
// Free the old buffer and allocate a new buffer. if (!request.isValid())
//
RequestBufferLength = bytesRead;
FREE_MEM(pRequestBuffer);
pRequestBuffer = (PCHAR)ALLOC_MEM(RequestBufferLength);
if (pRequestBuffer == NULL)
{ {
result = ERROR_NOT_ENOUGH_MEMORY; result = ERROR_NOT_ENOUGH_MEMORY;
break; break;
} }
pRequest = (PHTTP_REQUEST)pRequestBuffer;
} }
else if (ERROR_CONNECTION_INVALID == result && else if (ERROR_CONNECTION_INVALID == result && !HTTP_IS_NULL_ID(&request_id))
!HTTP_IS_NULL_ID(&requestId))
{ {
// The TCP connection was corrupted by the peer when // The TCP connection was corrupted by the peer when attempting to handle a request with more buffer. Continue to the next request.
// attempting to handle a request with more buffer. HTTP_SET_NULL_ID(&request_id);
// Continue to the next request.
HTTP_SET_NULL_ID(&requestId);
} }
else else
{ {
break; break;
} }
}
if (pRequestBuffer)
{
FREE_MEM(pRequestBuffer);
} }
} }

View file

@ -16,26 +16,31 @@
#include <string> #include <string>
class AbstractWebApp;
class Win32WebRequest;
class HttpResponse;
class Win32WebServer class Win32WebServer
{ {
public: public:
Win32WebServer(); Win32WebServer(AbstractWebApp* webApp);
~Win32WebServer(); ~Win32WebServer();
void initialize();
void clear(); void clear();
void initialize();
void run(); void run();
private: private:
void onRequest(PHTTP_REQUEST request); DWORD onRequest(const Win32WebRequest& request);
DWORD sendHttpResponse(PHTTP_REQUEST pRequest, USHORT StatusCode, const std::string& reason, const std::string& message = {}); DWORD sendHttpResponse(PHTTP_REQUEST pRequest, const HttpResponse& response);
DWORD sendHttpPostResponse(PHTTP_REQUEST pRequest); DWORD sendHttpPostResponse(PHTTP_REQUEST pRequest);
void initializeHttpResponse(HTTP_RESPONSE& response, USHORT StatusCode, const std::string& reason); AbstractWebApp* mWebApp{ nullptr };
HANDLE mWorkingQueue{ 0 }; HANDLE mWorkingQueue{ 0 };
std::string mListenUrl; std::string mListenUrl;

View file

@ -44,7 +44,6 @@ const std::vector<AbstractVisualNode*>& AbstractVisualNode::getChildren() const
void AbstractVisualNode::setIsVisible(bool isVisible) void AbstractVisualNode::setIsVisible(bool isVisible)
{ {
//std::cout << "Setting " << mName << " visibility to " << isVisible << std::endl;
mIsVisible = isVisible; mIsVisible = isVisible;
} }

View file

@ -2,8 +2,11 @@ set(MODULE_NAME visual_elements)
list(APPEND visual_elements_LIB_INCLUDES list(APPEND visual_elements_LIB_INCLUDES
GeometryNode.cpp GeometryNode.cpp
basic_shapes/RectangleNode.h
basic_shapes/RectangleNode.cpp basic_shapes/RectangleNode.cpp
basic_shapes/CircleNode.h
basic_shapes/CircleNode.cpp basic_shapes/CircleNode.cpp
basic_shapes/LineNode.h
basic_shapes/LineNode.cpp basic_shapes/LineNode.cpp
MaterialNode.cpp MaterialNode.cpp
MeshNode.cpp MeshNode.cpp
@ -14,6 +17,7 @@ list(APPEND visual_elements_LIB_INCLUDES
SceneText.cpp SceneText.cpp
Texture.cpp Texture.cpp
GridNode.cpp GridNode.cpp
AbstractVisualNode.h
AbstractVisualNode.cpp AbstractVisualNode.cpp
) )