FW: Why web-style session state management doesn't work for web services, methinks

I thought this may be interesting here as well, although it's somewhat
ASP.NET related...

-----Original Message-----
From: Clemens Vasters [mailto:clemensv@newtelligence.com] 
Sent: Friday, January 18, 2002 7:19 PM
To: 'dotnet@discuss.develop.com'
Subject: Why web-style session state management doesn't work for web
services, methinks


[First, sorry for the cross-posting on DOTNET/SOAP, but I really didn't
know where this fits best. Also, sorry for the abuse as my mind dump.]

I am currently writing something about web services performance and part
of the server-side performance consierations is, of course, state
management. Now, when I was writing that section this morning, I
suddenly realized why I always had this very bad gut feeling about using
something like cookie-based state management (as ASP.NET does it) for
web service state management. Here's what I think and it'd be great to
hear some opinons. I can't follow all of the discussions here (who can?)
so this may even have been discussed, but I didn't see it. 

Simple technical problems first. The classic approach to state
management with HTTP is to pass some kind of identifier to the client
that is returned in a subsequent request and allows the server to
reassociate the request with the buffered session state. There are three
ways to do this: Cookies, URL injection and soring references in the
response body: 

(a) Use of cookies: From the start, cookies aren't covered by RFC2616
(HTTP 1.1), but by RFC2964 (HTTP State Management) and entirely
optional. If a client receives a cookie that the server uses for
managing state, the client can just completely ignore that cookie and
still play fair by all written rules. While perfectly legal, this will
break cookie based state management.

(b) Use of URL injection: It's pretty obvious, but I am still going to
say that the ASP.NET style cookieless sessions that work by injecting
information into returned reference URLs won't work, because a web
service client simply doesn't receive reference URLs to click on.

(c) Response body: The most obvious place would be a SOAP header.
Perfectly doable, but there is no widely circulated proposal for this
(or I didn't look hard enough) and every approach is therefore
proprietary. My very special ASP.NET problem with this: ASP.NET's HTTP
session model is a closed shop. Most classes are private to the
HttpModule that sits in the ASP.NET pipeline and filters/injects the
cookie and/or URL from/into the in/outbound stream. So if I wanted to
relay some sort of session cookie via SOAP header I'd have to roll my
very own session management as well. This is unless I would write an
HTTP module that would sit before the ASP.NET session manager and would
parse and peel the session-id from the SOAP header and set it as an HTTP
Cookie header for ASP.NET to pick up. On the way out, I'd have to grab
the ASP.NET session cookie and stick it into the SOAP header. That'd be
very nasty, because that'd require SOAP processing entirely outside of
the web services infrastructure.

So, in essence, if the client chooses to ignore cookies, the state
management issue doesn't really look good anywhere at this point.

That were the technical "status quo" issues. Pretty bad, but manageable
in some ways and by convention. 

----

Now, thinking about this, I have come to the conclusion that the
web-style approach to state management using any of these three
techniques is (!) utter nonsense (!), because they are all based on a
completely wrong assumption. They still assume that the client is
completely dumb and just smart enough to push back an opaque value that
the server is providing. It is also based on the assumptions that the
transport is HTTP and that web service call sequences are never
cascading.

Here's the (somewhat lengthy and right off the top off my head ==
confusing) reason why I think that's wrong:

Given I have some kind of web service proxy for a web service interface
and a call sequence A->B. The call sequence happens to establishes some
sort of server state in an initial call to A (for some good and
justifiable reasons) and that state is required to execute B. When A is
called a second time, the state is not newly initialized but updated
taking current state information into account. This is perfectly okay as
long as the client stays in the same context. If the client switches
contexts (in the sense of "business logic contexts"), it will want a new
context (freshly initialized server session if you will) to be set up on
a call to A. 

How's that currently done? It works because when establishing a new
logical context you will create a new proxy that doesn't know about the
cookies that were sent previously and therefore ASP.NET will create a
new session and hand out a new session cookie. So, the ASP.NET proxy
("Add Web Reference" proxy) does indeed keep record of a remote object
identity through its caching of cookies. The remote object is the stored
state. If you want a new object, create a new proxy.

So far, so good. 

Given I have a web service proxy for a web service interface and a call
sequence A->B. The web service establishes some sort of server state in
an initial call to A and that state is require to execute B. The
implementation of A calls the method X of another web-service with a
call sequence X->Y, where that web service establishes some sort of
server state in an initial call to X and that state is require to
execute Y. Now, A calls X, B calls Y. Y depends on the state of X
established by the call from A.

Because A and B are stateless methods and the web service class is in
fact discarded between calls, B will build a new proxy and the state of
the secondary web service will no longer be available, because the
original proxy didn't live to remember the cookie. Bummer.

While this is just an effect of statelessness in ASP.NET and there are
technical workarounds that I could figure, I could just as well make the
scenario quite a bit worse and virtually unmanageable. The question that
will remain is: "Can any web service server implementation make a
correct judgement about the context of its invocation based on
traditional, web-style state management means?"

I say: The only instance that can make any proper judgement about call
contexts and about how and when state needs to be managed is the topmost
caller.

If the initial caller would tell A that it is in context "C" and A would
pass this on to X, the same context "C" could be used by B and Y to
retrieve their state information later on, independent of implementation
issues and even transport. I think it's fundamentally wrong to have any
web service server decide on state identity in a stateless environment
where cascaded service request can occur. The topmost client is the only
instance can decide about logical context boundaries and therefore it
must be the instance controlling state management boundaries.

Therefore it's my current state of opinon, that a SOAP header is
required that defines the current call context and the context origin
that must be understood by all state managing services. 

To illustrate my point:

<ctx:context contextId="uuid:0B4E71D0-5383-4db2-9BA5-EE17B7E46627" 
             contextExpires="2001-18-01T23:00:00+01:00"
             soap:mustUnderstand="1"
             xmlns:ctx="urn:schemas-newtelligence-com:soap:contexts"/>


* contextId is an URI that MUST be globally unique and that identifies
the logical call context originating from the topmost web service
client. 
* A web service server MUST understand the context and associate all
session state with the contextId value.
* If a web service client is itself part of a web service server
implementation, it MUST NOT create a new context, but MUST pass the
contextId and the contextExpiration on to its server, if the server
implementation received a context header.
* A web service server MUST keep the session information associated with
the context until the time indicated by contextExpires. If the server
decides that the time-span between "now" and the contextExpires time is
too long (to avoid DoS) it MUST fail with
ctx:Client.ContextTimeoutNotAccepted [this is a client provoked error]
* A server SHOULD allow a timespan of 10 minutes for context expiration.
* If the context has expired (independent of whether the information is
still available), the server MUST fail with
ctx:Client.ContextTimoutExpired [this is a client provoked error]
* The context owner (topmost client) MAY adjust the contextExpiration
value into the future at each call.


In essence I am saying that the identifier with which all state must be
associated throughout the web service call hierarchy must be defined by
the topmost caller.

Shoot me! :)
Clemens

Received on Friday, 18 January 2002 14:26:12 UTC