Skip to content
Merged
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
25 changes: 15 additions & 10 deletions src/System.Net.Http/CFContentStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,21 @@
using CFNetwork;
using CoreFoundation;

// Disable until we get around to enable + fix any issues.
#nullable disable
#nullable enable

namespace System.Net.Http {
class BufferData {
public byte [] Buffer;
public required byte [] Buffer;
public int Length;
}

class CFContentStream : HttpContent {
readonly CFHTTPStream http_stream;
BufferData data;
BufferData? data;
Mutex data_mutex;
AutoResetEvent data_event;
AutoResetEvent data_read_event;
ExceptionDispatchInfo http_exception;
ExceptionDispatchInfo? http_exception;

// The requirements are:
// * We must read at least one byte from the stream every time
Expand Down Expand Up @@ -83,13 +82,18 @@ public CFContentStream (CFHTTPStream stream)
data_mutex = new Mutex ();
}

void HandleErrorEvent (object sender, CFStream.StreamEventArgs e)
void HandleErrorEvent (object? sender, CFStream.StreamEventArgs e)
{
if (sender is null)
return;
var gotMutex = data_mutex.WaitOne ();
if (gotMutex) {
var stream = (CFHTTPStream) sender;
if (e.EventType == CFStreamEventType.ErrorOccurred)
Volatile.Write (ref http_exception, ExceptionDispatchInfo.Capture (stream.GetError ()));
if (e.EventType == CFStreamEventType.ErrorOccurred) {
var error = stream.GetError ();
if (error is not null)
Volatile.Write (ref http_exception, ExceptionDispatchInfo.Capture (error));
}
data_mutex.ReleaseMutex ();
}
}
Expand All @@ -99,7 +103,8 @@ public void ReadStreamData ()
data_read_event.WaitOne (); // make sure there's no pending data.

data_mutex.WaitOne ();
data.Length = (int) http_stream.Read (data.Buffer, 0, data.Buffer.Length);
if (data is not null)
data.Length = (int) http_stream.Read (data.Buffer, 0, data.Buffer.Length);
data_mutex.ReleaseMutex ();

data_event.Set ();
Expand All @@ -117,7 +122,7 @@ public void Close ()
data_event.Set ();
}

protected override async Task SerializeToStreamAsync (Stream stream, TransportContext context)
protected override async Task SerializeToStreamAsync (Stream stream, TransportContext? context)
{
while (data_event.WaitOne ()) {
data_mutex.WaitOne ();
Expand Down
59 changes: 35 additions & 24 deletions src/System.Net.Http/CFNetworkHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@
using CoreFoundation;
using CF = CoreFoundation;

// Disable until we get around to enable + fix any issues.
#nullable disable
#nullable enable

namespace System.Net.Http {
/// <summary>To be added.</summary>
Expand All @@ -53,10 +52,10 @@ namespace System.Net.Http {
[ObsoletedOSPlatform ("tvos", "Use 'NSUrlSessionHandler' instead.")]
public class CFNetworkHandler : HttpMessageHandler {
class StreamBucket {
public TaskCompletionSource<HttpResponseMessage> Response;
public HttpRequestMessage Request;
public required TaskCompletionSource<HttpResponseMessage> Response;
public required HttpRequestMessage Request;
public CancellationTokenRegistration CancellationTokenRegistration;
public CFContentStream ContentStream;
public CFContentStream? ContentStream;
public bool StreamCanBeDisposed;

public void Close ()
Expand Down Expand Up @@ -92,7 +91,7 @@ public void Close ()
bool allowAutoRedirect;
bool sentRequest;
bool useSystemProxy;
CookieContainer cookies;
CookieContainer? cookies;

Dictionary<IntPtr, StreamBucket> streamBuckets;

Expand Down Expand Up @@ -165,7 +164,8 @@ protected override void Dispose (bool disposing)

CFHTTPMessage CreateWebRequestAsync (HttpRequestMessage request)
{
var req = CFHTTPMessage.CreateRequest (request.RequestUri, request.Method.Method, request.Version);
var requestUri = request.RequestUri ?? throw new InvalidOperationException ("The request URI must not be null.");
var req = CFHTTPMessage.CreateRequest (requestUri, request.Method.Method, request.Version);

// TODO:
/*
Expand All @@ -186,7 +186,7 @@ CFHTTPMessage CreateWebRequestAsync (HttpRequestMessage request)
}
*/
if (cookies is not null) {
string cookieHeader = cookies.GetCookieHeader (request.RequestUri);
string cookieHeader = cookies.GetCookieHeader (requestUri);
if (cookieHeader != "")
req.SetHeaderFieldValue ("Cookie", cookieHeader);
}
Expand Down Expand Up @@ -234,7 +234,7 @@ internal async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request,

if (useSystemProxy) {
var proxies = CF.CFNetwork.GetSystemProxySettings ();
if (proxies.HTTPEnable) {
if (proxies is not null && proxies.HTTPEnable) {
stream.SetProxy (proxies);
}
}
Expand Down Expand Up @@ -268,8 +268,7 @@ internal async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request,
stream.Open ();

bucket.CancellationTokenRegistration = cancellationToken.Register (() => {
StreamBucket bucket2;
if (!streamBuckets.TryGetValue (stream.Handle, out bucket2))
if (!streamBuckets.TryGetValue (stream.Handle, out var bucket2))
return;

bucket2.Response.TrySetCanceled ();
Expand Down Expand Up @@ -305,21 +304,27 @@ static bool IsRedirect (HttpStatusCode status)
status == HttpStatusCode.RedirectKeepVerb; // 307
}

void HandleErrorEvent (object sender, CFStream.StreamEventArgs e)
void HandleErrorEvent (object? sender, CFStream.StreamEventArgs e)
{
if (sender is null)
return;
var stream = (CFHTTPStream) sender;

StreamBucket bucket;
if (!streamBuckets.TryGetValue (stream.Handle, out bucket))
if (!streamBuckets.TryGetValue (stream.Handle, out var bucket))
return;

var ex = stream.GetError ();
bucket.Response.TrySetException (new HttpRequestException (ex.FailureReason, ex));
if (ex is not null)
bucket.Response.TrySetException (new HttpRequestException (ex.FailureReason, ex));
else
bucket.Response.TrySetException (new HttpRequestException ("Unknown error"));
CloseStream (stream);
}

void HandleClosedEvent (object sender, CFStream.StreamEventArgs e)
void HandleClosedEvent (object? sender, CFStream.StreamEventArgs e)
{
if (sender is null)
return;
var stream = (CFHTTPStream) sender;
// might not have been called (e.g. no data) but initialize critical data
HandleHasBytesAvailableEvent (sender, e);
Expand All @@ -339,21 +344,25 @@ void CloseStream (CFHTTPStream stream)
stream.Close ();
}

void HandleHasBytesAvailableEvent (object sender, CFStream.StreamEventArgs e)
void HandleHasBytesAvailableEvent (object? sender, CFStream.StreamEventArgs e)
{
if (sender is null)
return;
var stream = (CFHTTPStream) sender;

StreamBucket bucket;
if (!streamBuckets.TryGetValue (stream.Handle, out bucket))
if (!streamBuckets.TryGetValue (stream.Handle, out var bucket))
return;

if (bucket.Response.Task.IsCompleted) {
bucket.ContentStream.ReadStreamData ();
bucket.ContentStream?.ReadStreamData ();
return;
}

var header = stream.GetResponseHeader ();

if (header is null)
throw new InvalidOperationException ("Failed to get response header.");

// Is this possible?
if (!header.IsHeaderComplete)
throw new NotImplementedException ();
Expand All @@ -372,14 +381,16 @@ void HandleHasBytesAvailableEvent (object sender, CFStream.StreamEventArgs e)
continue;

var key = entry.Key.ToString ();
var value = entry.Value is null ? string.Empty : entry.Value.ToString ();
if (key is null)
continue;
var value = entry.Value is null ? string.Empty : entry.Value.ToString () ?? string.Empty;
HttpHeaders item_headers;
if (IsContentHeader (key)) {
item_headers = response_msg.Content.Headers;
} else {
item_headers = response_msg.Headers;

if (cookies is not null && (key == "Set-Cookie" || key == "Set-Cookie2"))
if (cookies is not null && (key == "Set-Cookie" || key == "Set-Cookie2") && bucket.Request.RequestUri is not null)
AddCookie (value, bucket.Request.RequestUri, key);
}

Expand All @@ -391,14 +402,14 @@ void HandleHasBytesAvailableEvent (object sender, CFStream.StreamEventArgs e)
if (!bucket.Response.Task.IsCanceled) {
bucket.Response.TrySetResult (response_msg);

bucket.ContentStream.ReadStreamData ();
bucket.ContentStream?.ReadStreamData ();
}
}

void AddCookie (string value, Uri uri, string header)
{
try {
cookies.SetCookies (uri, value);
cookies?.SetCookies (uri, value);
} catch {
}
}
Expand Down
4 changes: 4 additions & 0 deletions tests/cecil-tests/ObsoleteTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public void GetAllObsoletedThings (AssemblyInfo info)

bool FilterMember (ICustomAttributeProvider provider)
{
// We don't care about APIs that aren't public
if (provider is MemberReference mr && !mr.IsPubliclyVisible ())
return false;

// If an API isn't obsolete, it's not under scrutiny from this test.
if (!provider.IsObsolete ())
return false;
Expand Down
Loading