HTTP connection (which is used to transfer HTML documents) is state-less. Let's look
at a sample interaction between a browser and a server to see what is mean by that.
Browser | Server |
---|---|
Generate an request HTTP header HTTP (as per RFC1945 for HTTP/1.0) contains a specific method such as POST or GET (other can also be used but these two are most common). Along with that, the browser includes additional HTTP information to let the server know what type it is and what the browsring capabilities are. Some browsers can also send HTTP Cookie information, but that is not supported by all browsers and may be turned off by users. Coincidently cookies are a good way to store state, but here we will seek an alternative method due to absence of complete compliance by all browsers. |
|
Parses the HTTP request header and sends HTTP response header At this point the server knows what is needed and attempts to retrieve it from storage. If an error occurs then an error is sent in the header and possible error message will follow. |
|
Receives response HTTP header and awaits the content The response header contains useful information about what is to be sent by the server. MIME-type is mandatory and for HTML is will look like Mime-Type: text/html. Other MIME types may be used here, such as image/jpeg, audio/wav, et.al. Another useful field in the header is Content-Length that is needed for binary MIME-types and in some cases to speed up loading of text-based pages. |
|
Send the data of the MIME-type specified in the response HTTP header This is the actual data from the content that was requested in the request HTTP header. At the end of this the server closes connection and forgets about this session. |
|
Receives data and displays as needed The browser at this point is probably not connected to the server and the session is over. |
Hopefully it is clear from the above table that the conection between the server and the browser exists
only for the duration of transfering headers and the actual data. Alas, all is not lost, there are several
methods for maintaining state. Form variable and cookies and the most common. We will not discuss cookies
in this tutorial (but rather defer it to a separate one).
Form variables
There are two ways to pass information back to the server, first let's see what they look like in HTML,
we will then discuss them:
1. Let's look at use of HIDDEN form variables to maintain state:
<FORM ACTION="/cgi-bin/MyProgram.cgi" METHOD="GET"> <INPUT TYPE=TEXT NAME=Username> <INPUT TYPE=PASSWORD NAME=Password MAXLENGTH=8> <INPUT TYPE=HIDDEN NAME=State VALUE=0> <INPUT TYPE=SUBMIT NAME=Login VALUE=Login> </FORM>When the user enters the name and password and hits the Login buttom the following request will be sent to the server:
GET /cgi-bin/MyProgram.cgi?Username=Homer&Password=Beer&State=0&Login=Login HTTP/1.0Here we have 4 variables, one of them is state. Our CGI program will be able to generate forms from now on with the state that contains information about the user so that logging in is not done every time a connection is made. Let's look at another method, then we will see how we can code this.
<FORM ACTION="/cgi-bin/MyProgram.cgi?State=0" METHOD="POST"> <INPUT TYPE=TEXT NAME=Username> <INPUT TYPE=PASSWORD NAME=Password MAXLENGTH=8> <INPUT TYPE=SUBMIT NAME=Login VALUE=Login> </FORM>Same thing, except the form variables are submitted via a POST method and the State is submitted via GET. Here what a sample HTTP header would look like for the following FORM (ommiting much of the HTTP header fields for clarity):
POST /cgi-bin/MyProgram.cgi?State=0 HTTP/1.0 Referer: http://www.myserver.com/LoginPage.html Connection: Keep-Alive User-Agent: Mozilla/4.01 [en] (WinNT; I) Content-type: application/x-www-form-urlencoded Content-length: 49 (must have a blank line here to signify end-of-header) Username=Homer&Password=Beer&State=0&Login=LoginThis may look a bit confusing, but if you look through RFC1945 must of HTTP confusion may be cleared up. We won't worry about the HTTP stuff, just that we need to know what the user is doing. Let's say after logon you have to know which user is submitting information, so that you can chanrge them differently. We need to determine state information about the user whenever a form is submitted.
//Create state data to be saved //Assume that pccState is where you will store name and password const char *pccState = "Homer:Beer"; const BYTE *pccEncryptionKey = "MyKey"; //You can do better than this :) int iKeyLength = 5; //Keys can contain any ASCII value //Specify which method to use for encryption and encoding UNIT uMethod = ACrypto::ectXOR_Convolver|AConverto::eat6Bit; //Create a temporary area for the state, the state can have any ASCII character //This is why you have to provide the length APairItem piTest; BYTE *pbState = aMemDup(NULL, iLength); int iStateLength = strlen(pccState); memcpy(pbState, pccState, iStateLength); //Preparing the posting of data //You may want to store the user's IP here for validation DWORD dwIP = cgiOut.cgiGetIP(); piTest.piSet("State"); //This method of APairItem will encrypt and encode the data into a pair variable piTest.piSetValueChecked(pbState, iStateLength, dwIP, uMethod, pcbEncryptionKey, iKeyLength); //Cleanup, we don't need this anymore delete []pbState; //At this point you can do anything you want with the APairItem, let's add it to a form //We will assume that FORM header was already created cgiOut << ">INPUT TYPE=HIDDEN " << piTest << ">" << endl;At this point we also need a way of retrieving the State variable when a user submits the form to us. We will assume that you have created an ACGI object and parsed FORM submissions.
//Start by finding the State variable in the submission APairItem *piState = acgiOut.plGetItemByName("State"); if (piState) { const BYTE *pcbNewData; DWORD dwIP = cgiOut.cgiGetIP(); //You get this from CGI environment variables int iLength; //Variable used to get the length of the new state int iTimeout = 100; //100 seconds timeout since the State was last created //Specify which method to use for encryption and encoding, must be consistent with creation UNIT uMethod = ACrypto::ectXOR_Convolver|AConverto::eat6Bit; //Decode our State variable pcbNewData = piTest.piDecodeCheckedValueAndGetUserData(iLength, dwIP, iTimeout, uMethod, pcbKey, iKeyLength); if (pcbNewData) { //Let's just display the output, you will of course do what you need with it cgiOut << "STATE=" << (const char*)pcbNewData; cgiOut << " length=" << iLength << endl; } else { In case it was not found cgiOut << "=(null)?" << endl; } }By now you should be a bit more comfortable with state variables. The classes you should look at are APairItem, ACrypto, AConverto, and ACGI; to get a better idea of the methods available and how to use them. Also refer to samples/test/state.cpp for a functional example of using state variables.
Enjoy!
Table Of Contents