The Common Gateway Interface (CGI) is a standard for interfacing external
applications with information servers, such as HTTP or Web servers. A plain
HTML document that the Web daemon retrieves is static, which
means it exists in a constant state: a text file that doesn't change. A CGI
program, on the other hand, is executed in real-time, so that it can
output dynamic information.
Simple diagram of how CGI is working:
Basically it's very simple:
User enters the input on his Web browser into the form (<FORM> tag)
Browser packs the input according to the CGI specifications and sends it
to the URL, which is specified in ACTION field of the FORM.
It's actually sent to the HTTP server.
Server extracts the program name from the URL and calls the program,
passing the packed user input to the program as well.
Server takes the output (standard output) of the program and passes it
back to the browser.
So basically in our CGI program we need to know how to do two things:
first, take the user's input & unpack, so we can use it and, second,
figure out what to print to stdout, so broswer will understand.
Getting the user's input
There are two methods (METHOD parameter of the
FORM tag) of passing parameters to CGI program:
POST - If form uses this method all the data is send
to CGI program's stdin.
GET - If this method is in use, data is passed to the
program using the environment variables (QUERY_STRING). This is the method,
which is used, when CGI program is called by URL with appended encoded
parameters (e.g. /cgi-bin/prog.cgi?aaa=bb&xxx=yyy).
Note, that if METHOD is not specified, the default is GET.
In most of the cases POST method should be used, because it't more flexible
and also, there is a high probability, that while using GET method, long
input will be truncated.
GET method is useful if you want to create a URL link to some CGI program
and pass some parameters (you just append them to the URL with '?'
character). Just don't forget to encode all the special characters (space
is encoded with '+'), but most of the CGI libraries have functions
to do the encoding for you.
In both cases, the content of the form will be encoded in the following
form: name=value&name=value&name=value
Most likely, you won't have to deal with encoding & decoding, because
it's a good idea to use some CGI library. In case of Perl it would be
CGI.pm, which comes with standard perl distribution.
Producing the output and sending it back to the client
The output of CGI program consists of two parts: header & body. The
delimiter between the header and body is an empty string.
The body is usually a simple HTML page, created by the program. It can
also be any other type of thing, like image or binary document, but in most
cases it'll be HTML.
Here is the list of valid HTTP headers:
Content-type - The MIME content type of the output stream.
Content-length - The length (in bytes) of the output stream.
Need for binary data.
Expires - Date and time when the document is no longer valid
and should be reloaded by the browser. Also used by search engines.
Location - Server redirection, means, that insted of this
document server should redirect the browser to another document (the one,
which URL is in the Location header). Since this type of header
redirects browser to another place, everything, that is send in the stream
after this header will be ignored.
Pragma - Turns document caching on and off. Usually for CGI
programs you want it off.
Status - Status of the request. e.g. 404 (Document not found)
Refresh - Also type of redirect, which allows to specify the
refresh time, so if for example it's set to 5, client will see your document
for 5 seconds and then will be redirected to another one.
Set-Cookie - Sets the cookie in the browser.
The minimal thing you'll need to do in your CGI program is to produce
Content-type header.e.g.:
Keeping the state (session) between the CGI calls
Since it's typical client-server model, to produce each screen, a new process
should be started on the server. Which means, that there is no way
for this process to know what happened in the previous one.
There are some techniques, that allow you to keep some info between the
sessions:
Hidden fields in the form
(<INPUT TYPE=HIDDEN NAME=name VALUE=value>) Using these
fields, you can embed information into a form that the user won't see,
but which will be sent to the CGI program when the form is submitted.
Cookies - This is a mechanism, which allows to store some info
in the browser. Each cookie has the following fields:
hostname - Hostname (if set to .company.com applies for all
hostnames inside company.com domain). This is the hostname of the server
to which this cookie applies. Browser will not send this cookie to any
other host.
name - Name of the cookie.
value - Value of the cookie.
Exp. date - Expiration date. Date when this cookie expires.
if not set, cookie expires, when the browser exits.
The List of environment variables, used by CGI:
GATEWAY_INTERFACE - The revision of the CGI that the server uses.
SERVER_NAME - The server's hostname or IP address.
SERVER_SOFTWARE - The name and version of the server software
that is answering the client request.
SERVER_PROTOCOL - The name and revision of the information
protocol the request came in with.
SERVER_PORT - The port number of the host on which the server
is running.
PATH_INFO - Extra path information passed to a CGI program.
PATH_TRANSLATED - The translated version of the path given by
the variable PATH_INFO.
SCRIPT_NAME - The virtual path (e.g., /cgi-bin/prog.cgi) of the
CGI program being executed.
DOCUMENT_ROOT - The directory from which Web documents are
served.
REQUEST_METHOD - The method with which the information request
was issued.
QUERY_STRING - The form content passed to the program.
This appended to the GET method only.
CONTENT_TYPE - The MIME type of the query data, e.g. "text/html".
CONTENT_LENGTH - The length of the data (in bytes) passed to
the CGI program through standard input (POST method).
REMOTE_HOST - The remote hostname of the user's host.
REMOTE_ADDR - The remote IP address of the user's host.
AUTH_TYPE - The authentication method used to validate a user.
REMOTE_USER - The authenticated name of the user.
REMOTE_IDENT - The user making the request. This variable will
only be set if ident daemon is running on user's machine and we can't trust
it anyway :-).
HTTP_ACCEPT - A list of the MIME types that the client can
accept.
HTTP_USER_AGENT - The browser the client is using to issue the
request. Useful if you want to do different things with netscape and IE or
to find out the version of the browser.
HTTP_REFERER - The URL of the document that the browser pointed
to before accessing the CGI program.
Note: All values should be in double quotes! Now it's
working without it, but next year it will not! XML doesn't allow it, it's
much more strict.
Summary:
Now we can receive user's input and do something with it. There are some
pluses and some minuses with CGI:
Pluses:
You can use any of your favorite programming languages, the most common is
Perl, but it can be C, C++, Shell script etc. etc.
Another nice thing is, that each request runs the process again, so if
you have a bug in your program it will not crash the whole system, in
the worst case scenario user will receive an error, but for the next user
it can work fine (if the rest of the system is fine).
Minuses:
Because it starts the process again and again, and it takes some time,
to fire the process, the performance is not that good. It becomes
especially problematic if you need to work with RDBMS, since each time you
need to open a database connection, and this can take a lot of time.
Application servers, which are doing database connection pooling can help
here.
It's inconvenient and complicated to keep the states between the requests
and you have to save these states on the client side, which can be a security
problem sometimes. This one is also solved in the Application servers.
Each time we need to process this data, we need to go to the HTTP
server and create a new connection. So if the connection is relatively
slow it's very inconvenient. We need to be able to do some simple processing
on the browser side to save the connection times.
Here helps JavaScript...