Tuesday, April 15, 2014

Dealing with HttpOnly cookies in Windows Phone 8

The way we have to pass cookies from one response to another request is by using the CookieContainer class. That also work for HttpOnly cookies which are stored inside the CookieContainer but they are never exposed publicly. If you set the same instance of that CookieContainer to the next request it will set the hidden cookie there (as long as the request is made to the same site the cookie specifies).

That’s ok until you need to serialize and deserialize the CookieContainer because you are restoring state in your phone application. As the HttpOnly cookie is keep in private fields the default serialization behavior doesn’t persist that data and on deserializing to the new CookieContainer instance the hidden cookie is gone.

The solution for that would be going one level down and use Sockets directly for that request, read the raw request as a string, extract the cookie and set it to the next requests. Thankfully there a new library part of the Windows 8 API for dealing with Sockets (Windows.Networking.Sockets) which leverage the latest asynchronous features of the .NET Framework (async, await).

Here's the code for using Sockets in Windows Phone 8 and getting the response as a string:
public class TcpClient : ITcpClient
    public async Task<string> Send(Uri requestUri, string request)
        //connect to host
        var socket = new StreamSocket();
        var hostname = new HostName(requestUri.Host);
        await socket.ConnectAsync(hostname, requestUri.Port.ToString());

        //send the request 
        var writer = new DataWriter(socket.OutputStream);
        await writer.StoreAsync();

        //read the response
        var reader = new DataReader(socket.InputStream) 
                          InputStreamOptions = InputStreamOptions.Partial 
        var count = await reader.LoadAsync(512);
        if (count > 0)
            return reader.ReadString(count);
        return null;

And here’s an example of using that class:
var responseString = await TcpClient.Send(requestUri, GetStringRequest());
var cookieValue = ParseCookieFromString(responseString);
//set the cookieValue to CookieContainer as a not HttpOnly cookie.
private string GetStringRequest()
    return string.Format(@"POST {0} HTTP/1.1
Accept: application/json
Content-Length: {1}
Accept-Encoding: identity
Referer: http://myreferer.com
Accept-Language: en-US
Content-Type: application/x-www-form-urlencoded
User-Agent: NativeHost
Host: {2}
Connection: Keep-Alive
Cache-Control: no-cache
Pragma: no-cache
{3}", yourRequestURL, yourRequestContentLenght, yourRequestHost, yourRequestContent);