Archive for October, 2009

Using the Silverlight HttpClient in WCF, and still passing cookies.

In addition to using the browser’s own HTTP stack, Silverlight 3 introduces the new HttpClient. For me, it shines mostly because of these reasons:

  • It supports SOAP faults that are passed with an HTTP 500 status code, and does not pretend everything that was not utterly right was a 404. The only way to work around it was to let the server return a 200 (OK) HTTP status code, which is a hack, a lot of code, and wrong.
  • It executes requests in parallel. When using the browser HTTP stack, WCF sends requests sequentially on the same thread. This is more than a small nuisance when you’re building a composite application that requests resources in parallel because its different parts do not know of each other. With the ClientHttp stack, the requests are executed in parallel, just as you would expect them to.

However, there are two problems (that I know of). First of all, flaky SSL certificates cause problems. You might have a development environment that uses SSL on build servers and developer machines (good) but does not use generally trusted certificates, but some that you’ve just created for yourself (good enough). When you use Firefox to access your app, you just add a permanent security exception and off you go… not. Because since the ClientHttp stack does not use the browser’s SSL negotiation, but WinINET, the certificate is not accepted, and you won’t be able to connect. But once you’ve added the certificate to the machine’s certificate store, you’re fine (I don’t know how things are on the Mac, but I suspect something similar there – got to check tomorrow. I don’t have one at home).

The more severe limitation is that cookies are not automatically passed back to the server. You might well do without cookies – if you don’t need to pass them to the server in WCF calls, there’s no worries here. But in case you use Forms Authentication or have a load balancer that relies on cookies, you’re going to miss them.

Like things are with WCF, there is of course a way to get around that, and it’s totally illogical and sparsely documented. There is a special BindingElement System.ServiceModel.Channels.HttpCookieContainerBindingElement that can be added to a Binding in order to enable cookies on channels. A very interesting fact about that class is that it only exists on Silverlight, not in the normal .NET library.

Here’s a simple factory class that creates WCF service proxies that uses that code.

using System;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Browser;

namespace GreenIcicle.WcfHttpClientWithCookies
{
  /// <summary>
  /// Creates WCF service clients that use SOAP 1.2 via the ClientHttp stack, and passes cookies
  /// to the server.
  /// </summary>
  public static class ServiceClientFactory
  {
    /// <summary>
    /// Backing store for cookies.
    /// </summary>
    private static IDictionary<string, string> m_Cookies;

    /// <summary>
    /// Return the lazily initialized dictionary of cookies.
    /// </summary>
    public static IDictionary<string, string> Cookies
    {
      get
      {
        if( m_Cookies == null )
        {
          throw new InvalidOperationException( "The cookie jar has not been initialized: Call ServiceClientFactory.ReadCookies when the application starts" );
        }
        return m_Cookies;
      }
    }

    /// <summary>
    /// Creates a service proxy for a given service contract.
    /// </summary>
    public static T CreateServiceClient<T>( Uri serviceUrl )
    {
      // Determine whether to use transport layer security (HTTP over SSL).
      // Depending on whether HTTPS is used, create a different BindingElement for
      // the transport layer of the Binding.
      bool useTransportSecurity = serviceUrl.Scheme.Equals( "https",  StringComparison.InvariantCultureIgnoreCase );
      BindingElement transportBinding = useTransportSecurity ?
        new HttpsTransportBindingElement() :
        new HttpTransportBindingElement();

      // Since the ClientHttp stack is used, it is necessary to define cookies manually.
      // We read the cookies from the browser window and attach them to the WCF binding
      // via an additional BindingElement that lets us inject cookies.
      BindingElement cookieBinding = new HttpCookieContainerBindingElement();

      // Create a custom binding that uses the specified elements for transport and 
      // accepting cookies. The order of the bindings is relevant; the transport binding 
      // needs to come last it's the lowest in the stack.
      Binding binding = new CustomBinding( cookieBinding, transportBinding );

      // Set the address for the service endpoint.
      EndpointAddress address = new EndpointAddress( serviceUrl );

      // Let the ChannelFactory create the service client.
      ChannelFactory<T> factory = new ChannelFactory<T>( binding, address );
      T channel = factory.CreateChannel();

      // Because we've injected the HttpCookieContainerBindingElement, the channel supports cookies:
      // it exposes a CookieContainerManager. 
      IHttpCookieContainerManager cookieManager =
        ( (IChannel)channel ).GetProperty<IHttpCookieContainerManager>();

      // The CookieContainerManager can, but does not have to, provide a Cookie Container;
      // in the case it has not yet been created instantiate one now.
      if( cookieManager.CookieContainer == null )
      {
        cookieManager.CookieContainer = new CookieContainer();
      }

      // Walk through the cookies of the browser window the application lives in and 
      // add the cookies to the channel. The URL for the cookie is set to the application root.
      // We get to that URL by quering the application URL - that's the URL of the XAP - 
      // and go one level up.
      Uri applicationUri = new Uri( Application.Current.Host.Source, "../" );
      foreach( var cookieContent in Cookies )
      {
        Cookie cookie = new Cookie( cookieContent.Key, cookieContent.Value );
        cookieManager.CookieContainer.Add( applicationUri, cookie );
      }
      return channel;
    }

    /// <summary>
    /// Read cookies from the browser into a local store.
    /// </summary>
    public static void ReadCookies()
    {
      Deployment.Current.Dispatcher.BeginInvoke( () =>
      {
        // Initialize dictionary
        m_Cookies = new Dictionary<string, string>();

        // Get the cookies from the browser. It returns it in a single, semicolon-delimited string.
        string cookies = HtmlPage.Document.Cookies;
        string[] cookieSplit = cookies.Split( new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries );
        foreach( string cookie in cookieSplit )
        {
          string[] keyAndValue = cookie.Split( new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries );
          if( keyAndValue.Length == 2 )
          {
            string cookieKey = keyAndValue[ 0 ].Trim();
            string cookieValue = keyAndValue[ 1 ].Trim();

            // add cookie to dictionary
            m_Cookies.Add( cookieKey, cookieValue );
          }
        }
      } );
    }  
  }
}

In order to tell Silverlight to use the ClientHttp stack instead of the browser’s, it needs to be registered. This can best be done in the application startup event handler. In addition, the ServiceClientFactory needs to read the cookies from the browser. That gives us this code block in the App.xaml.cs file:

    private void Application_Startup( object sender, StartupEventArgs args )
    {
      this.RootVisual = new MainPage();

      // Register the client http stacks for all web requests
      WebRequest.RegisterPrefix( "https://", System.Net.Browser.WebRequestCreator.ClientHttp );
      WebRequest.RegisterPrefix( "http://", System.Net.Browser.WebRequestCreator.ClientHttp );

      // Read the cookies so they can be used for WCF requests
      ServiceClientFactory.ReadCookies();
    }

, ,

8 Comments

Google Wave: It’s for Apps

I think a so far underhyped aspect of Google Wave is that it has huge potential for integration of communication and applications – and applications does not mean Twitter or a blog engine this time, but brick-and-mortar LOB enterprise applications.

A reoccurring use case is that people want to be updated when things are happening within their little business kingdom, and to have a way to directly react without having to leave the context of the communication application they’re currently working with. Today, you see this demand normally satisfied by an Outlook plug-in, or some awkward e-mail parsing.
Wave offers a much more elegant solution. It offers two important extensibility features:

  • Robots. Server-side application modules can take part in a Wave, and create new ones. This is the technology that enables the Twitter and Blogger integration shown in the demos.
  • Gadgets. A gadget is a client-side piece of user interface that provides data visualization and manipulation other than text – you see examples for this in the Yes/No/Maybe or Maps gadgets.

Via a robot, the application can create new wave messages, and react to changes in its content. Using a gadget, the application can provide a structured visualization for data, and more importantly: a structured way for the user to provide feedback, that does not require parsing text.

For example, a manager at a bike shop could be informed by a wave if the stock of spokes is below the critical level. The wave includes a gadget that includes a “Order new spokes” button. When the manager clicks the button, the gadget changes the content of the wave, the robot catches that change, and informs the ERP solution to get moving.

I think we’ll see a lot of great deal of things like that once Wave becomes ready for business – and that’s probably not before there are implementations of Wave not owned and run by Google. But that’s another story.

Leave a Comment

HTML to XAML

It was not before proposing HTML templates for formatting content in a Silverlight app in a heated discussion that I noticed that there’s no decent way to display HTML in Silverlight.

The indecent thing you can do is overlay the Silverlight app with a piece of the browser’s document. This is an OK approach for a lot of cases, but not for this one: you get problems with scrolling and the z-order of elements within your app, you become vulnerable to all the encoding and scripting problem you wish so hard you didn’t have, and don’t even mention accessibility or print support should it ever be implemented (and if they take the vote on Silverlight features only half-serious, if should come).

The other way is transforming HTML into Silverlight. There’s no built-in support in the platform for that, but I found this article by David Anson. It offers a very useful HTML control that renders HTML. When it find correct XHML it will transform it to XAML, otherwise it uses the browser’s capabilities. However, there are some things that I wish it would do:

  • Not use the browser – I don’t need to handle malformed HTML since it’s only my own templates I want to render.
  • Support table layout.
  • Support CSS. All the decent WYSIWYG editors create CSS formatting.

So I decided to do one by myself (well, mostly for the fun of it). As it turns out, the task is quite simple: gthe behavior of elements that are displayed as blocks in HTML can be mimicked with a StackPanel. Differently formatted text in <span>tags can be displayed as Run elements in TextBlocks. Tables care very much like Grids.

The hard part was to get CSS to work, but the most important building block was already at hand: the CSS Parser by BoneSoft prove to be extremely useful. It does not only understand the basic syntax of CSS, but adds niceties like recognizing the unit of property values and converting hex value to Color structs. It needed to slightly be adapted to Silverlight (System.Windows.Media instead of System.Drawing; no ASCIIEncoding), but that was it.

The current state of the project is that it understands the basic HTML formatting tags (p, div,br,  h1. h2, h3, big, small, u, i, b), CSS style attributes for the box model and text formatting, and tables. The TODOs are CSS style sheets defined in a <style> element with proper matching, and a proper test suite. Links and script are not supported because I don’t need them right now. When done, I’m going to publish the source code; I still have to find the right place and license, but free distribution and modification is intended.

UPDATE:
Ummm, no. The thing has now dissolved into a part of a larger and proprietary project, and publishing is out of the picture at the moment.

Leave a Comment

Hey Dropbox, what about this?

I’m extremely fond of Dropbox. If you don’t know it yet: it’s cloud storage made extremely easy. You get 2GB free, and more on a subscription basis. You’ll want to use once you’ve tried. They’ve also got a very seductive marketing tool: you get 250MB extra for each new customer you invite. So if you want to try Dropbox, this is the best link to use, because it adds them to my account. It’s not like they’ve got me hooked on that or so.

I’m missing a feature though: I want to be able to download files from somewhere directly into my Dropbox (or Skydrive, or other cloud storage). Reason number one is my DSL line: 6MBit down, His Trickling Snailness up. To get something into the box, I need to first download it and then wait for quite a time until it’s up. It would be so much greater if I could just instruct some cloud service to get the file at that URL and directly shove it into the box, and then have it downloaded onto my computer when it syncs.

Reason number two is that I might want to download stuff, but not on the computer or phone I’m on. For example, when I’m at work. When I’m using my phone. Or when I use my laptop on 3G. What I currently do is drop the URL into Evernote – not bad either, but I’d like it better to just have the file downloaded immediately, and then receive it when I’m on a fast and cheap network.

So what do we need?

  • A server app that you pass a URL and your DropBox account. It downloads the file and uploads it to Dropbox.
  • For more convenience, a client app that can store your credentials, and that can sit in the tray, watch the clipboard, or be a Firefox extension.

I don’t know if I’d do an iPhone app – it should be OK when the web app lends itself to that form factor.

And it would be good for Dropbox too. They just loose money on people like me (I’ve currently got about 500MB in the box). They’d be much happier if I got above my limit for the free account – and I would absolutely pay the 10$ per month. The easier it is for me to get stuff into the box, the easier I’d be tempted to reach the critical limit between free and paid.

UPDATE: Dropbox have done The Smart Thing for feature requests and let you vote for your favorite feature. If you like the idea, please vote here:
https://www.dropbox.com/votebox/817/right-click-download-to-my-dropbox

,

Leave a Comment

Follow

Get every new post delivered to your Inbox.