Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Make.config
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,14 @@ EXTRA_SIMULATORS=com.apple.pkg.iPhoneSimulatorSDK12_4 com.apple.pkg.AppleTVSimul

INCLUDE_IOS=1
INCLUDE_MAC=1
INCLUDE_WATCH=1
INCLUDE_TVOS=1
INCLUDE_MACCATALYST=1
INCLUDE_WATCH=
INCLUDE_TVOS=
INCLUDE_MACCATALYST=
INCLUDE_DEVICE=1
INCLUDE_DOTNET_WATCHOS=
INCLUDE_XAMARIN_LEGACY=1

ENABLE_DOTNET=1
ENABLE_DOTNET=

# disable source code install by default (it's enabled for CI builds)
ENABLE_INSTALL_SOURCE=
Expand Down
4 changes: 2 additions & 2 deletions mk/xamarin.mk
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ MONO_BRANCH := $(shell cd $(MONO_PATH) 2> /dev/null && git symbolic-ref --sho
endif

ifdef ENABLE_XAMARIN
NEEDED_MACCORE_VERSION := 29a1c1382e005e568b57d1b45e3877c8081dfc8b
NEEDED_MACCORE_BRANCH := d17-2
NEEDED_MACCORE_VERSION := f0121539680861877436991efcd14817eb678a54
NEEDED_MACCORE_BRANCH := d17-2-dotnet-disabled-aware

MACCORE_DIRECTORY := maccore
MACCORE_MODULE := [email protected]:xamarin/maccore.git
Expand Down
62 changes: 5 additions & 57 deletions src/Foundation/NSUrlSessionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,58 +62,6 @@ namespace Foundation {
#endif
public delegate bool NSUrlSessionHandlerTrustOverrideForUrlCallback (NSUrlSessionHandler sender, string url, SecTrust trust);

// useful extensions for the class in order to set it in a header
static class NSHttpCookieExtensions
{
static void AppendSegment (StringBuilder builder, string name, string? value)
{
if (builder.Length > 0)
builder.Append ("; ");

builder.Append (name);
if (value is not null)
builder.Append ("=").Append (value);
}

// returns the header for a cookie
public static string GetHeaderValue (this NSHttpCookie cookie)
{
var header = new StringBuilder();
AppendSegment (header, cookie.Name, cookie.Value);
AppendSegment (header, NSHttpCookie.KeyPath.ToString (), cookie.Path.ToString ());
AppendSegment (header, NSHttpCookie.KeyDomain.ToString (), cookie.Domain.ToString ());
AppendSegment (header, NSHttpCookie.KeyVersion.ToString (), cookie.Version.ToString ());

if (cookie.Comment is not null)
AppendSegment (header, NSHttpCookie.KeyComment.ToString (), cookie.Comment.ToString());

if (cookie.CommentUrl is not null)
AppendSegment (header, NSHttpCookie.KeyCommentUrl.ToString (), cookie.CommentUrl.ToString());

if (cookie.Properties.ContainsKey (NSHttpCookie.KeyDiscard))
AppendSegment (header, NSHttpCookie.KeyDiscard.ToString (), null);

if (cookie.ExpiresDate is not null) {
// Format according to RFC1123; 'r' uses invariant info (DateTimeFormatInfo.InvariantInfo)
var dateStr = ((DateTime) cookie.ExpiresDate).ToUniversalTime ().ToString("r", CultureInfo.InvariantCulture);
AppendSegment (header, NSHttpCookie.KeyExpires.ToString (), dateStr);
}

if (cookie.Properties.ContainsKey (NSHttpCookie.KeyMaximumAge)) {
var timeStampString = (NSString) cookie.Properties[NSHttpCookie.KeyMaximumAge];
AppendSegment (header, NSHttpCookie.KeyMaximumAge.ToString (), timeStampString);
}

if (cookie.IsSecure)
AppendSegment (header, NSHttpCookie.KeySecure.ToString(), null);

if (cookie.IsHttpOnly)
AppendSegment (header, "httponly", null); // Apple does not show the key for the httponly

return header.ToString ();
}
}

public partial class NSUrlSessionHandler : HttpMessageHandler
{
private const string SetCookie = "Set-Cookie";
Expand Down Expand Up @@ -152,7 +100,7 @@ public NSUrlSessionHandler () : this (CreateConfig ())
public NSUrlSessionHandler (NSUrlSessionConfiguration configuration)
{
if (configuration is null)
throw new ArgumentNullException (nameof (configuration));
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (configuration));

// HACK: we need to store the following because session.Configuration gets a copy of the object and the value gets lost
sessionType = configuration.SessionType;
Expand Down Expand Up @@ -578,7 +526,7 @@ public int MaxAutomaticRedirections {
set {
// I believe it's possible to implement support for MaxAutomaticRedirections (it just has to be done)
if (value != int.MaxValue)
throw new ArgumentOutOfRangeException (nameof (value), value, "It's not possible to lower the max number of automatic redirections.");;
ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (value), value, "It's not possible to lower the max number of automatic redirections.");;
}
}

Expand Down Expand Up @@ -669,7 +617,7 @@ public bool UseProxy {
get => true;
set {
if (!value)
throw new ArgumentOutOfRangeException (nameof (value), value, "It's not possible to disable the use of system proxies.");;
ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (value), value, "It's not possible to disable the use of system proxies.");;
}
}
#endif // NET
Expand Down Expand Up @@ -1083,10 +1031,10 @@ public MonoStreamContent (Stream content)
public MonoStreamContent (Stream content, int bufferSize)
{
if (content is null)
throw new ArgumentNullException ("content");
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (content));

if (bufferSize <= 0)
throw new ArgumentOutOfRangeException ("bufferSize");
ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (bufferSize), bufferSize, "Buffer size must be >0");

this.content = content;
this.bufferSize = bufferSize;
Expand Down
111 changes: 111 additions & 0 deletions src/Foundation/NSUrlSessionHandler/MonoStreamContent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//
// Copied from https://github.com/mono/mono/blob/2019-02/mcs/class/System.Net.Http/System.Net.Http/StreamContent.cs.
//
// This is not a perfect solution, but the most robust and risk-free approach.
//
// The implementation depends on Mono-specific behavior, which makes SerializeToStreamAsync() cancellable.
// Unfortunately, the CoreFX implementation of HttpClient does not support this.
//
// By copying Mono's old implementation here, we ensure that we're compatible with both HttpClient implementations,
// so when we eventually adopt the CoreFX version in all of Mono's profiles, we don't regress here.
//
using System;
using System.Net;
using System.Net.Http;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

#nullable enable

#if !MONOMAC
namespace System.Net.Http {
#else
namespace Foundation {
#endif

class MonoStreamContent : HttpContent
{
readonly Stream content;
readonly int bufferSize;
readonly CancellationToken cancellationToken;
readonly long startPosition;
bool contentCopied;

public MonoStreamContent (Stream content)
: this (content, 16 * 1024)
{
}

public MonoStreamContent (Stream content, int bufferSize)
{
if (content is null)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (content));

if (bufferSize <= 0)
ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (bufferSize), bufferSize, "Buffer size must be >0");

this.content = content;
this.bufferSize = bufferSize;

if (content.CanSeek) {
startPosition = content.Position;
}
}

//
// Workarounds for poor .NET API
// Instead of having SerializeToStreamAsync with CancellationToken as public API. Only LoadIntoBufferAsync
// called internally from the send worker can be cancelled and user cannot see/do it
//
internal MonoStreamContent (Stream content, CancellationToken cancellationToken)
: this (content)
{
// We don't own the token so don't worry about disposing it
this.cancellationToken = cancellationToken;
}

protected override Task<Stream> CreateContentReadStreamAsync ()
{
return Task.FromResult (content);
}

protected override void Dispose (bool disposing)
{
if (disposing) {
content.Dispose ();
}

base.Dispose (disposing);
}

protected override Task SerializeToStreamAsync (Stream stream, TransportContext? context)
{
if (contentCopied) {
if (!content.CanSeek) {
throw new InvalidOperationException ("The stream was already consumed. It cannot be read again.");
}

content.Seek (startPosition, SeekOrigin.Begin);
} else {
contentCopied = true;
}

return content.CopyToAsync (stream, bufferSize, cancellationToken);
}

#if !NET
internal
#endif
protected override bool TryComputeLength (out long length)
{
if (!content.CanSeek) {
length = 0;
return false;
}
length = content.Length - startPosition;
return true;
}
}

}
74 changes: 74 additions & 0 deletions src/Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Globalization;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;

using Foundation;

#if !MONOMAC
using UIKit;
#endif

#nullable enable

// useful extensions for the class in order to set it in a header used by the NSUrlSessionHandler
// for cookie management.
#if !MONOMAC
namespace System.Net.Http {
#else
namespace Foundation {
#endif

static class NSHttpCookieExtensions
{
static void AppendSegment (StringBuilder builder, string name, string? value)
{
if (builder.Length > 0)
builder.Append ("; ");

builder.Append (name);
if (value is not null)
builder.Append ("=").Append (value);
}

// returns the header for a cookie
public static string GetHeaderValue (this NSHttpCookie cookie)
{
var header = new StringBuilder();
AppendSegment (header, cookie.Name, cookie.Value);
AppendSegment (header, NSHttpCookie.KeyPath.ToString (), cookie.Path.ToString ());
AppendSegment (header, NSHttpCookie.KeyDomain.ToString (), cookie.Domain.ToString ());
AppendSegment (header, NSHttpCookie.KeyVersion.ToString (), cookie.Version.ToString ());

if (cookie.Comment is not null)
AppendSegment (header, NSHttpCookie.KeyComment.ToString (), cookie.Comment.ToString());

if (cookie.CommentUrl is not null)
AppendSegment (header, NSHttpCookie.KeyCommentUrl.ToString (), cookie.CommentUrl.ToString());

if (cookie.Properties.ContainsKey (NSHttpCookie.KeyDiscard))
AppendSegment (header, NSHttpCookie.KeyDiscard.ToString (), null);

if (cookie.ExpiresDate is not null) {
// Format according to RFC1123; 'r' uses invariant info (DateTimeFormatInfo.InvariantInfo)
var dateStr = ((DateTime) cookie.ExpiresDate).ToUniversalTime ().ToString("r", CultureInfo.InvariantCulture);
AppendSegment (header, NSHttpCookie.KeyExpires.ToString (), dateStr);
}

if (cookie.Properties.ContainsKey (NSHttpCookie.KeyMaximumAge)) {
var timeStampString = (NSString) cookie.Properties[NSHttpCookie.KeyMaximumAge];
AppendSegment (header, NSHttpCookie.KeyMaximumAge.ToString (), timeStampString);
}

if (cookie.IsSecure)
AppendSegment (header, NSHttpCookie.KeySecure.ToString(), null);

if (cookie.IsHttpOnly)
AppendSegment (header, "httponly", null); // Apple does not show the key for the httponly

return header.ToString ();
}
}

}
16 changes: 16 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,15 @@ IOS_EXTRA_SOURCES = \

IOS_DOTNET_HTTP_SOURCES = \
Foundation/NSUrlSessionHandler.cs \
Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \
Foundation/NSUrlSessionHandler/MonoStreamContent.cs \
System.Net.Http/CFContentStream.cs \
System.Net.Http/CFNetworkHandler.cs \

IOS_HTTP_SOURCES = \
Foundation/NSUrlSessionHandler.cs \
Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \
Foundation/NSUrlSessionHandler/MonoStreamContent.cs \
System.Net.Http/CFContentStream.cs \
System.Net.Http/CFNetworkHandler.cs \

Expand Down Expand Up @@ -515,6 +519,8 @@ MAC_CFNETWORK_SOURCES = \

MAC_HTTP_SOURCES = \
Foundation/NSUrlSessionHandler.cs \
Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \
Foundation/NSUrlSessionHandler/MonoStreamContent.cs \
System.Net.Http/CFContentStream.cs \
System.Net.Http/CFNetworkHandler.cs \

Expand Down Expand Up @@ -829,6 +835,8 @@ WATCHOS_EXTRA_CORE_SOURCES = \

WATCHOS_HTTP_SOURCES = \
Foundation/NSUrlSessionHandler.cs \
Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \
Foundation/NSUrlSessionHandler/MonoStreamContent.cs \

WATCHOS_CORE_SOURCES += \
$(WATCHOS_EXTRA_CORE_SOURCES) \
Expand Down Expand Up @@ -1121,12 +1129,16 @@ TVOS_CORE_SOURCES += \

TVOS_DOTNET_HTTP_SOURCES = \
Foundation/NSUrlSessionHandler.cs \
Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \
Foundation/NSUrlSessionHandler/MonoStreamContent.cs \
System.Net.Http/CFContentStream.cs \
System.Net.Http/CFNetworkHandler.cs \
$(SHARED_DESIGNER_CS) \

TVOS_HTTP_SOURCES = \
Foundation/NSUrlSessionHandler.cs \
Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \
Foundation/NSUrlSessionHandler/MonoStreamContent.cs \
System.Net.Http/CFContentStream.cs \
System.Net.Http/CFNetworkHandler.cs \
$(SHARED_DESIGNER_CS) \
Expand Down Expand Up @@ -1338,12 +1350,16 @@ MACCATALYST_DOTNET_CORE_SOURCES += \

MACCATALYST_DOTNET_HTTP_SOURCES = \
Foundation/NSUrlSessionHandler.cs \
Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \
Foundation/NSUrlSessionHandler/MonoStreamContent.cs \
System.Net.Http/CFContentStream.cs \
System.Net.Http/CFNetworkHandler.cs \
$(SHARED_DESIGNER_CS) \

MACCATALYST_HTTP_SOURCES = \
Foundation/NSUrlSessionHandler.cs \
Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \
Foundation/NSUrlSessionHandler/MonoStreamContent.cs \
System.Net.Http/CFContentStream.cs \
System.Net.Http/CFNetworkHandler.cs \
$(SHARED_DESIGNER_CS) \
Expand Down
6 changes: 6 additions & 0 deletions src/ObjCRuntime/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public static void ThrowArgumentOutOfRangeException (string argumentName, string
throw new ArgumentOutOfRangeException (argumentName, message);
}

[DoesNotReturn]
public static void ThrowArgumentOutOfRangeException (string argumentName, object actualValue, string message)
{
throw new ArgumentOutOfRangeException (argumentName, actualValue, message);
}

[DoesNotReturn]
public static void ThrowObjectDisposedException (object o)
{
Expand Down
Loading