5
// Martin Baulig (martin.baulig@gmail.com)
6
// Marek Safar (marek.safar@gmail.com)
8
// Copyright 2012-2013 Xamarin Inc. (http://www.xamarin.com)
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:
19
// The above copyright notice and this permission notice shall be
20
// included in all copies or substantial portions of the Software.
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.
32
using System.Security.Authentication;
33
using System.Runtime.InteropServices;
34
using MonoMac.Foundation;
35
using MonoMac.CoreFoundation;
36
using MonoMac.ObjCRuntime;
38
namespace MonoMac.CoreServices {
40
public class CFHTTPMessage : CFType, INativeObject, IDisposable {
41
internal IntPtr handle;
43
internal CFHTTPMessage (IntPtr handle)
44
: this (handle, false)
48
internal CFHTTPMessage (IntPtr handle, bool owns)
51
CFObject.CFRetain (handle);
55
static readonly NSString _HTTPVersion1_0;
56
static readonly NSString _HTTPVersion1_1;
57
static readonly NSString _AuthenticationSchemeBasic;
58
static readonly NSString _AuthenticationSchemeNegotiate;
59
static readonly NSString _AuthenticationSchemeNTLM;
60
static readonly NSString _AuthenticationSchemeDigest;
61
static readonly NSString _AuthenticationUsername;
62
static readonly NSString _AuthenticationPassword;
63
static readonly NSString _AuthenticationAccountDomain;
65
static CFHTTPMessage ()
67
var handle = Dlfcn.dlopen (Constants.CFNetworkLibrary, 0);
68
if (handle == IntPtr.Zero)
69
throw new InvalidOperationException ();
72
_HTTPVersion1_0 = GetStringConstant (handle, "kCFHTTPVersion1_0");
73
_HTTPVersion1_1 = GetStringConstant (handle, "kCFHTTPVersion1_1");
75
_AuthenticationSchemeBasic = GetStringConstant (
76
handle, "kCFHTTPAuthenticationSchemeBasic");
77
_AuthenticationSchemeNegotiate = GetStringConstant (
78
handle, "kCFHTTPAuthenticationSchemeNegotiate");
79
_AuthenticationSchemeNTLM = GetStringConstant (
80
handle, "kCFHTTPAuthenticationSchemeNTLM");
81
_AuthenticationSchemeDigest = GetStringConstant (
82
handle, "kCFHTTPAuthenticationSchemeDigest");
84
_AuthenticationUsername = GetStringConstant (
85
handle, "kCFHTTPAuthenticationUsername");
86
_AuthenticationPassword = GetStringConstant (
87
handle, "kCFHTTPAuthenticationPassword");
88
_AuthenticationAccountDomain = GetStringConstant (
89
handle, "kCFHTTPAuthenticationAccountDomain");
91
Dlfcn.dlclose (handle);
95
static NSString GetStringConstant (IntPtr handle, string name)
97
var result = Dlfcn.GetStringConstant (handle, name);
99
throw new InvalidOperationException (
100
string.Format ("Cannot get '{0}' property.", name));
104
[DllImport (Constants.CFNetworkLibrary, EntryPoint="CFHTTPMessageGetTypeID")]
105
public extern static int GetTypeID ();
112
protected void CheckHandle ()
114
if (handle == IntPtr.Zero)
115
throw new ObjectDisposedException (GetType ().Name);
118
public void Dispose ()
121
GC.SuppressFinalize (this);
124
public IntPtr Handle {
131
protected virtual void Dispose (bool disposing)
133
if (handle != IntPtr.Zero) {
134
CFObject.CFRelease (handle);
135
handle = IntPtr.Zero;
139
static IntPtr GetVersion (Version version)
141
if ((version == null) || version.Equals (HttpVersion.Version11))
142
return _HTTPVersion1_1.Handle;
143
else if (version.Equals (HttpVersion.Version10))
144
return _HTTPVersion1_0.Handle;
146
throw new ArgumentException ();
149
[DllImport (Constants.CFNetworkLibrary)]
150
extern static IntPtr CFHTTPMessageCreateEmpty (IntPtr allocator, bool isRequest);
152
public static CFHTTPMessage CreateEmpty (bool request)
154
var handle = CFHTTPMessageCreateEmpty (IntPtr.Zero, request);
155
if (handle == IntPtr.Zero)
158
return new CFHTTPMessage (handle);
161
[DllImport (Constants.CFNetworkLibrary)]
162
extern static IntPtr CFHTTPMessageCreateRequest (IntPtr allocator, IntPtr requestMethod,
163
IntPtr url, IntPtr httpVersion);
165
public static CFHTTPMessage CreateRequest (CFUrl url, NSString method, Version version)
167
var handle = CFHTTPMessageCreateRequest (
168
IntPtr.Zero, method.Handle, url.Handle, GetVersion (version));
169
if (handle == IntPtr.Zero)
172
return new CFHTTPMessage (handle);
175
public static CFHTTPMessage CreateRequest (Uri uri, string method)
177
return CreateRequest (uri, method, null);
180
public static CFHTTPMessage CreateRequest (Uri uri, string method, Version version)
183
NSString methodRef = null;
185
var escaped = Uri.EscapeUriString (uri.ToString ());
188
urlRef = CFUrl.FromUrlString (escaped, null);
190
throw new ArgumentException ("Invalid URL.");
191
methodRef = new NSString (method);
193
var msg = CreateRequest (urlRef, methodRef, version);
195
throw new ArgumentException ("Invalid URL.");
201
if (methodRef != null)
202
methodRef.Dispose ();
206
[DllImport (Constants.CFNetworkLibrary)]
207
extern static bool CFHTTPMessageIsRequest (IntPtr handle);
209
public bool IsRequest {
212
return CFHTTPMessageIsRequest (Handle);
216
[DllImport (Constants.CFNetworkLibrary)]
217
extern static CFIndex CFHTTPMessageGetResponseStatusCode (IntPtr handle);
219
public HttpStatusCode ResponseStatusCode {
222
throw new InvalidOperationException ();
223
int status = CFHTTPMessageGetResponseStatusCode (Handle);
224
return (HttpStatusCode)status;
228
[DllImport (Constants.CFNetworkLibrary)]
229
extern static IntPtr CFHTTPMessageCopyResponseStatusLine (IntPtr handle);
231
public string ResponseStatusLine {
234
throw new InvalidOperationException ();
235
var ptr = CFHTTPMessageCopyResponseStatusLine (Handle);
236
if (ptr == IntPtr.Zero)
238
using (var line = new NSString (ptr))
239
return line.ToString ();
243
[DllImport (Constants.CFNetworkLibrary)]
244
extern static IntPtr CFHTTPMessageCopyVersion (IntPtr handle);
246
public Version Version {
249
IntPtr ptr = CFHTTPMessageCopyVersion (handle);
251
if (ptr.Equals (_HTTPVersion1_0.Handle))
252
return HttpVersion.Version10;
254
return HttpVersion.Version11;
256
if (ptr != IntPtr.Zero)
257
CFObject.CFRelease (ptr);
262
[DllImport (Constants.CFNetworkLibrary)]
263
extern static bool CFHTTPMessageIsHeaderComplete (IntPtr handle);
265
public bool IsHeaderComplete {
268
return CFHTTPMessageIsHeaderComplete (Handle);
272
[DllImport (Constants.CFNetworkLibrary)]
273
extern static bool CFHTTPMessageAppendBytes (IntPtr message, ref byte[] newBytes, CFIndex numBytes);
275
public bool AppendBytes (byte[] bytes)
278
throw new ArgumentNullException ("bytes");
280
return AppendBytes (bytes, bytes.Length);
283
public bool AppendBytes (byte[] bytes, int count)
286
throw new ArgumentNullException ("bytes");
288
return CFHTTPMessageAppendBytes (Handle, ref bytes, count);
291
[DllImport (Constants.CFNetworkLibrary)]
292
extern static IntPtr CFHTTPMessageCopyAllHeaderFields (IntPtr handle);
294
public NSDictionary GetAllHeaderFields ()
297
IntPtr ptr = CFHTTPMessageCopyAllHeaderFields (handle);
298
if (ptr == IntPtr.Zero)
300
return new NSDictionary (ptr);
303
#region Authentication
305
struct CFStreamError {
310
enum ErrorHTTPAuthentication {
311
TypeUnsupported = -1000,
316
AuthenticationException GetException (ErrorHTTPAuthentication code)
319
case ErrorHTTPAuthentication.BadUserName:
320
throw new InvalidCredentialException ("Bad username.");
321
case ErrorHTTPAuthentication.BadPassword:
322
throw new InvalidCredentialException ("Bad password.");
323
case ErrorHTTPAuthentication.TypeUnsupported:
324
throw new AuthenticationException ("Authentication type not supported.");
326
throw new AuthenticationException ("Unknown error.");
330
[DllImport (Constants.CFNetworkLibrary)]
331
extern static bool CFHTTPMessageApplyCredentials (IntPtr request, IntPtr auth,
332
IntPtr user, IntPtr pass,
333
out CFStreamError error);
335
public void ApplyCredentials (CFHTTPAuthentication auth, NetworkCredential credential)
337
if (auth.RequiresAccountDomain) {
338
ApplyCredentialDictionary (auth, credential);
342
var username = new CFString (credential.UserName);
343
var password = new CFString (credential.Password);
348
var ok = CFHTTPMessageApplyCredentials (
349
Handle, auth.Handle, username.Handle, password.Handle,
352
throw GetException ((ErrorHTTPAuthentication)error.code);
359
public enum AuthenticationScheme {
367
internal static IntPtr GetAuthScheme (AuthenticationScheme scheme)
370
case AuthenticationScheme.Default:
372
case AuthenticationScheme.Basic:
373
return _AuthenticationSchemeBasic.Handle;
374
case AuthenticationScheme.Negotiate:
375
return _AuthenticationSchemeNegotiate.Handle;
376
case AuthenticationScheme.NTLM:
377
return _AuthenticationSchemeNTLM.Handle;
378
case AuthenticationScheme.Digest:
379
return _AuthenticationSchemeDigest.Handle;
381
throw new ArgumentException ();
385
[DllImport (Constants.CFNetworkLibrary)]
386
extern static bool CFHTTPMessageAddAuthentication (IntPtr request, IntPtr response,
387
IntPtr username, IntPtr password,
388
IntPtr scheme, bool forProxy);
390
public bool AddAuthentication (CFHTTPMessage failureResponse, NSString username,
391
NSString password, AuthenticationScheme scheme,
394
return CFHTTPMessageAddAuthentication (
395
Handle, failureResponse.Handle, username.Handle,
396
password.Handle, GetAuthScheme (scheme), forProxy);
399
[DllImport (Constants.CFNetworkLibrary)]
400
extern static bool CFHTTPMessageApplyCredentialDictionary (IntPtr request, IntPtr auth,
401
IntPtr dict, out CFStreamError error);
403
public void ApplyCredentialDictionary (CFHTTPAuthentication auth, NetworkCredential credential)
405
var keys = new NSString [3];
406
var values = new CFString [3];
407
keys [0] = _AuthenticationUsername;
408
keys [1] = _AuthenticationPassword;
409
keys [2] = _AuthenticationAccountDomain;
410
values [0] = (CFString)credential.UserName;
411
values [1] = (CFString)credential.Password;
412
values [2] = credential.Domain != null ? (CFString)credential.Domain : null;
414
var dict = CFDictionary.FromObjectsAndKeys (values, keys);
418
var ok = CFHTTPMessageApplyCredentialDictionary (
419
Handle, auth.Handle, dict.Handle, out error);
422
throw GetException ((ErrorHTTPAuthentication)error.code);
425
values [0].Dispose ();
426
values [1].Dispose ();
427
if (values [2] != null)
428
values [2].Dispose ();
434
[DllImport (Constants.CFNetworkLibrary)]
435
extern static void CFHTTPMessageSetHeaderFieldValue (IntPtr message, IntPtr headerField,
438
public void SetHeaderFieldValue (string name, string value)
440
NSString nstr = (NSString)name;
441
NSString vstr = value != null ? (NSString)value : null;
442
IntPtr vptr = vstr != null ? vstr.Handle : IntPtr.Zero;
444
CFHTTPMessageSetHeaderFieldValue (Handle, nstr.Handle, vptr);
451
[DllImport (Constants.CFNetworkLibrary)]
452
extern static void CFHTTPMessageSetBody (IntPtr message, IntPtr data);
454
internal void SetBody (CFData data)
456
CFHTTPMessageSetBody (Handle, data.Handle);
459
public void SetBody (byte[] buffer)
461
using (var data = new CFDataBuffer (buffer))
462
CFHTTPMessageSetBody (Handle, data.Handle);