PageRenderTime 57ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/CoreServices/CFHTTPMessage.cs

https://github.com/kjpou1/maccore
C# | 465 lines | 365 code | 71 blank | 29 comment | 51 complexity | a67738401a4193a6a45401a1fedd1161 MD5 | raw file
Possible License(s): Apache-2.0
  1. //
  2. // CFHTTPMessage.cs:
  3. //
  4. // Authors:
  5. // Martin Baulig (martin.baulig@gmail.com)
  6. // Marek Safar (marek.safar@gmail.com)
  7. //
  8. // Copyright 2012-2013 Xamarin Inc. (http://www.xamarin.com)
  9. //
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.Net;
  32. using System.Security.Authentication;
  33. using System.Runtime.InteropServices;
  34. using MonoMac.Foundation;
  35. using MonoMac.CoreFoundation;
  36. using MonoMac.ObjCRuntime;
  37. namespace MonoMac.CoreServices {
  38. public class CFHTTPMessage : CFType, INativeObject, IDisposable {
  39. internal IntPtr handle;
  40. internal CFHTTPMessage (IntPtr handle)
  41. : this (handle, false)
  42. {
  43. }
  44. internal CFHTTPMessage (IntPtr handle, bool owns)
  45. {
  46. if (!owns)
  47. CFObject.CFRetain (handle);
  48. this.handle = handle;
  49. }
  50. static readonly NSString _HTTPVersion1_0;
  51. static readonly NSString _HTTPVersion1_1;
  52. static readonly NSString _AuthenticationSchemeBasic;
  53. static readonly NSString _AuthenticationSchemeNegotiate;
  54. static readonly NSString _AuthenticationSchemeNTLM;
  55. static readonly NSString _AuthenticationSchemeDigest;
  56. static readonly NSString _AuthenticationUsername;
  57. static readonly NSString _AuthenticationPassword;
  58. static readonly NSString _AuthenticationAccountDomain;
  59. static CFHTTPMessage ()
  60. {
  61. var handle = Dlfcn.dlopen (Constants.CFNetworkLibrary, 0);
  62. if (handle == IntPtr.Zero)
  63. throw new InvalidOperationException ();
  64. try {
  65. _HTTPVersion1_0 = GetStringConstant (handle, "kCFHTTPVersion1_0");
  66. _HTTPVersion1_1 = GetStringConstant (handle, "kCFHTTPVersion1_1");
  67. _AuthenticationSchemeBasic = GetStringConstant (
  68. handle, "kCFHTTPAuthenticationSchemeBasic");
  69. _AuthenticationSchemeNegotiate = GetStringConstant (
  70. handle, "kCFHTTPAuthenticationSchemeNegotiate");
  71. _AuthenticationSchemeNTLM = GetStringConstant (
  72. handle, "kCFHTTPAuthenticationSchemeNTLM");
  73. _AuthenticationSchemeDigest = GetStringConstant (
  74. handle, "kCFHTTPAuthenticationSchemeDigest");
  75. _AuthenticationUsername = GetStringConstant (
  76. handle, "kCFHTTPAuthenticationUsername");
  77. _AuthenticationPassword = GetStringConstant (
  78. handle, "kCFHTTPAuthenticationPassword");
  79. _AuthenticationAccountDomain = GetStringConstant (
  80. handle, "kCFHTTPAuthenticationAccountDomain");
  81. } finally {
  82. Dlfcn.dlclose (handle);
  83. }
  84. }
  85. static NSString GetStringConstant (IntPtr handle, string name)
  86. {
  87. var result = Dlfcn.GetStringConstant (handle, name);
  88. if (result == null)
  89. throw new InvalidOperationException (
  90. string.Format ("Cannot get '{0}' property.", name));
  91. return result;
  92. }
  93. [DllImport (Constants.CFNetworkLibrary, EntryPoint="CFHTTPMessageGetTypeID")]
  94. public extern static int GetTypeID ();
  95. ~CFHTTPMessage ()
  96. {
  97. Dispose (false);
  98. }
  99. protected void CheckHandle ()
  100. {
  101. if (handle == IntPtr.Zero)
  102. throw new ObjectDisposedException (GetType ().Name);
  103. }
  104. public void Dispose ()
  105. {
  106. Dispose (true);
  107. GC.SuppressFinalize (this);
  108. }
  109. public IntPtr Handle {
  110. get {
  111. CheckHandle ();
  112. return handle;
  113. }
  114. }
  115. protected virtual void Dispose (bool disposing)
  116. {
  117. if (handle != IntPtr.Zero) {
  118. CFObject.CFRelease (handle);
  119. handle = IntPtr.Zero;
  120. }
  121. }
  122. static IntPtr GetVersion (Version version)
  123. {
  124. if ((version == null) || version.Equals (HttpVersion.Version11))
  125. return _HTTPVersion1_1.Handle;
  126. else if (version.Equals (HttpVersion.Version10))
  127. return _HTTPVersion1_0.Handle;
  128. else
  129. throw new ArgumentException ();
  130. }
  131. [DllImport (Constants.CFNetworkLibrary)]
  132. extern static IntPtr CFHTTPMessageCreateEmpty (IntPtr allocator, bool isRequest);
  133. public static CFHTTPMessage CreateEmpty (bool request)
  134. {
  135. var handle = CFHTTPMessageCreateEmpty (IntPtr.Zero, request);
  136. if (handle == IntPtr.Zero)
  137. return null;
  138. return new CFHTTPMessage (handle);
  139. }
  140. [DllImport (Constants.CFNetworkLibrary)]
  141. extern static IntPtr CFHTTPMessageCreateRequest (IntPtr allocator, IntPtr requestMethod,
  142. IntPtr url, IntPtr httpVersion);
  143. public static CFHTTPMessage CreateRequest (CFUrl url, NSString method, Version version)
  144. {
  145. var handle = CFHTTPMessageCreateRequest (
  146. IntPtr.Zero, method.Handle, url.Handle, GetVersion (version));
  147. if (handle == IntPtr.Zero)
  148. return null;
  149. return new CFHTTPMessage (handle);
  150. }
  151. public static CFHTTPMessage CreateRequest (Uri uri, string method)
  152. {
  153. return CreateRequest (uri, method, null);
  154. }
  155. public static CFHTTPMessage CreateRequest (Uri uri, string method, Version version)
  156. {
  157. CFUrl urlRef = null;
  158. NSString methodRef = null;
  159. var escaped = Uri.EscapeUriString (uri.ToString ());
  160. try {
  161. urlRef = CFUrl.FromUrlString (escaped, null);
  162. if (urlRef == null)
  163. throw new ArgumentException ("Invalid URL.");
  164. methodRef = new NSString (method);
  165. var msg = CreateRequest (urlRef, methodRef, version);
  166. if (msg == null)
  167. throw new ArgumentException ("Invalid URL.");
  168. return msg;
  169. } finally {
  170. if (urlRef != null)
  171. urlRef.Dispose ();
  172. if (methodRef != null)
  173. methodRef.Dispose ();
  174. }
  175. }
  176. [DllImport (Constants.CFNetworkLibrary)]
  177. extern static bool CFHTTPMessageIsRequest (IntPtr handle);
  178. public bool IsRequest {
  179. get {
  180. CheckHandle ();
  181. return CFHTTPMessageIsRequest (Handle);
  182. }
  183. }
  184. [DllImport (Constants.CFNetworkLibrary)]
  185. extern static CFIndex CFHTTPMessageGetResponseStatusCode (IntPtr handle);
  186. public HttpStatusCode ResponseStatusCode {
  187. get {
  188. if (IsRequest)
  189. throw new InvalidOperationException ();
  190. int status = CFHTTPMessageGetResponseStatusCode (Handle);
  191. return (HttpStatusCode)status;
  192. }
  193. }
  194. [DllImport (Constants.CFNetworkLibrary)]
  195. extern static IntPtr CFHTTPMessageCopyResponseStatusLine (IntPtr handle);
  196. public string ResponseStatusLine {
  197. get {
  198. if (IsRequest)
  199. throw new InvalidOperationException ();
  200. var ptr = CFHTTPMessageCopyResponseStatusLine (Handle);
  201. if (ptr == IntPtr.Zero)
  202. return null;
  203. using (var line = new NSString (ptr))
  204. return line.ToString ();
  205. }
  206. }
  207. [DllImport (Constants.CFNetworkLibrary)]
  208. extern static IntPtr CFHTTPMessageCopyVersion (IntPtr handle);
  209. public Version Version {
  210. get {
  211. CheckHandle ();
  212. IntPtr ptr = CFHTTPMessageCopyVersion (handle);
  213. try {
  214. if (ptr.Equals (_HTTPVersion1_0.Handle))
  215. return HttpVersion.Version10;
  216. else
  217. return HttpVersion.Version11;
  218. } finally {
  219. if (ptr != IntPtr.Zero)
  220. CFObject.CFRelease (ptr);
  221. }
  222. }
  223. }
  224. [DllImport (Constants.CFNetworkLibrary)]
  225. extern static bool CFHTTPMessageIsHeaderComplete (IntPtr handle);
  226. public bool IsHeaderComplete {
  227. get {
  228. CheckHandle ();
  229. return CFHTTPMessageIsHeaderComplete (Handle);
  230. }
  231. }
  232. [DllImport (Constants.CFNetworkLibrary)]
  233. extern static bool CFHTTPMessageAppendBytes (IntPtr message, ref byte[] newBytes, CFIndex numBytes);
  234. public bool AppendBytes (byte[] bytes)
  235. {
  236. if (bytes == null)
  237. throw new ArgumentNullException ("bytes");
  238. return AppendBytes (bytes, bytes.Length);
  239. }
  240. public bool AppendBytes (byte[] bytes, int count)
  241. {
  242. if (bytes == null)
  243. throw new ArgumentNullException ("bytes");
  244. return CFHTTPMessageAppendBytes (Handle, ref bytes, count);
  245. }
  246. [DllImport (Constants.CFNetworkLibrary)]
  247. extern static IntPtr CFHTTPMessageCopyAllHeaderFields (IntPtr handle);
  248. public NSDictionary GetAllHeaderFields ()
  249. {
  250. CheckHandle ();
  251. IntPtr ptr = CFHTTPMessageCopyAllHeaderFields (handle);
  252. if (ptr == IntPtr.Zero)
  253. return null;
  254. return new NSDictionary (ptr);
  255. }
  256. #region Authentication
  257. struct CFStreamError {
  258. public int domain;
  259. public int code;
  260. }
  261. enum ErrorHTTPAuthentication {
  262. TypeUnsupported = -1000,
  263. BadUserName = -1001,
  264. BadPassword = -1002
  265. }
  266. AuthenticationException GetException (ErrorHTTPAuthentication code)
  267. {
  268. switch (code) {
  269. case ErrorHTTPAuthentication.BadUserName:
  270. throw new InvalidCredentialException ("Bad username.");
  271. case ErrorHTTPAuthentication.BadPassword:
  272. throw new InvalidCredentialException ("Bad password.");
  273. case ErrorHTTPAuthentication.TypeUnsupported:
  274. throw new AuthenticationException ("Authentication type not supported.");
  275. default:
  276. throw new AuthenticationException ("Unknown error.");
  277. }
  278. }
  279. [DllImport (Constants.CFNetworkLibrary)]
  280. extern static bool CFHTTPMessageApplyCredentials (IntPtr request, IntPtr auth,
  281. IntPtr user, IntPtr pass,
  282. out CFStreamError error);
  283. public void ApplyCredentials (CFHTTPAuthentication auth, NetworkCredential credential)
  284. {
  285. if (auth.RequiresAccountDomain) {
  286. ApplyCredentialDictionary (auth, credential);
  287. return;
  288. }
  289. var username = new CFString (credential.UserName);
  290. var password = new CFString (credential.Password);
  291. try {
  292. CFStreamError error;
  293. var ok = CFHTTPMessageApplyCredentials (
  294. Handle, auth.Handle, username.Handle, password.Handle,
  295. out error);
  296. if (!ok)
  297. throw GetException ((ErrorHTTPAuthentication)error.code);
  298. } finally {
  299. username.Dispose ();
  300. password.Dispose ();
  301. }
  302. }
  303. public enum AuthenticationScheme {
  304. Default,
  305. Basic,
  306. Negotiate,
  307. NTLM,
  308. Digest
  309. }
  310. internal static IntPtr GetAuthScheme (AuthenticationScheme scheme)
  311. {
  312. switch (scheme) {
  313. case AuthenticationScheme.Default:
  314. return IntPtr.Zero;
  315. case AuthenticationScheme.Basic:
  316. return _AuthenticationSchemeBasic.Handle;
  317. case AuthenticationScheme.Negotiate:
  318. return _AuthenticationSchemeNegotiate.Handle;
  319. case AuthenticationScheme.NTLM:
  320. return _AuthenticationSchemeNTLM.Handle;
  321. case AuthenticationScheme.Digest:
  322. return _AuthenticationSchemeDigest.Handle;
  323. default:
  324. throw new ArgumentException ();
  325. }
  326. }
  327. [DllImport (Constants.CFNetworkLibrary)]
  328. extern static bool CFHTTPMessageAddAuthentication (IntPtr request, IntPtr response,
  329. IntPtr username, IntPtr password,
  330. IntPtr scheme, bool forProxy);
  331. public bool AddAuthentication (CFHTTPMessage failureResponse, NSString username,
  332. NSString password, AuthenticationScheme scheme,
  333. bool forProxy)
  334. {
  335. return CFHTTPMessageAddAuthentication (
  336. Handle, failureResponse.Handle, username.Handle,
  337. password.Handle, GetAuthScheme (scheme), forProxy);
  338. }
  339. [DllImport (Constants.CFNetworkLibrary)]
  340. extern static bool CFHTTPMessageApplyCredentialDictionary (IntPtr request, IntPtr auth,
  341. IntPtr dict, out CFStreamError error);
  342. public void ApplyCredentialDictionary (CFHTTPAuthentication auth, NetworkCredential credential)
  343. {
  344. var keys = new NSString [3];
  345. var values = new CFString [3];
  346. keys [0] = _AuthenticationUsername;
  347. keys [1] = _AuthenticationPassword;
  348. keys [2] = _AuthenticationAccountDomain;
  349. values [0] = (CFString)credential.UserName;
  350. values [1] = (CFString)credential.Password;
  351. values [2] = credential.Domain != null ? (CFString)credential.Domain : null;
  352. var dict = CFDictionary.FromObjectsAndKeys (values, keys);
  353. try {
  354. CFStreamError error;
  355. var ok = CFHTTPMessageApplyCredentialDictionary (
  356. Handle, auth.Handle, dict.Handle, out error);
  357. if (ok)
  358. return;
  359. throw GetException ((ErrorHTTPAuthentication)error.code);
  360. } finally {
  361. dict.Dispose ();
  362. values [0].Dispose ();
  363. values [1].Dispose ();
  364. if (values [2] != null)
  365. values [2].Dispose ();
  366. }
  367. }
  368. #endregion
  369. [DllImport (Constants.CFNetworkLibrary)]
  370. extern static void CFHTTPMessageSetHeaderFieldValue (IntPtr message, IntPtr headerField,
  371. IntPtr value);
  372. public void SetHeaderFieldValue (string name, string value)
  373. {
  374. NSString nstr = (NSString)name;
  375. NSString vstr = value != null ? (NSString)value : null;
  376. IntPtr vptr = vstr != null ? vstr.Handle : IntPtr.Zero;
  377. CFHTTPMessageSetHeaderFieldValue (Handle, nstr.Handle, vptr);
  378. nstr.Dispose ();
  379. if (vstr != null)
  380. vstr.Dispose ();
  381. }
  382. [DllImport (Constants.CFNetworkLibrary)]
  383. extern static void CFHTTPMessageSetBody (IntPtr message, IntPtr data);
  384. internal void SetBody (CFData data)
  385. {
  386. CFHTTPMessageSetBody (Handle, data.Handle);
  387. }
  388. public void SetBody (byte[] buffer)
  389. {
  390. using (var data = new CFDataBuffer (buffer))
  391. CFHTTPMessageSetBody (Handle, data.Handle);
  392. }
  393. }
  394. }