Merge pull request #2408 from tastywheattasteslikechicken/MoreInterfaceSupport
[mono.git] / mcs / class / System.Net.Http / HttpClientEx.cs
1 using System;
2 using System.IO;
3 using System.Net;
4 #if XAMCORE_2_0
5 using CoreFoundation;
6 using Foundation;
7 using ObjCRuntime;
8 #elif MONOMAC
9 using MonoMac.CoreFoundation;
10 using MonoMac.Foundation;
11 using MonoMac.ObjCRuntime;
12 #else
13 using MonoTouch.CoreFoundation;
14 using MonoTouch.Foundation;
15 using MonoTouch.ObjCRuntime;
16 #endif
17
18 #if SYSTEM_NET_HTTP && !MONOMAC
19 namespace System.Net.Http {
20
21         public partial class HttpClient {
22
23                 public HttpClient ()
24                         : this (GetDefaultHandler (), true)
25                 {
26                 }
27
28                 // note: the linker will re-write this to only reference the selected handler
29                 // but we want this to work "as expected" even if the application is not being linked
30                 static HttpMessageHandler GetDefaultHandler ()
31                 {
32 #if MONOTOUCH_WATCH
33                         // There's only one valid handler type for watchOS
34                         return new NSUrlSessionHandler ();
35 #else
36                         return RuntimeOptions.GetHttpMessageHandler ();
37 #endif
38                 }
39         }
40 #else
41 // due to historical reasons (around CFNetwork) Xamarin.Mac includes this inside it's profile assembly
42 // and not a custom System.Net.Http assembly
43 namespace Foundation {
44 #endif
45
46         partial class NSUrlSessionHandler {
47
48                 bool allowAutoRedirect;
49                 ICredentials credentials;
50                 bool sentRequest;
51
52                 public bool AllowAutoRedirect {
53                         get {
54                                 return allowAutoRedirect;
55                         }
56                         set {
57                                 EnsureModifiability ();
58                                 allowAutoRedirect = value;
59                         }
60                 }
61
62                 public ICredentials Credentials {
63                         get {
64                                 return credentials;
65                         }
66                         set {
67                                 EnsureModifiability ();
68                                 credentials = value;
69                         }
70                 }
71
72                 internal void EnsureModifiability ()
73                 {
74                         if (sentRequest)
75                                 throw new InvalidOperationException (
76                                         "This instance has already started one or more requests. " +
77                                         "Properties can only be modified before sending the first request.");
78                 }
79
80                 // almost identical to ModernHttpClient version but it uses the constants from monotouch.dll | Xamarin.[iOS|WatchOS|TVOS].dll
81                 static Exception createExceptionForNSError(NSError error)
82                 {
83                         var webExceptionStatus = WebExceptionStatus.UnknownError;
84
85                         var innerException = new NSErrorException(error);
86
87                         // errors that exists in both share the same error code, so we can use a single switch/case
88                         // this also ease watchOS integration as if does not expose CFNetwork but (I would not be 
89                         // surprised if it)could return some of it's error codes
90 #if MONOTOUCH_WATCH
91                         if (error.Domain == NSError.NSUrlErrorDomain) {
92 #else
93                         if ((error.Domain == NSError.NSUrlErrorDomain) || (error.Domain == NSError.CFNetworkErrorDomain)) {
94 #endif
95                                 // Parse the enum into a web exception status or exception. Some
96                                 // of these values don't necessarily translate completely to
97                                 // what WebExceptionStatus supports, so made some best guesses
98                                 // here.  For your reading pleasure, compare these:
99                                 //
100                                 // 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
101                                 // .NET docs: http://msdn.microsoft.com/en-us/library/system.net.webexceptionstatus(v=vs.110).aspx
102                                 switch ((NSUrlError) (long) error.Code) {
103                                 case NSUrlError.Cancelled:
104                                 case NSUrlError.UserCancelledAuthentication:
105 #if !MONOTOUCH_WATCH
106                                 case (NSUrlError) NSNetServicesStatus.CancelledError:
107 #endif
108                                         // No more processing is required so just return.
109                                         return new OperationCanceledException(error.LocalizedDescription, innerException);
110                                 case NSUrlError.BadURL:
111                                 case NSUrlError.UnsupportedURL:
112                                 case NSUrlError.CannotConnectToHost:
113                                 case NSUrlError.ResourceUnavailable:
114                                 case NSUrlError.NotConnectedToInternet:
115                                 case NSUrlError.UserAuthenticationRequired:
116                                 case NSUrlError.InternationalRoamingOff:
117                                 case NSUrlError.CallIsActive:
118                                 case NSUrlError.DataNotAllowed:
119 #if !MONOTOUCH_WATCH
120                                 case (NSUrlError) CFNetworkErrors.Socks5BadCredentials:
121                                 case (NSUrlError) CFNetworkErrors.Socks5UnsupportedNegotiationMethod:
122                                 case (NSUrlError) CFNetworkErrors.Socks5NoAcceptableMethod:
123                                 case (NSUrlError) CFNetworkErrors.HttpAuthenticationTypeUnsupported:
124                                 case (NSUrlError) CFNetworkErrors.HttpBadCredentials:
125                                 case (NSUrlError) CFNetworkErrors.HttpBadURL:
126 #endif
127                                         webExceptionStatus = WebExceptionStatus.ConnectFailure;
128                                         break;
129                                 case NSUrlError.TimedOut:
130 #if !MONOTOUCH_WATCH
131                                 case (NSUrlError) CFNetworkErrors.NetServiceTimeout:
132 #endif
133                                         webExceptionStatus = WebExceptionStatus.Timeout;
134                                         break;
135                                 case NSUrlError.CannotFindHost:
136                                 case NSUrlError.DNSLookupFailed:
137 #if !MONOTOUCH_WATCH
138                                 case (NSUrlError) CFNetworkErrors.HostNotFound:
139                                 case (NSUrlError) CFNetworkErrors.NetServiceDnsServiceFailure:
140 #endif
141                                         webExceptionStatus = WebExceptionStatus.NameResolutionFailure;
142                                         break;
143                                 case NSUrlError.DataLengthExceedsMaximum:
144                                         webExceptionStatus = WebExceptionStatus.MessageLengthLimitExceeded;
145                                         break;
146                                 case NSUrlError.NetworkConnectionLost:
147 #if !MONOTOUCH_WATCH
148                                 case (NSUrlError) CFNetworkErrors.HttpConnectionLost:
149 #endif
150                                         webExceptionStatus = WebExceptionStatus.ConnectionClosed;
151                                         break;
152                                 case NSUrlError.HTTPTooManyRedirects:
153                                 case NSUrlError.RedirectToNonExistentLocation:
154 #if !MONOTOUCH_WATCH
155                                 case (NSUrlError) CFNetworkErrors.HttpRedirectionLoopDetected:
156 #endif
157                                         webExceptionStatus = WebExceptionStatus.ProtocolError;
158                                         break;
159                                 case NSUrlError.RequestBodyStreamExhausted:
160 #if !MONOTOUCH_WATCH
161                                 case (NSUrlError) CFNetworkErrors.SocksUnknownClientVersion:
162                                 case (NSUrlError) CFNetworkErrors.SocksUnsupportedServerVersion:
163                                 case (NSUrlError) CFNetworkErrors.HttpParseFailure:
164 #endif
165                                         webExceptionStatus = WebExceptionStatus.SendFailure;
166                                         break;
167                                 case NSUrlError.BadServerResponse:
168                                 case NSUrlError.ZeroByteResource:
169                                 case NSUrlError.CannotDecodeRawData:
170                                 case NSUrlError.CannotDecodeContentData:
171                                 case NSUrlError.CannotParseResponse:
172                                 case NSUrlError.FileDoesNotExist:
173                                 case NSUrlError.FileIsDirectory:
174                                 case NSUrlError.NoPermissionsToReadFile:
175                                 case NSUrlError.CannotLoadFromNetwork:
176                                 case NSUrlError.CannotCreateFile:
177                                 case NSUrlError.CannotOpenFile:
178                                 case NSUrlError.CannotCloseFile:
179                                 case NSUrlError.CannotWriteToFile:
180                                 case NSUrlError.CannotRemoveFile:
181                                 case NSUrlError.CannotMoveFile:
182                                 case NSUrlError.DownloadDecodingFailedMidStream:
183                                 case NSUrlError.DownloadDecodingFailedToComplete:
184 #if !MONOTOUCH_WATCH
185                                 case (NSUrlError) CFNetworkErrors.Socks4RequestFailed:
186                                 case (NSUrlError) CFNetworkErrors.Socks4IdentdFailed:
187                                 case (NSUrlError) CFNetworkErrors.Socks4IdConflict:
188                                 case (NSUrlError) CFNetworkErrors.Socks4UnknownStatusCode:
189                                 case (NSUrlError) CFNetworkErrors.Socks5BadState:
190                                 case (NSUrlError) CFNetworkErrors.Socks5BadResponseAddr:
191                                 case (NSUrlError) CFNetworkErrors.CannotParseCookieFile:
192                                 case (NSUrlError) CFNetworkErrors.NetServiceUnknown:
193                                 case (NSUrlError) CFNetworkErrors.NetServiceCollision:
194                                 case (NSUrlError) CFNetworkErrors.NetServiceNotFound:
195                                 case (NSUrlError) CFNetworkErrors.NetServiceInProgress:
196                                 case (NSUrlError) CFNetworkErrors.NetServiceBadArgument:
197                                 case (NSUrlError) CFNetworkErrors.NetServiceInvalid:
198 #endif
199                                         webExceptionStatus = WebExceptionStatus.ReceiveFailure;
200                                         break;
201                                 case NSUrlError.SecureConnectionFailed:
202                                         webExceptionStatus = WebExceptionStatus.SecureChannelFailure;
203                                         break;
204                                 case NSUrlError.ServerCertificateHasBadDate:
205                                 case NSUrlError.ServerCertificateHasUnknownRoot:
206                                 case NSUrlError.ServerCertificateNotYetValid:
207                                 case NSUrlError.ServerCertificateUntrusted:
208                                 case NSUrlError.ClientCertificateRejected:
209                                 case NSUrlError.ClientCertificateRequired:
210                                         webExceptionStatus = WebExceptionStatus.TrustFailure;
211                                         break;
212 #if !MONOTOUCH_WATCH
213                                 case (NSUrlError) CFNetworkErrors.HttpProxyConnectionFailure:
214                                 case (NSUrlError) CFNetworkErrors.HttpBadProxyCredentials:
215                                 case (NSUrlError) CFNetworkErrors.PacFileError:
216                                 case (NSUrlError) CFNetworkErrors.PacFileAuth:
217                                 case (NSUrlError) CFNetworkErrors.HttpsProxyConnectionFailure:
218                                 case (NSUrlError) CFNetworkErrors.HttpsProxyFailureUnexpectedResponseToConnectMethod:
219                                         webExceptionStatus = WebExceptionStatus.RequestProhibitedByProxy;
220                                         break;
221 #endif
222                                 }
223                         } 
224
225                         // Always create a WebException so that it can be handled by the client.
226                         return new WebException(error.LocalizedDescription, innerException); //, webExceptionStatus, response: null);
227                 }
228         }
229 }