HTTPServerWrappers and
CPPCThreadPool v1.12 A set of C++ classes
to encapsulate the HTTP server APIs on Windows and a pure C++ 17 Thread Pool
implementation.
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
- Encapsulates all the
HTTP Server v2.0 functionality in a "HTTPServer" C++ namespace
and is implemented in the HTTPServerWrappers.h header file.
- Allows all the complicated HTTP server functionality to be customized using
simple virtual methods in the HTTPServer::CServer class.
- Provides a complete thread pool implementation using standard C++. This
CppConcurrency::CThreadPool class is based on the thread pool implementation in
the Advanced Thread Management Chapter in the book
Concurrency in Action by Anthony Williams. The code also draws inspiration
from https://github.com/dabbertorres/ThreadPool,
https://github.com/f-squirrel/thread_pool & http://roar11.com/2016/01/a-platform-independent-thread-pool-using-c14/.
- The thread pool is implemented in the CPPCThreadPool.h header file and
CPPCThreadPoolTLS.cpp source file in just
over 600 lines of code. It demonstrates a lot of C++ features such as: std::thread,
std::thread::hardware_concurrency, std::unique_ptr, std::make_unique, std::shared_ptr,
std::make_shared, std::move, r-value references, std::atomic, std::mutex, std::condition_variable,
std::vector, std::queue, std::dequeue, thread local storage, std::unique_lock,
std::lock_guard, std::this_thread::yield, std::for_each, std::generate_n, std::back_inserter,
std::future, std::bind, std::invoke_result_t & std::packaged_task.
- The thread pool implements optional per thread queues and task stealing,
both unbounded and bounded synchronized queues and submitting tasks to a specific thread in the thread
pool.
- The thread pool implements waitable tasks using std::future and
std::packaged_task.
- The thread pool also supports optionally pumping the Windows message
queue in the worker threads when compiled for Windows, custom worker thread
initialization and cleanup and changing the number of threads in the thread
pool dynamically.
- A complete suite of unit tests are included in the demo application to
test the thread pool classes.
- Unicode enabled and all code compiles cleanly at warning level 4.
- The code is /analyze clean and has been compiled using Clang-Tidy.
- Please note that unlike W3MFC, the HTTPServerWrappers classes does not provide
prebuilt ISAPI or CGI functionality. For this you will need to
build your own functionality on top of the HTTPServerWrappers classes.
- The sample application included in the download includes examples on handling
a simple GET request example from in-memory, a GET directory system listing
example, a GET example which returns a response from a file on the file
system, a POST request which simply echoes back the received form request parameters,
Basic authentication, NTLM authentication. Negotiate (i.e. Kerberos / NTLM)
authentication, cookie authentication and Digest (RFC 7616) authentication.
The enclosed zip file contains
the HTTPServerWrappers and CPPThreadPool
source code and a VC 2019 console based application with demo implementation of
a HTTP server.
Copyright
- You are allowed to include the source code in any product (commercial, shareware,
freeware or otherwise) when your product is released in binary form.
- You are allowed to modify the source code in any way you want except you
cannot modify the copyright details at the top of each module.
- If you want to distribute source code with your application, then you are
only allowed to distribute versions released by the author. This is to maintain
a single distribution point for the source code.
Usage
- To use the thread pool classes, simply #include "CPPCThreadPool.h"
in the module you want and instantiate a CThreadPool
instance and uses its various methods. You also need to add
"CPPCThreadPoolTLS.cpp" source file to your project.
- The HTTPServer class framework uses the Windows HTTP Server APIs built into
Windows. Because this web server is implemented in HTTP.sys, a kernel component
of Windows, it is a shared resource amongst all running applications. To allow
multiple applications to share this resource, you first need to reserve the
URL which you want your web server application to use. This allows multiple
applications to operate as web servers at the same time on your computer. This
reservation process can be done programmatically using the
HttpSetServiceConfiguration API or using the Windows command line utility
netsh.
An example netsh command line would be:
netsh http add urlacl
url=http://+:80/MyUri user=DOMAIN\user
This would setup a reservation
for a web server listening on port 80 over HTTP and responding to any requests
which include /MyUri in its HTTP request. The "user" part of the command
line is used to limit what specific Windows accounts can actually open the HTTP
request queue associated with this URL reservation. The application which houses your web
server must be running under this account to allow it to open the request queue
successfully. To list all the current URL reservations setup on your computer
you would use:
netsh http show urlacl
Normally this
reservation process would be done at installation time of your Windows application.
Please see
https://docs.microsoft.com/en-us/windows/win32/http/routing-incoming-requests
and the
netsh
documentation for further details on the reservation process.
- To use the web server framework classes simply #include "HTTPServerWrappers.h"
in the module you want and implement a class derived from
HTTPServer::CServer and override its HandleRequest
method to customize the HTTP responses your web server should return.
- Once you have your reservation made, you should then be able to start your
web server by passing the string "http://+:80/MyUri" as one of the
values to the HTTPServer::CServer::Start method.
You can provide multiple URLs to the Start method if you want to handle multiple
URLs from your application. The sample application included in the download
passes the command line parameters it receives to this method and waits for
a keypress to shutdown the application.
- Note that the code has been developed using Visual Studio 2019 and requires
C++ 17 compilation mode and probably will not compile on earlier versions of
Visual Studio.
- The thread pool code has been developed in standard C++ 17 and should be compilable
on non-Windows platforms although this has not been explicitly tested.
Updates
v1.12 (18 May 2023)
- Updated modules to indicate that it needs to be compiled using
/std:c++17. Thanks to Martin Richter for reporting this issue.
v1.11 (21 March 2023)
- Optimized code to emplace_back call in resize method
- Updated copyright details.
- The thread pool now does not start by default when you call the CThreadPool
constructor. Instead now client code is expected to call a new "Start"
method. This breaking change was implemented to allow various virtual functions
in the CThreadPool framework to work correctly as calling virtual functions
is of course not supported from C++ constructors.
v1.10 (13 February 2022)
- Updated the code to use C++ uniform initialization for all variable
declarations.
v1.09 (23 January 2022)
- Updated copyright details.
- Fixed more static code analysis warnings in Visual Studio 2022.
v1.08 (29 June 2021)
- Fixed a bug in CServer::SendResponse where an entity chunk would be
added to the response even when the "nEntityLength" length parameter was 0.
Thanks to David Conalty for reporting this issue.
v1.07 (16 April 2021)
- CServer::Stop now shuts down the thread pool before it closes the
request queue. This reordering of operations can avoid asserts from
happening in the thread pool when the request queue has been closed.
v1.06 (15 April 2021)
- Added new CPPCThreadPoolTLS.cpp source file to the CPPCThreadPool
framework. This is required to avoid linker errors where the two
thread_local member variables declared in CPPCThreadPool.h
(CThreadPool::_LocalQueue & CThreadPool::_ThreadIndex) can be defined in
multiple compilation units if the CPPCThreadPool.h header is included by
your source code multiple times.
- Reworked CServer class to be template based. This allows the
CppConcurrency::CThreadPool instance to be customized at runtime.
- Fixed a bug in the ParseFormVariables methods where the entity body
would not be parsed correctly if "request.EntityChunkCount is > 1".
v1.05 (22 January 2021)
- Updated copyright details.
- Reimplemented HandleFileRequest method in the sample app with a new
CServer::SendFileResponse method which provides new resuable functionality
to return the full contents of a specific file in a HTTP response.
v1.04 (27 December 2020)
- Added a new CServer::ParseDateHeader method to aid decoding HTTP
If-Modified-Since headers.
- Updated the sample HTTP server to support handling If-Modified-Since
headers for the sample file request. These are known as Conditional get
requests in the HTTP RFC.
v1.03 (23 December 2020)
- Updated the sample app's cookie authentication example to use
SHA256 hashing instead of MD5. In addition the password hashing now uses 16
bytes of salt to increase the cryptographic robustness.
- Added support for new HttpDelegateRequestEx API available in
latest Windows 10 SDK.
v1.02 (22 December 2020)
- Added new CServer::ParseFormVariables methods to aid parsing
application/x-www-form-urlencoded encoded form variables.
- Added new CServer::DecodeUrlEncodedFormValue methods to aid decoding
application/x-www-form-urlencoded encoded form variables.
- Optimized the code in the sample HTTP server to minimize the time a lock
is taken on the session cache in various locations.
- Removed the use of the explicit ATL namespace in the sample HTTP server
so that the MFC CString classes could be used if so desired.
- Updated the included sample HTTP server to implement a fully
worked cookie authentication example.
v1.01 (21 December 2020)
- Fixed a bug in CThreadPool::OtherQueuesAreEmpty where the return
value was being calculated incorrectly. This bug was causing the threads in
the thread pool to do busy waits instead of waiting correctly on the
"_TasksAvailable" condition variable.
- Fixed an issue in the CThreadPool constructor where a lock was not
taken prior to modifying the _Dones array.
- Fixed an issue in CThreadPool::pause where a lock was not taken prior to
modifying the _Paused variable.
- Fixed an issue in CThreadPool::resize where a lock was not taken
prior to modifying the _Dones array.
- Added a new override of the CServer::SendResponse method which allows an
already initialized HTTP_RESPONSE structure to be provided as a parameter.
- Provided a new CServer::EqualsSplit method to aid parsing HTTP
header values.
- Provided a new CServer::IsInQuotedString method to aid parsing
HTTP header values.
- Provided a new CServer::HeaderTokenize method to aid parsing HTTP
header values.
- Provided a new CServer::UnquoteHeaderValue method to aid parsing
HTTP header values.
- Provided a new CServer::ParseCookieFromCookieHeader method to aid
parsing HTTP cookies.
- Provided a new CServer::ParseBasicAuthorizationHeader method to
aid parsing Basic authentication headers.
- Provided a new CServer::ParseAuthorizationHeader method to aid
parsing NTLM and Negotiate authentication headers.
- Provided a new CServer::ParseNegotiateAuthorizationHeader method
to aid parsing Negotiate authentication headers.
- Provided a new CServer::ParseNTLMAuthorizationHeader method to aid
parsing NTLM authentication headers.
- Provided a new CServer::DetermineDigestAlgorithm to aid parsing
Digest authentication headers.
- Provided a new CServer::GenerateRandom method to aid generating
random data for Digest authentication headers.
- Provided a new CServer::Hash method to aid generating
cryptographic hashes data for Digest authentication headers.
- Provided new CServer::MD5 methods to aid generating MD5 hashes
data for Digest authentication headers.
- Provided a new CServer::GenerateDigestNonce method to aid
generating cryptographic nonces for Digest authentication headers.
- Provided a new CServer::GenerateDigestHA1 method to aid validating
Digest authentication headers.
- Provided a new CServer::GenerateDigestHA2 method to aid validating
Digest authentication headers.
- Provided a new CServer::GenerateDigestResponse method to aid
validating Digest authentication headers.
- Provided a new CServer::ParseDigestAuthorizationHeader method to
aid parsing Digest authentication headers.
- Provided a new CCredHandle RAII class in CCredHandle.h to
encapsulate an SDK CredHandle handle value.
- Provided a new CSecHandle RAII class in CSecHandle.h to
encapsulate an SDK SecHandle handle value.
- Updated the existing basic authentication code in the included
sample HTTP server to log important data to the console.
- Updated the included sample HTTP server to implement a fully
worked NTLM authentication example.
- Updated the included sample HTTP server to implement a fully
worked Negotiate (i.e. NTLM / Kerberbos) authentication example.
- Updated the included sample HTTP server to implement a fully
worked Digest (RFC 7616) authentication example. Please note that the sample
code only supports the digest authentication logic supported by the most
common web browsers (i.e. Internet Explorer, Firefox, Chrome and Edge). What
this means is that only MD5 and MD5-sess is supported and features such as
auth-int, SHA-256, SHA-256-sess, SHA-512-256, SHA-512-256-sess, username*,
stale & userhash are not implemented.
v1.0 (8 November 2020)