Skip to content

Commit ca23b1a

Browse files
authored
Pool HttpSys request buffers (#17314)
1 parent 6f2b107 commit ca23b1a

File tree

9 files changed

+102
-191
lines changed

9 files changed

+102
-191
lines changed

src/Servers/HttpSys/samples/TestClient/App.config

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/Servers/HttpSys/samples/TestClient/Program.cs

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,45 @@ namespace TestClient
1111
public class Program
1212
{
1313
private const string Address =
14-
// "http://localhost:5000/public/1kb.txt";
15-
"https://localhost:9090/public/1kb.txt";
14+
"http://localhost:5000/public/1kb.txt";
15+
// "https://localhost:9090/public/1kb.txt";
1616

1717
public static void Main(string[] args)
1818
{
19-
WebRequestHandler handler = new WebRequestHandler();
20-
handler.ServerCertificateValidationCallback = (_, __, ___, ____) => true;
19+
Console.WriteLine("Ready");
20+
Console.ReadKey();
21+
22+
var handler = new HttpClientHandler();
23+
handler.MaxConnectionsPerServer = 500;
24+
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
2125
// handler.UseDefaultCredentials = true;
22-
handler.Credentials = new NetworkCredential(@"redmond\chrross", "passwird");
2326
HttpClient client = new HttpClient(handler);
2427

25-
/*
28+
RunParallelRequests(client);
29+
30+
// RunManualRequests(client);
31+
32+
// RunWebSocketClient().Wait();
33+
34+
Console.WriteLine("Done");
35+
// Console.ReadKey();
36+
}
37+
38+
private static void RunManualRequests(HttpClient client)
39+
{
40+
while (true)
41+
{
42+
Console.WriteLine("Press any key to send request");
43+
Console.ReadKey();
44+
var result = client.GetAsync(Address).Result;
45+
Console.WriteLine(result);
46+
}
47+
}
48+
49+
private static void RunParallelRequests(HttpClient client)
50+
{
2651
int completionCount = 0;
27-
int iterations = 30000;
52+
int iterations = 100000;
2853
for (int i = 0; i < iterations; i++)
2954
{
3055
client.GetAsync(Address)
@@ -34,19 +59,7 @@ public static void Main(string[] args)
3459
while (completionCount < iterations)
3560
{
3661
Thread.Sleep(10);
37-
}*/
38-
39-
while (true)
40-
{
41-
Console.WriteLine("Press any key to send request");
42-
Console.ReadKey();
43-
var result = client.GetAsync(Address).Result;
44-
Console.WriteLine(result);
4562
}
46-
47-
// RunWebSocketClient().Wait();
48-
// Console.WriteLine("Done");
49-
// Console.ReadKey();
5063
}
5164

5265
public static async Task RunWebSocketClient()

src/Servers/HttpSys/samples/TestClient/Properties/AssemblyInfo.cs

Lines changed: 0 additions & 53 deletions
This file was deleted.
Lines changed: 8 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,13 @@
1-
2-
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3-
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
43
<PropertyGroup>
5-
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6-
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7-
<ProjectGuid>{8B828433-B333-4C19-96AE-00BFFF9D8841}</ProjectGuid>
4+
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
85
<OutputType>Exe</OutputType>
9-
<AppDesignerFolder>Properties</AppDesignerFolder>
10-
<RootNamespace>TestClient</RootNamespace>
11-
<AssemblyName>TestClient</AssemblyName>
12-
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
13-
<FileAlignment>512</FileAlignment>
14-
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
15-
<RestorePackages>true</RestorePackages>
16-
<TargetFrameworkProfile />
6+
<StartupObject>TestClient.Program</StartupObject>
177
</PropertyGroup>
18-
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19-
<PlatformTarget>AnyCPU</PlatformTarget>
20-
<DebugSymbols>true</DebugSymbols>
21-
<DebugType>full</DebugType>
22-
<Optimize>false</Optimize>
23-
<OutputPath>bin\Debug\</OutputPath>
24-
<DefineConstants>DEBUG;TRACE</DefineConstants>
25-
<ErrorReport>prompt</ErrorReport>
26-
<WarningLevel>4</WarningLevel>
27-
</PropertyGroup>
28-
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
29-
<PlatformTarget>AnyCPU</PlatformTarget>
30-
<DebugType>pdbonly</DebugType>
31-
<Optimize>true</Optimize>
32-
<OutputPath>bin\Release\</OutputPath>
33-
<DefineConstants>TRACE</DefineConstants>
34-
<ErrorReport>prompt</ErrorReport>
35-
<WarningLevel>4</WarningLevel>
36-
</PropertyGroup>
37-
<ItemGroup>
38-
<Reference Include="System" />
39-
<Reference Include="System.Core" />
40-
<Reference Include="System.Net.Http" />
41-
<Reference Include="System.Net.Http.WebRequest" />
42-
<Reference Include="System.Xml.Linq" />
43-
<Reference Include="System.Data.DataSetExtensions" />
44-
<Reference Include="Microsoft.CSharp" />
45-
<Reference Include="System.Data" />
46-
<Reference Include="System.Xml" />
47-
</ItemGroup>
48-
<ItemGroup>
49-
<Compile Include="Program.cs" />
50-
<Compile Include="Properties\AssemblyInfo.cs" />
51-
</ItemGroup>
8+
529
<ItemGroup>
53-
<None Include="App.config" />
10+
<Reference Include="Microsoft.Extensions.Logging.Console" />
5411
</ItemGroup>
55-
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
56-
<Target Name="Pack" />
57-
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
58-
Other similar extension points exist, see Microsoft.Common.targets.
59-
<Target Name="BeforeBuild">
60-
</Target>
61-
<Target Name="AfterBuild">
62-
</Target>
63-
-->
64-
</Project>
12+
13+
</Project>

src/Servers/HttpSys/src/AsyncAcceptContext.cs

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
using System;
55
using System.Diagnostics.CodeAnalysis;
6-
using System.Runtime.InteropServices;
76
using System.Threading;
87
using System.Threading.Tasks;
98
using Microsoft.AspNetCore.Http;
@@ -18,8 +17,6 @@ internal unsafe class AsyncAcceptContext : IAsyncResult, IDisposable
1817
private TaskCompletionSource<RequestContext> _tcs;
1918
private HttpSysListener _server;
2019
private NativeRequestContext _nativeRequestContext;
21-
private const int DefaultBufferSize = 4096;
22-
private const int AlignmentPadding = 8;
2320

2421
internal AsyncAcceptContext(HttpSysListener server)
2522
{
@@ -192,32 +189,14 @@ internal void AllocateNativeRequest(uint? size = null, ulong requestId = 0)
192189
{
193190
_nativeRequestContext?.ReleasePins();
194191
_nativeRequestContext?.Dispose();
195-
//Debug.Assert(size != 0, "unexpected size");
196192

197193
// We can't reuse overlapped objects
198-
uint newSize = size.HasValue ? size.Value : DefaultBufferSize;
199-
var backingBuffer = new byte[newSize + AlignmentPadding];
200-
201194
var boundHandle = Server.RequestQueue.BoundHandle;
202195
var nativeOverlapped = new SafeNativeOverlapped(boundHandle,
203-
boundHandle.AllocateNativeOverlapped(IOCallback, this, backingBuffer));
204-
205-
var requestAddress = Marshal.UnsafeAddrOfPinnedArrayElement(backingBuffer, 0);
206-
207-
// TODO:
208-
// Apparently the HttpReceiveHttpRequest memory alignment requirements for non - ARM processors
209-
// are different than for ARM processors. We have seen 4 - byte - aligned buffers allocated on
210-
// virtual x64/x86 machines which were accepted by HttpReceiveHttpRequest without errors. In
211-
// these cases the buffer alignment may cause reading values at invalid offset. Setting buffer
212-
// alignment to 0 for now.
213-
//
214-
// _bufferAlignment = (int)(requestAddress.ToInt64() & 0x07);
215-
216-
var bufferAlignment = 0;
196+
boundHandle.AllocateNativeOverlapped(IOCallback, this, pinData: null));
217197

218-
var nativeRequest = (HttpApiTypes.HTTP_REQUEST*)(requestAddress + bufferAlignment);
219198
// nativeRequest
220-
_nativeRequestContext = new NativeRequestContext(nativeOverlapped, bufferAlignment, nativeRequest, backingBuffer, requestId);
199+
_nativeRequestContext = new NativeRequestContext(nativeOverlapped, Server.MemoryPool, size, requestId);
221200
}
222201

223202
public object AsyncState

src/Servers/HttpSys/src/HttpSysListener.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Buffers;
56
using System.Collections.Generic;
67
using System.Diagnostics;
78
using System.Runtime.InteropServices;
@@ -32,6 +33,8 @@ internal class HttpSysListener : IDisposable
3233
// 0.5 seconds per request. Respond with a 400 Bad Request.
3334
private const int UnknownHeaderLimit = 1000;
3435

36+
internal MemoryPool<byte> MemoryPool { get; } = SlabMemoryPoolFactory.Create();
37+
3538
private volatile State _state; // m_State is set only within lock blocks, but often read outside locks.
3639

3740
private ServerSession _serverSession;

src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<Description>ASP.NET Core HTTP server that uses the Windows HTTP Server API.</Description>
@@ -13,6 +13,10 @@
1313

1414
<ItemGroup>
1515
<Compile Include="$(SharedSourceRoot)HttpSys\**\*.cs" />
16+
<Compile Include="$(SharedSourceRoot)Buffers.MemoryPool\*.cs" LinkBase="MemoryPool" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
1620
</ItemGroup>
1721

1822
<ItemGroup>

src/Shared/HttpSys/RequestProcessing/HeaderEncoding.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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 System;
45
using System.Text;
56

67
namespace Microsoft.AspNetCore.HttpSys.Internal
@@ -14,16 +15,7 @@ internal static class HeaderEncoding
1415

1516
internal static unsafe string GetString(byte* pBytes, int byteCount)
1617
{
17-
// net451: return new string(pBytes, 0, byteCount, Encoding);
18-
19-
var charCount = Encoding.GetCharCount(pBytes, byteCount);
20-
var chars = new char[charCount];
21-
fixed (char* pChars = chars)
22-
{
23-
var count = Encoding.GetChars(pBytes, byteCount, pChars, charCount);
24-
System.Diagnostics.Debug.Assert(count == charCount);
25-
}
26-
return new string(chars);
18+
return Encoding.GetString(new ReadOnlySpan<byte>(pBytes, byteCount));
2719
}
2820

2921
internal static byte[] GetBytes(string myString)

0 commit comments

Comments
 (0)