Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
17ddbce
init
Jul 21, 2020
2afc76f
Http2 ping
Aug 3, 2020
c590b7c
Merge remote-tracking branch 'upstream/master' into jajahoda/httpping
Aug 3, 2020
eb37332
clean up
Aug 3, 2020
712479f
enable tests
Aug 3, 2020
95bc2f3
Fix mac build
Aug 3, 2020
de89629
Merge branch 'master' of github.com:dotnet/runtime into jajahoda/http…
Aug 3, 2020
b3e4b3d
Split HttpHandlerDefaults
Aug 4, 2020
e06f092
Merge branch 'master' of github.com:dotnet/runtime into jajahoda/http…
Aug 4, 2020
6e2f371
Merge remote-tracking branch 'origin/jajahoda/httpping' into jajahoda…
Aug 4, 2020
b7ed6ce
Fix mac build
Aug 4, 2020
20f44cf
Fix browser and test csproj
Aug 4, 2020
b684730
Apply PR comments
Aug 5, 2020
014c8d6
Merge remote-tracking branch 'upstream/master' into jajahoda/httpping
Aug 5, 2020
60a757d
Remove Http2KeepAlice class
Aug 5, 2020
886a2da
fix trace message
Aug 5, 2020
863f636
ProcessPingAck no longer return bool
Aug 5, 2020
a987919
Apply suggestions from code review
aik-jahoda Aug 6, 2020
b221f43
Add argument out of ramnge message
Aug 6, 2020
be468cd
Merge branch 'jajahoda/httpping' of github.com:aik-jahoda/runtime int…
Aug 6, 2020
db89b58
Apply PR comments
Aug 6, 2020
02785d6
SocketsHttpHandler setters
Aug 6, 2020
d354a29
Merge remote-tracking branch 'upstream/master' into jajahoda/httpping
Aug 7, 2020
b83cfc3
Update src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnec…
aik-jahoda Aug 7, 2020
63e3d3f
Update src/libraries/System.Net.Http/src/Resources/Strings.resx
aik-jahoda Aug 7, 2020
258f1c4
Apply suggestions from code review
aik-jahoda Aug 7, 2020
2695f2e
PR comments
Aug 7, 2020
0ea45e8
Merge branch 'master' into jajahoda/httpping
ManickaP Aug 11, 2020
7078d8e
Addressed the last batch of PR feedback.
ManickaP Aug 11, 2020
9c1b6e7
Merge branch 'master' into jajahoda/httpping
ManickaP Aug 12, 2020
86c8413
Fixed KeepAlive test dependency on frame chronology.
ManickaP Aug 12, 2020
c10beb9
Fixed FW compilation.
ManickaP Aug 12, 2020
b9c6b33
Split and moved tests to theirs proper places.
ManickaP Aug 12, 2020
40e0e5c
More test fixing for super slow CI.
ManickaP Aug 12, 2020
3a753b7
Exception texts.
ManickaP Aug 13, 2020
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
Prev Previous commit
Next Next commit
Http2 ping
  • Loading branch information
Jan Jahoda committed Aug 3, 2020
commit 2afc76f46af5a431f83654c0930786cb80c5cc1f
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal static class HttpHandlerDefaults
public static readonly TimeSpan DefaultExpect100ContinueTimeout = TimeSpan.FromSeconds(1);
public static readonly TimeSpan DefaultKeepAlivePingTimeout = TimeSpan.FromSeconds(20);
public static readonly TimeSpan DefaultKeepAlivePingDelay = TimeSpan.FromSeconds(0);
public const bool DefaultKeepAlivePingWithoutRequests = true;
public const HttpKeepAlivePingPolicy DefaultKeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always;
public static readonly TimeSpan DefaultConnectTimeout = Timeout.InfiniteTimeSpan;

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,25 @@ public async Task PingPong()
Assert.Equal(pingData, pingAck.Data);
}

public async Task<PingFrame> ReadPingAsync(TimeSpan timeout)
{
Frame frame = await ReadFrameAsync(timeout).ConfigureAwait(false);
Assert.NotNull(frame);
Assert.Equal(FrameType.Ping, frame.Type);
Assert.Equal(0, frame.StreamId);
Assert.False(frame.AckFlag);

PingFrame ping = frame as PingFrame;
Assert.NotNull(ping);
return ping;
}

public async Task SendPingAckAsync(byte[] payload)
{
PingFrame pingAck = new PingFrame(payload, FrameFlags.Ack, 0);
await WriteFrameAsync(pingAck).ConfigureAwait(false);
}

public async Task SendDefaultResponseHeadersAsync(int streamId)
{
byte[] headers = new byte[] { 0x88 }; // Encoding for ":status: 200"
Expand Down
7 changes: 6 additions & 1 deletion src/libraries/System.Net.Http/ref/System.Net.Http.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ public SocketsHttpHandler() { }
public System.TimeSpan Expect100ContinueTimeout { get { throw null; } set { } }
public System.TimeSpan KeepAlivePingDelay { get { throw null; } set { } }
public System.TimeSpan KeepAlivePingTimeout { get { throw null; } set { } }
public bool KeepAlivePingWithoutRequests { get { throw null; } set { } }
public HttpKeepAlivePingPolicy KeepAlivePingPolicy { get { throw null; } set { } }
public int MaxAutomaticRedirections { get { throw null; } set { } }
public int MaxConnectionsPerServer { get { throw null; } set { } }
public int MaxResponseDrainSize { get { throw null; } set { } }
Expand All @@ -314,6 +314,11 @@ protected override void Dispose(bool disposing) { }
protected internal override System.Net.Http.HttpResponseMessage Send(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { throw null; }
protected internal override System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage> SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { throw null; }
}
public enum HttpKeepAlivePingPolicy
{
WithActiveRequests,
Always
}
public partial class StreamContent : System.Net.Http.HttpContent
{
public StreamContent(System.IO.Stream content) { }
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Net.Http/src/System.Net.Http.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
<Compile Include="System\Net\Http\SocketsHttpHandler\FailedProxyCache.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2Connection.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2ConnectionException.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2KeepAlive.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2ProtocolErrorCode.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2ProtocolException.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2Stream.cs" />
Expand All @@ -164,6 +165,7 @@
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpContentReadStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpContentStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpContentWriteStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpKeepAlivePingPolicy.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\IHttpTrace.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\IMultiWebProxy.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\MultiProxy.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,90 +95,7 @@ internal sealed partial class Http2Connection : HttpConnectionBase, IDisposable
// Channel options for creating _writeChannel
private static readonly UnboundedChannelOptions s_channelOptions = new UnboundedChannelOptions() { SingleReader = true };


private readonly Http2KeepAlive _keepAlive;

internal enum KeepAliveState
{
None,
PingSent
}

internal enum KeepAliveVerifyAction
{
None,
SendPing,
Throw
}

internal class Http2KeepAlive
{

private long _pingPayload;

private readonly TimeSpan _keepAliveInterval;
private readonly TimeSpan _keepAliveTimeout;
private DateTimeOffset _nextPingRequestTimestamp;
private DateTimeOffset _pingTimeoutTimestamp;

private KeepAliveState _state;

public Http2KeepAlive(TimeSpan keepAliveInterval, TimeSpan keepAliveTimeout)
{
_keepAliveInterval = keepAliveInterval;
_keepAliveTimeout = keepAliveTimeout;
_nextPingRequestTimestamp = DateTimeOffset.Now.Add(keepAliveInterval);
}

public long PingPayload => _pingPayload;

public void ProcessFrame()
{
_nextPingRequestTimestamp = DateTimeOffset.Now.Add(_keepAliveInterval);
}

public bool ProcessPingAck(long payload)
{
if (_state != KeepAliveState.PingSent)
return false;
if (Interlocked.Read(ref _pingPayload) != payload)
return false;
_state = KeepAliveState.None;
return true;
}

public KeepAliveVerifyAction VerifyKeepAlive()
{
var now = DateTimeOffset.Now;
switch (_state)
{
case KeepAliveState.None:
// Check whether keep alive interval has passed since last frame received
if (_keepAliveInterval > TimeSpan.Zero && now > _nextPingRequestTimestamp)
{
// Ping will be sent immeditely after this method finishes.
// Set the status directly to ping sent and set the timestamp
_state = KeepAliveState.PingSent;
// System clock only has 1 second of precision, so the clock could be up to 1 second in the past.
// To err on the side of caution, add a second to the clock when calculating the ping sent time.
_pingTimeoutTimestamp = now.Add(_keepAliveTimeout);
Interlocked.Increment(ref _pingPayload);
return KeepAliveVerifyAction.SendPing;
}
break;
case KeepAliveState.PingSent:
if (_keepAliveTimeout != TimeSpan.MaxValue)
{
if (now > _pingTimeoutTimestamp)
return KeepAliveVerifyAction.Throw;
}

break;
}

return KeepAliveVerifyAction.None;
}
}
private Http2KeepAlive _keepAlive;

public Http2Connection(HttpConnectionPool pool, Stream stream)
{
Expand All @@ -201,7 +118,7 @@ public Http2Connection(HttpConnectionPool pool, Stream stream)
_maxConcurrentStreams = int.MaxValue;
_pendingWindowUpdate = 0;

_keepAlive = new Http2KeepAlive(_pool.Settings._keepAlivePingDelay, _pool.Settings._keepAlivePingTimeout);
_keepAlive = new Http2KeepAlive(this, _pool.Settings);

if (NetEventSource.Log.IsEnabled()) TraceConnection(stream);
}
Expand Down Expand Up @@ -1048,15 +965,7 @@ internal void HeartBeat()
{
try
{
switch (_keepAlive.VerifyKeepAlive())
{
case KeepAliveVerifyAction.SendPing:
SendPingAsync(_keepAlive.PingPayload);
break;
case KeepAliveVerifyAction.Throw:
ThrowProtocolError();
break;
}
_keepAlive.VerifyKeepAlive();
}
catch (Exception e)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Http.Headers;
using System.Net.Http.HPack;
using System.Net.Security;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;

namespace System.Net.Http
{
internal sealed partial class Http2Connection
{
internal enum KeepAliveState
{
None,
PingSent
}

internal class Http2KeepAlive
{

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: extra empty line

private long _pingPayload;

private readonly TimeSpan _keepAlivePingDelay;
private readonly TimeSpan _keepAlivePingTimeout;
private DateTimeOffset _nextPingRequestTimestamp;
private DateTimeOffset _pingTimeoutTimestamp;
private HttpKeepAlivePingPolicy _keepAlivePingPolicy;
private KeepAliveState _state;
private Http2Connection _connection;

public Http2KeepAlive(Http2Connection connection, HttpConnectionSettings settings)
{
_keepAlivePingDelay = settings._keepAlivePingDelay;
_keepAlivePingTimeout = settings._keepAlivePingTimeout;
_nextPingRequestTimestamp = DateTimeOffset.Now.Add(settings._keepAlivePingDelay);
_keepAlivePingPolicy = settings._keepAlivePingPolicy;
_connection = connection;
}

public void ProcessFrame()
{
_nextPingRequestTimestamp = DateTimeOffset.Now.Add(_keepAlivePingDelay);
}

public bool ProcessPingAck(long payload)
{
if (_state != KeepAliveState.PingSent)
return false;
if (Interlocked.Read(ref _pingPayload) != payload)
return false;
_state = KeepAliveState.None;
ProcessFrame();
return true;
}

public void VerifyKeepAlive()
{
if (_keepAlivePingPolicy == HttpKeepAlivePingPolicy.WithActiveRequests && _connection._httpStreams.Count == 0)
return;

var now = DateTimeOffset.Now;
switch (_state)
{
case KeepAliveState.None:
// Check whether keep alive delay has passed since last frame received
if (_keepAlivePingDelay > TimeSpan.Zero && now > _nextPingRequestTimestamp)
{
// Set the status directly to ping sent and set the timestamp
_state = KeepAliveState.PingSent;
_pingTimeoutTimestamp = now.Add(_keepAlivePingTimeout);
Interlocked.Increment(ref _pingPayload);
_connection.SendPingAsync(_pingPayload);
return;
}
break;
case KeepAliveState.PingSent:
if (_keepAlivePingTimeout != TimeSpan.MaxValue)
{
if (now > _pingTimeoutTimestamp)
Http2Connection.ThrowProtocolError();
}

break;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ internal sealed class HttpConnectionSettings
internal TimeSpan _expect100ContinueTimeout = HttpHandlerDefaults.DefaultExpect100ContinueTimeout;
internal TimeSpan _keepAlivePingTimeout = HttpHandlerDefaults.DefaultKeepAlivePingTimeout;
internal TimeSpan _keepAlivePingDelay = HttpHandlerDefaults.DefaultKeepAlivePingDelay;
internal bool _keepAlivePingWithoutRequests = HttpHandlerDefaults.DefaultKeepAlivePingWithoutRequests;
internal HttpKeepAlivePingPolicy _keepAlivePingPolicy = HttpHandlerDefaults.DefaultKeepAlivePingPolicy;
internal TimeSpan _connectTimeout = HttpHandlerDefaults.DefaultConnectTimeout;

internal Version _maxHttpVersion;
Expand Down Expand Up @@ -106,7 +106,8 @@ public HttpConnectionSettings CloneAndNormalize()
_allowUnencryptedHttp2 = _allowUnencryptedHttp2,
_assumePrenegotiatedHttp3ForTesting = _assumePrenegotiatedHttp3ForTesting,
_keepAlivePingTimeout = _keepAlivePingTimeout,
_keepAlivePingDelay = _keepAlivePingDelay
_keepAlivePingDelay = _keepAlivePingDelay,
_keepAlivePingPolicy = _keepAlivePingPolicy
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Net.Http
{
public enum HttpKeepAlivePingPolicy
{
WithActiveRequests,
Always
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,15 @@ public TimeSpan KeepAlivePingTimeout
}


public bool KeepAlivePingWithoutRequests => _settings._keepAlivePingWithoutRequests;
public HttpKeepAlivePingPolicy KeepAlivePingPolicy
{
get => _settings._keepAlivePingPolicy;
set
{
CheckDisposedOrStarted();
_settings._keepAlivePingPolicy = value;
}
}

public IDictionary<string, object?> Properties =>
_settings._properties ?? (_settings._properties = new Dictionary<string, object?>());
Expand Down
Loading