A Basic CGI Tutorial using C++

C, C++, Visual C++, C++.Net Topics
Post Reply
User avatar
Neo
Site Admin
Site Admin
Posts: 2642
Joined: Wed Jul 15, 2009 2:07 am
Location: Colombo

A Basic CGI Tutorial using C++

Post by Neo » Fri Feb 05, 2010 4:06 am

Overview
This tutorial describes the creation of CGI scripts in the C++ programming language. You should have prior knowledge of ANSI C++ before continuing. A number of excellent resources can be found in our CGI Links section.

That said, let’s get down to the nitty-gritty CGI internals! Making CGI scripts is actually quite simple if you know your way around C++. The server automatically takes whatever your program outputs to stdout and redirects it to the browser. So, for instance, if you wanted to send the text “Hello, World!” to the browser, you would simply state:

Code: Select all

cout << "Hello, World!";
Easy, huh? Well, there are a few more things that have to be done…

The content-type header
Every CGI script must first output a content-type header that specifies the MIME type of the data that is being output by the script - for instance, text/plain if it is outputting plaintext, or text/html if it is outputting an HTML webpage.

The actual code for this header looks like this:

Code: Select all

cout << "Content-type: text/plain" << endl << endl;
Note the two newlines following the header: this tells the server where the headers end and where the data begins.

Your first CGI script
Armed with this knowledge, we can create our first functional CGI script:

Code: Select all

#include <iostream.h> 

void main()
{
  cout << "Content-type: text/plain" << endl << endl
   << "Hello, World!";
} 
Yes, believe it or not, this is a real, bona fide CGI script, even though it doesn't do much. All it does, in fact, is sends the text "Hello, World!" to the browser.

Now, let's spruce it up. Say we want to output not just "Hello, World!" in text, but why not make it pretty with some HTML formatting?

Code: Select all

#include <iostream.h>

void main()
{
  cout << "Content-type: text/html" << endl << endl
   << "<html>" << endl
   << "<head>" << endl
   << "<title>CGI Test</title>" << endl
   << "</head>" << endl
   << "<body>" << endl
   << "<h1><em>" << endl
   << "Hello, World!" << endl
   << "</em></h1>" << endl
   << "</body>" << endl
   << "</html>";
}
Now we've outputted a real HTML page that the browser will recognize.

Using environment variables
Now, outputting an HTML page is all well and good - but why would you want to do so when you could just put the HTML page on the server? Well, here's where the fact that you're using C++ comes in handy.

When the server executes a CGI script, it first sets a number of environment variables that you can retrieve using the getenv() function. These will tell you a number of useful things. Have you ever seen one of those nifty pages that tells you your IP address?

Code: Select all

#include <iostream.h>
#include <stdlib.h> 

void main()
{
  char* lpszRemoteHost = getenv("REMOTE_HOST");

  cout << "Content-type: text/html" << endl << endl
   << "<html>" << endl
   << "<body>" << endl
   << "<p>" < endl
   << "Hello, "
   << lpszRemoteHost
   << "!" << endl
   << "</p>" << endl
   << "</body>" << endl
   << "</html>";
}
You might also tell the user the current time, or even what web browser they're using (stored in the HTTP_USER_AGENT environment variable).

Receiving data from a form
Okay, here's where we get into what makes a CGI script really useful; receiving the data sent by a form on a webpage.

Using the POST method, a C++ CGI script retrieves the form data from stdin, the input buffer. The easiest way to do so is with the fread() function. There are a few things that have to be done first, however. You must retrieve the CONTENT_LENGTH environment variable, which tells you how much data is waiting. You must then allocate a buffer of this or greater length to hold the data.

Now, the data comes in a specific format. Each HTML <input> field and its value is sent along. Let's say the following is the form in the webpage:

Code: Select all

<form method="post" action="http://path.to/my/cgi/program">
<input type="hidden" name="value1" value="test1">
<input type="hidden" name="value2" value="test2">
<input type="hidden" name="value3" value="test3">
<input type="submit">
</form> 
These are hidden input fields for the purpose of simplicity. The Webworks forms section in Advanced HTML will tell you how to make more complex forms.

Anyway, when the user submits this, this is what gets put into stdin for the script:

Code: Select all

value1=test1&value2=test2&value3=test3 
The CGI script is then responsible for parsing this into data that it can use.

Knowing all this, here's a script that sends back what was sent to it:

Code: Select all

#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>

void main()
{
  char* lpszContentLength = getenv("CONTENT_LENGTH");
  char* lpszBuffer;
  int nContentLength = atoi(lpszContentLength);

  lpszBuffer = malloc(lpszContentLength+1);  // allocate a buffer
  memset(lpszBuffer, 0, lpszContentLength+1);  // zero it out

  fread(lpszBuffer,1,lpszContentLength,stdin);  // get data

  cout << "Content-type: text/html" << endl << endl
   << "<html>" << endl
   << "<body>" << endl
   << "<p>" << endl
   << "Hello! You sent " << lpszContentLength << " bytes of data which read: <br>" << endl
   << lpszBuffer << endl
   << "</p>" << endl
   << "</body>" << endl
   << "</html>";

  free(lpszBuffer);
}
Parsing the form data
You probably noticed that the server doesn't do much to parse the form data for you; you have to do that yourself. It's a very repetitive task, so I wrote a couple classes to do so. It's too much code to reproduce here, but you can go ahead and download the source files:
cgi.cpp
(9.21 KiB) Downloaded 330 times
cgi.h
(1.86 KiB) Downloaded 311 times
These two classes, CCGI and CCGIItem, make it easy to parse the data.

Here's how they work: you create a CCGI and a CCGIItem. Then you call CCGI::Load(), which reads in the data. You can then call CCGI::GetItemCount() to retrieve the number of items that were sent. You then pass CCGI::GetItem for however many items you want, passing a pointer to the CCGIItem which is filled with that item's name and value. It's quite easy to use, once you get used to it. Read through the source and you should get a better understanding of how it works (it's fairly well commented).

There are many other libraries that provide much more complete CGI functionality; cgic and libcgi++ are two examples. You'll find them in the CGI links section.

And here's my final example, using my CGI classes:

Code: Select all

#include <iostream.h>
#include <malloc.h>
#include "CGI.h"

void main()
{
  CCGI cgi;
  CCGIItem item;
  char* lpszOut;
  char* lpszContentLength = getenv("CONTENT_LENGTH");
  int i,nLength;

  cout << "Content-type: text/html" << endl << endl
   << "<html>" << endl
   << "<body>" << endl
   << "<p>You sent " << lpszContentLength

   << " bytes of data, which contained the following values: </p>" << endl
   << "<ul>" << endl;

  cgi.Load();

  for(i=0; i<cgi.GetItemCount(); i++)
  {
    cgi.GetItem(i,&item);

    nLength = item.GetNameLength();
    lpszOut = (char*) malloc(nLength);
    item.GetName(lpszOut,nLength);

    cout << "<li>" << lpszOut << " - ";

    free(lpszOut);

    nLength = item.GetLength();
    lpszOut = (char*) malloc(nLength);
    item.GetValue(lpszOut,nLength);

    cout << lpszOut << endl;

    free(lpszOut);
  }

  cout << "</ul>" << endl
   << "</body>" << endl
   << "</html>" << endl;
}
You should now have the basic knowledge you need to go on and create useful & interactive CGI scripts in C++.
Post Reply

Return to “C/C++ Programming”