Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Add IHttpConnectionFeature.ConnectionId. #638

Merged
merged 1 commit into from
Feb 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
66 changes: 48 additions & 18 deletions src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Filter;
Expand All @@ -14,18 +13,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
public class Connection : ConnectionContext, IConnectionControl
{
// Base32 encoding - in ascii sort order for easy text based sorting
private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV";

private static readonly Action<UvStreamHandle, int, object> _readCallback =
(handle, status, state) => ReadCallback(handle, status, state);
private static readonly Func<UvStreamHandle, int, object, Libuv.uv_buf_t> _allocCallback =
(handle, suggestedsize, state) => AllocCallback(handle, suggestedsize, state);

private static long _lastConnectionId;
// Seed the _lastConnectionId for this application instance with
// the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001
// for a roughly increasing _requestId over restarts
private static long _lastConnectionId = DateTime.UtcNow.Ticks;

private readonly UvStreamHandle _socket;
private Frame _frame;
private ConnectionFilterContext _filterContext;
private LibuvStream _libuvStream;
private readonly long _connectionId;

private readonly SocketInput _rawSocketInput;
private readonly SocketOutput _rawSocketOutput;
Expand All @@ -34,19 +38,16 @@ public class Connection : ConnectionContext, IConnectionControl
private ConnectionState _connectionState;
private TaskCompletionSource<object> _socketClosedTcs;

private IPEndPoint _remoteEndPoint;
private IPEndPoint _localEndPoint;

public Connection(ListenerContext context, UvStreamHandle socket) : base(context)
{
_socket = socket;
socket.Connection = this;
ConnectionControl = this;

_connectionId = Interlocked.Increment(ref _lastConnectionId);
ConnectionId = GenerateConnectionId(Interlocked.Increment(ref _lastConnectionId));

_rawSocketInput = new SocketInput(Memory2, ThreadPool);
_rawSocketOutput = new SocketOutput(Thread, _socket, Memory2, this, _connectionId, Log, ThreadPool, WriteReqPool);
_rawSocketOutput = new SocketOutput(Thread, _socket, Memory2, this, ConnectionId, Log, ThreadPool, WriteReqPool);
}

// Internal for testing
Expand All @@ -56,16 +57,16 @@ internal Connection()

public void Start()
{
Log.ConnectionStart(_connectionId);
Log.ConnectionStart(ConnectionId);

// Start socket prior to applying the ConnectionFilter
_socket.ReadStart(_allocCallback, _readCallback, this);

var tcpHandle = _socket as UvTcpHandle;
if (tcpHandle != null)
{
_remoteEndPoint = tcpHandle.GetPeerIPEndPoint();
_localEndPoint = tcpHandle.GetSockIPEndPoint();
RemoteEndPoint = tcpHandle.GetPeerIPEndPoint();
LocalEndPoint = tcpHandle.GetSockIPEndPoint();
}

// Don't initialize _frame until SocketInput and SocketOutput are set to their final values.
Expand Down Expand Up @@ -218,6 +219,8 @@ private void ApplyConnectionFilter()
SocketOutput = _rawSocketOutput;
}

PrepareRequest = _filterContext.PrepareRequest;

_frame = CreateFrame();
_frame.Start();
}
Expand Down Expand Up @@ -256,12 +259,12 @@ private void OnRead(UvStreamHandle handle, int status)

if (normalRead)
{
Log.ConnectionRead(_connectionId, readCount);
Log.ConnectionRead(ConnectionId, readCount);
}
else
{
_socket.ReadStop();
Log.ConnectionReadFin(_connectionId);
Log.ConnectionReadFin(ConnectionId);
}

Exception error = null;
Expand All @@ -280,18 +283,18 @@ private void OnRead(UvStreamHandle handle, int status)

private Frame CreateFrame()
{
return FrameFactory(this, _remoteEndPoint, _localEndPoint, _filterContext?.PrepareRequest);
return FrameFactory(this);
Copy link
Member

Choose a reason for hiding this comment

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

👍

}

void IConnectionControl.Pause()
{
Log.ConnectionPause(_connectionId);
Log.ConnectionPause(ConnectionId);
_socket.ReadStop();
}

void IConnectionControl.Resume()
{
Log.ConnectionResume(_connectionId);
Log.ConnectionResume(ConnectionId);
_socket.ReadStart(_allocCallback, _readCallback, this);
}

Expand All @@ -307,7 +310,7 @@ void IConnectionControl.End(ProduceEndType endType)
return;
}

Log.ConnectionKeepAlive(_connectionId);
Log.ConnectionKeepAlive(ConnectionId);
break;
case ProduceEndType.SocketShutdown:
case ProduceEndType.SocketDisconnect:
Expand All @@ -318,13 +321,40 @@ void IConnectionControl.End(ProduceEndType endType)
}
_connectionState = ConnectionState.Disconnecting;

Log.ConnectionDisconnect(_connectionId);
Log.ConnectionDisconnect(ConnectionId);
_rawSocketOutput.End(endType);
break;
}
}
}

private static unsafe string GenerateConnectionId(long id)
{
// The following routine is ~310% faster than calling long.ToString() on x64
// and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations
// See: https://github.com/aspnet/Hosting/pull/385

// stackalloc to allocate array on stack rather than heap
char* charBuffer = stackalloc char[13];

charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31];
charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31];
charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31];
charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31];
charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31];
charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31];
charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31];
charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31];
charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31];
charBuffer[9] = _encode32Chars[(int)(id >> 15) & 31];
charBuffer[10] = _encode32Chars[(int)(id >> 10) & 31];
charBuffer[11] = _encode32Chars[(int)(id >> 5) & 31];
charBuffer[12] = _encode32Chars[(int)id & 31];

// string ctor overload that takes char*
return new string(charBuffer, 0, 13);
}

private enum ConnectionState
{
CreatingFrame,
Expand Down
18 changes: 18 additions & 0 deletions src/Microsoft.AspNetCore.Server.Kestrel/Http/ConnectionContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Net;
using Microsoft.AspNetCore.Http.Features;

namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
public class ConnectionContext : ListenerContext
Expand All @@ -18,10 +22,24 @@ public ConnectionContext(ConnectionContext context) : base(context)
SocketInput = context.SocketInput;
SocketOutput = context.SocketOutput;
ConnectionControl = context.ConnectionControl;
RemoteEndPoint = context.RemoteEndPoint;
LocalEndPoint = context.LocalEndPoint;
ConnectionId = context.ConnectionId;
PrepareRequest = context.PrepareRequest;
}

public SocketInput SocketInput { get; set; }

public ISocketOutput SocketOutput { get; set; }

public IConnectionControl ConnectionControl { get; set; }

public IPEndPoint RemoteEndPoint { get; set; }

public IPEndPoint LocalEndPoint { get; set; }

public string ConnectionId { get; set; }

public Action<IFeatureCollection> PrepareRequest { get; set; }
Copy link
Member

Choose a reason for hiding this comment

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

Update copy ctor.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ bool IHttpUpgradeFeature.IsUpgradableRequest

int IHttpConnectionFeature.LocalPort { get; set; }

string IHttpConnectionFeature.ConnectionId { get; set; }

object IFeatureCollection.this[Type key]
{
get { return FastFeatureGet(key); }
Expand Down
27 changes: 7 additions & 20 deletions src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,26 +67,11 @@ public abstract partial class Frame : FrameContext, IFrameControl

private HttpVersionType _httpVersion;

private readonly IPEndPoint _localEndPoint;
private readonly IPEndPoint _remoteEndPoint;
private readonly Action<IFeatureCollection> _prepareRequest;

private readonly string _pathBase;

public Frame(ConnectionContext context)
: this(context, remoteEndPoint: null, localEndPoint: null, prepareRequest: null)
{
}

public Frame(ConnectionContext context,
IPEndPoint remoteEndPoint,
IPEndPoint localEndPoint,
Action<IFeatureCollection> prepareRequest)
: base(context)
{
_remoteEndPoint = remoteEndPoint;
_localEndPoint = localEndPoint;
_prepareRequest = prepareRequest;
_pathBase = context.ServerAddress.PathBase;

FrameControl = this;
Expand Down Expand Up @@ -249,13 +234,15 @@ public void Reset()
ReasonPhrase = null;

var httpConnectionFeature = this as IHttpConnectionFeature;
httpConnectionFeature.RemoteIpAddress = _remoteEndPoint?.Address;
httpConnectionFeature.RemotePort = _remoteEndPoint?.Port ?? 0;
httpConnectionFeature.RemoteIpAddress = RemoteEndPoint?.Address;
httpConnectionFeature.RemotePort = RemoteEndPoint?.Port ?? 0;

httpConnectionFeature.LocalIpAddress = LocalEndPoint?.Address;
httpConnectionFeature.LocalPort = LocalEndPoint?.Port ?? 0;

httpConnectionFeature.LocalIpAddress = _localEndPoint?.Address;
httpConnectionFeature.LocalPort = _localEndPoint?.Port ?? 0;
httpConnectionFeature.ConnectionId = ConnectionId;

_prepareRequest?.Invoke(this);
PrepareRequest?.Invoke(this);

_manuallySetRequestAbortToken = null;
_abortedCts = null;
Expand Down
11 changes: 1 addition & 10 deletions src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,7 @@ public class Frame<TContext> : Frame

public Frame(IHttpApplication<TContext> application,
ConnectionContext context)
: this(application, context, remoteEndPoint: null, localEndPoint: null, prepareRequest: null)
{
}

public Frame(IHttpApplication<TContext> application,
ConnectionContext context,
IPEndPoint remoteEndPoint,
IPEndPoint localEndPoint,
Action<IFeatureCollection> prepareRequest)
: base(context, remoteEndPoint, localEndPoint, prepareRequest)
: base(context)
{
_application = application;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class SocketOutput : ISocketOutput
private readonly KestrelThread _thread;
private readonly UvStreamHandle _socket;
private readonly Connection _connection;
private readonly long _connectionId;
private readonly string _connectionId;
private readonly IKestrelTrace _log;
private readonly IThreadPool _threadPool;

Expand Down Expand Up @@ -58,7 +58,7 @@ public SocketOutput(
UvStreamHandle socket,
MemoryPool2 memory,
Connection connection,
long connectionId,
string connectionId,
IKestrelTrace log,
IThreadPool threadPool,
Queue<UvWriteReq> writeReqPool)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
{
public interface IKestrelTrace : ILogger
{
void ConnectionStart(long connectionId);
void ConnectionStart(string connectionId);

void ConnectionStop(long connectionId);
void ConnectionStop(string connectionId);

void ConnectionRead(long connectionId, int count);
void ConnectionRead(string connectionId, int count);

void ConnectionPause(long connectionId);
void ConnectionPause(string connectionId);

void ConnectionResume(long connectionId);
void ConnectionResume(string connectionId);

void ConnectionReadFin(long connectionId);
void ConnectionReadFin(string connectionId);

void ConnectionWriteFin(long connectionId);
void ConnectionWriteFin(string connectionId);

void ConnectionWroteFin(long connectionId, int status);
void ConnectionWroteFin(string connectionId, int status);

void ConnectionKeepAlive(long connectionId);
void ConnectionKeepAlive(string connectionId);

void ConnectionDisconnect(long connectionId);
void ConnectionDisconnect(string connectionId);

void ConnectionWrite(long connectionId, int count);
void ConnectionWrite(string connectionId, int count);

void ConnectionWriteCallback(long connectionId, int status);
void ConnectionWriteCallback(string connectionId, int status);

void ConnectionError(long connectionId, Exception ex);
void ConnectionError(string connectionId, Exception ex);

void ConnectionDisconnectedWrite(long connectionId, int count, Exception ex);
void ConnectionDisconnectedWrite(string connectionId, int count, Exception ex);

void NotAllConnectionsClosedGracefully();

Expand Down
Loading