Files
GacUI/Import/VlppOS.Windows.h
T

688 lines
22 KiB
C++

/***********************************************************************
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
DEVELOPER: Zihan Chen(vczh)
***********************************************************************/
#include "VlppOS.h"
#include "Vlpp.h"
/***********************************************************************
.\NETWORKPROTOCOL.WINDOWS.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Interfaces:
***********************************************************************/
#ifndef VCZH_INTERPROCESS_WINDOWS_NETWORKPROTOCOL
#define VCZH_INTERPROCESS_WINDOWS_NETWORKPROTOCOL
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define _WINSOCKAPI_
#include <windows.h>
#include <http.h>
#define RPC_USE_NATIVE_WCHAR
#include <rpc.h>
#include <winhttp.h>
namespace vl::inter_process
{
/*
* GET: /Request
* To connect and initialize the server.
* Returns available URLs.
*
* It can only be called once, all subsequence calls will be rejected.
*/
constexpr const wchar_t* HttpServerUrl_Connect = L"/VlppInterProcess/Connect";
/*
* POST: /Request/GUID
* Client should always maintain a living request on the server.
*
* Returns only when a request is issued.
* It will be pending or timeout if no request is issued.
* If a request is issued but no living request available, it waits.
*/
constexpr const wchar_t* HttpServerUrl_Request = L"/VlppInterProcess/Request";
/*
* POST: /Response/GUID
* To send responses or events to the server.
* Returns nothing.
*/
constexpr const wchar_t* HttpServerUrl_Response = L"/VlppInterProcess/Response";
}
#endif
/***********************************************************************
.\HTTPCLIENTAPI.WINDOWS.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Interfaces:
HttpClientApi
***********************************************************************/
#ifndef VCZH_INTERPROCESS_WINDOWS_HTTPCLIENTAPI
#define VCZH_INTERPROCESS_WINDOWS_HTTPCLIENTAPI
namespace vl::inter_process
{
/// <summary>An http request.</summary>
class HttpRequest
{
typedef collections::Array<char> BodyBuffer;
typedef collections::List<WString> StringList;
typedef collections::Dictionary<WString, WString> HeaderMap;
public:
/// <summary>Query of the request, like "/index.html".</summary>
WString query;
/// <summary>Set to true if the request uses SSL, or https.</summary>
bool secure = false;
/// <summary>User name to authorize. Set to empty if authorization is not needed.</summary>
WString username;
/// <summary>Password to authorize. Set to empty if authorization is not needed.</summary>
WString password;
/// <summary>HTTP method, like "GET", "POST", "PUT", "DELETE", etc.</summary>
WString method;
/// <summary>Cookie. Set to empty if cookie is not needed.</summary>
WString cookie;
/// <summary>Request body. This is a byte array.</summary>
BodyBuffer body;
/// <summary>Content type, like "text/xml".</summary>
WString contentType;
/// <summary>Accept type list, elements like "text/xml".</summary>
StringList acceptTypes;
/// <summary>A dictionary to contain extra headers.</summary>
HeaderMap extraHeaders;
/// <summary>Set to true to let this request finish when <see cref="HttpClientApi.Stop"/> is called.</summary>
bool keepAliveOnStop = false;
/// <summary>Timeout for resolving the host name. 0 or -1 means infinite.</summary>
vint resolveTimeout = 0;
/// <summary>Timeout for connecting to the server. 0 or -1 means infinite.</summary>
vint connectTimeout = 60000;
/// <summary>Timeout for sending the request. 0 or -1 means infinite.</summary>
vint sendTimeout = 30000;
/// <summary>Timeout for receiving the response. 0 or -1 means infinite.</summary>
vint receiveTimeout = 30000;
HttpRequest() = default;
void SetBodyUtf8(const WString& bodyString);
};
/// <summary>A type representing an http response.</summary>
class HttpResponse
{
typedef collections::Array<char> BodyBuffer;
public:
/// <summary>Status code, like 200.</summary>
vint statusCode = 0;
/// <summary>Response body. This is a byte array.</summary>
BodyBuffer body;
/// <summary>Returned cookie from the server.</summary>
WString cookie;
/// <summary>Returned content type from the server.</summary>
WString contentType;
HttpResponse() = default;
WString GetBodyUtf8() const;
};
/// <summary>A transport error reported by the underlying Windows HTTP API.</summary>
class HttpError
{
public:
DWORD errorCode = 0;
WString operation;
WString message;
};
/// <summary>A Windows-only async HTTP client for a single host and port.</summary>
class HttpClientApi : public Object
{
static constexpr vint32_t HttpRespondBodyStep = 65536;
class HttpRequestContext : public Object
{
public:
HttpClientApi* api = nullptr;
HINTERNET httpRequest = NULL;
Func<void(Variant<HttpResponse, HttpError>)> callback;
collections::Array<char> requestBody;
HttpResponse response;
DWORD bodyBufferWriting = 0;
DWORD bodyBufferWritingAvailable = 0;
bool keepAliveOnStop = false;
bool completed = false;
bool closing = false;
SpinLock lockContext;
};
WString server;
vint port = 0;
HINTERNET httpSession = NULL;
HINTERNET httpConnection = NULL;
SpinLock lockActiveRequests;
collections::List<Ptr<HttpRequestContext>> activeRequests;
bool stopping = false;
atomic_vint pendingCallbacks = 0;
EventObject eventPendingCallbacks;
static void CALLBACK HttpStatusCallback(HINTERNET httpRequest, DWORD_PTR context, DWORD status, LPVOID statusInformation, DWORD statusInformationLength);
static HttpError MakeError(const WString& operation, DWORD errorCode);
static vint HexValue(wchar_t c);
bool IsStopping();
void BeginPendingCallback();
void EndPendingCallback();
void AttachRequestUnsafe(Ptr<HttpRequestContext> context);
void RemoveRequestUnsafe(Ptr<HttpRequestContext> context);
void CloseRequest(Ptr<HttpRequestContext> context);
void OnRequestHandleClosing(Ptr<HttpRequestContext> context);
void CompleteRequest(Ptr<HttpRequestContext> context, HttpResponse&& response);
void CompleteRequest(Ptr<HttpRequestContext> context, HttpError&& error);
void CompleteRequestWithLastError(Ptr<HttpRequestContext> context, const WString& operation, DWORD errorCode);
public:
HttpClientApi(const WString& _server, vint _port);
~HttpClientApi();
HttpClientApi(const HttpClientApi&) = delete;
HttpClientApi(HttpClientApi&&) = delete;
HttpClientApi& operator=(const HttpClientApi&) = delete;
HttpClientApi& operator=(HttpClientApi&&) = delete;
void HttpQuery(const HttpRequest& request, Func<void(Variant<HttpResponse, HttpError>)> callback);
void Stop();
static WString UrlEncodeQuery(const WString& query);
static WString UrlDecodeQuery(const WString& query);
};
}
#endif
/***********************************************************************
.\HTTPCLIENT.WINDOWS.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Interfaces:
HttpClient
***********************************************************************/
#ifndef VCZH_INTERPROCESS_WINDOWS_HTTPCLIENT
#define VCZH_INTERPROCESS_WINDOWS_HTTPCLIENT
namespace vl::inter_process
{
class HttpClient : public Object, public virtual INetworkProtocolConnection, public virtual INetworkProtocolClient
{
protected:
static constexpr vint HttpRequestMaxAttempts = 3;
enum class State
{
Ready,
WaitForServerConnection,
Running,
Stopping,
};
State state = State::Ready;
INetworkProtocolCallback* callback = nullptr;
WString baseUrl;
Ptr<HttpClientApi> httpClientApi;
WString urlConnect;
WString urlRequest;
WString urlResponse;
SpinLock lockState;
/***********************************************************************
HttpClient (Reading)
***********************************************************************/
protected:
static constexpr const wchar_t* JsonContentType = L"application/json; charset=utf8";
void RaiseLocalError(WString errorMessage, bool fatal);
bool IsStopping();
public:
void BeginReadingLoopUnsafe() override;
/***********************************************************************
HttpClient (WaitForServer)
***********************************************************************/
protected:
EventObject eventWaitForServer;
SpinLock lockConnectResult;
bool connectCompleted = false;
WString connectResponse;
WString connectError;
void CompleteConnectRequest(const WString& response, const WString& error);
public:
INetworkProtocolConnection* GetConnection() override;
void WaitForServer() override;
ClientStatus GetStatus() override;
/***********************************************************************
HttpClient (Writing)
***********************************************************************/
protected:
enum class HttpRequestType
{
Connect,
Request,
Response,
};
bool SendHttpRequest(HttpRequestType requestType, const wchar_t* method, const WString& url, const WString& body, vint attempt = 1);
void OnHttpRequestCompleted(HttpRequestType requestType, WString body, vint attempt, Variant<HttpResponse, HttpError> result);
void OnHttpRequestFailed(HttpRequestType requestType, const WString& body, vint attempt, const WString& errorMessage);
public:
void SendString(const WString& str) override;
/***********************************************************************
HttpClient
***********************************************************************/
public:
HttpClient(const WString _baseUrl, vint port);
~HttpClient();
void InstallCallback(INetworkProtocolCallback* _callback) override;
void Stop() override;
};
}
#endif
/***********************************************************************
.\HTTPSERVERAPI.WINDOWS.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Interfaces:
HttpServerApi
***********************************************************************/
#ifndef VCZH_INTERPROCESS_WINDOWS_HTTPSERVERAPI
#define VCZH_INTERPROCESS_WINDOWS_HTTPSERVERAPI
namespace vl::inter_process
{
/// <summary>A response to be sent by <see cref="HttpServerApi"/>.</summary>
struct HttpServerResponse
{
vint statusCode = 200;
WString reason;
WString body;
WString contentType;
};
/// <summary>A Windows-only async HTTP server for a single URL prefix.</summary>
class HttpServerApi : public Object
{
static constexpr vint32_t HttpRequestBufferInitSize = 1024;
protected:
enum class State
{
Ready,
Running,
Stopping,
};
WString urlPrefix;
bool respondToOptions = false;
HANDLE httpRequestQueue = INVALID_HANDLE_VALUE;
HTTP_SERVER_SESSION_ID httpSessionId = HTTP_NULL_ID;
HTTP_URL_GROUP_ID httpUrlGroupId = HTTP_NULL_ID;
State state = State::Ready;
collections::Array<BYTE> bufferRequest;
HANDLE hWaitHandleRequest = INVALID_HANDLE_VALUE;
OVERLAPPED overlappedRequest;
HANDLE hEventRequest = INVALID_HANDLE_VALUE;
EventObject eventPendingCallbacks;
atomic_vint pendingCallbacks = 0;
void OnHttpConnectionBrokenUnsafe();
void OnHttpRequestReceivedUnsafe(PHTTP_REQUEST pRequest);
ULONG ListenToHttpRequest_Init(OVERLAPPED* overlapped);
ULONG ListenToHttpRequest_InitMoreData(ULONG* bytesReturned);
ULONG ListenToHttpRequest_OverlappedMoreData(vint expectedBufferSize);
void ListenToHttpRequest();
void BeginPendingCallback();
void EndPendingCallback();
virtual void OnHttpRequestReceived(PHTTP_REQUEST pRequest) = 0;
virtual void OnHttpServerStopping();
static void SendOptionsResponse(HANDLE httpRequestQueue, HTTP_REQUEST_ID requestId);
public:
HttpServerApi(const WString& _urlPrefix, bool _respondToOptions);
~HttpServerApi();
HttpServerApi(const HttpServerApi&) = delete;
HttpServerApi(HttpServerApi&&) = delete;
HttpServerApi& operator=(const HttpServerApi&) = delete;
HttpServerApi& operator=(HttpServerApi&&) = delete;
void Start();
void Stop();
bool IsStopped();
HANDLE GetHttpRequestQueue() const;
Nullable<WString> GetUtf8Body(PHTTP_REQUEST pRequest);
static ULONG SendResponse(HANDLE httpRequestQueue, HTTP_REQUEST_ID requestId, const HttpServerResponse& response);
static void SendResponseUtf8(HANDLE httpRequestQueue, HTTP_REQUEST_ID requestId, WString body);
};
}
#endif
/***********************************************************************
.\HTTPSERVER.WINDOWS.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Interfaces:
HttpServer
***********************************************************************/
#ifndef VCZH_INTERPROCESS_WINDOWS_HTTPSERVER
#define VCZH_INTERPROCESS_WINDOWS_HTTPSERVER
namespace vl::inter_process
{
class HttpServer;
class HttpServerConnection : public Object, public virtual INetworkProtocolConnection
{
friend class HttpServer;
protected:
HttpServer* server = nullptr;
WString guid;
INetworkProtocolCallback* callback = nullptr;
SpinLock lockQueuedStrings;
collections::List<WString> queuedStrings;
SpinLock pendingRequestLock;
HTTP_REQUEST_ID httpPendingRequestId = HTTP_NULL_ID;
collections::List<WString> pendingRequestsToSend;
bool submittingResponse = false;
collections::List<WString> responsesToSubmit;
// All following functions must be called inside SPIN_LOCK(pendingRequestLock)
void OnCancelCurrentHttpRequestForPendingRequest();
void OnNewHttpRequestForPendingRequest(HTTP_REQUEST_ID httpRequestId);
WString SubmitResponse(PHTTP_REQUEST pRequest);
public:
void InstallCallback(INetworkProtocolCallback* _callback) override;
void BeginReadingLoopUnsafe() override;
void SendString(const WString& str) override;
void Stop() override;
static WString GenerateNewGuid();
};
class HttpServer : public HttpServerApi, public virtual INetworkProtocolServer
{
friend class HttpServerConnection;
using ConnectionMap = collections::Dictionary<WString, Ptr<HttpServerConnection>>;
protected:
WString baseUrl;
WString urlConnect;
WString urlRequestPrefix;
WString urlResponsePrefix;
// covers connections
SpinLock lockConnections;
ConnectionMap connections;
/***********************************************************************
HttpServer (BeginReadingLoopUnsafe)
***********************************************************************/
protected:
/***********************************************************************
HttpServer (HttpServerApi)
***********************************************************************/
protected:
void OnHttpRequestReceived(PHTTP_REQUEST pRequest) override;
void OnHttpServerStopping() override;
/***********************************************************************
HttpServer
***********************************************************************/
public:
HttpServer(const WString _baseUrl, vint port);
~HttpServer();
WaitForClientResult OnClientConnected(INetworkProtocolConnection* connection) override;
void Start() override;
void Stop() override;
bool IsStopped() override;
};
}
#endif
/***********************************************************************
.\NAMEDPIPE.WINDOWS.H
***********************************************************************/
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Interfaces:
NamedPipeServer
NamedPipeClient
***********************************************************************/
#ifndef VCZH_INTERPROCESS_WINDOWS_NAMEDPIPE
#define VCZH_INTERPROCESS_WINDOWS_NAMEDPIPE
namespace vl::inter_process
{
class NamedPipeServer;
class NamedPipeConnection : public Object, public virtual INetworkProtocolConnection
{
friend class NamedPipeServer;
// -----------------------------------------------------------------------
// Reading
// -----------------------------------------------------------------------
private:
class ReadWaitContext;
bool firstRead = true;
atomic_vint stopped = 0;
collections::Array<BYTE> bufferReadFile;
stream::MemoryStream streamReadFile;
std::atomic<ReadWaitContext*> readWaitContext = nullptr;
OVERLAPPED overlappedReadFile;
HANDLE hEventReadFile = INVALID_HANDLE_VALUE;
atomic_vint pendingCallbacks = 0;
EventObject eventPendingCallbacks;
void BeginReadingUnsafe();
void SubmitReadBufferUnsafe(vint bytes);
void EndReadingUnsafe();
void BeginPendingCallback();
void EndPendingCallback();
public:
void BeginReadingLoopUnsafe() override;
// -----------------------------------------------------------------------
// Writing
// -----------------------------------------------------------------------
private:
SpinLock lockWrite;
stream::MemoryStream streamWriteFile;
OVERLAPPED overlappedWriteFile;
HANDLE hEventWriteFile = INVALID_HANDLE_VALUE;
vint32_t WriteInt32ToStream(vint32_t number);
vint32_t WriteStringToStream(const WString& str);
void BeginSendStream();
void EndSendStream(vint32_t bytes);
protected:
void SendString(const WString& str) override;
// -----------------------------------------------------------------------
// Connection
// -----------------------------------------------------------------------
protected:
// NamedPipe doesn't support a single message that is larger than 64K
static constexpr vint32_t MaxMessageSize = 65536;
NamedPipeServer* server = nullptr;
INetworkProtocolCallback* callback = nullptr;
HANDLE hPipe = INVALID_HANDLE_VALUE;
void OnLocalError(const WString& errorMessage);
void OnDisconnected();
NamedPipeConnection(HANDLE _hPipe);
public:
~NamedPipeConnection();
void InstallCallback(INetworkProtocolCallback* _callback) override;
void Stop() override;
};
class NamedPipeServer : public Object, public virtual INetworkProtocolServer
{
friend class NamedPipeConnection;
protected:
class PendingConnection : public Object
{
public:
class ConnectWaitContext;
NamedPipeServer* server = nullptr;
Ptr<NamedPipeConnection> connection;
std::atomic<ConnectWaitContext*> connectWaitContext = nullptr;
OVERLAPPED overlappedConnect;
HANDLE hEventConnect = INVALID_HANDLE_VALUE;
atomic_vint pendingCallbacks = 0;
EventObject eventPendingCallbacks;
PendingConnection(NamedPipeServer* _server, Ptr<NamedPipeConnection> _connection);
~PendingConnection();
void Stop();
void BeginPendingCallback();
void EndPendingCallback();
};
static HANDLE ServerCreatePipe(const WString& pipeName);
WString pipeName;
// covers started, stopped, connections and pendingConnections
SpinLock lockConnections;
bool started = false;
bool stopped = false;
collections::List<Ptr< NamedPipeConnection>> connections;
collections::List<Ptr<PendingConnection>> pendingConnections;
void BeginListening();
void CompletePendingConnection(Ptr<PendingConnection> pendingConnection, bool connected);
void CompletePendingConnection(PendingConnection* pendingConnection, bool connected);
public:
NamedPipeServer(const WString& _pipeName);
~NamedPipeServer();
WaitForClientResult OnClientConnected(INetworkProtocolConnection* connection) override;
void Start() override;
void Stop() override;
bool IsStopped() override;
};
class NamedPipeClient : public NamedPipeConnection, public virtual INetworkProtocolClient
{
protected:
static HANDLE ClientCreatePipe(const WString& pipeName);
ClientStatus status = ClientStatus::Ready;
public:
NamedPipeClient(const WString& _pipeName);
~NamedPipeClient();
INetworkProtocolConnection* GetConnection() override;
void WaitForServer() override;
ClientStatus GetStatus() override;
void Stop() override;
};
}
#endif