SessionStorage via client side cookies

classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

SessionStorage via client side cookies

johnrengelman
I'm looking at implementing client side cookie storage for Sessions, but I'm getting a bit lost in the Ratpack Session API. (Disclaimer: I don't know much about how cookie sessions are implemented, so I'm referring mostly to the Grails plugin code here: https://github.com/benlucchesi/grails-cookie-session-v2)

Basically my understanding of the flow is this (ignoring the encrypted cookies for the moment):
1) client sends request w/ cookies
2) Server unpacks the contents of the cookies
  a) combine all cookies values (since each is limited by size) in order (String[].join basically)
  b) decodeBase64 on the resulting String to get a byte[]
  c) Potentially decompress this byte stream using GZIP
  d) Deserialize the byte[] buffer into an object that represents the session
3) Server does it processing including updating any session values
4) Serialize the session back to cookies and set in the response
5) Respond to the client

Correct me if I'm wrong, but I only want to do the deserialization/serialization once, correct?
I'm failing to see how that would fit in with the current Session API in Ratpack.
For example, all the items provided by `ratpack-session` seem highly tied to storing the session on the server side by providing an ID reference for the session. This isn't really necessary since the client is sending the entire session along with the request. In the case of the `MapsSessionModule` implementation, the Session ID is used to index into a Map of SessionStorage instances on the server.

So my thoughts are that I only need to provide the binding for SessionStorage to the Request and that binding would do the deserialization of the cookies into a SessionStorage instance that can then be bound.
However, the issue then is when do I serialize the session back onto the response?

Right now it appears to me the only way to really do this is by using an `ExecInterceptor` and doing the serialization at the end of the `intercept` method like so:

```
void intercept(ExecInterceptor.ExecType execType, Runnable continuation) {
  continuation.run()
  saveSession(sessionStorageInstance)
}
```

Am I heading down the right path?
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

johnrengelman
So I've encountered an issue with my approach. Using the `ExecInterceptor` doesn't work because a handler in the chain could send the response to the client before the cookies are added to the response object.

This is the code that I'm using in my handler:
```
@Override
    void handle(Context context) throws Exception {

        context.addInterceptor(new CookieSessionExecIntercepter(context.getRequest(), context.getResponse()), new Action<Execution>() {
            @Override
            void execute(Execution execution) throws Exception {
                context.insert(handler)
            }
        })

    }
```

and then my interceptor does this:
```
@Override
    void intercept(ExecInterceptor.ExecType execType, Runnable continuation) {
        continuation.run()
        saveSession(sessionStorage, response)
    }
```

But if I have a handler like this:
```
get('test') { SessionStorage session ->
            println "Current session:"
            println session

            ++requestCount
            session['requestCount'] = requestCount
            session[requestCount.toString()] = UUID.randomUUID().toString()
            render json(session)
        }
```

then the call to `render json(session)` sends the response back to the client, before the context is returned to the interceptor to call `saveSession()`

It seems like there needs to be a means for an interceptor to register some code to be executed immediately before any send, some type of Response decoration.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

Luke Daley
Administrator
In reply to this post by johnrengelman
You’re right, we don’t have the appropriate hook. We should add one. Want to take a crack?

BTW the session stuff is going to change: https://github.com/ratpack/ratpack/issues/447
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

Luke Daley
Administrator
In reply to this post by johnrengelman
I’d never heard of client side session storage.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

Massimo Lusetti
It's used somewhere, Play and Apache Tapestry5 sure have one but it's limited in the number of bytes you can store.

Basically is used as a pointer to get to retrieve "session" data from other locations and have the http handling stuff completely "stateless".

BTW It's nice to see #447 getting attention.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

johnrengelman
In reply to this post by Luke Daley
Perhaps "client-side" isn't the right terms. But yeah, its allows sessions to be persisted in cookies thus allowing server scaling / reboots without losing sessions (and not requiring some external session storage).

Yeah, I'll try and take a crack at an API and see what you think.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

johnrengelman
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

Luke Daley
Administrator
might be better to be more specific. 

void beforeSend(Action<? super MutableHeaders> headerFinalizer)
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

johnrengelman
Updated - https://github.com/johnrengelman/ratpack/commit/347fbd4c6b77836872a4616fac87217d54a9d805

The only thing about this is that you lose the convenience of the the `cookie(name, value)` method in my particular scenario. But that's easy enough to replicate where needed with the direct access to the headers.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

Luke Daley
Administrator
Hrmm, I wonder if we should introduce ResponseHeaders extends MutableHeaders that has the cookie convenience methods on it.

Will think about it.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

Luke Daley
Administrator
In reply to this post by johnrengelman
Might just make beforeSend() take an Action<? super Response>. 

It’s a bit awkward because the user really shouldn’t use any of the response sending methods from beforeSend(). It would be nice to indicate this in the API, but that gets awkward as you point out.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

johnrengelman
What about if we make a super interface to Response that doesn't include the sending methods?

interface ResponseDecoration {
  void cookie(String name, String value);
  ....
}

interface Response extends ResponseDecoration {
  void send();
  ....
}
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

johnrengelman
I guess that interface could just be a sibling interface to Response....just defines the same methods and then DefaultResponse would implement both.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

Luke Daley
Administrator
Let’s introduce ResponseMetaData, that has all the Response methods minus the send* methods and have Response extend this.

Response will have to override the “this” returning methods to return Response instead of ResponseMetaData.
Reply | Threaded
Open this post in threaded view
|

Re: SessionStorage via client side cookies

johnrengelman