Move remaining System.Net.Http pieces from mono-extensions to mono
authorAlexander Köplinger <alex.koeplinger@outlook.com>
Wed, 13 Apr 2016 16:58:43 +0000 (18:58 +0200)
committerAlexander Köplinger <alex.koeplinger@outlook.com>
Wed, 13 Apr 2016 16:58:43 +0000 (18:58 +0200)
Discussed with @rolfbjarne, these were missed in the initial import of the mono-extension sources.

mcs/class/System.Net.Http/CFContentStream.cs [new file with mode: 0644]
mcs/class/System.Net.Http/CFNetworkHandler.cs [new file with mode: 0644]
mcs/class/System.Net.Http/HttpClientEx.cs [new file with mode: 0644]

diff --git a/mcs/class/System.Net.Http/CFContentStream.cs b/mcs/class/System.Net.Http/CFContentStream.cs
new file mode 100644 (file)
index 0000000..270846c
--- /dev/null
@@ -0,0 +1,133 @@
+//
+// CFContentStream.cs
+//
+// Authors:
+//     Marek Safar  <marek.safar@gmail.com>
+//
+// Copyright (C) 2013 Xamarin Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Threading;
+using System.Threading.Tasks;
+using System.IO;
+using System.Net;
+
+#if XAMCORE_4_0
+using CFNetwork;
+#elif XAMCORE_2_0
+using CoreServices;
+#else
+using MonoTouch.CoreServices;
+#endif
+
+namespace System.Net.Http
+{
+       class BufferData 
+       {
+               public byte[] Buffer;
+               public int Length;
+       }
+
+       class CFContentStream : HttpContent
+       {
+               readonly CFHTTPStream http_stream;
+               BufferData data;
+               Mutex data_mutex;
+               AutoResetEvent data_event;
+               AutoResetEvent data_read_event;
+
+               // The requirements are:
+               // * We must read at least one byte from the stream every time
+               //   we get a HasBytesAvailable event.
+               // * SerializeToStreamAsync is executed on a separate thread,
+               //   so reads must somehow be synchronized with that thread.
+               //
+               // Current implementation:
+               // * We read data in ReadStreamData (on the same thread
+               //   we got the HasBytesAvailable event, i.e. inside the 
+               //   HasBytesAvailable event handler).
+               // * Data is stored in a class-level buffer.
+               // * SerializeToStreamAsync blocks while waiting for
+               //   data from ReadStreamData.
+               // * ReadStreamData will only read more data once SerializeToStreamAsync
+               //   has consumed any existing data. This means we'll be
+               //   blocking in the HasBytesAvailable event handler until
+               //   any previously read data has been processed (this prevents
+               //   any unbound memory growth).
+
+               public CFContentStream (CFHTTPStream stream)
+               {
+                       this.http_stream = stream;
+                       data = new BufferData () {
+                               Buffer = new byte [4096],
+                       };
+                       data_event = new AutoResetEvent (false);
+                       data_read_event = new AutoResetEvent (true);
+                       data_mutex = new Mutex ();
+               }
+
+               public void ReadStreamData ()
+               {
+                       data_read_event.WaitOne (); // make sure there's no pending data.
+
+                       data_mutex.WaitOne ();
+                       data.Length = (int) http_stream.Read (data.Buffer, 0, data.Buffer.Length);
+                       data_mutex.ReleaseMutex ();
+
+                       data_event.Set ();
+               }
+
+               public void Close ()
+               {
+                       data_read_event.WaitOne (); // make sure there's no pending data
+
+                       data_mutex.WaitOne ();
+                       data = null;
+                       data_mutex.ReleaseMutex ();
+
+                       data_event.Set ();
+               }
+
+               protected internal override async Task SerializeToStreamAsync (Stream stream, TransportContext context)
+               {
+                       while (data_event.WaitOne ()) {
+                               data_mutex.WaitOne ();
+                               if (data == null || data.Length <= 0) {
+                                       data_mutex.ReleaseMutex ();
+                                       data_read_event.Set ();
+                                       break;
+                               }
+
+                               await stream.WriteAsync (data.Buffer, 0, data.Length).ConfigureAwait (false);
+                               data_mutex.ReleaseMutex ();
+
+                               data_read_event.Set ();
+                       }
+               }
+
+               protected internal override bool TryComputeLength (out long length)
+               {
+                       length = 0;
+                       return false;
+               }
+       }
+}
diff --git a/mcs/class/System.Net.Http/CFNetworkHandler.cs b/mcs/class/System.Net.Http/CFNetworkHandler.cs
new file mode 100644 (file)
index 0000000..5723acb
--- /dev/null
@@ -0,0 +1,332 @@
+//
+// CFNetworkHandler.cs
+//
+// Authors:
+//     Marek Safar  <marek.safar@gmail.com>
+//
+// Copyright (C) 2013 Xamarin Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Threading;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+using System.IO;
+using System.Collections.Generic;
+using System.Net;
+
+#if XAMCORE_4_0
+using CFNetwork;
+using CoreFoundation;
+using CF=CoreFoundation;
+#elif XAMCORE_2_0
+using CoreServices;
+using CoreFoundation;
+using CF=CoreFoundation;
+#else
+using MonoTouch.CoreServices;
+using MonoTouch.CoreFoundation;
+using CF=MonoTouch.CoreFoundation;
+#endif
+
+namespace System.Net.Http
+{
+       public class CFNetworkHandler : HttpMessageHandler
+       {
+               class StreamBucket
+               {
+                       public TaskCompletionSource<HttpResponseMessage> Response;
+                       public HttpRequestMessage Request;
+                       public CancellationTokenRegistration CancellationTokenRegistration;
+                       public CFContentStream ContentStream;
+
+                       public void Close ()
+                       {
+                               CancellationTokenRegistration.Dispose ();
+                               ContentStream.Close ();
+                       }
+               }
+
+               bool allowAutoRedirect;
+               bool sentRequest;
+               bool useSystemProxy;
+               CookieContainer cookies;
+
+               Dictionary<IntPtr, StreamBucket> streamBuckets;
+
+               public CFNetworkHandler ()
+               {
+                       allowAutoRedirect = true;
+                       streamBuckets = new Dictionary<IntPtr, StreamBucket> ();
+               }
+
+               void EnsureModifiability ()
+               {
+                       if (sentRequest)
+                               throw new InvalidOperationException (
+                                       "This instance has already started one or more requests. " +
+                                       "Properties can only be modified before sending the first request.");
+               }
+
+               public bool AllowAutoRedirect {
+                       get {
+                               return allowAutoRedirect;
+                       }
+                       set {
+                               EnsureModifiability ();
+                               allowAutoRedirect = value;
+                       }
+               }
+
+               public CookieContainer CookieContainer {
+                       get {
+                               return cookies;
+                       }
+                       set {
+                               EnsureModifiability ();
+                               cookies = value;
+                       }
+               }
+
+               public bool UseSystemProxy {
+                       get {
+                               return useSystemProxy;
+                       }
+                       set {
+                               EnsureModifiability ();
+                               useSystemProxy = value;
+                       }
+               }
+
+               // TODO: Add more properties
+
+               protected override void Dispose (bool disposing)
+               {
+                       // TODO: CloseStream remaining stream buckets if there are any
+
+                       base.Dispose (disposing);
+               }
+
+               CFHTTPMessage CreateWebRequestAsync (HttpRequestMessage request)
+               {
+                       var req = CFHTTPMessage.CreateRequest (request.RequestUri, request.Method.Method, request.Version);
+
+                       // TODO:
+/*
+                       if (wr.ProtocolVersion == HttpVersion.Version10) {
+                               wr.KeepAlive = request.Headers.ConnectionKeepAlive;
+                       } else {
+                               wr.KeepAlive = request.Headers.ConnectionClose != true;
+                       }
+
+                       if (useDefaultCredentials) {
+                               wr.UseDefaultCredentials = true;
+                       } else {
+                               wr.Credentials = credentials;
+                       }
+
+                       if (useProxy) {
+                               wr.Proxy = proxy;
+                       }
+*/
+                       if (cookies != null) {
+                               string cookieHeader = cookies.GetCookieHeader (request.RequestUri);
+                               if (cookieHeader != "")
+                                       req.SetHeaderFieldValue ("Cookie", cookieHeader);
+                       }
+
+                       foreach (var header in request.Headers) {
+                               foreach (var value in header.Value) {
+                                       req.SetHeaderFieldValue (header.Key, value);
+                               }
+                       }
+
+                       if (request.Content != null) {
+                               foreach (var header in request.Content.Headers) {
+                                       foreach (var value in header.Value) {
+                                               req.SetHeaderFieldValue (header.Key, value);
+                                       }
+                               }
+                       }
+
+                       return req;
+               }
+
+               protected internal override async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
+               {
+                       sentRequest = true;
+
+                       CFHTTPStream stream;
+                       using (var message = CreateWebRequestAsync (request))
+                       {
+                               if (request.Content != null) {
+                                       var data = await request.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
+                                       message.SetBody (data);
+                               }
+
+                               stream = CFHTTPStream.CreateForHTTPRequest (message);
+                       }
+
+                       if (useSystemProxy) {
+                               var proxies = CF.CFNetwork.GetSystemProxySettings ();
+                               if (proxies.HTTPEnable) {
+                                       stream.SetProxy (proxies);
+                               }
+                       }
+
+                       stream.ShouldAutoredirect = allowAutoRedirect;
+                       stream.HasBytesAvailableEvent += HandleHasBytesAvailableEvent;
+                       stream.ErrorEvent += HandleErrorEvent;
+                       stream.ClosedEvent += HandleClosedEvent;
+
+                       var response = new TaskCompletionSource<HttpResponseMessage> ();
+
+                       if (cancellationToken.IsCancellationRequested) {
+                               response.SetCanceled ();
+                               return await response.Task;
+                       }
+
+                       var bucket = new StreamBucket () {
+                               Request = request,
+                               Response = response,
+                       };
+
+                       streamBuckets.Add (stream.Handle, bucket);
+
+                       //
+                       // Always schedule stream events handling on main-loop. Due to ConfigureAwait (false) we may end up
+                       // on any thread-pool thread which may not have run-loop running
+                       //
+#if XAMCORE_2_0
+                       stream.EnableEvents (CF.CFRunLoop.Main, CF.CFRunLoop.ModeCommon);
+#else
+                       stream.EnableEvents (CF.CFRunLoop.Main, CF.CFRunLoop.CFRunLoopCommonModes);
+#endif
+
+                       stream.Open ();
+
+                       bucket.CancellationTokenRegistration = cancellationToken.Register (() => {
+                               StreamBucket bucket2;
+                               if (!streamBuckets.TryGetValue (stream.Handle, out bucket2))
+                                       return;
+
+                               bucket2.Response.TrySetCanceled ();
+                               CloseStream (stream);
+                       });
+
+                       return await response.Task;
+               }
+
+               void HandleErrorEvent (object sender, CFStream.StreamEventArgs e)
+               {
+                       var stream = (CFHTTPStream)sender;
+
+                       StreamBucket bucket;
+                       if (!streamBuckets.TryGetValue (stream.Handle, out bucket))
+                               return;
+
+                       bucket.Response.TrySetException (stream.GetError ());
+                       CloseStream (stream);
+               }
+
+               void HandleClosedEvent (object sender, CFStream.StreamEventArgs e)
+               {
+                       var stream = (CFHTTPStream)sender;
+                       CloseStream (stream);
+               }
+
+               void CloseStream (CFHTTPStream stream)
+               {
+                       StreamBucket bucket;
+                       if (streamBuckets.TryGetValue (stream.Handle, out bucket)) {
+                               bucket.Close ();
+                               streamBuckets.Remove (stream.Handle);
+                       }
+
+                       stream.Close ();
+               }
+
+               void HandleHasBytesAvailableEvent (object sender, CFStream.StreamEventArgs e)
+               {
+                       var stream = (CFHTTPStream) sender;
+
+                       StreamBucket bucket;
+                       if (!streamBuckets.TryGetValue (stream.Handle, out bucket))
+                               return;
+
+                       if (bucket.Response.Task.IsCompleted) {
+                               bucket.ContentStream.ReadStreamData ();
+                               return;
+                       }
+
+                       var header = stream.GetResponseHeader ();
+
+                       // Is this possible?
+                       if (!header.IsHeaderComplete)
+                               throw new NotImplementedException ();
+
+                       bucket.ContentStream = new CFContentStream (stream);
+                               
+                       var response_msg = new HttpResponseMessage (header.ResponseStatusCode);
+                       response_msg.RequestMessage = bucket.Request;
+                       response_msg.ReasonPhrase = header.ResponseStatusLine;
+                       response_msg.Content = bucket.ContentStream;
+
+                       var fields = header.GetAllHeaderFields ();
+                       if (fields != null) {
+                               foreach (var entry in fields) {
+                                       if (entry.Key == null)
+                                               continue;
+
+                                       var key = entry.Key.ToString ();
+                                       var value = entry.Value == null ? string.Empty : entry.Value.ToString ();
+                                       HttpHeaders item_headers;
+                                       if (HttpHeaders.GetKnownHeaderKind (key) == Headers.HttpHeaderKind.Content) {
+                                               item_headers = response_msg.Content.Headers;
+                                       } else {
+                                               item_headers = response_msg.Headers;
+
+                                               if (cookies != null && (key == "Set-Cookie" || key == "Set-Cookie2"))
+                                                       AddCookie (value, bucket.Request.RequestUri, key);
+                                       }
+
+                                       item_headers.TryAddWithoutValidation (key, value);
+                               }
+                       }
+
+                       bucket.Response.TrySetResult (response_msg);
+
+                       bucket.ContentStream.ReadStreamData ();
+               }
+
+               void AddCookie (string value, Uri uri, string header)
+               {
+                       CookieCollection cookies1 = null;
+                       try {
+                               cookies1 = cookies.CookieCutter (uri, header, value, false);
+                       } catch {
+                       }
+
+                       if (cookies1 != null && cookies1.Count != 0) 
+                               cookies.Add (cookies1);
+               }
+       }
+}
diff --git a/mcs/class/System.Net.Http/HttpClientEx.cs b/mcs/class/System.Net.Http/HttpClientEx.cs
new file mode 100644 (file)
index 0000000..59456d6
--- /dev/null
@@ -0,0 +1,224 @@
+using System;
+using System.IO;
+using System.Net;
+#if XAMCORE_2_0
+using CoreFoundation;
+using Foundation;
+using ObjCRuntime;
+#elif MONOMAC
+using MonoMac.CoreFoundation;
+using MonoMac.Foundation;
+using MonoMac.ObjCRuntime;
+#else
+using MonoTouch.CoreFoundation;
+using MonoTouch.Foundation;
+using MonoTouch.ObjCRuntime;
+#endif
+
+#if SYSTEM_NET_HTTP && !MONOMAC
+namespace System.Net.Http {
+
+       public partial class HttpClient {
+
+               public HttpClient ()
+                       : this (GetDefaultHandler (), true)
+               {
+               }
+
+               // note: the linker will re-write this to only reference the selected handler
+               // but we want this to work "as expected" even if the application is not being linked
+               static HttpMessageHandler GetDefaultHandler ()
+               {
+                       return RuntimeOptions.GetHttpMessageHandler ();
+               }
+       }
+#else
+// due to historical reasons (around CFNetwork) Xamarin.Mac includes this inside it's profile assembly
+// and not a custom System.Net.Http assembly
+namespace Foundation {
+#endif
+
+       partial class NSUrlSessionHandler {
+
+               bool allowAutoRedirect;
+               ICredentials credentials;
+               bool sentRequest;
+
+               public bool AllowAutoRedirect {
+                       get {
+                               return allowAutoRedirect;
+                       }
+                       set {
+                               EnsureModifiability ();
+                               allowAutoRedirect = value;
+                       }
+               }
+
+               public ICredentials Credentials {
+                       get {
+                               return credentials;
+                       }
+                       set {
+                               EnsureModifiability ();
+                               credentials = value;
+                       }
+               }
+
+               internal void EnsureModifiability ()
+               {
+                       if (sentRequest)
+                               throw new InvalidOperationException (
+                                       "This instance has already started one or more requests. " +
+                                       "Properties can only be modified before sending the first request.");
+               }
+
+               // almost identical to ModernHttpClient version but it uses the constants from monotouch.dll | Xamarin.[iOS|WatchOS|TVOS].dll
+               static Exception createExceptionForNSError(NSError error)
+               {
+                       var webExceptionStatus = WebExceptionStatus.UnknownError;
+
+                       var innerException = new NSErrorException(error);
+
+                       // errors that exists in both share the same error code, so we can use a single switch/case
+                       // this also ease watchOS integration as if does not expose CFNetwork but (I would not be 
+                       // surprised if it)could return some of it's error codes
+#if MONOTOUCH_WATCH
+                       if (error.Domain == NSError.NSUrlErrorDomain) {
+#else
+                       if ((error.Domain == NSError.NSUrlErrorDomain) || (error.Domain == NSError.CFNetworkErrorDomain)) {
+#endif
+                               // Parse the enum into a web exception status or exception. Some
+                               // of these values don't necessarily translate completely to
+                               // what WebExceptionStatus supports, so made some best guesses
+                               // here.  For your reading pleasure, compare these:
+                               //
+                               // Apple docs: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/index.html#//apple_ref/doc/constant_group/URL_Loading_System_Error_Codes
+                               // .NET docs: http://msdn.microsoft.com/en-us/library/system.net.webexceptionstatus(v=vs.110).aspx
+                               switch ((NSUrlError) (long) error.Code) {
+                               case NSUrlError.Cancelled:
+                               case NSUrlError.UserCancelledAuthentication:
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) NSNetServicesStatus.CancelledError:
+#endif
+                                       // No more processing is required so just return.
+                                       return new OperationCanceledException(error.LocalizedDescription, innerException);
+                               case NSUrlError.BadURL:
+                               case NSUrlError.UnsupportedURL:
+                               case NSUrlError.CannotConnectToHost:
+                               case NSUrlError.ResourceUnavailable:
+                               case NSUrlError.NotConnectedToInternet:
+                               case NSUrlError.UserAuthenticationRequired:
+                               case NSUrlError.InternationalRoamingOff:
+                               case NSUrlError.CallIsActive:
+                               case NSUrlError.DataNotAllowed:
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) CFNetworkErrors.Socks5BadCredentials:
+                               case (NSUrlError) CFNetworkErrors.Socks5UnsupportedNegotiationMethod:
+                               case (NSUrlError) CFNetworkErrors.Socks5NoAcceptableMethod:
+                               case (NSUrlError) CFNetworkErrors.HttpAuthenticationTypeUnsupported:
+                               case (NSUrlError) CFNetworkErrors.HttpBadCredentials:
+                               case (NSUrlError) CFNetworkErrors.HttpBadURL:
+#endif
+                                       webExceptionStatus = WebExceptionStatus.ConnectFailure;
+                                       break;
+                               case NSUrlError.TimedOut:
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) CFNetworkErrors.NetServiceTimeout:
+#endif
+                                       webExceptionStatus = WebExceptionStatus.Timeout;
+                                       break;
+                               case NSUrlError.CannotFindHost:
+                               case NSUrlError.DNSLookupFailed:
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) CFNetworkErrors.HostNotFound:
+                               case (NSUrlError) CFNetworkErrors.NetServiceDnsServiceFailure:
+#endif
+                                       webExceptionStatus = WebExceptionStatus.NameResolutionFailure;
+                                       break;
+                               case NSUrlError.DataLengthExceedsMaximum:
+                                       webExceptionStatus = WebExceptionStatus.MessageLengthLimitExceeded;
+                                       break;
+                               case NSUrlError.NetworkConnectionLost:
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) CFNetworkErrors.HttpConnectionLost:
+#endif
+                                       webExceptionStatus = WebExceptionStatus.ConnectionClosed;
+                                       break;
+                               case NSUrlError.HTTPTooManyRedirects:
+                               case NSUrlError.RedirectToNonExistentLocation:
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) CFNetworkErrors.HttpRedirectionLoopDetected:
+#endif
+                                       webExceptionStatus = WebExceptionStatus.ProtocolError;
+                                       break;
+                               case NSUrlError.RequestBodyStreamExhausted:
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) CFNetworkErrors.SocksUnknownClientVersion:
+                               case (NSUrlError) CFNetworkErrors.SocksUnsupportedServerVersion:
+                               case (NSUrlError) CFNetworkErrors.HttpParseFailure:
+#endif
+                                       webExceptionStatus = WebExceptionStatus.SendFailure;
+                                       break;
+                               case NSUrlError.BadServerResponse:
+                               case NSUrlError.ZeroByteResource:
+                               case NSUrlError.CannotDecodeRawData:
+                               case NSUrlError.CannotDecodeContentData:
+                               case NSUrlError.CannotParseResponse:
+                               case NSUrlError.FileDoesNotExist:
+                               case NSUrlError.FileIsDirectory:
+                               case NSUrlError.NoPermissionsToReadFile:
+                               case NSUrlError.CannotLoadFromNetwork:
+                               case NSUrlError.CannotCreateFile:
+                               case NSUrlError.CannotOpenFile:
+                               case NSUrlError.CannotCloseFile:
+                               case NSUrlError.CannotWriteToFile:
+                               case NSUrlError.CannotRemoveFile:
+                               case NSUrlError.CannotMoveFile:
+                               case NSUrlError.DownloadDecodingFailedMidStream:
+                               case NSUrlError.DownloadDecodingFailedToComplete:
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) CFNetworkErrors.Socks4RequestFailed:
+                               case (NSUrlError) CFNetworkErrors.Socks4IdentdFailed:
+                               case (NSUrlError) CFNetworkErrors.Socks4IdConflict:
+                               case (NSUrlError) CFNetworkErrors.Socks4UnknownStatusCode:
+                               case (NSUrlError) CFNetworkErrors.Socks5BadState:
+                               case (NSUrlError) CFNetworkErrors.Socks5BadResponseAddr:
+                               case (NSUrlError) CFNetworkErrors.CannotParseCookieFile:
+                               case (NSUrlError) CFNetworkErrors.NetServiceUnknown:
+                               case (NSUrlError) CFNetworkErrors.NetServiceCollision:
+                               case (NSUrlError) CFNetworkErrors.NetServiceNotFound:
+                               case (NSUrlError) CFNetworkErrors.NetServiceInProgress:
+                               case (NSUrlError) CFNetworkErrors.NetServiceBadArgument:
+                               case (NSUrlError) CFNetworkErrors.NetServiceInvalid:
+#endif
+                                       webExceptionStatus = WebExceptionStatus.ReceiveFailure;
+                                       break;
+                               case NSUrlError.SecureConnectionFailed:
+                                       webExceptionStatus = WebExceptionStatus.SecureChannelFailure;
+                                       break;
+                               case NSUrlError.ServerCertificateHasBadDate:
+                               case NSUrlError.ServerCertificateHasUnknownRoot:
+                               case NSUrlError.ServerCertificateNotYetValid:
+                               case NSUrlError.ServerCertificateUntrusted:
+                               case NSUrlError.ClientCertificateRejected:
+                               case NSUrlError.ClientCertificateRequired:
+                                       webExceptionStatus = WebExceptionStatus.TrustFailure;
+                                       break;
+#if !MONOTOUCH_WATCH
+                               case (NSUrlError) CFNetworkErrors.HttpProxyConnectionFailure:
+                               case (NSUrlError) CFNetworkErrors.HttpBadProxyCredentials:
+                               case (NSUrlError) CFNetworkErrors.PacFileError:
+                               case (NSUrlError) CFNetworkErrors.PacFileAuth:
+                               case (NSUrlError) CFNetworkErrors.HttpsProxyConnectionFailure:
+                               case (NSUrlError) CFNetworkErrors.HttpsProxyFailureUnexpectedResponseToConnectMethod:
+                                       webExceptionStatus = WebExceptionStatus.RequestProhibitedByProxy;
+                                       break;
+#endif
+                               }
+                       } 
+
+                       // Always create a WebException so that it can be handled by the client.
+                       return new WebException(error.LocalizedDescription, innerException); //, webExceptionStatus, response: null);
+               }
+       }
+}