Call OnStarting and OnCompleted callbacks in LIFO order (#1042).
This commit is contained in:
parent
5181e4196c
commit
08a91f17bf
|
|
@ -47,9 +47,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
protected bool _requestRejected;
|
protected bool _requestRejected;
|
||||||
private Streams _frameStreams;
|
private Streams _frameStreams;
|
||||||
|
|
||||||
protected List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
protected Stack<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||||
|
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
||||||
protected List<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
|
||||||
|
|
||||||
private Task _requestProcessingTask;
|
private Task _requestProcessingTask;
|
||||||
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
||||||
|
|
@ -395,9 +394,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
if (_onStarting == null)
|
if (_onStarting == null)
|
||||||
{
|
{
|
||||||
_onStarting = new List<KeyValuePair<Func<object, Task>, object>>();
|
_onStarting = new Stack<KeyValuePair<Func<object, Task>, object>>();
|
||||||
}
|
}
|
||||||
_onStarting.Add(new KeyValuePair<Func<object, Task>, object>(callback, state));
|
_onStarting.Push(new KeyValuePair<Func<object, Task>, object>(callback, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,15 +406,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
if (_onCompleted == null)
|
if (_onCompleted == null)
|
||||||
{
|
{
|
||||||
_onCompleted = new List<KeyValuePair<Func<object, Task>, object>>();
|
_onCompleted = new Stack<KeyValuePair<Func<object, Task>, object>>();
|
||||||
}
|
}
|
||||||
_onCompleted.Add(new KeyValuePair<Func<object, Task>, object>(callback, state));
|
_onCompleted.Push(new KeyValuePair<Func<object, Task>, object>(callback, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task FireOnStarting()
|
protected async Task FireOnStarting()
|
||||||
{
|
{
|
||||||
List<KeyValuePair<Func<object, Task>, object>> onStarting = null;
|
Stack<KeyValuePair<Func<object, Task>, object>> onStarting = null;
|
||||||
lock (_onStartingSync)
|
lock (_onStartingSync)
|
||||||
{
|
{
|
||||||
onStarting = _onStarting;
|
onStarting = _onStarting;
|
||||||
|
|
@ -439,7 +438,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
protected async Task FireOnCompleted()
|
protected async Task FireOnCompleted()
|
||||||
{
|
{
|
||||||
List<KeyValuePair<Func<object, Task>, object>> onCompleted = null;
|
Stack<KeyValuePair<Func<object, Task>, object>> onCompleted = null;
|
||||||
lock (_onCompletedSync)
|
lock (_onCompletedSync)
|
||||||
{
|
{
|
||||||
onCompleted = _onCompleted;
|
onCompleted = _onCompleted;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
|
@ -833,9 +834,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
"",
|
"",
|
||||||
"");
|
"");
|
||||||
|
|
||||||
Assert.Equal(2, onStartingCallCount1);
|
Assert.Equal(2, onStartingCallCount2);
|
||||||
// The second OnStarting callback should not be called since the first failed.
|
|
||||||
Assert.Equal(0, onStartingCallCount2);
|
// The first registered OnStarting callback should not be called,
|
||||||
|
// since they are called LIFO and the other one failed.
|
||||||
|
Assert.Equal(0, onStartingCallCount1);
|
||||||
|
|
||||||
Assert.Equal(2, testLogger.ApplicationErrorsLogged);
|
Assert.Equal(2, testLogger.ApplicationErrorsLogged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1199,5 +1203,93 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
|
public async Task OnStartingCallbacksAreCalledInLastInFirstOutOrder(TestServiceContext testContext)
|
||||||
|
{
|
||||||
|
const string response = "hello, world";
|
||||||
|
|
||||||
|
var callOrder = new Stack<int>();
|
||||||
|
|
||||||
|
using (var server = new TestServer(async context =>
|
||||||
|
{
|
||||||
|
context.Response.OnStarting(_ =>
|
||||||
|
{
|
||||||
|
callOrder.Push(1);
|
||||||
|
return TaskUtilities.CompletedTask;
|
||||||
|
}, null);
|
||||||
|
context.Response.OnStarting(_ =>
|
||||||
|
{
|
||||||
|
callOrder.Push(2);
|
||||||
|
return TaskUtilities.CompletedTask;
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
context.Response.ContentLength = response.Length;
|
||||||
|
await context.Response.WriteAsync(response);
|
||||||
|
}, testContext))
|
||||||
|
{
|
||||||
|
using (var connection = server.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.SendEnd(
|
||||||
|
"GET / HTTP/1.1",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.ReceiveEnd(
|
||||||
|
"HTTP/1.1 200 OK",
|
||||||
|
$"Date: {testContext.DateHeaderValue}",
|
||||||
|
$"Content-Length: {response.Length}",
|
||||||
|
"",
|
||||||
|
"hello, world");
|
||||||
|
|
||||||
|
Assert.Equal(1, callOrder.Pop());
|
||||||
|
Assert.Equal(2, callOrder.Pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
|
public async Task OnCompletedCallbacksAreCalledInLastInFirstOutOrder(TestServiceContext testContext)
|
||||||
|
{
|
||||||
|
const string response = "hello, world";
|
||||||
|
|
||||||
|
var callOrder = new Stack<int>();
|
||||||
|
|
||||||
|
using (var server = new TestServer(async context =>
|
||||||
|
{
|
||||||
|
context.Response.OnCompleted(_ =>
|
||||||
|
{
|
||||||
|
callOrder.Push(1);
|
||||||
|
return TaskUtilities.CompletedTask;
|
||||||
|
}, null);
|
||||||
|
context.Response.OnCompleted(_ =>
|
||||||
|
{
|
||||||
|
callOrder.Push(2);
|
||||||
|
return TaskUtilities.CompletedTask;
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
context.Response.ContentLength = response.Length;
|
||||||
|
await context.Response.WriteAsync(response);
|
||||||
|
}, testContext))
|
||||||
|
{
|
||||||
|
using (var connection = server.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.SendEnd(
|
||||||
|
"GET / HTTP/1.1",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.ReceiveEnd(
|
||||||
|
"HTTP/1.1 200 OK",
|
||||||
|
$"Date: {testContext.DateHeaderValue}",
|
||||||
|
$"Content-Length: {response.Length}",
|
||||||
|
"",
|
||||||
|
"hello, world");
|
||||||
|
|
||||||
|
Assert.Equal(1, callOrder.Pop());
|
||||||
|
Assert.Equal(2, callOrder.Pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue