N342 User Sessions |
Modified: |
Session variables - One of the fundamental requirements for implementing serious Web-based systems, particularly those involving money or sensitive data, is to distinguish one client from another while apparently handling many clients simultaneously.
The IIS server can automatically maintain unique data for each client in the form of session variables. A server script program can define and manipulate session variables that can hold data independently for each individual client. This provides a useful means to maintain the appearance of state for each user on the server similar to cookies and hidden form variables. In fact, session variables are just automated cookies for which the details of expiration, etc. are handled by the scripting language and server.
So the key advantage of session variables is generally simpler use compared to direct cookie use or other state maintaining mechanisms. The state is managed by the server which can place a time limit on the duration a session can exist with no client activity. This is useful when clients lose interest, fall asleep, disconnects, etc. to be able to forget any uncommitted data entered by the client.
In theory, session variables are not needed since the HTTP 1.0 protocol allows clients to send Keep-Alive messages to keep the connection open. That would allow the Web server and client to complete multiple data exchanges for each connection rather than a single client request and response per connection. With multiple data exchanges, there is no need to maintain state for individual clients in cookies since each connection would have its own set of program variables distinct from other client connections. However, proxy-servers may or may not recognize Keep-Alive requests; session variables, cookies, and methods allow maintaining state in the face of uncertain connections.
Example - Session variables
The project uses a session variable to track the trader.
The session variable is set to the trader's login ID; for example, when Ray logs in, the following is executed:
Login.asp
Session("trader") = "Ray"; Other applications can then access the session variable, for example, the following retrieves the record for Ray:
TraderSummary.asp
rs = conn.Execute( "SELECT * FROM Trader WHERE ID = '" + Session("trader") + "'");
What is a Session? - Not a trivial question since it has implications as to how you write or organize applications into directories of CGI, HTML, ASP, etc. files. The definition that Microsoft uses is that a session includes all accesses by the same client to any program in the same virtual directory or subdirectories on the server. A client that accesses two different ASPs, etc. on the same virtual directory would be considered the same session. The accesses can be to different physical subdirectories as long as under the same virtual directory.
Cookies and Sessions - Session variables are maintained as cookies on the client so the client browser must have cookies enabled in order for the server to maintain session variables, otherwise the server sees every access by the same client as a new session when cookies are disabled. The server script can test whether session state is being maintained (i.e. cookies enabled) by:
setting a session variable,
immediately invoke another ASP that tests if the session variable is still defined,
if not defined then most likely the browser isn't accepting cookies.
Global.Asa - On the client opening an ASP script on a virtual directory for the first time (i.e. no cookie is sent by the client), the server first reads the file Global.Asa from the root directory of the same virtual directory as the ASP script. The Global.Asa file would normally contain session timeout limits, initialization, etc.
The example below sets the important Session.Timeout to 1 minute. If the client associated with the session has no activity in 1 minute that causes a call to the Session_OnEnd subroutine in the Global.Asa script by the server, the the session will be undefined by the server which effectively forgets about the client. With a one minute timeout, the session cookie's expiration time is set for one minute later than the current time. When a cookie has expired, it is not returned by the browser. The Session.Timeout default is 20 minutes.
| <Script LANGUAGE = VBScript RUNAT = Server> Sub Session_OnStart Session.Timeout = 1 ' End session after 1 minute Response.Expires = 0 ' Ensure page is not cached Server.ScriptTimeout = 10 ' Stop server executing script after 10 seconds Response.write("<H1>New Session " & Session.SessionID & "</H1>") ' Will output before cookie header End Sub ' possibly causing error so remove after testing Sub Session_OnEnd |
Points of note
In the following diagram, there exists a D:\Summer\Courses\A348\Global.Asa
file. A virtual directory A348 is mapped to the real
directory D:\Summer\Courses\A348 and contains the Global.Asa file.
Any ASP scripts run in A348 or sub-directories (i.e. the ASP directory in
the figure below right) will use the same D:\Summer\Courses\A348\Global.Asa
file.
Since session variables are stored on the client as a cookie, cookies must be enabled for cookies and hence, session variables to work. In IE, cookies are controlled through the Internet Tools | Internet Options | Privacy | Advanced panel.
It is important to determine if cookies are enabled on the client before naively using session variables.
However, let's take the naive approach and assume that the Session.SessionID can be used to determine that cookies are enabled on the client since SessionID is maintained as a cookie. The following script tests for Session.SessionID = "" which should occur when cookies are disabled. Running the script several times from a browser with cookies disabled and the above Global.Asa demonstrates that each execution is seen as a new session and a new Session.SessionID is generated.
Not useful since a SessionID is generated each time by the server before running an ASP (or other) program but the SessionID is not accepted by the client when cookies are disabled.
The following erroneously indicates to the server
that cookies are always enabled on the client.
| <%@ LANGUAGE = JScript %> <% if (Session.SessionID == "") Response.Write("Cookies Disabled"); else Response.Write("Cookies Enabled"); %> |
Another approach might be to set a session variable and then test if it is the same value later. Unfortunately, the session variable keeps its value for the execution duration of the current ASP script, only being retrieved when the browser accesses another file on the server.
The following
erroneously indicates that cookies are always enabled.
| <%@ LANGUAGE = JScript %> <% Session("CookieTest") = 1; if(Session("CookieTest") == 1) Response.Write ("Cookies Enabled") else Response.Write ("Cookies Disabled") %> |
One might also think the solution is to set a cookie value, then get the same cookie as in the example below; basically the same as the previous example. If the cookie value existed then the client could be assumed to have enabled cookies.
The fly in this ointment, using the ASP Response.Cookies at least, is
that the cookie is cached so that Request.Cookies returns
the same value of the Response.Cookies; cookies are passed to the browser
when the ASP script completes and passed from the browser when the ASP script
starts. The client again erroneously reports to have cookies enabled.
| <%@ LANGUAGE = JScript %> <% Response.Cookies("CookieTest") = 1; if(Request.Cookies("CookieTest") == 1) Response.Write ("Cookies Enabled") else Response.Write ("Cookies Disabled") %> |
A test that correctly determines whether cookies are enabled is to set a session variable to a value in one ASP script file and test the same session variable in a different script file for that value.
Setting the session variable value in one script and testing in another forces the cookie to be passed to the browser at the end of one script and retrieved from the browser at the beginning of the other. When cookies are not enabled on the browser, a Request.Cookies will not return a value for the session variable.
Note in the example below, CookieTest.Asp is executed first to set the session variable, then the Response.Redirect causes the script CookieEnabled.Asp to be executed, there is no return from a redirect to another script.
| CookieTest.Asp
<%@ LANGUAGE = JScript %>
|
CookieEnabled.Asp
<%@ LANGUAGE = JScript %> |
Exercise 1 - Global.Asa and Session Variables
|
The following examples illustrate some simple uses of session variables for maintaining state. There are three examples, the first two are essentially the same in that both use a programmer defined session variable to maintain state.
Personal Visit Count - The ASP script uses a session variable named SessionCounterJScript to keep track of a single user visits. Each user has a unique copy of each session variable that exists for the length of time defined by Session.Timeout, defined in the Global.Asa file or in the ASP script.
A critical step is to initialize the session variable before using its value, since it is initially null. In the counter example, the session variable is incremented, incrementing the variable before initilization would cause a program failure. The test Session("SessionCountJScript") == null ensures that the session variable is correctly initialized whenever a new session is started for the user. In the case that a session times out (after 1 minute with the above Global.Asa) the next visit would be treated as a new session.
The Session.SessionID is a session identifier that can be used over short periods of time to relate a session with other data. In a shopping cart application the session ID could be used to identify the items in a database specific for each cart using the session ID as a database field. Another related use is to purge a database of any data related to a specific session, this is often needed on a session timeout when the session data is no longer valid.
It should be noted that the session ID is not guaranteed unique, that
restarting the server would likely reproduce the same session ID at some point
for some other user. For that reason, the session ID cannot be used where
uniqueness is required, such as the primary key of a database. Relational
database systems generally support an autoincrement field that is
automatically generated when a new record is inserted into the table that is
unique, a unique user login or the concatentation of the session ID with the
current time and date can serve as the database key.
| Global.Asa - Store in root directory of virtual
<Script LANGUAGE = VBScript RUNAT = Server> Sub Session_OnEnd
|
What the browser client sends the server
http://localhost/ASP/JCount.ASP
<%@ LANGUAGE = JScript %> You have personally visited this page |
Exercise 2 - StateASP uses the Session object to maintain client state. Create a modulus 4 counter using an ASP and session variables.
<A HREF="Exercise2.asp"><IMG SRC="digit0.gif"></A>
|
Exercise 3 - Checking for Cookie Enabled
|
Database Implementation of a Phrase Cart - The Phrase Cart allows phrases to be added to a cart, displayed, and the cart emptied by the user. The phrase cart is also emptied automatically when the session times out. The session ID is used to distinguish one set of phrases from another client's by associating all phrases with the client's session ID when the phrase is entered to the data base. New phrases from a client are added to a data base table with the same session ID as others for that session. The client can empty the cart contents (all or none) based upon the session ID. The example also uses redirection from one ASP to another to implement the phrase entry and summary functions, adding phrases, and emptying the cart. There are three ASPs that do the work and one that prints a message when cookies are not enabled.
Note that while the session ID is not used as the primary key of the database table it is used to maintain, update and retrieve table entries.
| PhraseCart.Asp
<%@ LANGUAGE = JScript %>
|
PhraseAdd.Asp
<%@ LANGUAGE = JScript %>
<%@ LANGUAGE = JScript %>
In order to process your phrase a
temporary cookie must be assigned. |
No phrases for this session ![]() |
After entering two phrases for this session ![]() |
Ending the Session - Note that no effort is made in the above to
remove phrases when the session ends. Since the session ID is still valid when
the session ends, this could be done similarly to PhraseClear.Asp but
without the cookie check and redirect (which is not valid when the session is
ended). The Global.Asa file contains a Session_OnEnd
subroutine that is executed by the OnEnd event. The Session.Session_ID
value is used to delete all entries for that specific session. The Global.Asa
file can be modified to delete session data by:
| Global.Asa - Store in root directory of virtual directory
<Script LANGUAGE = VBScript RUNAT = Server> Sub Session_OnEnd conn.Execute( "DELETE From Phrases WHERE SessionID
=" & Session.SessionID & ";" ) |
Further information - See Visual InterDev Help Index on Session.
Exercise 4 - Session and DatabasesMake the PhraseCart example work on your server.
|
The same concept of using session variables to maintain user state is applied to the TicTacToe board game. You might recall that the game was examined in some detail as an example of client side programming. The same basic program has been moved to the server side as an ASP using a single session variable named Board that holds the state of the current board for each user. As a client side only program, the client browser held the complete JavaScript program, though images resided on the server.
In the server side program example below, the client is initially sent some text and a list of image URLs pointing back to the server that display the board. Empty board positions contain an image (of a blank, empty position) and a link back to the server ASP program. For example:
<A HREF="JTTT.asp?move=2">
<IMG ALIGN=bottom BORDER=0
SRC="gameb.gif">
</A>
places a blank position and when clicked will call the server program JTT.ASP with the move=2 to indicate a move of 2 was clicked. The server ASP is then executed, receiving the move=2 from the client.
One critical but somewhat subtle point. The server checks if Session("Board") == null to determine if this is a new session. It also checks Request.QueryString(newGame="New Game?") to determine if an old session needs to initialize the game state. Note that if only the Session("Board") == null were checked the session would have to time out before a new game could be played.
Otherwise the game is programmed nearly identical to the Client-side JavaScript, the document.write of the client program were changed to Response.write of the server, but otherwise the code is unchanged. As in the previous example, the Global.Asa file defines session parameters to the server. If the user doesn't do anything for 1 minute, the session times out and is killed.
You can run this program.
| JTTT.Asp - The server ASP program using session variable to
maintain the state of the game. <%@ LANGUAGE = JScript %> Session("Board") = newBoard; function display(board) { function win(player, board) { function tie(board) { function place(symbol, move, board) { function process(move, oldBoard) { if (win("X", newBoard) || tie(newBoard)) return newBoard; do { //
Random O move return oBoard; |
What the browser sends the server
http://localhost/ASP/JTTT.asp?move=7 Some of what is sent to the browser - To create <HTML>
|