One of the class frameworks I developed and published on my web site many years ago was W3MFC and CThreadPoolServer. These helped me learn how HTTP and HTTPS worked as well as learn multi-threaded coding on Windows. A lot has changed in the years since this code was initially developed and I wanted to develop a new framework which encapsulates the HTTP Server API's available on Windows. These APIs let you embed an enterprise ready web server in your C++ Windows application. HTTPServerWrappers is a set of classes to encapsulate this functionality. To provide good performance you also need to tie this code to some sort of thread pool architecture. The CPPCThreadPool classes provide this thread pool functionality.
The classes provided are:
HTTPServer::CAutoInit This class provides a RAII wrapper for the SDK functions
HttpInitialize and HttpTerminate.
HTTPServer::CSession This class provides
a RAII wrapper for the SDK HTTP_SERVER_SESSION_ID typedef.
HTTPServer::CURLGroup
This class provides a RAII wrapper for the SDK HTTP_URL_GROUP_ID typedef.
HTTPServer::CRequestQueue This class provides a RAII wrapper for the SDK request
queue handle.
HTTPServer::CServer This class provides a actual class you
can use in your client code to implement the HTTP server in your application.
CppConcurrency::CFunctionWrapper This class is used by the CThreadPool framework
to implement its internal queue using std::packaged_task<>.
CppConcurrency::CThreadPool
This class provides a thread pool framework written using just pure C++ 17 code.
CSecHandle This class provides a simple RAII wrapper for an SDK
SecHandle.
CCredHandle This class provides a simple RAII wrapper
for an SDK CredHandle.
Features |
Usage |
Copyright |
Class Framework Reference |
History |
Contacting the Author |
netsh http add urlacl
url=http://+:80/MyUri user=DOMAIN\user
netsh http show urlacl
The framework consists of the following classes:
HTTPServer::CAutoInit
HTTPServer::CSession
HTTPServer::CURLGroup
HTTPServer::CRequestQueue
HTTPServer::CServer
CppConcurrency::CFunctionWrapper
CppConcurrency::CThreadPool
This class provides a simple RAII wrapper for the SDK functions HttpInitialize and HttpTerminate. This class is implemented in the HTTPServer namespace. All applications which use the HTTP Server APIs need to call HttpInitialize on startup and HttpTerminate on shutdown.
Functions this class provides include:
CAutoInit();
Remarks
Standard class constructor
~CAutoInit();
Remarks
Standard class destructor. Internally this will call the HttpTerminate SDK API.
ULONG Init(ULONG nFlags = HTTP_INITIALIZE_SERVER);
Remarks
This method encapsulates the HttpInitialize SDK API.
This class provides a RAII wrapper for the SDK HTTP_SERVER_SESSION_ID typedef. This class is implemented in the HTTPServer namespace. The session represents a logical connection to the HTTP Server APIs from your application.
Functions this class provides include:
operator HTTP_SERVER_SESSION_ID
CSession();
Remarks
Standard class constructor.
~CSession();
Remarks
Standard class destructor. Internally this will call the Close method if the session object is currently valid.
ULONG Create();
Remarks
This method encapsulates the HttpCreateServerSession SDK API.
ULONG Close();
Remarks
This method encapsulates the HttpCloseServerSession SDK API.
ULONG SetProperty(HTTP_SERVER_PROPERTY Property, PVOID PropertyInformation, ULONG PropertyInformationLength);
Remarks
This method encapsulates the HttpSetServerSessionProperty SDK API.
ULONG QueryProperty(HTTP_SERVER_PROPERTY Property, PVOID PropertyInformation, ULONG PropertyInformationLength, PULONG ReturnLength);
Remarks
This method encapsulates the HttpSetServerSessionProperty SDK API.
CSession::operator HTTP_SERVER_SESSION_ID
operator HTTP_SERVER_SESSION_ID();
Remarks
This method provided access to the underlying HTTP_SERVER_SESSION_ID value which the CSession class encapsulates.
This class provides a RAII wrapper for the SDK HTTP_URL_GROUP_ID typedef. This class is implemented in the HTTPServer namespace. A URL Group represents a collection of URLs in the HTTP Server APIs.
Functions this class provides include:
CURLGroup();
Remarks
Standard class constructor.
~CURLGroup();
Remarks
Standard class destructor. Internally this will call the Close method if the URL Group object is currently valid.
ULONG Create(const CSession& session);
Remarks
This method encapsulates the HttpCreateUrlGroup SDK API.
ULONG Close();
Remarks
This method encapsulates the HttpCloseUrlGroup SDK API.
ULONG AddUrl(PCWSTR pFullyQualifiedUrl, HTTP_URL_CONTEXT UrlContext = 0);
Remarks
This method encapsulates the HttpAddUrlToUrlGroup SDK API.
ULONG RemoveUrl(PCWSTR pFullyQualifiedUrl, ULONG Flags);
Remarks
This method encapsulates the HttpRemoveUrlFromUrlGroup SDK API.
ULONG SetProperty(HTTP_SERVER_PROPERTY Property, PVOID PropertyInformation, ULONG PropertyInformationLength);
Remarks
This method encapsulates the HttpSetUrlGroupProperty SDK API.
ULONG QueryProperty(HTTP_SERVER_PROPERTY Property, PVOID PropertyInformation, ULONG PropertyInformationLength, PULONG ReturnLength);
Remarks
This method encapsulates the HttpQueryUrlGroupProperty SDK API.
CURLGroup::operator HTTP_URL_GROUP_ID
operator HTTP_URL_GROUP_ID();
Remarks
This method provided access to the underlying HTTP_URL_GROUP_ID value which the CURLGroup class encapsulates.
This class provides a RAII wrapper for the SDK HTTP request queue handle. This class is implemented in the HTTPServer namespace. A request queue is the queue structure which client applications used to read HTTP requests from and deliver HTTP responses to.
Functions this class provides include:
CRequestQueue();
Remarks
Standard class constructor.
~CRequestQueue();
Remarks
Standard class destructor. Internally this will call the Close method if the request queue object is currently valid.
ULONG Create(PCWSTR Name = nullptr, PSECURITY_ATTRIBUTES SecurityAttributes = nullptr, ULONG Flags = 0);
Remarks
This method encapsulates the HttpCreateRequestQueue SDK API.
ULONG Close();
Remarks
This method encapsulates the HttpCloseRequestQueue SDK API.
ULONG SetProperty(HTTP_SERVER_PROPERTY Property, PVOID PropertyInformation, ULONG PropertyInformationLength);
Remarks
This method encapsulates the HttpSetRequestQueueProperty SDK API.
ULONG QueryProperty(HTTP_SERVER_PROPERTY Property, PVOID PropertyInformation, ULONG PropertyInformationLength, PULONG ReturnLength);
Remarks
This method encapsulates the HttpQueryRequestQueueProperty SDK API.
CRequestQueue::WaitForDemandStart
ULONG WaitForDemandStart(LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpWaitForDemandStart SDK API.
ULONG Shutdown();
Remarks
This method encapsulates the HttpShutdownRequestQueue SDK API.
CRequestQueue::ReceiveClientCertificate
ULONG ReceiveClientCertificate(HTTP_CONNECTION_ID ConnectionId, ULONG Flags, PHTTP_SSL_CLIENT_CERT_INFO SslClientCertInfo, ULONG SslClientCertInfoSize, PULONG BytesReceived, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpReceiveClientCertificate SDK API.
ULONG ReceiveRequest(HTTP_REQUEST_ID RequestId, ULONG Flags, PHTTP_REQUEST RequestBuffer, ULONG RequestBufferLength, PULONG BytesReturned, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpReceiveHttpRequest SDK API.
CRequestQueue::ReceiveRequestEntityBody
ULONG ReceiveRequestEntityBody(HTTP_REQUEST_ID RequestId, ULONG Flags, PVOID EntityBuffer, ULONG EntityBufferLength, PULONG BytesReturned, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpReceiveRequestEntityBody SDK API.
ULONG SendResponse(HTTP_REQUEST_ID RequestId, ULONG Flags, PHTTP_RESPONSE HttpResponse, PHTTP_CACHE_POLICY CachePolicy, PULONG BytesSent, LPOVERLAPPED Overlapped, PHTTP_LOG_DATA LogData);
Remarks
This method encapsulates the HttpSendHttpResponse SDK API.
ULONG SendEntityBody(HTTP_REQUEST_ID RequestId, ULONG Flags, USHORT EntityChunkCount, PHTTP_DATA_CHUNK EntityChunks, PULONG BytesSent, LPOVERLAPPED Overlapped, PHTTP_LOG_DATA LogData);
Remarks
This method encapsulates the HttpSendResponseEntityBody SDK API.
ULONG DeclarePush(HTTP_REQUEST_ID RequestId, HTTP_VERB Verb, PCWSTR Path, PCSTR Query, PHTTP_REQUEST_HEADERS Headers);
Remarks
This method encapsulates the HttpDeclarePush SDK API.
CRequestQueue::WaitForDisconnect
ULONG WaitForDisconnect(HTTP_CONNECTION_ID ConnectionId, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpWaitForDisconnect SDK API.
CRequestQueue::WaitForDisconnectEx
ULONG WaitForDisconnectEx(HTTP_CONNECTION_ID ConnectionId, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpWaitForDisconnectEx SDK API.
ULONG CancelRequest(HTTP_REQUEST_ID RequestId, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpCancelHttpRequest SDK API.
CRequestQueue::FlushResponseCache
ULONG FlushResponseCache(PCWSTR UrlPrefix, ULONG Flags, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpFlushResponseCache SDK API.
CRequestQueue::AddFragmentToCache
ULONG AddFragmentToCache(PCWSTR UrlPrefix, PHTTP_DATA_CHUNK DataChunk, PHTTP_CACHE_POLICY CachePolicy, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpAddFragmentToCache SDK API.
CRequestQueue::ReadFragmentFromCache
ULONG ReadFragmentFromCache(PCWSTR UrlPrefix, PHTTP_BYTE_RANGE ByteRange, PVOID Buffer, ULONG BufferLength, PULONG BytesRead, LPOVERLAPPED Overlapped);
Remarks
This method encapsulates the HttpReadFragmentFromCache SDK API.
operator HANDLE();
Remarks
This method provided access to the underlying HANDLE value which the CRequestQueue class encapsulates.
This class provides the main class which client applications use to implement the HTTP server functionality. This class is implemented in the HTTPServer namespace.
Functions this class provides include:
A number of other methods are provided in the CServer class. They are EqualsSplit, IsInQuotedString, HeaderTokenize, ParseFormVariables, DecodeUrlEncodedFormValue, UnquoteHeaderValue, ParseCookieFromCookieHeader, ParseBasicAuthorizationHeader, ParseAuthorizationHeader, ParseNegotiateAuthorizationHeader, ParseNTLMAuthorizationHeader, DetermineDigestAlgorithm, GenerateRandom, Hash, MD5, GenerateDigestNonce, GenerateDigestHA1, GenerateDigestHA2, GenerateDigestResponse, ParseDigestAuthorizationHeader, ParseWeekDay, ParseMonth, ParseDateHeader & GenerateDateHeader. All these methods support the various examples in the demo HTTP server included in the download. These methods may be useful for implementing your HTTP server.
CServer(size_t nThreadCount = 0, bool bPermitTaskStealing = true, bool bPaused = false, bool bPumpWindowsMessages = false);
Remarks
Standard class constructor.
Parameters
nThreadCount The number of threads to create in the thread pool. If you provide the value 0, then the value return from std::thread::hardware_concurrency is actually used.
bPermitTaskStealing Should worker threads in the thread pool by allowed to steal tasks from other worker threads.
bPaused Should the worker threads by created in an initially "paused" state.
bPumpWindowsMessages Should the worker threads pump windows messages.
~CServer();
Remarks
Standard class destructor. Internally this will call the Stop method.
HRESULT Start(const std::vector<std::wstring>& URLs);
Remarks
This method actually starts the web server waiting for HTTP requests. Note that internally this method will create a worker thread which calls ReceiveRequestThread to wait for HTTP requests and dispatch them to the thread pool.
Return Value
A standard HRESULT value.
Parameters
URLs The array of urls in HTTP Server UrlPrefix format to listen on
HRESULT Stop();
Remarks
Stops the web server. This is the corollary method to the Start method.
Return Value
A standard HRESULT value.
virtual void ReceiveRequestThread();
Remarks
This method is started up as a worker thread as part of the Start method. This thread reads HTTP requests from the HTTP Server response queue and dispatches these requests to the ProcessRequest method in the thread pool for processing.
virtual void ProcessRequest(std::shared_ptr<std::vector<BYTE>> request);
Remarks
This method is the callback method used to handle HTTP requests in the thread pool. The default implementation converts the "request" parameter to a HTTP_REQUEST pointer and calls the HandleRequest method.
virtual void HandleRequest(PHTTP_REQUEST pRequest);
Remarks
This method is the main virtual function which client application code can override to handle HTTP requests. The default implementation in CServer returns a simple GET static response for all GET requests and a 503 response for all other requests.
CServer::InitializeHTTPResponse
static void InitializeHTTPResponse(HTTP_RESPONSE& response, USHORT nStatus, PCSTR pszReason);
Remarks
This helper method sets the StatusCode, pReason and ReasonLength member variables of the "response" parameter. These three fields will need to be set for most HTTP responses from your web server.
Parameters
response The response structure to update.
nStatus The HTTP status code to set into "response" e.g. 200, 404 or 503.
pszReason The Reason text to set into "response" e.g. "OK", "File Not Found" or "Not Implemented".
static void AddKnownHeader(HTTP_RESPONSE& response, int nHeaderId, PCSTR pszValue);
Remarks
This helper method adds a "Known" HTTP header into the "KnownHeaders" member variable of the "response" parameter. These three fields will need to be set for most HTTP responses from your web server.
Parameters
response The response structure to update.
nStatus The header identifier to update. Values to use for this parameter are taken from the SDK HTTP_HEADER_ID enum. An example would be HttpHeaderContentLength.
pszValue The actual text to associate with the header.
static void SendResponse(HTTP_REQUEST_ID RequestID, USHORT nStatusCode, PCSTR pReason, PCSTR pContentType, const void* pEntity, ULONG nEntityLength);
static void SendResponse(HTTP_RESPONSE& response, HTTP_REQUEST_ID RequestID, USHORT nStatusCode, PCSTR pReason, PCSTR pContentType, const void* pEntity, ULONG nEntityLength);
Remarks
This helper method returns a HTTP response using just the supplied function parameters.
Parameters
response The response structure to use or the response.
RequestID The ID for the request which this response corresponds to.
nStatusCode The HTTP status code to send e.g. 200, 404 or 503.
pReason The Reason text to set into "response" e.g. "OK", "File Not Found" or "Not Implemented".
pContentType The HTTP content-type header to send.
pEntity The body of the HTTP response to send.
nEntityLength The length of "pEntity" in bytes.
This class is used internally by the thread pool class to allow std::packaged_task which is used by the thread pool queues to handle move-only types. This class is implemented in the CppConcurrency namespace. This is based on the function_wrapper class from the Advanced Thread Management Chapter in the book Concurrency in Action by Anthony Williams.
This class provides the thread pool functionality used by the CServer class. It provides a generic thread pool implementation in pure C++ 17 code. It implements optional per thread queues, task stealing, unbounded and bounded synchronized queues, pumping the Windows message queue in the worker threads, custom worker thread initialization and cleanup, dynamic thread count resizing, "pausing" the thread pool and submitting tasks to a specific thread in the thread pool. This class is implemented in the CppConcurrency namespace.
Functions this class provides include:
CThreadPool(size_t threadCount = 0, bool permitTaskStealing = true, bool paused = false, bool pumpWindowsMessages = false);
Remarks
Standard class constructor.
Parameters
threadCount The number of threads to create in the thread pool. If you provide the value 0, then the value return from std::thread::hardware_concurrency is actually used.
permitTaskStealing Should worker threads in the thread pool by allowed to steal tasks from other worker threads.
paused Should the worker threads by created in an initially "paused" state.
pumpWindowsMessages Should the worker threads pump windows messages. Note this parameter is only available when compiled on Windows.
~CThreadPool();
Remarks
Standard class destructor. Internally this will call the Stop method.
virtual void WorkerThread(size_t threadIndex);
Remarks
This method is the actual worker thread created by the thread pool. Internally this method waits for tasks on the local queue for this thread, the global queue, or a task is available to be stolen from another thread in the thread pool. It then calls the task before looping around to wait for more tasks. It calls InitInstanceThread as part of its startup and ExitInstanceThread prior to finishing. The method is marked virtual to allow it to be customized by derived classes if required.
Parameters
threadIndex The index of this thread in the thread pool. This is used to work our which local queue this worker thread should be using.
CThreadPool::InitInstanceThread
virtual bool InitInstanceThread(size_t threadIndex);
Remarks
This method is called during the startup of the worker thread. You can override this method in your derived class to implement custom worker thread initialization.
Return Value
Returning false from this method will cause the worker thread to fail startup. The default implementation in this class simply returns true.
Parameters
threadIndex The index of this thread in the thread pool.
CThreadPool::ExitInstanceThread
virtual void InitInstanceThread(size_t threadIndex);
Remarks
This method is called during the termination of the worker thread. You can override this method in your derived class to implement custom worker thread cleanup. The default implementation in this class does nothing.
Parameters
threadIndex The index of this thread in the thread pool.
size_t size() const;
Return Value
Returns how many threads are in the thread pool.
CThreadPool::pumpWindowsMessages
bool pumpWindowsMessages() const;
Remarks
This method is only available when compiled on Windows.
Return Value
Returns true if the worker threads will be pumping windows messages otherwise false.
std::thread& get_thread(size_t threadIndex) const;
Parameters
threadIndex The index of the thread in the thread pool.
Return Value
Provides access to a specific thread in the thread pool.
size_t queueSize() const;
Return Value
Returns how many items are on the main thread pool queue.
size_t threadQueueSize(size_t threadIndex) const;
Parameters
threadIndex The index of the thread in the thread pool.
Return Value
Returns how many items are on a specific thread's local queue.
size_t threadsBusy() const;
Return Value
Returns how many threads are busy executing tasks in the thread pool.
bool paused() const;
Return Value
Returns true if the thread pool is currently "paused" or false otherwise. When a thread pool is paused no tasks will be processed by its worker threads even though task submission can continue.
void pause(bool pause);
Remarks
Sets the "pause" state of the thread pool. When a thread pool is "paused" no tasks will be processed by its worker threads even though task submission can continue.
Parameters
pause true to "pause" the thread pool and false to "unpause" the thread pool.
void clear();
Remarks
Clears down the main thread pool queue and all threads local queues. Note this does not affect the count of threads in the thread pool or any currently running tasks.
void stop();
Remarks
Shuts down the threadpool. This will undo all the work which the constructor and start method did. Called in the destructor if you do not explicitly call it yourself.
void stop();
Remarks
Starts the threadpool.
void resize(size_t threadCount);
Remarks
Changes the number of threads in the thread pool. Note this method should only be called on the same thread on which the CThreadPool instance was created.
Parameters
threadCount The number of threads you want in the thread pool.
template<typename Func, typename... Args>
auto submit(Func&&
f, Args&&... args) -> std::future<typename
std::invoke_result_t<Func, Args...>>
template<typename Func, typename... Args>
auto submit(size_t
threadIndex, Func&& f, Args&&...
args) -> std::future<typename std::invoke_result_t<Func, Args...>>
Remarks
Submit a task to be processed in the thread pool. The override of submit which takes a "threadIndex" allows a task to be sent to a specific thread in the thread pool. These methods implement an unbounded queue size. This means that if you submit tasks quicker than they are processed in the thread pool, the memory usage will grow unbounded. If you want to implement a bounded queue then you can use blocked_submit methods.
Parameters
f The first variable argument to the thing / callable you want to execute in the thread pool.
args The remaining arguments to the thing / callable you want to execute in the thread pool.
threadIndex The thread index of the specific thread on which you want the task executed.
Return Value
A std::future wrapper for the callable which you can use to wait on a task to be processed.
template<typename Func, typename... Args>
auto blocked_submit(size_t
maxQueuSize, Func&& f, Args&&...
args) -> std::future<typename std::invoke_result_t<Func,
Args...>>
template<typename Func, typename... Args>
auto blocked_submit(size_t
maxQueueSize, size_t threadIndex, Func&&
f, Args&&... args) -> std::future<typename
std::invoke_result_t<Func, Args...>>
Remarks
Submit a task to be processed in the thread pool. If the queue size in the thread pool is already "maxQueueSize", then this method will block until the queue size reduces. The override of submit which takes a "threadIndex" allows a task to be sent to a specific thread in the thread pool. These methods allow you to implement a bounded queue size instead of the unbounded queue which would result if you used the submit methods.
Parameters
maxQueueSize The maximum items which can be put on the queue before blocking will occur.
f The first variable argument to the thing / callable you want to execute in the thread pool.
args The remaining arguments to the thing / callable you want to execute in the thread pool.
threadIndex The thread index of the specific thread on which you want the task executed.
Return Value
A std::future wrapper for the callable which you can use to wait on a task to be processed.
CThreadPool::WaitForInitInstanceThreads
bool WaitForInitInstanceThreads() const;
Remarks
Waits for the all threads in the thread pool to have called InitInstanceThread. This allows client code to check the initialization status of threads in the thread pool and act upon that result.
Return Value
true if all threads returned true from their InitInstanceThread method, otherwise false.
v1.12 (18 May 2023)
v1.11 (21 March 2023)
v1.10 (13 February 2022)
v1.09 (23 January 2022)
v1.08 (29 June 2021)
v1.07 (16 April 2021)
v1.06 (15 April 2021)
v1.05 (22 January 2021)
v1.04 (27 December 2020)
v1.03 (23 December 2020)
v1.02 (22 December 2020)
v1.01 (21 December 2020)
v1.0 (8 November 2020)
PJ Naughter
Email: pjna@naughter.com
Web: http://www.naughter.com
18 May 2023