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

Commit 9ade227

Browse files
committed
Implement support for UNIX sockets.
The common use-case for Kestrel in production will be behind a reverse proxy such as Nginx. In cases where the reverse proxy is located on the same machine as the application, connecting via a UNIX socket is more efficient than a TCP socket, as it avoids going through the network layer. Accessing 127.0.0.1 through TCP still needs to initiate a TCP connection and perform handshaking, checksumming, etc, all of which is avoided by using UNIX sockets. - Moved TCP-specific stuff from Listener into new TcpListener class (same with ListenerPrimary and ListenerSecondary) - Made Listener abstract - Created new PipeListener. Note that while the use case is for UNIX sockets, this is called "Pipe" in uv, so I've called this "PipeListener" so the terminology is consistant - Uses "unix" URL scheme to determine whether to use socket. "http://127.0.0.1:5000" is for listening via TCP while "unix:///var/run/kestrel-test.sock" is for listening via UNIX socket #156
1 parent 62a909d commit 9ade227

16 files changed

+358
-41
lines changed

samples/SampleApp/project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"commands": {
1515
"run": "Microsoft.AspNet.Server.Kestrel",
16+
"run-socket": "Microsoft.AspNet.Server.Kestrel --server.urls unix:///tmp/kestrel-test.sock",
1617
"kestrel": "Microsoft.AspNet.Server.Kestrel",
1718
"web": "Microsoft.AspNet.Hosting"
1819
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
7+
namespace Microsoft.AspNet.Server.Kestrel.Http
8+
{
9+
/// <summary>
10+
/// A listener waits for incoming connections on a specified socket.
11+
/// </summary>
12+
public interface IListener : IDisposable
13+
{
14+
Task StartAsync(
15+
string scheme,
16+
string host,
17+
int port,
18+
KestrelThread thread,
19+
Func<Frame, Task> application);
20+
}
21+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
7+
namespace Microsoft.AspNet.Server.Kestrel.Http
8+
{
9+
/// <summary>
10+
/// A primary listener waits for incoming connections on a specified socket. Incoming
11+
/// connections may be passed to a secondary listener to handle.
12+
/// </summary>
13+
public interface IListenerPrimary : IListener
14+
{
15+
Task StartAsync(
16+
string pipeName,
17+
string scheme,
18+
string host,
19+
int port,
20+
KestrelThread thread,
21+
Func<Frame, Task> application);
22+
}
23+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
7+
namespace Microsoft.AspNet.Server.Kestrel.Http
8+
{
9+
/// <summary>
10+
/// A secondary listener is delegated requests from a primary listener via a named pipe or
11+
/// UNIX domain socket.
12+
/// </summary>
13+
public interface IListenerSecondary : IDisposable
14+
{
15+
Task StartAsync(
16+
string pipeName,
17+
KestrelThread thread,
18+
Func<Frame, Task> application);
19+
}
20+
}

src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,34 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
54
using Microsoft.AspNet.Server.Kestrel.Networking;
65
using System;
76
using System.Diagnostics;
8-
using System.Net;
97
using System.Threading.Tasks;
108

119
namespace Microsoft.AspNet.Server.Kestrel.Http
1210
{
1311
/// <summary>
14-
/// Summary description for Accept
12+
/// Base class for listeners in Kestrel. Listens for incoming connections
1513
/// </summary>
16-
public class Listener : ListenerContext, IDisposable
14+
/// <typeparam name="T">Type of socket used by this listener</typeparam>
15+
public abstract class Listener<T> : ListenerContext, IListener where T : UvStreamHandle
1716
{
18-
private static readonly Action<UvStreamHandle, int, Exception, object> _connectionCallback = ConnectionCallback;
17+
protected T ListenSocket { get; private set; }
1918

20-
UvTcpHandle ListenSocket { get; set; }
21-
22-
private static void ConnectionCallback(UvStreamHandle stream, int status, Exception error, object state)
19+
protected static void ConnectionCallback(UvStreamHandle stream, int status, Exception error, object state)
2320
{
2421
if (error != null)
2522
{
2623
Trace.WriteLine("Listener.ConnectionCallback " + error.ToString());
2724
}
2825
else
2926
{
30-
((Listener)state).OnConnection(stream, status);
27+
((Listener<T>)state).OnConnection((T)stream, status);
3128
}
3229
}
3330

34-
public Listener(IMemoryPool memory)
31+
protected Listener(IMemoryPool memory)
3532
{
3633
Memory = memory;
3734
}
@@ -51,10 +48,7 @@ public Task StartAsync(
5148
{
5249
try
5350
{
54-
ListenSocket = new UvTcpHandle();
55-
ListenSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
56-
ListenSocket.Bind(new IPEndPoint(IPAddress.Any, port));
57-
ListenSocket.Listen(Constants.ListenBacklog, _connectionCallback, this);
51+
ListenSocket = CreateListenSocket(host, port);
5852
tcs.SetResult(0);
5953
}
6054
catch (Exception ex)
@@ -65,16 +59,19 @@ public Task StartAsync(
6559
return tcs.Task;
6660
}
6761

68-
private void OnConnection(UvStreamHandle listenSocket, int status)
69-
{
70-
var acceptSocket = new UvTcpHandle();
71-
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
72-
listenSocket.Accept(acceptSocket);
62+
/// <summary>
63+
/// Creates the socket used to listen for incoming connections
64+
/// </summary>
65+
protected abstract T CreateListenSocket(string host, int port);
7366

74-
DispatchConnection(acceptSocket);
75-
}
67+
/// <summary>
68+
/// Handles an incoming connection
69+
/// </summary>
70+
/// <param name="listenSocket">Socket being used to listen on</param>
71+
/// <param name="status">Connection status</param>
72+
protected abstract void OnConnection(T listenSocket, int status);
7673

77-
protected virtual void DispatchConnection(UvTcpHandle socket)
74+
protected virtual void DispatchConnection(T socket)
7875
{
7976
var connection = new Connection(this, socket);
8077
connection.Start();

src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
99

1010
namespace Microsoft.AspNet.Server.Kestrel.Http
1111
{
12-
public class ListenerPrimary : Listener
12+
/// <summary>
13+
/// A primary listener waits for incoming connections on a specified socket. Incoming
14+
/// connections may be passed to a secondary listener to handle.
15+
/// </summary>
16+
abstract public class ListenerPrimary<T> : Listener<T>, IListenerPrimary where T : UvStreamHandle
1317
{
1418
UvPipeHandle ListenPipe { get; set; }
1519

1620
List<UvPipeHandle> _dispatchPipes = new List<UvPipeHandle>();
1721
int _dispatchIndex;
1822
ArraySegment<ArraySegment<byte>> _1234 = new ArraySegment<ArraySegment<byte>>(new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }) });
1923

20-
public ListenerPrimary(IMemoryPool memory) : base(memory)
24+
protected ListenerPrimary(IMemoryPool memory) : base(memory)
2125
{
2226
}
2327

@@ -61,7 +65,7 @@ private void OnListenPipe(UvStreamHandle pipe, int status, Exception error, obje
6165
_dispatchPipes.Add(dispatchPipe);
6266
}
6367

64-
protected override void DispatchConnection(UvTcpHandle socket)
68+
protected override void DispatchConnection(T socket)
6569
{
6670
var index = _dispatchIndex++ % (_dispatchPipes.Count + 1);
6771
if (index == _dispatchPipes.Count)
@@ -80,7 +84,7 @@ protected override void DispatchConnection(UvTcpHandle socket)
8084
(write2, status, error, state) =>
8185
{
8286
write2.Dispose();
83-
((UvTcpHandle)state).Dispose();
87+
((T)state).Dispose();
8488
},
8589
socket);
8690
}

src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@
99

1010
namespace Microsoft.AspNet.Server.Kestrel.Http
1111
{
12-
public class ListenerSecondary : ListenerContext, IDisposable
12+
/// <summary>
13+
/// A secondary listener is delegated requests from a primary listener via a named pipe or
14+
/// UNIX domain socket.
15+
/// </summary>
16+
public abstract class ListenerSecondary<T> : ListenerContext, IListenerSecondary where T : UvStreamHandle
1317
{
1418
UvPipeHandle DispatchPipe { get; set; }
1519

16-
public ListenerSecondary(IMemoryPool memory)
20+
protected ListenerSecondary(IMemoryPool memory)
1721
{
1822
Memory = memory;
1923
}
@@ -64,8 +68,7 @@ public Task StartAsync(
6468
return;
6569
}
6670

67-
var acceptSocket = new UvTcpHandle();
68-
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
71+
var acceptSocket = CreateAcceptSocket();
6972

7073
try
7174
{
@@ -102,6 +105,11 @@ public Task StartAsync(
102105
return tcs.Task;
103106
}
104107

108+
/// <summary>
109+
/// Creates a socket which can be used to accept an incoming connection
110+
/// </summary>
111+
protected abstract T CreateAcceptSocket();
112+
105113
public void Dispose()
106114
{
107115
// Ensure the event loop is still running.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
5+
using Microsoft.AspNet.Server.Kestrel.Networking;
6+
7+
namespace Microsoft.AspNet.Server.Kestrel.Http
8+
{
9+
/// <summary>
10+
/// Implementation of <see cref="Listener{T}"/> that uses UNIX domain sockets as its transport.
11+
/// </summary>
12+
public class PipeListener : Listener<UvPipeHandle>
13+
{
14+
public PipeListener(IMemoryPool memory) : base(memory)
15+
{
16+
}
17+
18+
/// <summary>
19+
/// Creates the socket used to listen for incoming connections
20+
/// </summary>
21+
protected override UvPipeHandle CreateListenSocket(string host, int port)
22+
{
23+
var socket = new UvPipeHandle();
24+
socket.Init(Thread.Loop, false);
25+
socket.Bind(host);
26+
socket.Listen(Constants.ListenBacklog, ConnectionCallback, this);
27+
return socket;
28+
}
29+
30+
/// <summary>
31+
/// Handles an incoming connection
32+
/// </summary>
33+
/// <param name="listenSocket">Socket being used to listen on</param>
34+
/// <param name="status">Connection status</param>
35+
protected override void OnConnection(UvPipeHandle listenSocket, int status)
36+
{
37+
var acceptSocket = new UvPipeHandle();
38+
acceptSocket.Init(Thread.Loop, false);
39+
listenSocket.Accept(acceptSocket);
40+
41+
DispatchConnection(acceptSocket);
42+
}
43+
}
44+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
5+
using Microsoft.AspNet.Server.Kestrel.Networking;
6+
7+
namespace Microsoft.AspNet.Server.Kestrel.Http
8+
{
9+
/// <summary>
10+
/// An implementation of <see cref="ListenerPrimary{T}"/> using UNIX sockets.
11+
/// </summary>
12+
public class PipeListenerPrimary : ListenerPrimary<UvPipeHandle>
13+
{
14+
public PipeListenerPrimary(IMemoryPool memory) : base(memory)
15+
{
16+
}
17+
18+
/// <summary>
19+
/// Creates the socket used to listen for incoming connections
20+
/// </summary>
21+
protected override UvPipeHandle CreateListenSocket(string host, int port)
22+
{
23+
var socket = new UvPipeHandle();
24+
socket.Init(Thread.Loop, false);
25+
socket.Bind(host);
26+
socket.Listen(Constants.ListenBacklog, ConnectionCallback, this);
27+
return socket;
28+
}
29+
30+
/// <summary>
31+
/// Handles an incoming connection
32+
/// </summary>
33+
/// <param name="listenSocket">Socket being used to listen on</param>
34+
/// <param name="status">Connection status</param>
35+
protected override void OnConnection(UvPipeHandle listenSocket, int status)
36+
{
37+
var acceptSocket = new UvPipeHandle();
38+
acceptSocket.Init(Thread.Loop, false);
39+
listenSocket.Accept(acceptSocket);
40+
41+
DispatchConnection(acceptSocket);
42+
}
43+
}
44+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNet.Server.Kestrel.Networking;
5+
6+
namespace Microsoft.AspNet.Server.Kestrel.Http
7+
{
8+
/// <summary>
9+
/// An implementation of <see cref="ListenerSecondary{T}"/> using UNIX sockets.
10+
/// </summary>
11+
public class PipeListenerSecondary : ListenerSecondary<UvPipeHandle>
12+
{
13+
public PipeListenerSecondary(IMemoryPool memory) : base(memory)
14+
{
15+
}
16+
17+
/// <summary>
18+
/// Creates a socket which can be used to accept an incoming connection
19+
/// </summary>
20+
protected override UvPipeHandle CreateAcceptSocket()
21+
{
22+
var acceptSocket = new UvPipeHandle();
23+
acceptSocket.Init(Thread.Loop, false);
24+
return acceptSocket;
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)