Silverlight Networking - Getting credentials to work, a possible work-around?

safeA few days ago I blogged about the trouble of getting Credentials to work in Silverlight Networking. The troubles still exists sadly.  I've been thinking about creating a simple Silverlight application that makes use of the del.isio.us API. But sadly this API, as most of the online API's do, makes use of Basic Auth over HTTP or HTTPS. I have thought about many different Silverlight applications that makes use of online API's, but sadly I wasn't able to create them because of the lacking of authentication possibilities. But I just got an idea, by thinking out-of-the-box. I might just get out of this very limiting box. Although it's just an idea that's not tried out in the wild.

The idea

Some time ago I watched a part of the "ASP.NET MVC Storefront" screencast which stated something about making use of the ASP.NET Ajax javascript libraries manually. This standalone library can be downloaded from here.

I know it is possible to call Javascript from within a Silverlight application, and also to call back to the Silverlight application from within Javascript. So why not making use of the Browser and Javascript capabilities for creating WebRequests. I'm not totally sure it's possible to set the "Auth" header from Javascript, but I've got a good feeling about this. The only thing I still need is some time to work it out. More will follow...

Silverlight Networking - Getting credentials to work, unsuccessful.

One of the things the Silverlight Networking stack lacks is the support for credentials. Where we have things like the below in the full CLR, Silverlight doesn't have any support.

httpWebRequest.Credentials = new NetworkCredential(username, password);

Investigation

So what about some investigation of the use of credentials. I remembered something about the usage of request headers for sending credentials. After a few minutes googleing I got support from a page on Oreilly about HTTP Headers. I found out the headername is "Authorization" and the value consist of authorization scheme and the credentials base64 encoded.

Tryout in full CLR

I tried to get it to work within the full CLR. I made two extension methods, one to set the RequestHeader and the other to convert a string to base64 encoding. As you can see the username and password will be divided by a colon before encoding the string to base64.

This code seemed to work on the CLR. So I thought very nice, on to the limited Silverlight CLR.

1 internal static class Extensions 2 { 3 public static void SetCredentials(this WebRequest request, string username, string password) 4 { 5 request.Headers["Authorization"] = string.Format("Basic {0}", 6 string.Format("{0}:{1}",username, password).EncodeTo64()); 7 } 8 9 public static string EncodeTo64(this string toEncode) 10 { 11 byte[] toEncodeAsBytes 12 = Encoding.ASCII.GetBytes(toEncode); 13 14 string returnValue 15 = Convert.ToBase64String(toEncodeAsBytes); 16 return returnValue; 17 } 18 }

Trying to get it to work with the Silverlight CLR

The first thing I had to do was to change the Encoding to use Encoding.Unicode, the ASCII character set isn't available as it seems. After this all the compilation errors were gone. But something very frustrating came around: "Error: This header must be modified using the appropriate property." Was I doing something wrong? It worked on the full CLR, why not within Silverlight?

After an investigation with Reflector I found out about a line of code that checks if a header is supported. A few clicks further and there was the list of not allowed headers.

And this list sadly also contains the headername "Authorization". I also found out that the list of allowed headers has a length of 12 headers. But also new custom headers are supported. The lists below are read from doing some inspection with Reflector.

List of unsupported headers: Allow, Accept, Authorization, Accept-Charset, Accept-Encoding, Accept-Language, Cookie, Connection, Content-Type, Content-Range, Content-Length, Content-Location, Date, Expect, Host, Keep-Alive, Last-Modified, Max-Forwards, Proxy-Authorization, Range, Referer, TE, Trailer, Transfer-Encoding, Upgrade, User-Agent, Via, Warning, Allowed, Connect, Content-Transfer-Encoding, Delete, Get, Head, Options, Post, Proxy-Connection, Public, Put, Request-Range, Trace, Uri, X-Flash-Version, Accept-Ranges, Age, ETag, Location, Proxy-Authenticate, Retry-After, Server, Vary, WWW-Authenticate.

List of supported headers:  Cache-Control, Content-Encoding, Content-Language, Content-MD5, Expires, From, If-Match, If-Range, If-None-Match, If-Modified-Since, If-Unmodified-Since, Pragma. Besides this also custom headers are supported.

I hope we get support for using credentials within requests as soon as possible. Almost all services on the web use some form of credentials don't they?

Silverlight Threading - Getting back to the UI thread

I've been investigating Silverlight for a few days. This will be the first post in the Silverlight category, more will follow. After experimenting with some user interface effects I started investigating the Silverlight Networking possibilities. I was able to get some results of a network call but I wasn't able to get it to the UI in the first place. So after some investigation I found out about the following solution.

Working Asynchronously

It's only possible to work asynchronously with the networking stack. This is to prevent a hanging UI / Browser. This is not a very bad decision although it makes communicating with Webservices and other things on the web more difficult. I'm not into explaining a lot of things about networking and working asynchronously in common. When starting an asynchronous call to for example a Webservice you will give the call a pointer to the function that needs to be called on completion. This could be for example a not working callback function.

1 void helper_ResponseComplete(HttpResponseCompleteEventArgs e) 2 { 3 resultBox.Text = e.Response; 4 }

What's wrong?

The trouble with the code example above is the call to a UI part 'resultBox'. You could thing that the asynchronous call works on a background thread and that the callback automatically works on the UI thread. This is not the case. The trouble is that you don't really get an exception that your doing something wrong, just nothing happens. It took me some time to find out the solution.

A simple solution

Every DependencyObject has a property called Dispatcher. This Dispatcher has some intelligent code to make it possible to get back to the UI thread. It is possible to call 'BeginInvoke' on the Dispatcher property. This method will require an Action delegate or a simple delegate with eventually arguments. I really like the use of a lambda expression. The following code wraps the code from the previous example in a lambda expression and uses the Dispatcher to get it back to the UI.

1 void helper_ResponseComplete(HttpResponseCompleteEventArgs e) 2 { 3 Dispatcher.BeginInvoke(() => resultBox.Text = e.Response); 4 }

Luckily the code being executed is just one line, so this simple solution works. But most of the time the code to be executed on the UI thread is more than just one line. So we need a more advanced solution.

A more advanced solution

The Dispatcher property also has a method to check whether or not the current thread is the UI thread. The example below checks if the current thread is an UI thread, when not it calls itself with the same arguments but then through the Dispatcher. The advantage with this is the possibility to have a code block of a few lines within the else-statement.

1 void helper_ResponseComplete(HttpResponseCompleteEventArgs e) 2 { 3 if(!Dispatcher.CheckAccess()) 4 Dispatcher.BeginInvoke(()=>helper_ResponseComplete(e)); 5 else 6 { 7 resultBox.Text = e.Response; 8 } 9 }

Updates 17/07

Some of my readers had additions for this article. So it's fair to add this information at the end of the article I think.

Bart Czernicki noted: When calling a WCF service through a proxy, the results get properly dispatched on the UI thread.

Brian Leahy noted: When making use of databinding the binding will take of resynchronizing. His code example:

1 void helper_ResponseComplete(HttpResponseCompleteEventArgs e) 2 { 3 this.Message= e.Response; 4 } 5 6 private String _message; 7 public String Message 8 { 9 get 10 { 11 if(_message == null) 12 { 13 GetMessageAsync(); 14 } 15 return _message; 16 } 17 set 18 { 19 _message = value; 20 PropertyChanged(value,”Message”); 21 } 22 }

Thanks for the comments!