Building a Website with C++

[Today’s random Sourcerer profile: https://sourcerer.io/bitfyre]

Is This Even Possible?

Yes.

I know it sounds strange, and perhaps even an exercise in novel futility, but it isn’t.

In this article, I’ll explain how you can use C++ to develop a website and some concrete reasons why you might consider doing so.

While this will be fun, it can also be entirely practical and incredibly useful.

Hosting Environment

You might think that such an interesting configuration would only be possible in a dedicated environment, but this generally isn’t the case. While a dedicated or virtual dedicated server would be ideal for most sites (not just with C++ sites), you can, in most cases, use C++ with shared hosting.

Any web host that supports CGI (which is likely all of them) will also support websites built with C++. Depending on the provider, you may or may not be able to compile your site locally, or may have to have compilers enabled for your account. Check with them if you plan on editing and compiling via SSH on the web server itself.

A Simple Example

In the examples I’ll be using, I will assume a shared cPanel web hosting account. They are easily obtainable, inexpensive, and consistent. You can adapt these techniques quite easily to a virtual or dedicated server or Amazon EC2 instance with a few simple modifications to the Apache configuration.

cPanel provides a cgi-bin folder for us, but we’re not going to use it. There’s no need. In most situations, any file with a .cgi extension will be automatically processed if it has the correct permissions (usually 0755). Here are the essential files (be sure to use TAB in the Makefile).

Makefile:

all:
g++ -O3 -s hello.cpp -o hello.cgi
clean:
rm -f hello.cgi

hello.cpp:

#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
void set_content_type(string content_type) {
cout << “Content-type: “ << content_type << “\r\n\r\n”;
}
void set_page_title(string title) {
cout << “<title>” << title << “</title>\n”;
}
void h1_text(string text) {
cout << text << “\n”;
}
int main() {
set_content_type(“text/html”);
  // Output HTML boilerplate
cout << “<!doctype html>\n”;
cout << “<html lang=\”en\”>\n”;
cout << “<head>\n”;
set_page_title(“Hello, World!”);
cout << “</head>\n”;
cout << “<body>\n”;
h1_text(“Hello, World!”);
cout << “</body>\n”;
cout << “</html>”;
  return 0;
}

If you have compilers enabled on your account (you may need to ask your web hosting support team about this), simply SSH into your account, put these files in your public_html folder, then run:

make

A hello.cgi file will be produced. If you navigate to the file in your browser, like this:

http://your-test-site.com/hello.cgi

Replace your-test-site.com with your domain name or hosted URL. You should see “Hello World” on the screen.

Before we dive into the code itself, let’s review the process of how this works via the web server. When Apache receives a request, it first looks for an internal handler or rewrite rule, then checks for a file matching the request on disk. In our case, it finds hello.cgi and executes it. Our program takes no input and simply outputs a hello world message. Apache then takes this content and returns it to the user.

As for the code itself, it is worth mentioning that it could be simpler. I didn’t have to include the separate functions set_content_type, set_page_title, and h1_text. These are simply helpers that keep the main function cleaner. You could simply output all of this in the main function and it would work just the same.

But I’m hoping you see the advantage in defining these functions. If you were to create a function for each common HTML element, you could use code like this to create a very clean response directly in your C++ program:

void p(string text) {
cout << “<p>” << text << “</p>\n”;
}

Then you could use something like:

p(“This would be paragraph text.”);

to output a paragraph.

You could even expand on this idea to have the helper functions like p, h1_text, etc., return text instead of directly output to standard out via cout. With this, you could build a templating system or nest responses to create quite complex pages with very streamlined and efficient C++ code.

This example is as bare-bones and bare-metal as it gets. You have complete access to every response header, giving you extreme control over the response cycle.

Look at All the Input

Our example doesn’t take any input in any meaningful way. It simply returns “Hello, World”. But on each request, Apache sends a tremendous amount of information to the program via environment variables. You can use the getenv() function in the C standard library to obtain these values (don’t forget to add “#include <stdlib.h> at the top of your file”).

For example, if you wanted to know the full request URI, you’d use:

string request_uri = getenv(“REQUEST_URI”);

to obtain that value. Other useful variables are:

· REMOTE_ADDR — Obtain the visitor’s IP address

· REQUEST_METHOD — Returns the method (i.e. GET, POST, etc.)

· DOCUMENT_ROOT — The root of the website (usually ~/public_html on shared systems, or /var/www/html on virtual/dedicated servers).

· QUERY_STRING — The GET query string. You can use this to retrieve GET variables.

A More Robust Example

Parsing GET variables manually is certainly possible, and processing POST variables can be done by checking the standard input. You can even get and set cookies by altering the request and response headers. But both approaches are tedious.

You could write your own wrappers, or, you could use the ready-made GNU cgicc library. It contains helper functions to modify HTML and process forms. For a large project, using a library like this would be a huge time saver.

On Debian and Ubuntu, you can install the library and headers with:

apt install libcgicc5 libcgicc5-dev

But on CentOS / RHEL, there are no native packages. To install on those, run:

cd /usr/local/src
wget ftp://ftp.gnu.org/gnu/cgicc/cgicc-3.2.19.tar.gz
tar xfz cgicc*.tar.gz
cd cgicc*
./configure — prefix=/usr
make
make install

NOTE: 3.2.19 was the most recent version at time of writing, but you may want to check for a newer copy at ftp://ftp.gnu.org/gnu/cgicc/. I also used /usr as the prefix to avoid library linking issues. Feel free to change this if you like.

Once cgicc is installed, you can compile against it. Try this example that accepts input from a form and displays it in the browser.

Makefile:

all:
g++ -O3 -s hello.cpp -o hello.cgi
g++ -O3 -s cgicc.cpp -o cgicc.cgi /usr/lib/libcgicc.a
clean:
rm -f hello.cgi cgicc.cgi

cgicc.html:

<!doctype html>
<html lang="en">
<head>
<title>cgicc Test</title>
</head>
<body>
<form method="POST" action="cgicc.cgi">
<label for="name">Name</label>
<input name="name" type="text" value="">
<input name="submit" type="submit" value="Submit">
</form>
</body>
</html>

cgicc.cpp:

#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
void set_content_type(string content_type) {
cout << “Content-type: “ << content_type << “\r\n\r\n”;
}
void set_page_title(string title) {
cout << “<title>” << title << “</title>\n”;
}
void h1_text(string text) {
cout << text << “\n”;
}
int main() {
Cgicc cgi;
string name;
  set_content_type(“text/html”);
  cout << “<!doctype html>\n”;
cout << “<html lang=\”en\”>\n”;
cout << “<head>\n”;
set_page_title(“cgicc Test”);
cout << “</head>\n”;
cout << “<body>\n”;
cout << “<p>”;
  // Grab the “name” variable from the form
name = cgi(“name”);
// Check to make sure it isn’t empty.
if (!name.empty()) {
cout << “Name is “ << name << “\n”;
} else {
cout << “Name was not provided.”;
}
  cout << “</p>\n”;
cout << “</body>\n”;
cout << “</html>”;
  return 0;
}

You may notice that I statically link the cgicc library in the Makefile. While not necessary (you could substitute this with -lcgicc), I prefer to statically link binaries I’m sending to a server so that everything necessary to execute the program is bundled with it.

In this example, the cgicc library does the heavy lifting of parsing the POST variables and returning the “name” field for us.

I didn’t escape the input POST variable in this example. For a production site, you should, especially if you’re interacting with a database.

This is just the tip of the iceberg of cgicc’s capabilities. You can read the full documentation here:

https://www.gnu.org/software/cgicc/doc/index.html

Performance

C++ is amazingly fast if you write good code. The CGI interface does slow things down a bit, but even with that, you’ll still achieve better performance than interpreted languages like PHP.

That said, there is always room for improvement. On a dedicated or virtual environment, you could use Apache or Nginx’s FastCGI support to reduce the small (and not noticeable unless server is heavily loaded) delay while the program is loaded. In my tests, I never experienced a slowdown, but a website with heavy traffic will want to examine these solutions to even further increase performance.

Extra Credit

It would be easy to wrap a C++ program in a thin Docker container. This would provide you with tremendous flexibility with hosting the site.

You could also access a MySQL database by including the C/C++ MySQL development headers in your program. If you’re familiar with using MySQL with PHP, you’ll find the function names very similar.

Rather than spawning extra command line processes to process images, you can include the ImageMagick C++ headers to process those images directly in your program.

What a Long, Strange Trip It’s Been

Building a website is practical with C++, especially if performance is critical. I wouldn’t recommend it for a blog or personal site — these can easily be made with WordPress or similar. But if you have an extreme need for speed and a desire to code well off the beaten path, consider C++ for your next niche website project.


Follow me and other software engineers on Sourcerer Blog