C++ web programming
What are CGIs?
- Common Gateway Interface (CGI), is a set of standards that defines how information is exchanged between web servers and client-side scripts.
- The CGI specification is currently maintained by the NCSA, which defines CGI as follows:
- Common Gateway Interface (CGI) is an interface standard for connecting external gateway programs to information servers (such as HTTP servers).
- The current version is CGI/1.1, and a CGI/1.2 version is in progress.
web browsing
To better understand the concept of CGI, let's click on a hyperlink, browse to a specific web page or URL, and see what happens.
- Your browser contacts the HTTP web server and requests the URL, which is the file name.
- The web server will parse the URL and look for the filename. If the requested file is found, the web server sends the file back to the browser, otherwise it sends an error message indicating that you requested a wrong file.
- A web browser gets a response from a web server and, depending on the response received, displays a file or an error message.
However, the HTTP server built in this way, whenever a file in the directory is requested, the HTTP server does not send back the file, but executes it in the form of a program, and sends the output generated by the execution back to the browser for display come out.
Common Gateway Interface (CGI), is a standard protocol that enables application programs (called CGI programs or CGI scripts) to interact with Web servers and clients. These CGI programs can be written in Python, PERL, Shell, C or C++, etc.
CGI architecture diagram
The following diagram demonstrates the architecture of CGI:
Web server configuration
Before you start CGI programming, make sure your Web server supports CGI and is configured to handle CGI programs. All CGI programs executed by the HTTP server must be in the preconfigured directory. This directory is called the CGI directory and is named by convention /var/www/cgi-bin. Although a CGI file is a C++ executable, by convention it has the extension .cgi .
By default, the Apache web server is configured to run CGI programs in /var/www/cgi-bin. If you want to specify another directory to run CGI scripts, you can modify the following section in the httpd.conf file:
<Directory "/var/www/cgi-bin"> AllowOverride None Options ExecCGI Order allow,deny Allow from all </Directory> <Directory "/var/www/cgi-bin"> Options All </Directory>
Here, we assume that the Web server has been configured and can run successfully, you can run any CGI program, such as Perl or Shell, etc.
The first CGI program
Consider the following C++ program:
#include <iostream> using namespace std; int main () { cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>Hello World - The first CGI program</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<h2>Hello World! This is my first CGI program</h2>\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }
Compile the above code, name the executable file cplusplus.cgi, and save this file in the /var/www/cgi-bin directory. Before running the CGI program, please use the chmod 755 cplusplus.cgi UNIX command to modify the file mode to ensure that the file is executable. Access the executable and you'll see the following output:
Hello World! This is my first CGI program
The above C++ program is a simple program that writes its output to the STDOUT file, ie displays it on the screen. Here, it is worth noting that the first line outputs Content-type: text/html\r\n\r\n . This line is sent back to the browser and specifies the type of content to be displayed on the browser window. You must understand the basic concepts of CGI, so that you can further use Python to write more complex CGI programs. C++ CGI programs can interact with any other external system such as an RDBMS.
HTTP header information
The line Content-type: text/html\r\n\r\n is part of the HTTP header information that is sent to the browser to better understand the content of the page. The form of HTTP header information is as follows:
HTTP field name: field content For example Content-type: text/html\r\n\r\n
There are some other important HTTP headers that will be used frequently in your CGI programming.
header information | describe |
---|---|
Content-type: | A MIME string defining the returned file format. For example Content-type: text/html. |
Expires: Date | The date the information became invalid. Browsers use it to determine when a page needs to be refreshed. A valid date string should be in the format 01 Jan 1998 12:00:00 GMT. |
Location: URL | This URL refers to the URL that should be returned, not the requested URL. You can use this to redirect a request to an arbitrary file. |
Last-modified: Date | The date the resource was last modified. |
Content-length: N | The length of the data to return, in bytes. Browsers use this value to indicate the estimated download time for a file. |
Set-Cookie: String | Set cookie by string . |
CGI environment variables
All CGI programs can access the following environment variables. These variables play a very important role in writing CGI programs.
variable name | describe |
---|---|
CONTENT_TYPE | The data type of the content. Used when the client sends additional content to the server. For example, functions such as file upload. |
CONTENT_LENGTH | The message length of the query. Only available for POST requests. |
HTTP_COOKIE | Returns the set cookies as key & value pairs. |
HTTP_USER_AGENT | The user agent request header field is used to submit information about the request initiated by the user, including the name, version and other platform-specific additional information of the browser. |
PATH_INFO | The path to the CGI script. |
QUERY_STRING | URL-encoded information when a request is sent via the GET method, including parameters after the question mark in the URL. |
REMOTE_ADDR | The IP address of the remote host making the request. This is very useful for logging and authentication. |
REMOTE_HOST | The fully qualified name of the host making the request. If this information is not available, REMOTE_ADDR can be used to obtain the IP address. |
REQUEST_METHOD | The method used to make the request. The most common methods are GET and POST. |
SCRIPT_FILENAME | Full path to the CGI script. |
SCRIPT_NAME | The name of the CGI script. |
SERVER_NAME | The hostname or IP address of the server. |
SERVER_SOFTWARE | The name and version of the software running on the server. |
The following CGI program lists all CGI variables.
#include <iostream>
#include <stdlib.h>
using namespace std;
const string ENV[ 24 ] = {
"COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE",
"HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING",
"HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION",
"HTTP_HOST", "HTTP_USER_AGENT", "PATH",
"QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT",
"REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME",
"SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN",
"SERVER_NAME","SERVER_PORT","SERVER_PROTOCOL",
"SERVER_SIGNATURE","SERVER_SOFTWARE" };
int main ()
{
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>CGI 环境变量</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "<table border = \"0\" cellspacing = \"2\">";
for ( int i = 0; i < 24; i++ )
{
cout << "<tr><td>" << ENV[ i ] << "</td><td>";
// 尝试检索环境变量的值
char *value = getenv( ENV[ i ].c_str() );
if ( value != 0 ){
cout << value;
}else{
cout << "环境变量不存在。";
}
cout << "</td></tr>\n";
}
cout << "</table><\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
C++ CGI library
In a real instance, you would need to perform many operations through a CGI program. Here is a CGI library specially written for C++ programs, we can download this CGI library from ftp://ftp.gnu.org/gnu/cgicc/ , and follow the steps below to install the library:
$tar xzf cgicc-X.X.X.tar.gz $cd cgicc-X.X.X/ $./configure --prefix=/usr $make $make install
You can click C++ CGI Lib Documentation to view related library documents.
GET and POST methods
You've probably come across a situation when you needed to pass some information from your browser to a web server and finally to a CGI program. Usually the browser will use two methods to transmit this information to the Web server, namely the GET and POST methods.
Pass information using the GET method
The GET method sends encoded user information appended to the page request. Pages and encoded information are separated by a ? character, as follows:
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
The GET method is the default method of transferring information from the browser to the web server, and it will generate a long string of characters in the browser's address bar. Do not use the GET method when you are passing passwords or some other sensitive information to the server. The GET method has a size limit, and a maximum of 1024 characters can be passed in a request string.
When using the GET method, the information is passed using the QUERY_STRING http header, which can be accessed using the QUERY_STRING environment variable in the CGI program.
You can pass information by simply concatenating key-value pairs following the URL, or by using the GET method of the HTML <FORM> tag.
Simple URL Example: Get Method
Below is a simple URL that uses the GET method to pass two values to the hello_get.py program.
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
The following example generates the cpp_get.cgi CGI program for processing input given by a Web browser. The passed information can be easily accessed by using the C++ CGI library:
#include <iostream>
#include <vector>
#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;
int main ()
{
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>使用 GET 和 POST 方法</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("first_name");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "名:" << **fi << endl;
}else{
cout << "No text entered for first name" << endl;
}
cout << "<br/>\n";
fi = formData.getElement("last_name");
if( !fi->isEmpty() &&fi != (*formData).end()) {
cout << "姓:" << **fi << endl;
}else{
cout << "No text entered for last name" << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
Now, compile the above program as follows:
$g++ -o cpp_get.cgi cpp_get.cpp -lcgicc
Generate cpp_get.cgi, put it in the CGI directory, and try to access it using the link below:
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
This produces the following result:
Name: ZARA Surname: ALI
Simple form example: GET method
Below is a simple example using an HTML form and a submit button to pass two values. We will use the same CGI script cpp_get.cgi to process the input.
<form action="/cgi-bin/cpp_get.cgi" method="get">
名:<input type="text" name="first_name"> <br />
姓:<input type="text" name="last_name" />
<input type="submit" value="提交" />
</form>
Below is the actual output of the above form, please enter first and last name and click submit button to see the result.
Pass information using the POST method
A more reliable method of passing information to a CGI program is the POST method. This method packs information in the same way as the GET method, except that instead of passing the information as a text string after the ? in the URL, it passes it in a separate message. This message is passed to the CGI script on standard input.
We also use the cpp_get.cgi program to handle the POST method. Let us take the same example and pass two values by using HTML form and submit button, only this time instead of GET method we use POST method as follows:
<form action="/cgi-bin/cpp_get.cgi" method="post"> 名:<input type="text" name="first_name"><br /> 姓:<input type="text" name="last_name" /> <input type="submit" value="提交" /> </form>
Pass checkbox data to CGI program
We use checkboxes when multiple options need to be selected.
The following HTML code example is a form with two check boxes:
<form action="/cgi-bin/cpp_checkbox.cgi" method="POST" target="_blank"> <input type="checkbox" name="maths" value="on" /> 数学 <input type="checkbox" name="physics" value="on" /> 物理 <input type="submit" value="Select subject" /> </form>
The following C++ program generates the cpp_checkbox.cgi script for processing input given by a web browser via check boxes.
#include <iostream>
#include <vector>
#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;
int main ()
{
Cgicc formData;
bool maths_flag, physics_flag;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>向 CGI 程序传递复选框数据</title>\n";
cout << "</head>\n";
cout << "<body>\n";
maths_flag = formData.queryCheckbox("maths");
if( maths_flag ) {
cout << "Maths Flag: ON " << endl;
}else{
cout << "Maths Flag: OFF " << endl;
}
cout << "<br/>\n";
physics_flag = formData.queryCheckbox("physics");
if( physics_flag ) {
cout << "Physics Flag: ON " << endl;
}else{
cout << "Physics Flag: OFF " << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
Pass radio button data to CGI program
We use radio buttons when only one option needs to be selected.
The following HTML code example is a form with two radio buttons:
<form action="/cgi-bin/cpp_radiobutton.cgi" method="post" target="_blank"> <input type="radio" name="subject" value="maths" checked="checked"/> math <input type="radio" name="subject" value="physics" /> 物理 <input type="submit" value="Select subject" /> </form>
The following C++ program generates the cpp_radiobutton.cgi script that handles the input given by the web browser via the radio buttons.
#include <iostream>
#include <vector>
#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;
int main ()
{
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>向 CGI 程序传递单选按钮数据</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("subject");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "Radio box selected: " << **fi << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
Pass textarea data to a CGI program
We use the TEXTAREA element when multiple lines of text need to be passed to a CGI program.
The following HTML code example is a form with a TEXTAREA box:
<form action="/cgi-bin/cpp_textarea.cgi" method="post" target="_blank"> <textarea name="textcontent" cols="40" rows="4"> Please enter text here... </textarea> <input type="submit" value="提交" /> </form>
The following C++ program generates the cpp_textarea.cgi script for handling input given by the web browser through the text area.
#include <iostream>
#include <vector>
#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;
int main ()
{
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>向 CGI 程序传递文本区域数据</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("textcontent");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "Text Content: " << **fi << endl;
}else{
cout << "No text entered" << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
Pass the drop-down box data to the CGI program
We use dropdown boxes when there are multiple options available, but only one or two options can be selected.
The following HTML code example is a form with a drop-down box:
<form action="/cgi-bin/cpp_dropdown.cgi" method="post" target="_blank"> <select name="dropdown"> <option value="Maths" selected>数学</option> <option value="Physics">物理</option> </select> <input type="submit" value="提交"/> </form>
The following C++ program generates the cpp_dropdown.cgi script for processing input given by a web browser through a drop-down box.
#include <iostream>
#include <vector>
#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;
int main ()
{
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>向 CGI 程序传递下拉框数据</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("dropdown");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "Value Selected: " << **fi << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
Using Cookies in CGI
The HTTP protocol is a stateless protocol. But for a commercial website, it needs to keep session information between different pages. For example, a user ends registration after completing several pages of steps. But how to keep user's session information across all web pages.
In many cases, the use of cookies is the most effective method of remembering and tracking user preferences, purchases, commissions, and other information needed for a better visitor experience or website statistics.
how it works
The server sends some data to the visitor's browser in the form of a cookie. If the browser accepts the cookie, the cookie is stored as a plain text record on the visitor's hard drive. Now, when a visitor visits another page on the website, the cookie is retrieved. Once the cookie is found, the server knows what was stored.
A cookie is a plain text data record with 5 variable-length fields:
- Expires : The expiration date of the cookie. If this field is left blank, the cookie will expire when the visitor exits the browser.
- Domain : The domain name of the website.
- Path : The path of the directory or web page where the cookie is set. This field can be left blank if you want to retrieve cookies from any directory or web page.
- Secure : If this field contains the word "secure", then the cookie can only be retrieved through a secure server. If this field is left blank, the restriction does not exist.
- Name=Value : The cookie is set and retrieved as a key-value pair.
set cookies
Sending cookies to the browser is very simple. These cookies are sent with the HTTP header before the Content-type field. Suppose you want to set UserID and Password as cookies, the steps to set cookies are as follows:
#include <iostream>
using namespace std;
int main ()
{
cout << "Set-Cookie:UserID=XYZ;\r\n";
cout << "Set-Cookie:Password=XYZ123;\r\n";
cout << "Set-Cookie:Domain=www.w3cschool.cn;\r\n";
cout << "Set-Cookie:Path=/perl;\n";
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>CGI 中的 Cookies</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "设置 cookies" << endl;
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
From this example, we learned how to set cookies. We use the Set-Cookie HTTP header to set cookies.
Here, some attributes for setting cookies are optional, such as Expires, Domain, and Path. It's worth noting that the cookies are set before the line "Content-type:text/html\r\n\r\n is sent .
Compile the above program, generate setcookies.cgi, and try to set cookies using the link below. It sets four cookies on your computer:
/cgi-bin/setcookies.cgi
Get Cookies
Retrieving all set cookies is very simple. Cookies are stored in the CGI environment variable HTTP_COOKIE, and they have the following form:
key1=value1;key2=value2;key3=value3....
The following example demonstrates how to get cookies.
#include <iostream>
#include <vector>
#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;
int main ()
{
Cgicc cgi;
const_cookie_iterator cci;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>CGI 中的 Cookies</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "<table border = \"0\" cellspacing = \"2\">";
// 获取环境变量
const CgiEnvironment& env = cgi.getEnvironment();
for( cci = env.getCookieList().begin();
cci != env.getCookieList().end();
++cci )
{
cout << "<tr><td>" << cci->getName() << "</td><td>";
cout << cci->getValue();
cout << "</td></tr>\n";
}
cout << "</table><\n";
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
Now, compile the above program, generate getcookies.cgi, and try to get all available cookies on your computer using the link below:
/cgi-bin/getcookies.cgi
This produces a list showing the four cookies set in the previous section along with all other cookies on your computer:
UserID XYZ Password XYZ123 Domain www.w3cschool.cn Path /perl
File upload example
In order to upload a file, the HTML form must have the enctype attribute set to multipart/form-data . An input tag with a file type creates a "Browse" button.
<html> <body> <form enctype="multipart/form-data" action="/cgi-bin/cpp_uploadfile.cgi" method="post"> <p>文件:<input type="file" name="userfile" /></p> <p><input type="submit" value="上传" /></p> </form> </body> </html>
You can try the code above on your own server.
Here is the script cpp_uploadfile.cpp for handling file uploads :
#include <iostream>
#include <vector>
#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;
int main ()
{
Cgicc cgi;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>CGI 中的文件上传</title>\n";
cout << "</head>\n";
cout << "<body>\n";
// 获取要被上传的文件列表
const_file_iterator file = cgi.getFile("userfile");
if(file != cgi.getFiles().end()) {
// 在 cout 中发送数据类型
cout << HTTPContentHeader(file->getDataType());
// 在 cout 中写入内容
file->writeToStream(cout);
}
cout << "<文件上传成功>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
The above example writes content in the cout stream, but you can open the file stream and save the uploaded file content in a file at the target location.