-
Notifications
You must be signed in to change notification settings - Fork 447
Conversation
@@ -71,6 +78,7 @@ public DefaultConnectionContext CreateConnection(PipeOptions transportPipeOption | |||
var connectionTimer = SocketEventSource.Log.ConnectionStart(id); | |||
var pair = DuplexPipe.CreateConnectionPair(transportPipeOptions, appPipeOptions); | |||
|
|||
// TODO: We can possibly delay creating connections until we get the first request and check the connectionId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a todo? If we're not doing this I'm not sure what the point is?
The issue talks about using an HMAC to validate the ID, but the PR is using HMAC as the ID, and no validation. Issue change in offline discussion? |
return Guid.NewGuid().ToString(); | ||
var id = Interlocked.Increment(ref _id); | ||
// HMAC the id because we want a cryptographically random id, which GUID is not | ||
var hashed = _hmacHash.ComputeHash(Encoding.UTF8.GetBytes(id.ToString())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this whole thing makes no sense and we should just use rngcryptoserviceprovider
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, if the only outcome is to generate a more-different random number, why not just do that? If the goal is to validate something, we need a different approach.
Yeah, lets talk offline today |
|
||
// This tcs exists so that multiple calls to DisposeAsync all wait asynchronously | ||
// on the same task | ||
private TaskCompletionSource<object> _disposeTcs = new TaskCompletionSource<object>(); | ||
|
||
public DefaultConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application) | ||
public DefaultConnectionContext(string id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of these changes. I think you should just set the pipes on the outside. The options shouldn't be part of this class at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we care about people using this layer having to do that themselves?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's OK to require that callers have to set the pipe themselves. User applications (Endpoints) will get a fully configured ConnectionContext
, so we're the only consumers of this during initialization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can keep the other ctor to avoid changing all of the callers. We're just adding a new optional overload.
Span<byte> buffer = stackalloc byte[16]; | ||
_keyGen.GetBytes(buffer); | ||
// Generate the id with RNGCrypto because we want a cryptographically random id, which GUID is not | ||
return WebEncoders.Base64UrlEncode(buffer.ToArray()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convert to hex, don't base64 encode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Y u hate base64? It looks so much cleaner, and no ugly ==
because it's the URL encode variant.
Plus, the hex encoding code looks gross (BitConverter.ToString(buffer.ToArray()).Replace("-", "")
)
Base64URL: MTIzNDU2NzgxMjM0NTY3OA
Hex: 31323334353637383132333435363738
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So much cleaner, looks more encrypty (it's "Base64 Encrypted" after all ;))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fite me irl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then we need to update the Base64 encoder to support taking a span and we need to update it to use the new base64 decoder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until then, lets remove the stackalloc since that just forces us to allocate more.
To close the loop on the discussions, we decided that the impact of HMACing or encrypting the key was heavy and would only gain us the ability to remove the initial allocation of the entry in the connection list. Keeping track of the IDs we've issued (which we do now) is equally secure, but we can reduce allocations by avoiding allocating the full |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's tidy up the pipe initialization.
Base64 4 lyfe.
return WebEncoders.Base64UrlEncode(buffer.ToArray()); | ||
#else | ||
// 128 bit buffer / 8 bits per byte = 16 bytes | ||
var buffer = new byte[16]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this buffer is so short-lived and limited in scope, it it worth using ArrayPool<byte>.Shared
to rent it? Legit not sure exactly what the use cases are for that. @davidfowl ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I think this is stackalloc worthy but only if we update WebEncoders.Base64UrlEncode(buffer)
to take a Span<byte>
|
||
// This tcs exists so that multiple calls to DisposeAsync all wait asynchronously | ||
// on the same task | ||
private TaskCompletionSource<object> _disposeTcs = new TaskCompletionSource<object>(); | ||
|
||
public DefaultConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application) | ||
public DefaultConnectionContext(string id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's OK to require that callers have to set the pipe themselves. User applications (Endpoints) will get a fully configured ConnectionContext
, so we're the only consumers of this during initialization.
@@ -22,6 +24,8 @@ public class ConnectionManager | |||
// TODO: Consider making this configurable? At least for testing? | |||
private static readonly TimeSpan _heartbeatTickRate = TimeSpan.FromSeconds(1); | |||
|
|||
private static readonly RNGCryptoServiceProvider _keyGen = new RNGCryptoServiceProvider(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_keyGenerator
@@ -23,7 +24,6 @@ public void NewConnectionsHaveConnectionId() | |||
Assert.Null(connection.TransportTask); | |||
Assert.Null(connection.Cancellation); | |||
Assert.NotEqual(default(DateTime), connection.LastSeenUtc); | |||
Assert.NotNull(connection.Transport); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert.Null
4fdfea9
to
caa12b8
Compare
|
||
Assert.NotNull(connection.ConnectionId); | ||
Assert.NotNull(connection.Transport); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add this back
@@ -555,6 +558,15 @@ private DefaultConnectionContext CreateConnectionInternal(HttpSocketOptions opti | |||
return null; | |||
} | |||
|
|||
if (connection.Transport == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment here.
|
||
private DefaultConnectionContext CreateConnectionInternal(HttpSocketOptions options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not bring this back and just check the transport here internally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nits and comments, otherwise good to merge IMO
@@ -22,16 +22,20 @@ public class DefaultConnectionContext : ConnectionContext, | |||
IConnectionHeartbeatFeature, | |||
ITransferFormatFeature | |||
{ | |||
private List<(Action<object> handler, object state)> _heartbeatHandlers = new List<(Action<object> handler, object state)>(); | |||
private object _heartBeatLock = new object(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: _heartbeatLock
(lower case b
)
public DefaultConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application) | ||
/// <summary> | ||
/// Creates the DefaultConnectionContext without Pipes to avoid upfront allocations. | ||
/// You'll need to add Transport and Application pipes on your own. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need to add Transport and Application pipes on your own
The caller is expected to set the <see cref="Transport"/> and <see cref="Application"/> pipes manually
@@ -94,8 +106,12 @@ public void RemoveConnection(string id) | |||
|
|||
private static string MakeNewConnectionId() | |||
{ | |||
// TODO: We need to sign and encyrpt this | |||
return Guid.NewGuid().ToString(); | |||
// TODO: Use Span when WebEncoders implements Span methods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File a bug
var transportPipeOptions = new PipeOptions(pauseWriterThreshold: options.TransportMaxBufferSize, resumeWriterThreshold: options.TransportMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false); | ||
var appPipeOptions = new PipeOptions(pauseWriterThreshold: options.ApplicationMaxBufferSize, resumeWriterThreshold: options.ApplicationMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false); | ||
return _manager.CreateConnection(transportPipeOptions, appPipeOptions); | ||
// If the connection doesn't have a pipe yet then create one, we lazily create the pipe to save on allocations until the connection actually connects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
until the client actually connects
#1523