Introduction
This article will explain how to integrate an embedded webserver with your Win32/C++ application, using the Lacewing networking library. It could be used for things like:
- Web administration - Server applications often have a web interface, for remote administration - nothing but a web browser is required to administrate the server
- Web applications - Web applications written in C++ can be much faster and more scalable than PHP or ASP, with lower resource usage and nothing else to be installed
Background
I decided to write my own webserver class as an experiment over the Lacewing::Server
class, which is designed for scalability using IO completion ports, epoll or kqueue.
Lacewing::Webserver
soon became a powerful HTTP server, featuring things like:
- HTTP
GET
/POST
with automatic parameter parsing - Non-blocking file sending
- Cookies and sessions
- Multipart file upload
- Getting/setting the last modified date, to easily implement caching
- Full HTTPS support
Installing the Library
Lacewing runs on both Windows and many flavours of *nix (including Linux, BSD, Mac OS X and even Android). This article, however, was originally written for the Windows version - the code for other platforms is identical, but the installation procedure will be different.
For Windows, the first thing is you'll need to download and install the Lacewing Win32 binaries from the liblacewing website. The Lacewing.dll file will need to be distributed alongside your executable file, but you may put it in your Windows\System32 folder (or Windows\SysWOW64 on a 64 bit system) for development convenience if you want.
Put the path to the folder with Lacewing.h and Lacewing.lib in your VC++ directories, in both the header and library sections. This will be under Tools > Options > Projects and Solutions > VC++ directories
in Visual C++ 2008. Once the path is added, you should be able to #include <Lacewing.h>
in your code, which will automatically link with Lacewing.lib.
Using the Class
For any class in Lacewing to function, you'll first need an instance of Lacewing::EventPump
, which is responsible for watching for events and "pumping" them to the interested classes.
You can then create an instance of Lacewing::Webserver
, passing the event pump to the constructor.
Lacewing::EventPump EventPump;
Lacewing::Webserver Webserver (EventPump);
This is already ready to host, and would show a blank page. To make it show something interesting, you'll need to register some handlers. We're only interested in responding to GET
for now, but there are more handlers available for things like HEAD
, POST
, error handling and multipart file upload - see the Lacewing::Webserver<code>
class definition in Lacewing.h for the full list.
An example GET
handler looks like this:
void onGet (Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request)
{
Request << "Welcome to " << Request.URL ();
}
This will output the text "Welcome to
" followed by the requested URL. To register it with Lacewing so that it will be called when a GET
request comes through, simply pass it to the onGet
method of your Webserver
instance.
When all the initialisation is complete, Host()
can be called to start the webserver. If no parameters are passed, it defaults to port 80. You might want to use Host(81)
or something similar if port 80 is taken on your system.
Webserver.onGet (onGet);
Webserver.Host ();
And that's it - providing the Webserver
object stays in scope, you should be able to open a web browser and visit http://localhost/home to be greeted with:
If you ran the webserver on a port other than 80, you'll need to visit something like http://localhost:82/home.
Working with Files
The Request
object passed to the GET
handler provides two functions to make it simple to send files from the disk:
SendFile
- Sends a file by filename. The file will not be loaded into memory (TransmitFile
is used on Windows, sendfile
on *nix) GuessMimeType
- Sets an appropriate MIME type for a file extension or filename. For example, a htm
file will set the MIME type text/html
, while a png
file will set the MIME type image/png
.
We can easily use these to build a simple webserver, hosting the current directory.
void onGet(Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request)
{
Request.GuessMimeType(Request.URL());
Request.SendFile(Request.URL());
}
Note that nothing is actually sent until after the handler is complete, so it wouldn't matter if the GuessMimeType
and SendFile
functions were called the other way around.
All the functions in Lacewing return instantly, and you can do things like set cookies and change HTTP headers right until the handler returns.
With this new GET
handler calling the two functions, it's now possible to put an entire website with HTML/CSS/JS in the same folder as the webserver and access it from a web browser. This can be combined with custom behaviour in C++ to do things like C++ based AJAX applications (which is a pretty awesome idea). For now, I just tried it with an image:
Setting Up HTTPS
It might surprise you that Lacewing (on Windows) doesn't use OpenSSL or any other library to implement HTTPS. Like the rest of the library, it's built on the APIs provided by the operating system - Windows provides functions for secure sockets, as used by Internet Explorer and IIS. The API isn't very pleasant to use (start here if you're interested), but Lacewing wraps it nicely in Lacewing::Webserver
into these functions:
HostSecure
- Like the Host
function, but for HTTPS rather than HTTP. After this has been called, the handlers will be called as normal for HTTPS requests, but Request.Secure()
will return true
. Either LoadCertificateFile
or LoadSystemCertificate
must be called before this. LoadCertificateFile
- Loads a certificate from a certificate file (CER or PFX). Takes two parameters - the first is the filename of the certificate file, and the second is the common name used when creating the certificate. *nix OR Windows Vista and greater only. LoadSystemCertificate
- Loads a certificate that has been imported into the system, (import it by opening the certificate file and following the wizard). Takes three parameters - the first is the store name (the predefined system stores are "MY", "Root", "Trust" and "CA"), the second is the common name used when creating the certificate, and the third (optionally) is which certificate store to look in, the default being "CurrentUser". More information about the system certificate stores can be found here. Windows XP and greater only.
You will need to obtain a certificate from a signing authority, or generate a self-signed certificate for testing. Doing this is beyond the scope of this article, but there's a nice tutorial on generating self-signed certificates with OpenSSL here.
My test certificate with the common name "localhost" has already been imported, so now I can change my initialization code to this:
Webserver.onGet(onGet);
Webserver.Host();
Webserver.LoadSystemCertificate("MY", "localhost");
Webserver.HostSecure();
And now if I visit https://localhost/lacewing_02.png...
Forms
Lacewing::Webserver
has full support for two kinds of request parameters:
- GET parameters are added as part of the URL. If you were to load http://localhost/home?foo=bar, you could then use
Request.GET("foo")
in the GET
handler, which would return "bar
". Multiple parameters can be used in the form http://localhost/home?foo=bar&foo2=bar2. These can be added manually by the user, via links, or via HTML forms with method="GET"
. - POST parameters are the body of the HTTP request, rather than part of the URL. They are added by HTML forms with
method="POST"
, and can be retrieved in the POST
handler with Request.POST("name")
.
GET
parameters are limited by the URL length, and GET
requests can be subject to browser caching. The general rule for deciding whether to use GET
or POST
is that GET
is for specifying parameters for reading data, and POST
is for specifying parameters for writing data.
Both GET
and POST
parameters can be used from JavaScript. I highly recommend jQuery, which I have used extensively for web applications with Lacewing::Webserver
as a backend. The stream <<
operator in the Request
object makes it very easy to generate JSON, which translates directly into Javascript objects.
I won't be covering anything like that in this article, but there is an example available in the library examples folder on GitHub. For a simple HTML form test to try out the POST
functionality, you will first need a POST
handler in addition to the GET
handler:
void onGet(Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request)
{
if(!strcmp(Request.URL(), "form"))
{
Request.SendFile("form.html");
return;
}
Request << "Page not found";
}
void onPost(Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request)
{
if(!strcmp(Request.URL(), "submit"))
{
Request << "Hello, " << Request.POST("name") << "!";
return;
}
Request << "Page not found";
}
The POST
handler can be registered alongside the GET
handler.
Webserver.onGet (onGet);
Webserver.onPost (onPost);
You'll also need this form.html file:
<form action="submit" method="post">
Enter your name: <input name="name">
<input type="submit" value="Go!">
</form>
And that's it! If you run the application, you should be able to visit http://localhost/form:
And submit...
Conclusion
Although I didn't cover every aspect of Lacewing::Webserver
, I hope the article was a useful introduction. The rest of the functions are quite straightforward and consistently named, so it should be pretty easy to find your way around.
If you have any questions or suggestions, please do leave a comment or send me an email. The library is also now open source on GitHub with an issue tracker.
History
- 3rd October, 2010: Initial post
- 16th July, 2011: Updated with the new open source API