Skip to content

Commit 0269733

Browse files
author
John Luo
authored
Merge pull request #18205 from dotnet/johluo/2.1-feb20
Update branding to 2.1.16
2 parents 049cdec + 183a09f commit 0269733

27 files changed

+1026
-118
lines changed

eng/Baseline.Designer.props

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Project>
33
<PropertyGroup>
44
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
5-
<AspNetCoreBaselineVersion>2.1.14</AspNetCoreBaselineVersion>
5+
<AspNetCoreBaselineVersion>2.1.15</AspNetCoreBaselineVersion>
66
</PropertyGroup>
77
<!-- Package: dotnet-dev-certs-->
88
<PropertyGroup Condition=" '$(PackageId)' == 'dotnet-dev-certs' ">
@@ -453,25 +453,27 @@
453453
</ItemGroup>
454454
<!-- Package: Microsoft.AspNetCore.Http.Connections-->
455455
<PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections' ">
456-
<BaselinePackageVersion>1.0.4</BaselinePackageVersion>
456+
<BaselinePackageVersion>1.0.15</BaselinePackageVersion>
457457
</PropertyGroup>
458458
<ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections' AND '$(TargetFramework)' == 'netcoreapp2.1' ">
459-
<BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Common" Version="[1.0.4, )" />
460459
<BaselinePackageReference Include="Microsoft.AspNetCore.Authorization.Policy" Version="[2.1.1, )" />
461460
<BaselinePackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="[2.1.1, )" />
462461
<BaselinePackageReference Include="Microsoft.AspNetCore.Http" Version="[2.1.1, )" />
462+
<BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Common" Version="[1.0.4, )" />
463463
<BaselinePackageReference Include="Microsoft.AspNetCore.Routing" Version="[2.1.1, )" />
464464
<BaselinePackageReference Include="Microsoft.AspNetCore.WebSockets" Version="[2.1.1, )" />
465465
<BaselinePackageReference Include="Newtonsoft.Json" Version="[11.0.2, )" />
466+
<BaselinePackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="[4.5.3, )" />
466467
</ItemGroup>
467468
<ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Connections' AND '$(TargetFramework)' == 'netstandard2.0' ">
468-
<BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Common" Version="[1.0.4, )" />
469469
<BaselinePackageReference Include="Microsoft.AspNetCore.Authorization.Policy" Version="[2.1.1, )" />
470470
<BaselinePackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="[2.1.1, )" />
471471
<BaselinePackageReference Include="Microsoft.AspNetCore.Http" Version="[2.1.1, )" />
472+
<BaselinePackageReference Include="Microsoft.AspNetCore.Http.Connections.Common" Version="[1.0.4, )" />
472473
<BaselinePackageReference Include="Microsoft.AspNetCore.Routing" Version="[2.1.1, )" />
473474
<BaselinePackageReference Include="Microsoft.AspNetCore.WebSockets" Version="[2.1.1, )" />
474475
<BaselinePackageReference Include="Newtonsoft.Json" Version="[11.0.2, )" />
476+
<BaselinePackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="[4.5.3, )" />
475477
</ItemGroup>
476478
<!-- Package: Microsoft.AspNetCore.Http.Extensions-->
477479
<PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Http.Extensions' ">
@@ -1061,12 +1063,12 @@
10611063
</ItemGroup>
10621064
<!-- Package: Microsoft.AspNetCore.SignalR.Core-->
10631065
<PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Core' ">
1064-
<BaselinePackageVersion>1.0.4</BaselinePackageVersion>
1066+
<BaselinePackageVersion>1.0.15</BaselinePackageVersion>
10651067
</PropertyGroup>
10661068
<ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.SignalR.Core' AND '$(TargetFramework)' == 'netstandard2.0' ">
1069+
<BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[2.1.1, )" />
10671070
<BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="[1.0.4, )" />
10681071
<BaselinePackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="[1.0.4, )" />
1069-
<BaselinePackageReference Include="Microsoft.AspNetCore.Authorization" Version="[2.1.1, )" />
10701072
<BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[2.1.1, )" />
10711073
<BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[2.1.1, )" />
10721074
<BaselinePackageReference Include="System.Reflection.Emit" Version="[4.3.0, )" />

eng/Baseline.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file contains a list of all the packages and their versions which were rele
44
build of ASP.NET Core 2.1.x. Update this list when preparing for a new patch.
55
66
-->
7-
<Baseline Version="2.1.14">
7+
<Baseline Version="2.1.15">
88
<Package Id="dotnet-dev-certs" Version="2.1.1" />
99
<Package Id="dotnet-sql-cache" Version="2.1.1" />
1010
<Package Id="dotnet-user-secrets" Version="2.1.1" />
@@ -53,7 +53,7 @@ build of ASP.NET Core 2.1.x. Update this list when preparing for a new patch.
5353
<Package Id="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.1" />
5454
<Package Id="Microsoft.AspNetCore.Http.Connections.Client" Version="1.0.4" />
5555
<Package Id="Microsoft.AspNetCore.Http.Connections.Common" Version="1.0.4" />
56-
<Package Id="Microsoft.AspNetCore.Http.Connections" Version="1.0.4" />
56+
<Package Id="Microsoft.AspNetCore.Http.Connections" Version="1.0.15" />
5757
<Package Id="Microsoft.AspNetCore.Http.Extensions" Version="2.1.1" />
5858
<Package Id="Microsoft.AspNetCore.Http.Features" Version="2.1.1" />
5959
<Package Id="Microsoft.AspNetCore.Http" Version="2.1.1" />
@@ -109,7 +109,7 @@ build of ASP.NET Core 2.1.x. Update this list when preparing for a new patch.
109109
<Package Id="Microsoft.AspNetCore.SignalR.Client.Core" Version="1.0.4" />
110110
<Package Id="Microsoft.AspNetCore.SignalR.Client" Version="1.0.4" />
111111
<Package Id="Microsoft.AspNetCore.SignalR.Common" Version="1.0.4" />
112-
<Package Id="Microsoft.AspNetCore.SignalR.Core" Version="1.0.4" />
112+
<Package Id="Microsoft.AspNetCore.SignalR.Core" Version="1.0.15" />
113113
<Package Id="Microsoft.AspNetCore.SignalR.Protocols.Json" Version="1.0.4" />
114114
<Package Id="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="1.0.11" />
115115
<Package Id="Microsoft.AspNetCore.SignalR.Redis" Version="1.0.11" />

eng/PatchConfig.props

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ Later on, this will be checked using this condition:
4444
Microsoft.AspNetCore.CookiePolicy;
4545
</PackagesInPatch>
4646
</PropertyGroup>
47-
<PropertyGroup Condition=" '$(VersionPrefix)' == '2.1.15' ">
47+
<PropertyGroup Condition=" '$(VersionPrefix)' == '2.1.16' ">
4848
<PackagesInPatch>
49+
Microsoft.AspNetCore.Http.Connections;
50+
Microsoft.AspNetCore.SignalR.Core;
4951
</PackagesInPatch>
5052
</PropertyGroup>
5153
</Project>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/SignalR/clients/ts/FunctionalTests/selenium/run-tests.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
import { ChildProcess, spawn } from "child_process";
2-
import * as fs from "fs";
2+
import * as _fs from "fs";
33
import { EOL } from "os";
44
import * as path from "path";
5+
import { promisify } from "util";
56
import { PassThrough, Readable } from "stream";
67

78
import { run } from "../../webdriver-tap-runner/lib";
89

910
import * as _debug from "debug";
1011
const debug = _debug("signalr-functional-tests:run");
1112

13+
const ARTIFACTS_DIR = path.resolve(__dirname, "..", "..", "..", "..", "artifacts");
14+
const LOGS_DIR = path.resolve(ARTIFACTS_DIR, "logs");
15+
16+
// Promisify things from fs we want to use.
17+
const fs = {
18+
createWriteStream: _fs.createWriteStream,
19+
exists: promisify(_fs.exists),
20+
mkdir: promisify(_fs.mkdir),
21+
};
22+
1223
process.on("unhandledRejection", (reason) => {
1324
console.error(`Unhandled promise rejection: ${reason}`);
1425
process.exit(1);
@@ -102,6 +113,13 @@ if (chromePath) {
102113
try {
103114
const serverPath = path.resolve(__dirname, "..", "bin", configuration, "netcoreapp2.1", "FunctionalTests.dll");
104115

116+
if (!await fs.exists(ARTIFACTS_DIR)) {
117+
await fs.mkdir(ARTIFACTS_DIR);
118+
}
119+
if (!await fs.exists(LOGS_DIR)) {
120+
await fs.mkdir(LOGS_DIR);
121+
}
122+
105123
debug(`Launching Functional Test Server: ${serverPath}`);
106124
const dotnet = spawn("dotnet", [serverPath], {
107125
env: {
@@ -117,6 +135,9 @@ if (chromePath) {
117135
}
118136
}
119137

138+
const logStream = fs.createWriteStream(path.resolve(LOGS_DIR, "ts.functionaltests.dotnet.log"));
139+
dotnet.stdout.pipe(logStream);
140+
120141
process.on("SIGINT", cleanup);
121142
process.on("exit", cleanup);
122143

src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public class HttpConnectionContext : ConnectionContext,
2727
IHttpTransportFeature,
2828
IConnectionInherentKeepAliveFeature
2929
{
30+
private static long _tenSeconds = TimeSpan.FromSeconds(10).Ticks;
31+
3032
private readonly object _itemsLock = new object();
3133
private readonly object _heartbeatLock = new object();
3234
private List<(Action<object> handler, object state)> _heartbeatHandlers;
@@ -35,6 +37,13 @@ public class HttpConnectionContext : ConnectionContext,
3537
private IDuplexPipe _application;
3638
private IDictionary<object, object> _items;
3739

40+
private CancellationTokenSource _sendCts;
41+
private bool _activeSend;
42+
private long _startedSendTime;
43+
private readonly object _sendingLock = new object();
44+
45+
internal CancellationToken SendingToken { get; private set; }
46+
3847
// This tcs exists so that multiple calls to DisposeAsync all wait asynchronously
3948
// on the same task
4049
private readonly TaskCompletionSource<object> _disposeTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
@@ -274,24 +283,45 @@ private async Task WaitOnTasks(Task applicationTask, Task transportTask, bool cl
274283
// Cancel any pending flushes from back pressure
275284
Application?.Output.CancelPendingFlush();
276285

277-
// Shutdown both sides and wait for nothing
278-
Transport?.Output.Complete(applicationTask.Exception?.InnerException);
279-
Application?.Output.Complete(transportTask.Exception?.InnerException);
280-
286+
// Normally it isn't safe to try and acquire this lock because the Send can hold onto it for a long time if there is backpressure
287+
// It is safe to wait for this lock now because the Send will be in one of 4 states
288+
// 1. In the middle of a write which is in the middle of being canceled by the CancelPendingFlush above, when it throws
289+
// an OperationCanceledException it will complete the PipeWriter which will make any other Send waiting on the lock
290+
// throw an InvalidOperationException if they call Write
291+
// 2. About to write and see that there is a pending cancel from the CancelPendingFlush, go to 1 to see what happens
292+
// 3. Enters the Send and sees the Dispose state from DisposeAndRemoveAsync and releases the lock
293+
// 4. No Send in progress
294+
await WriteLock.WaitAsync();
281295
try
282296
{
283-
Log.WaitingForTransportAndApplication(_logger, TransportType);
284-
// A poorly written application *could* in theory get stuck forever and it'll show up as a memory leak
285-
await Task.WhenAll(applicationTask, transportTask);
297+
// Complete the applications read loop
298+
Application?.Output.Complete(transportTask.Exception?.InnerException);
286299
}
287300
finally
288301
{
289-
Log.TransportAndApplicationComplete(_logger, TransportType);
290-
291-
// Close the reading side after both sides run
292-
Application?.Input.Complete();
293-
Transport?.Input.Complete();
302+
WriteLock.Release();
294303
}
304+
305+
Application?.Input.CancelPendingRead();
306+
307+
await transportTask.NoThrow();
308+
Application?.Input.Complete();
309+
310+
Log.WaitingForTransportAndApplication(_logger, TransportType);
311+
312+
// A poorly written application *could* in theory get stuck forever and it'll show up as a memory leak
313+
// Wait for application so we can complete the writer safely
314+
await applicationTask.NoThrow();
315+
Log.TransportAndApplicationComplete(_logger, TransportType);
316+
317+
// Shutdown application side now that it's finished
318+
Transport?.Output.Complete(applicationTask.Exception?.InnerException);
319+
320+
// Close the reading side after both sides run
321+
Transport?.Input.Complete();
322+
323+
// Observe exceptions
324+
await Task.WhenAll(transportTask, applicationTask);
295325
}
296326

297327
// Notify all waiters that we're done disposing
@@ -311,6 +341,43 @@ private async Task WaitOnTasks(Task applicationTask, Task transportTask, bool cl
311341
}
312342
}
313343

344+
internal void StartSendCancellation()
345+
{
346+
lock (_sendingLock)
347+
{
348+
if (_sendCts == null || _sendCts.IsCancellationRequested)
349+
{
350+
_sendCts = new CancellationTokenSource();
351+
SendingToken = _sendCts.Token;
352+
}
353+
354+
_startedSendTime = DateTime.UtcNow.Ticks;
355+
_activeSend = true;
356+
}
357+
}
358+
359+
internal void TryCancelSend(long currentTicks)
360+
{
361+
lock (_sendingLock)
362+
{
363+
if (_activeSend)
364+
{
365+
if (currentTicks - _startedSendTime > _tenSeconds)
366+
{
367+
_sendCts.Cancel();
368+
}
369+
}
370+
}
371+
}
372+
373+
internal void StopSendCancellation()
374+
{
375+
lock (_sendingLock)
376+
{
377+
_activeSend = false;
378+
}
379+
}
380+
314381
private static class Log
315382
{
316383
private static readonly Action<ILogger, string, Exception> _disposingConnection =

src/SignalR/common/Http.Connections/src/Internal/HttpConnectionDispatcher.cs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connecti
144144
connection.SupportedFormats = TransferFormat.Text;
145145

146146
// We only need to provide the Input channel since writing to the application is handled through /send.
147-
var sse = new ServerSentEventsTransport(connection.Application.Input, connection.ConnectionId, _loggerFactory);
147+
var sse = new ServerSentEventsTransport(connection.Application.Input, connection.ConnectionId, connection, _loggerFactory);
148148

149149
await DoPersistentConnection(connectionDelegate, sse, context, connection);
150150
}
@@ -264,7 +264,7 @@ private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connecti
264264
context.Response.RegisterForDispose(timeoutSource);
265265
context.Response.RegisterForDispose(tokenSource);
266266

267-
var longPolling = new LongPollingTransport(timeoutSource.Token, connection.Application.Input, _loggerFactory);
267+
var longPolling = new LongPollingTransport(timeoutSource.Token, connection.Application.Input, _loggerFactory, connection);
268268

269269
// Start the transport
270270
connection.TransportTask = longPolling.ProcessRequestAsync(context, tokenSource.Token);
@@ -291,7 +291,9 @@ private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connecti
291291
connection.Transport.Output.Complete(connection.ApplicationTask.Exception);
292292

293293
// Wait for the transport to run
294-
await connection.TransportTask;
294+
// Ignore exceptions, it has been logged if there is one and the application has finished
295+
// So there is no one to give the exception to
296+
await connection.TransportTask.NoThrow();
295297

296298
// If the status code is a 204 it means the connection is done
297299
if (context.Response.StatusCode == StatusCodes.Status204NoContent)
@@ -307,6 +309,18 @@ private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connecti
307309
pollAgain = false;
308310
}
309311
}
312+
else if (connection.TransportTask.IsFaulted || connection.TransportTask.IsCanceled)
313+
{
314+
// Cancel current request to release any waiting poll and let dispose aquire the lock
315+
currentRequestTcs.TrySetCanceled();
316+
317+
// We should be able to safely dispose because there's no more data being written
318+
// We don't need to wait for close here since we've already waited for both sides
319+
await _manager.DisposeAndRemoveAsync(connection, closeGracefully: false);
320+
321+
// Don't poll again if we've removed the connection completely
322+
pollAgain = false;
323+
}
310324
else if (context.Response.StatusCode == StatusCodes.Status204NoContent)
311325
{
312326
// Don't poll if the transport task was canceled
@@ -511,6 +525,14 @@ private async Task ProcessSend(HttpContext context, HttpConnectionDispatcherOpti
511525

512526
context.Response.StatusCode = StatusCodes.Status404NotFound;
513527
context.Response.ContentType = "text/plain";
528+
529+
// There are no writes anymore (since this is the write "loop")
530+
// So it is safe to complete the writer
531+
// We complete the writer here because we already have the WriteLock acquired
532+
// and it's unsafe to complete outside of the lock
533+
// Other code isn't guaranteed to be able to acquire the lock before another write
534+
// even if CancelPendingFlush is called, and the other write could hang if there is backpressure
535+
connection.Application.Output.Complete();
514536
return;
515537
}
516538

@@ -549,11 +571,8 @@ private async Task ProcessDeleteAsync(HttpContext context)
549571

550572
Log.TerminatingConection(_logger);
551573

552-
// Complete the receiving end of the pipe
553-
connection.Application.Output.Complete();
554-
555-
// Dispose the connection gracefully, but don't wait for it. We assign it here so we can wait in tests
556-
connection.DisposeAndRemoveTask = _manager.DisposeAndRemoveAsync(connection, closeGracefully: true);
574+
// Dispose the connection, but don't wait for it. We assign it here so we can wait in tests
575+
connection.DisposeAndRemoveTask = _manager.DisposeAndRemoveAsync(connection, closeGracefully: false);
557576

558577
context.Response.StatusCode = StatusCodes.Status202Accepted;
559578
context.Response.ContentType = "text/plain";

0 commit comments

Comments
 (0)