This is going to be a two parter. :) Today, we’ll dig into the core networking capabilities of the HTTP stack for site of origin communication. Next time, we’ll go deep into understanding our HTTP cross domain support.
(Series Links: Part 1, Part 2, Part 3)
So, You Want To Phone Home…
In Silverlight 2, by default, you are only able to talk back to your site of origin server. This is in line with the general browser sandbox, and is designed to prevent cross site forgery.

To determine if a HTTP request is going back to site of origin, we look at the deployment URI of the XAP and the request URI. Comparing these two URIs, 3 things must match:
- Same domain. http://foo.com is different than http://bar.foo.com or http://www.foo.com
- Same protocol. http://foo.com is different than https://foo.com
- Same port. http://foo.com is different than http://foo.com:8080
If all three things match, the request will be allowed to go out into the world. If they don’t, by default, we’ll disallow the request and throw an exception. (Cross domain policies will be covered in part 2 of this post series.)
What Can You Say to Your Site of Origin Server?
You can talk to your site of origin server with the below capabilities:
- Verb support: GET & POST
- Request header support: Most standard & custom headers
- Status codes: 200 (OK) or 404 (NotFound) only
Also, all the requests you send will have the “right thing happen” to them from a cookies and authentication standpoint. This is a result of us leveraging the hosting browser to make the HTTP request.
HTTP requests themselves are:
- Asynchronous only
Why Those Capabilities?
The above capabilities are the result of:
- Requirements for basic web services
- Restrictions in the underlying browser plug-in networking APIs
To implement our Silverlight HTTP stack, we use the browser plug-in’s networking APIs. The HTTP capabilities we expose are therefore bound by the common set of capabilities exposed by the browsers that Silverlight supports.
Note: There are other ways we could have implemented the networking stack. For instance, we could have proxied all calls through the browser’s XmlHttpRequest object. However, this would have imposed a site of origin restriction on the requests, and we wanted to enable cross domain communication to existing web services. Similarly, we could have gone directly to the operating system’s networking APIs, but then we would have lost the cookies and authentication integration with the browser. In the future, exploring multiple of these stacks to expose more capabilities is definitely a possibility, and is something we would like feedback on.
What APIs Should I Use?
There are two APIs in Silverlight for HTTP communication. The first is WebClient and the second is HttpWebRequest.
WebClient
WebClient is a great API with a simple, events-based paradigm. You can use it to easily download or upload a strings and Streams.
For a download, WebClient does a GET request and then gives you back the result in the form you wanted. For an upload, WebClient does a POST and sends the data you passed it. It also automatically resolves relative URIs against the deployment URI of the XAP.
Sample: Downloading a string using WebClient
1: private void DownloadString()
2: {
3: WebClient webClient = new WebClient();
4:
5: // Hook up events
6: webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
7: webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
8:
9: // Initiate download
10: webClient.DownloadStringAsync(new Uri("myfeed.xml", UriKind.Relative));
11: }
12:
13: void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
14: {
15: // Update progress UI
16: progressTextBox.Text = e.ProgressPercentage;
17: }
18:
19: void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
20: {
21: if (e.Error == null)
22: {
23: // Clear progress UI and show downloaded string
24: progressTextBox.Text = "";
25: feedTextBox.Text = e.Result;
26: }
27: }
28:
HttpWebRequest & HttpWebResponse
HttpWebRequest and HttpWebResponse are the standard .NET HTTP apis. They are much more powerful than WebClient but also more complex to use.
Sample: Sending POST request using HttpWebRequest
1: SynchronizationContext syncContext;
2:
3: private void Page_Loaded(object sender, EventArgs e)
4: {
5: // Grab SynchronizationContext while on UI Thread
6: syncContext = SynchronizationContext.Current;
7:
8: // Create request
9: HttpWebRequest request = WebRequest.Create("http://msite.com/myFeed") as HttpWebRequest;
10: request.Method = "POST";
11: request.Headers["x-custom-header"] = "value";
12:
13: // Make async call for request stream. Callback will be called on a background thread.
14: IAsyncResult asyncResult =
15: request.BeginGetRequestStream(new AsyncCallback(RequestStreamCallback), request);
16: }
17:
18: private void RequestStreamCallback(IAsyncResult ar)
19: {
20: HttpWebRequest request = ar.AsyncState as HttpWebRequest;
21: Request.ContentType = "text/xml";
22: Stream requestStream = request.EndGetRequestStream(ar);
23: StreamWriter streamWriter = new StreamWriter(requestStream);
24: streamWriter.Encoding = Encoding.UTF8;
25: streamWriter.Write("<?xml version=\"1.0\"?>"
26: + "<entry xmlns=\"http://www.w3.org/2005/Atom\">"
27: + "<author>"
28: + "<name>Elizabeth Bennet</name>"
29: + "<email>liz@gmail.com</email>"
30: + "</author>"
31: + "<title type=\"text\">Entry 1</title>"
32: + "<content type=\"text\">This is my entry</content>"
33: + "</entry>");
34:
35: streamWriter.Close();
36:
37: // Make async call for response. Callback will be called on a background thread.
38: request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
39: }
40: private void ResponseCallback(IAsyncResult ar)
41: {
42: HttpWebRequest request = ar.AsyncState as HttpWebRequest;
43: WebResponse response = request.EndGetResponse(ar);
44:
45: // Invoke onto UI thread
46: syncContext.Post(ExtractResponse, response);
47:
48: // use response. Could include reading response stream.
49: }
50:
51: private void ExtractResponse(object state)
52: {
53: HttpWebResponse response = state as HttpWebResponse;
54: // use response. Could include reading response stream.
55: }
Feedback – We Love It
Do the capabilities of our HTTP stack satisfy your needs? What do you think of the API set? We’d love to hear your thoughts.
Stay Tuned for Cross Domain Support in Part 2….