Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Merge pull request #7964 from stephentoub/port7946
Browse files Browse the repository at this point in the history
Port to RC2: Fix CurlResponseStream.Dispose to cancel the request
  • Loading branch information
Petermarcu committed Apr 21, 2016
2 parents aa4498a + 2356f37 commit eede273
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ public override Task<int> ReadAsync(byte[] buffer, int offset, int count, Cancel
}

/// <summary>Notifies the stream that no more data will be written.</summary>
internal void SignalComplete(Exception error = null)
internal void SignalComplete(Exception error = null, bool forceCancel = false)
{
lock (_lockObject)
{
Expand All @@ -362,6 +362,17 @@ internal void SignalComplete(Exception error = null)
error :
s_completionSentinel;

// If the request wasn't already completed, and if requested, send a cancellation
// request to ensure that the connection gets cleaned up. This is only necessary
// to do if this method is being called for a reason other than the request/response
// completing naturally, e.g. if the response stream is being disposed of before
// all of the response has been downloaded.
if (forceCancel)
{
EventSourceTrace("Completing the response stream prematurely.");
_easy._associatedMultiAgent.RequestCancel(_easy);
}

// If there's a pending read request, complete it, either with 0 bytes for success
// or with the exception/CancellationToken for failure.
if (_pendingReadRequest != null)
Expand Down Expand Up @@ -409,7 +420,7 @@ protected override void Dispose(bool disposing)
if (disposing && !_disposed)
{
_disposed = true;
SignalComplete();
SignalComplete(forceCancel: true);
}

base.Dispose(disposing);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ internal void RequestUnpause(EasyRequest easy)
Queue(new IncomingRequest { Easy = easy, Type = IncomingRequestType.Unpause });
}

/// <summary>Requests that the request associated with the easy operation be canceled.</summary>
internal void RequestCancel(EasyRequest easy)
{
EventSourceTrace(null, easy: easy);
Queue(new IncomingRequest { Easy = easy, Type = IncomingRequestType.Cancel });
}

/// <summary>Creates and configures a new multi handle.</summary>
private SafeCurlMultiHandle CreateAndConfigureMultiHandle()
{
Expand Down Expand Up @@ -369,7 +376,6 @@ private void HandleIncomingRequest(SafeCurlMultiHandle multiHandle, IncomingRequ

case IncomingRequestType.Cancel:
Debug.Assert(easy._associatedMultiAgent == this, "Should only cancel associated easy requests");
Debug.Assert(easy._cancellationToken.IsCancellationRequested, "Cancellation should have been requested");
FindAndFailActiveRequest(multiHandle, easy, new OperationCanceledException(easy._cancellationToken));
break;

Expand Down Expand Up @@ -435,17 +441,14 @@ private void ActivateNewRequest(SafeCurlMultiHandle multiHandle, EasyRequest eas

// And if cancellation can be requested, hook up a cancellation callback.
// This callback will put the easy request back into the queue, which will
// ensure that a wake-up request has been issued. When we pull
// the easy request out of the request queue, we'll see that it's already
// associated with this agent, meaning that it's a cancellation request,
// and we'll deal with it appropriately.
// ensure that a wake-up request has been issued.
var cancellationReg = default(CancellationTokenRegistration);
if (easy._cancellationToken.CanBeCanceled)
{
cancellationReg = easy._cancellationToken.Register(s =>
{
var state = (Tuple<MultiAgent, EasyRequest>)s;
state.Item1.Queue(new IncomingRequest { Easy = state.Item2, Type = IncomingRequestType.Cancel });
state.Item1.RequestCancel(state.Item2);
}, Tuple.Create<MultiAgent, EasyRequest>(this, easy));
}

Expand Down

0 comments on commit eede273

Please sign in to comment.