Sessions





Sessions

The concept of sessions is difficult to define. Some people use the word to define any conversation among a set of messaging endpoints. But I prefer a more liberal definition: A session is the logical and unique name given to a set of semantically and temporally related messages. It's a way to let a message receiver state that a particular message is part of the same conversation, or session.

Examples of a session might be the series of messages

  • Signing up a new customer.

  • Doing online shopping, including checking out.

  • Submitting a purchase order.

HTTP Sessions

Most typically, sessions are seen in Web applications in which there is a need to tie a particular browser instance together. This is usually done in one of two ways: either with an HTTP cookie; or with a special URL value, either in the URL or as a POST value that is carried forward from each Web page. In both instances, the value is a GUID (Global Unique Identifier) that eventually times out.

With Active Server Pages (ASPs), and ASP.NET, there is a Session object that is available only on the server side and which can place custom application data. The correct Session object is then presented to the server code each time the HTTP GET or POST request contains the session cookie, as based on the value. This also can work well for Web services, and as a result, ASP.NET Web services support the exact same programming mode, using the same cookie-based Session key.

To take advantage of this server-side feature, the client that is sending the request message must obey the common rules for HTTP cookie handling. To enable this with the .NET Web services proxy class, you need to create a CookieContainer object and set the CookieContainer property of the class to this instance:

MyClientProxyClass clientProxy = new MyClientProxyClass(); 
clientProxy.CookieContainer = new CookieContainer();

Now, whenever you make a method call to a server, the correct cookies, including HTTP-based session cookies, will be sent to this client proxy class and accepted by it. Because this container is programmatically accessible, you can also persist it to disk or to a database. In fact, you will have to manage this collection of cookies in between client-proxy-class lifetimes, because the underlying .NET runtime won't do this for you the same way the browser does.

On the server side, the session is initiated by code that tries to access it. This requires no explicit call to create a cookie. Instead, you need only to access a session value. The ASP.NET infrastructure will automatically do the right thing in terms of looking for a cookie, and creating one if none exists yet. Listing 14.1 shows exactly how easy session initiation can be with ASP.NET Web services. Similar levels of ease exist in most HTTP-aware SOAP stacks.

Sessions with ASP.NET Web Services
<%@ WebService Class="SessionTest" Language="C#" %>
using System.Web.Services;
using System.Web;
public class SessionTest : WebService
{
     [WebMethod]
     public String AddToSession( String text )
     {
          String s = (String)Session["text"];
          S = s + text;
          Session["text"] = s;
        return s;
    }
}

Message-Level Sessions

As of this writing, there is no SOAP-based specification for sessions that has gained wide acceptance in the industry. But there has been enough discussion to make some good guesses about how a session specification would work.

The first thing to decide is whether session information should be header based, body based, or some combination. It seems logical that sessions are exactly the kind of out-of-band information that is best kept in the header. We'll keep that as a working hypothesis.

What Should Go in the Header?

  • Out-of-band information that applies to multiple messages, such as security tokens

  • "Horizontal" industry information that isn't application specific

  • Sometimes, information that doesn't need to be signed or encrypted

With that in mind, you can imagine a very simple session header, called <Session>, with a single child element, called <Id>. We'll need a namespace as well, so we'll use http://keithba.com/2002/05/Session. Listing 14.2 shows the resulting session header.

Choosing Namespaces

Choosing namespaces isn't rocket science. And for the most part, it doesn't matter what the namespace URI is. But here are some guidelines most people use to great effect:

  • Use a URL that resolves to the specification or schema to which the namespace refers.

  • Use a domain name that you control and which is in charge of the namespace. (This is really a corollary of the first rule.)

  • Include a year and a month. Typically this is done in YYYY/MM format and indicates the specification's first use.

  • Don't include a trailing slash.

  • Make sure that what the namespace refers to is discernible to some degree by including words after the year that make it clear.

An Example Session Header
<soap:Envelope
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
 <Session xmlns="http://keithba.com/2002/05/Session">
     <Id>uuid:1111-1111-1111-1111</Id>
 </Session>
</soap:Header>
 <soap:Body>
    ...
 </soap:Body>
</soap:Envelope>

It's interesting that the <Id> can take as a value any URI. In the schema for this, it would be of type <xsd:anyURI>. This allows the session's identifier to be a GUID, as in Listing 14.2, or even a more "normal" URI such as http://keithba.com/purchaseOrder/20202198. It would be unusual not to have a unique session identifier; therefore, anything but a GUID would be uncommon.

With ASP.NET's HTTP cookies, there is no specific way to initiate a session, and the session is truly understood only on the server's end. In effect, there is no client side of a session. With a SOAP-based session protocol, on the other hand, it makes sense to want some additional features:

  • Explicit initiation and expiration

  • Expiration date and time

  • Ability for the client or server to contain, initiate, and expire a session

With this in mind, we need to add an <Initiate> element—which also contains the expiration date and time of the session—as well as a <Terminate> element for ending the session. Listings 14.3 and 14.4 show a session initiation and termination, respectively.

An Example of Session Initiation
<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
  <Session xmlns="http://keithba.com/2002/05/Session">
      <Initiate>
           <Expires>4/4/2002 12:00:00PM PST</Expires>
      </Initiate>
      <Id>uuid:1111-1111-1111-1111</Id>
  </Session>
</soap:Header>
  <soap:Body>
    ...
  </soap:Body>
</soap:Envelope>
An Example of Session Termination
<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
 <Session xmlns="http://keithba.com/2002/05/Session">
     <Terminate />
     <Id>uuid:1111-1111-1111-1111</Id>
 </Session>
</soap:Header>
 <soap:Body>
   ...
 </soap:Body>
</soap:Envelope>

The classes for building this session header are fairly straightforward. Listing 14.5 shows the class for the <Session> header itself.

The <Session> Header Class
[XmlRoot(Namespace="http://keithba.com/2002/05/Session")]
public class Session : SoapHeader
{
     private String _Id;
     public String Id
     {
          get
          {
               return _Id;
          }
          set
          {
               _Id = value;
          }
     }
     private Initiate _Initiate;
     public Initiate Initiate
     {
          get
          {
               return _Initiate;
          }
          set
          {
               _Initiate = value;
          }
     }
     private Terminate _Terminate;
      public Terminate Terminate
     {
          get
          {
               return _Terminate;
          }
          set
          {
               _Terminate = value;
          }
     }
}

Notice a couple of things from Listing 14.5:

  • The [XmlRoot] attribute is used to set the namespace of the <Session> element and its children.

  • The Initiate and Terminate elements are classes.

The Initiate class contains an expiration date and time as well, as shown in Listing 14.6.

The Initiate Class
public class Initiate
{
     private DateTime _Expires;
     public DateTime Expires
     {
          get
          {
               return _Expires;
          }
          set
          {
               _Expires = value;
          }
     }
}

The Terminate class is entirely uninteresting, but it must be a class in order to be a child element. Here is the code:

public class Terminate 
{
}

Listing 14.7 shows the schema for the session header.

The Session Header Schema
<xs:schema
     targetNamespace="http://keithba.com/2002/05/Session"
     xmlns:tns="http://keithba.com/2002/05/Session"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     elementFormDefault="qualified"
     attributeFormDefault="unqualified">
     <xs:element name="Session" type="tns:Session"/>
     <xs:complexType name="Session">
          <xs:sequence>
               <xs:element
                    name="Id"
                    type="xs:string"
                    minOccurs="0"/>
               <xs:element
                    name="Initiate"
                    type="tns:Initiate"
                    minOccurs="0"/>
               <xs:element
                    name="Terminate"
                    type="tns:Terminate"
                    minOccurs="0"/>
               <xs:any namespace="##other"/>
          </xs:sequence>
</xs:complexType>
<xs:complexType name="Initiate">
        <xs:sequence>
               <xs:element name="Expires" type="xs:dateTime"/>
               <xs:any namespace="##other"/>
        </xs:sequence>
</xs:complexType>
<xs:complexType name="Terminate">
      <xs:sequence>
          <xs:any namespace="##other"/>
     </xs:sequence>
</xs:complexType>
</xs:schema>

Notice that all of the elements have an <any> element defined for them. This allows for extending this schema without needing to version it— which will come in handy later, when we extend this session to handle reliability features. Figure diagrams this schema.

1. A Graphical Look at the Session Schema

graphics/14fig01.gif

At this point, using this session class within a service would be very simple:

public class Service1 : WebService 
{
     public Session Session;
     [WebMethod]
      [SoapHeader("Session",
                Direction=SoapHeaderDirection.InOut)]
     public void SubmitPO( String PO )
     {
          //code would go here
     }
}

The client side would work similarly to any header. Of course, having access to these headers isn't enough, because the application needs to be able to tie any particular sessions together. However, it's outside the scope of this book to fully explain how this could happen. In general, a property bag or named object dictionary would be more than enough. The only code then needed would be another dictionary to contain the specific session's dictionary.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows