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

Commit 714816e

Browse files
committed
Limit size of memory buffer when reading request (#304)
1 parent 69bd0dc commit 714816e

File tree

3 files changed

+118
-6
lines changed

3 files changed

+118
-6
lines changed

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Connection.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,15 @@ public Connection(ListenerContext context, UvStreamHandle socket) : base(context
4949

5050
ConnectionId = GenerateConnectionId(Interlocked.Increment(ref _lastConnectionId));
5151

52-
_rawSocketInput = new SocketInput(Memory, ThreadPool);
52+
if (ServerOptions.MaxInputBufferLength == -1)
53+
{
54+
_rawSocketInput = new SocketInput(Memory, ThreadPool);
55+
}
56+
else
57+
{
58+
_rawSocketInput = new SocketInput(Memory, ThreadPool, ServerOptions.MaxInputBufferLength, this, Thread);
59+
}
60+
5361
_rawSocketOutput = new SocketOutput(Thread, _socket, Memory, this, ConnectionId, Log, ThreadPool, WriteReqPool);
5462
}
5563

src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketInput.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,21 @@ public class SocketInput : ICriticalNotifyCompletion, IDisposable
3232
private bool _consuming;
3333
private bool _disposed;
3434

35+
private readonly BufferLengthConnectionController _bufferLengthConnectionController;
36+
3537
public SocketInput(MemoryPool memory, IThreadPool threadPool)
3638
{
3739
_memory = memory;
3840
_threadPool = threadPool;
3941
_awaitableState = _awaitableIsNotCompleted;
4042
}
4143

44+
public SocketInput(MemoryPool memory, IThreadPool threadPool, int maxBufferLength, IConnectionControl connectionControl,
45+
KestrelThread connectionThread) : this(memory, threadPool)
46+
{
47+
_bufferLengthConnectionController = new BufferLengthConnectionController(maxBufferLength, connectionControl, connectionThread);
48+
}
49+
4250
public bool RemoteIntakeFin { get; set; }
4351

4452
public bool IsCompleted => ReferenceEquals(_awaitableState, _awaitableIsCompleted);
@@ -63,6 +71,9 @@ public void IncomingData(byte[] buffer, int offset, int count)
6371
{
6472
lock (_sync)
6573
{
74+
// Must call Add() before bytes are available to consumer, to ensure that Length is >= 0
75+
_bufferLengthConnectionController?.Add(count);
76+
6677
if (count > 0)
6778
{
6879
if (_tail == null)
@@ -93,6 +104,9 @@ public void IncomingComplete(int count, Exception error)
93104
{
94105
lock (_sync)
95106
{
107+
// Must call Add() before bytes are available to consumer, to ensure that Length is >= 0
108+
_bufferLengthConnectionController?.Add(count);
109+
96110
if (_pinned != null)
97111
{
98112
_pinned.End += count;
@@ -189,10 +203,16 @@ public void ConsumingComplete(
189203
{
190204
if (!consumed.IsDefault)
191205
{
206+
var lengthConsumed = new MemoryPoolIterator(_head).GetLength(consumed);
207+
192208
returnStart = _head;
193209
returnEnd = consumed.Block;
194210
_head = consumed.Block;
195211
_head.Start = consumed.Index;
212+
213+
// Must call Subtract() after bytes have been freed, to avoid producer starting too early and growing
214+
// buffer beyond max length.
215+
_bufferLengthConnectionController?.Subtract(lengthConsumed);
196216
}
197217

198218
if (!examined.IsDefault &&
@@ -321,5 +341,73 @@ private static void ReturnBlocks(MemoryPoolBlock block, MemoryPoolBlock end)
321341
returnBlock.Pool.Return(returnBlock);
322342
}
323343
}
344+
345+
private class BufferLengthConnectionController
346+
{
347+
private readonly int _maxLength;
348+
private readonly IConnectionControl _connectionControl;
349+
private readonly KestrelThread _connectionThread;
350+
351+
private readonly object _lock = new object();
352+
353+
private int _length;
354+
private bool _connectionPaused;
355+
356+
public BufferLengthConnectionController(int maxLength, IConnectionControl connectionControl, KestrelThread connectionThread)
357+
{
358+
_maxLength = maxLength;
359+
_connectionControl = connectionControl;
360+
_connectionThread = connectionThread;
361+
}
362+
363+
public int Length
364+
{
365+
get
366+
{
367+
return _length;
368+
}
369+
set
370+
{
371+
// Caller should ensure that bytes are never consumed before the producer has called Add()
372+
Debug.Assert(value >= 0);
373+
374+
_length = value;
375+
}
376+
}
377+
378+
public void Add(int count)
379+
{
380+
lock (_lock)
381+
{
382+
// Add() should never be called while connection is paused, since ConnectionControl.Pause() runs on a libuv thread
383+
// and should take effect immediately.
384+
Debug.Assert(!_connectionPaused);
385+
386+
Length += count;
387+
if (Length >= _maxLength)
388+
{
389+
_connectionPaused = true;
390+
_connectionControl.Pause();
391+
}
392+
}
393+
}
394+
395+
public void Subtract(int count)
396+
{
397+
lock (_lock)
398+
{
399+
Length -= count;
400+
401+
if (_connectionPaused && Length < _maxLength)
402+
{
403+
_connectionPaused = false;
404+
_connectionThread.Post(
405+
(connectionControl) => ((IConnectionControl)connectionControl).Resume(),
406+
_connectionControl);
407+
}
408+
}
409+
}
410+
}
411+
324412
}
325413
}

src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerOptions.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel
88
{
99
public class KestrelServerOptions
1010
{
11+
/// <summary>
12+
/// Gets or sets whether the <c>Server</c> header should be included in each response.
13+
/// </summary>
14+
public bool AddServerHeader { get; set; } = true;
15+
1116
public IServiceProvider ApplicationServices { get; set; }
1217

1318
public IConnectionFilter ConnectionFilter { get; set; }
1419

15-
public bool NoDelay { get; set; } = true;
20+
private int _maxInputBufferLength = 1024 * 1024;
21+
public int MaxInputBufferLength
22+
{
23+
get
24+
{
25+
return _maxInputBufferLength;
26+
}
27+
set
28+
{
29+
if (value < -1 || value == 0)
30+
{
31+
throw new ArgumentOutOfRangeException("value", "Value must be positive or -1.");
32+
}
33+
}
34+
}
1635

17-
/// <summary>
18-
/// Gets or sets whether the <c>Server</c> header should be included in each response.
19-
/// </summary>
20-
public bool AddServerHeader { get; set; } = true;
36+
public bool NoDelay { get; set; } = true;
2137

2238
/// <summary>
2339
/// The amount of time after the server begins shutting down before connections will be forcefully closed.

0 commit comments

Comments
 (0)