/src/CoreServices/CFHTTPMessage.cs
C# | 465 lines | 365 code | 71 blank | 29 comment | 51 complexity | a67738401a4193a6a45401a1fedd1161 MD5 | raw file
Possible License(s): Apache-2.0
- //
- // CFHTTPMessage.cs:
- //
- // Authors:
- // Martin Baulig (martin.baulig@gmail.com)
- // Marek Safar (marek.safar@gmail.com)
- //
- // Copyright 2012-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;
- using System.Net;
- using System.Security.Authentication;
- using System.Runtime.InteropServices;
- using MonoMac.Foundation;
- using MonoMac.CoreFoundation;
- using MonoMac.ObjCRuntime;
- namespace MonoMac.CoreServices {
- public class CFHTTPMessage : CFType, INativeObject, IDisposable {
- internal IntPtr handle;
- internal CFHTTPMessage (IntPtr handle)
- : this (handle, false)
- {
- }
- internal CFHTTPMessage (IntPtr handle, bool owns)
- {
- if (!owns)
- CFObject.CFRetain (handle);
- this.handle = handle;
- }
- static readonly NSString _HTTPVersion1_0;
- static readonly NSString _HTTPVersion1_1;
- static readonly NSString _AuthenticationSchemeBasic;
- static readonly NSString _AuthenticationSchemeNegotiate;
- static readonly NSString _AuthenticationSchemeNTLM;
- static readonly NSString _AuthenticationSchemeDigest;
- static readonly NSString _AuthenticationUsername;
- static readonly NSString _AuthenticationPassword;
- static readonly NSString _AuthenticationAccountDomain;
- static CFHTTPMessage ()
- {
- var handle = Dlfcn.dlopen (Constants.CFNetworkLibrary, 0);
- if (handle == IntPtr.Zero)
- throw new InvalidOperationException ();
- try {
- _HTTPVersion1_0 = GetStringConstant (handle, "kCFHTTPVersion1_0");
- _HTTPVersion1_1 = GetStringConstant (handle, "kCFHTTPVersion1_1");
- _AuthenticationSchemeBasic = GetStringConstant (
- handle, "kCFHTTPAuthenticationSchemeBasic");
- _AuthenticationSchemeNegotiate = GetStringConstant (
- handle, "kCFHTTPAuthenticationSchemeNegotiate");
- _AuthenticationSchemeNTLM = GetStringConstant (
- handle, "kCFHTTPAuthenticationSchemeNTLM");
- _AuthenticationSchemeDigest = GetStringConstant (
- handle, "kCFHTTPAuthenticationSchemeDigest");
- _AuthenticationUsername = GetStringConstant (
- handle, "kCFHTTPAuthenticationUsername");
- _AuthenticationPassword = GetStringConstant (
- handle, "kCFHTTPAuthenticationPassword");
- _AuthenticationAccountDomain = GetStringConstant (
- handle, "kCFHTTPAuthenticationAccountDomain");
- } finally {
- Dlfcn.dlclose (handle);
- }
- }
- static NSString GetStringConstant (IntPtr handle, string name)
- {
- var result = Dlfcn.GetStringConstant (handle, name);
- if (result == null)
- throw new InvalidOperationException (
- string.Format ("Cannot get '{0}' property.", name));
- return result;
- }
- [DllImport (Constants.CFNetworkLibrary, EntryPoint="CFHTTPMessageGetTypeID")]
- public extern static int GetTypeID ();
- ~CFHTTPMessage ()
- {
- Dispose (false);
- }
-
- protected void CheckHandle ()
- {
- if (handle == IntPtr.Zero)
- throw new ObjectDisposedException (GetType ().Name);
- }
- public void Dispose ()
- {
- Dispose (true);
- GC.SuppressFinalize (this);
- }
- public IntPtr Handle {
- get {
- CheckHandle ();
- return handle;
- }
- }
-
- protected virtual void Dispose (bool disposing)
- {
- if (handle != IntPtr.Zero) {
- CFObject.CFRelease (handle);
- handle = IntPtr.Zero;
- }
- }
- static IntPtr GetVersion (Version version)
- {
- if ((version == null) || version.Equals (HttpVersion.Version11))
- return _HTTPVersion1_1.Handle;
- else if (version.Equals (HttpVersion.Version10))
- return _HTTPVersion1_0.Handle;
- else
- throw new ArgumentException ();
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static IntPtr CFHTTPMessageCreateEmpty (IntPtr allocator, bool isRequest);
- public static CFHTTPMessage CreateEmpty (bool request)
- {
- var handle = CFHTTPMessageCreateEmpty (IntPtr.Zero, request);
- if (handle == IntPtr.Zero)
- return null;
- return new CFHTTPMessage (handle);
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static IntPtr CFHTTPMessageCreateRequest (IntPtr allocator, IntPtr requestMethod,
- IntPtr url, IntPtr httpVersion);
- public static CFHTTPMessage CreateRequest (CFUrl url, NSString method, Version version)
- {
- var handle = CFHTTPMessageCreateRequest (
- IntPtr.Zero, method.Handle, url.Handle, GetVersion (version));
- if (handle == IntPtr.Zero)
- return null;
- return new CFHTTPMessage (handle);
- }
- public static CFHTTPMessage CreateRequest (Uri uri, string method)
- {
- return CreateRequest (uri, method, null);
- }
- public static CFHTTPMessage CreateRequest (Uri uri, string method, Version version)
- {
- CFUrl urlRef = null;
- NSString methodRef = null;
- var escaped = Uri.EscapeUriString (uri.ToString ());
- try {
- urlRef = CFUrl.FromUrlString (escaped, null);
- if (urlRef == null)
- throw new ArgumentException ("Invalid URL.");
- methodRef = new NSString (method);
- var msg = CreateRequest (urlRef, methodRef, version);
- if (msg == null)
- throw new ArgumentException ("Invalid URL.");
- return msg;
- } finally {
- if (urlRef != null)
- urlRef.Dispose ();
- if (methodRef != null)
- methodRef.Dispose ();
- }
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static bool CFHTTPMessageIsRequest (IntPtr handle);
- public bool IsRequest {
- get {
- CheckHandle ();
- return CFHTTPMessageIsRequest (Handle);
- }
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static CFIndex CFHTTPMessageGetResponseStatusCode (IntPtr handle);
- public HttpStatusCode ResponseStatusCode {
- get {
- if (IsRequest)
- throw new InvalidOperationException ();
- int status = CFHTTPMessageGetResponseStatusCode (Handle);
- return (HttpStatusCode)status;
- }
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static IntPtr CFHTTPMessageCopyResponseStatusLine (IntPtr handle);
- public string ResponseStatusLine {
- get {
- if (IsRequest)
- throw new InvalidOperationException ();
- var ptr = CFHTTPMessageCopyResponseStatusLine (Handle);
- if (ptr == IntPtr.Zero)
- return null;
- using (var line = new NSString (ptr))
- return line.ToString ();
- }
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static IntPtr CFHTTPMessageCopyVersion (IntPtr handle);
- public Version Version {
- get {
- CheckHandle ();
- IntPtr ptr = CFHTTPMessageCopyVersion (handle);
- try {
- if (ptr.Equals (_HTTPVersion1_0.Handle))
- return HttpVersion.Version10;
- else
- return HttpVersion.Version11;
- } finally {
- if (ptr != IntPtr.Zero)
- CFObject.CFRelease (ptr);
- }
- }
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static bool CFHTTPMessageIsHeaderComplete (IntPtr handle);
- public bool IsHeaderComplete {
- get {
- CheckHandle ();
- return CFHTTPMessageIsHeaderComplete (Handle);
- }
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static bool CFHTTPMessageAppendBytes (IntPtr message, ref byte[] newBytes, CFIndex numBytes);
- public bool AppendBytes (byte[] bytes)
- {
- if (bytes == null)
- throw new ArgumentNullException ("bytes");
- return AppendBytes (bytes, bytes.Length);
- }
- public bool AppendBytes (byte[] bytes, int count)
- {
- if (bytes == null)
- throw new ArgumentNullException ("bytes");
- return CFHTTPMessageAppendBytes (Handle, ref bytes, count);
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static IntPtr CFHTTPMessageCopyAllHeaderFields (IntPtr handle);
- public NSDictionary GetAllHeaderFields ()
- {
- CheckHandle ();
- IntPtr ptr = CFHTTPMessageCopyAllHeaderFields (handle);
- if (ptr == IntPtr.Zero)
- return null;
- return new NSDictionary (ptr);
- }
- #region Authentication
- struct CFStreamError {
- public int domain;
- public int code;
- }
- enum ErrorHTTPAuthentication {
- TypeUnsupported = -1000,
- BadUserName = -1001,
- BadPassword = -1002
- }
- AuthenticationException GetException (ErrorHTTPAuthentication code)
- {
- switch (code) {
- case ErrorHTTPAuthentication.BadUserName:
- throw new InvalidCredentialException ("Bad username.");
- case ErrorHTTPAuthentication.BadPassword:
- throw new InvalidCredentialException ("Bad password.");
- case ErrorHTTPAuthentication.TypeUnsupported:
- throw new AuthenticationException ("Authentication type not supported.");
- default:
- throw new AuthenticationException ("Unknown error.");
- }
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static bool CFHTTPMessageApplyCredentials (IntPtr request, IntPtr auth,
- IntPtr user, IntPtr pass,
- out CFStreamError error);
- public void ApplyCredentials (CFHTTPAuthentication auth, NetworkCredential credential)
- {
- if (auth.RequiresAccountDomain) {
- ApplyCredentialDictionary (auth, credential);
- return;
- }
- var username = new CFString (credential.UserName);
- var password = new CFString (credential.Password);
- try {
- CFStreamError error;
- var ok = CFHTTPMessageApplyCredentials (
- Handle, auth.Handle, username.Handle, password.Handle,
- out error);
- if (!ok)
- throw GetException ((ErrorHTTPAuthentication)error.code);
- } finally {
- username.Dispose ();
- password.Dispose ();
- }
- }
- public enum AuthenticationScheme {
- Default,
- Basic,
- Negotiate,
- NTLM,
- Digest
- }
- internal static IntPtr GetAuthScheme (AuthenticationScheme scheme)
- {
- switch (scheme) {
- case AuthenticationScheme.Default:
- return IntPtr.Zero;
- case AuthenticationScheme.Basic:
- return _AuthenticationSchemeBasic.Handle;
- case AuthenticationScheme.Negotiate:
- return _AuthenticationSchemeNegotiate.Handle;
- case AuthenticationScheme.NTLM:
- return _AuthenticationSchemeNTLM.Handle;
- case AuthenticationScheme.Digest:
- return _AuthenticationSchemeDigest.Handle;
- default:
- throw new ArgumentException ();
- }
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static bool CFHTTPMessageAddAuthentication (IntPtr request, IntPtr response,
- IntPtr username, IntPtr password,
- IntPtr scheme, bool forProxy);
- public bool AddAuthentication (CFHTTPMessage failureResponse, NSString username,
- NSString password, AuthenticationScheme scheme,
- bool forProxy)
- {
- return CFHTTPMessageAddAuthentication (
- Handle, failureResponse.Handle, username.Handle,
- password.Handle, GetAuthScheme (scheme), forProxy);
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static bool CFHTTPMessageApplyCredentialDictionary (IntPtr request, IntPtr auth,
- IntPtr dict, out CFStreamError error);
- public void ApplyCredentialDictionary (CFHTTPAuthentication auth, NetworkCredential credential)
- {
- var keys = new NSString [3];
- var values = new CFString [3];
- keys [0] = _AuthenticationUsername;
- keys [1] = _AuthenticationPassword;
- keys [2] = _AuthenticationAccountDomain;
- values [0] = (CFString)credential.UserName;
- values [1] = (CFString)credential.Password;
- values [2] = credential.Domain != null ? (CFString)credential.Domain : null;
- var dict = CFDictionary.FromObjectsAndKeys (values, keys);
- try {
- CFStreamError error;
- var ok = CFHTTPMessageApplyCredentialDictionary (
- Handle, auth.Handle, dict.Handle, out error);
- if (ok)
- return;
- throw GetException ((ErrorHTTPAuthentication)error.code);
- } finally {
- dict.Dispose ();
- values [0].Dispose ();
- values [1].Dispose ();
- if (values [2] != null)
- values [2].Dispose ();
- }
- }
- #endregion
- [DllImport (Constants.CFNetworkLibrary)]
- extern static void CFHTTPMessageSetHeaderFieldValue (IntPtr message, IntPtr headerField,
- IntPtr value);
- public void SetHeaderFieldValue (string name, string value)
- {
- NSString nstr = (NSString)name;
- NSString vstr = value != null ? (NSString)value : null;
- IntPtr vptr = vstr != null ? vstr.Handle : IntPtr.Zero;
- CFHTTPMessageSetHeaderFieldValue (Handle, nstr.Handle, vptr);
- nstr.Dispose ();
- if (vstr != null)
- vstr.Dispose ();
- }
- [DllImport (Constants.CFNetworkLibrary)]
- extern static void CFHTTPMessageSetBody (IntPtr message, IntPtr data);
- internal void SetBody (CFData data)
- {
- CFHTTPMessageSetBody (Handle, data.Handle);
- }
- public void SetBody (byte[] buffer)
- {
- using (var data = new CFDataBuffer (buffer))
- CFHTTPMessageSetBody (Handle, data.Handle);
- }
- }
- }