1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Collections.Generic;
6 using System.Net.Test.Common;
8 using System.Threading;
9 using System.Threading.Tasks;
11 using Xunit.Abstractions;
13 namespace System.Net.Http.Functional.Tests
15 using Configuration = System.Net.Test.Common.Configuration;
17 public abstract class HttpClientHandler_MaxResponseHeadersLength_Test : HttpClientHandlerTestBase
19 public HttpClientHandler_MaxResponseHeadersLength_Test(ITestOutputHelper output) : base(output) { }
24 public void InvalidValue_ThrowsException(int invalidValue)
26 using (HttpClientHandler handler = CreateHttpClientHandler())
28 AssertExtensions.Throws<ArgumentOutOfRangeException>("value", () => handler.MaxResponseHeadersLength = invalidValue);
35 [InlineData(int.MaxValue)]
36 public void ValidValue_SetGet_Roundtrips(int validValue)
38 using (HttpClientHandler handler = CreateHttpClientHandler())
40 handler.MaxResponseHeadersLength = validValue;
41 Assert.Equal(validValue, handler.MaxResponseHeadersLength);
46 public async Task SetAfterUse_Throws()
48 await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
50 using HttpClientHandler handler = CreateHttpClientHandler();
51 using HttpClient client = CreateHttpClient(handler);
53 handler.MaxResponseHeadersLength = 1;
54 (await client.GetStreamAsync(uri)).Dispose();
55 Assert.Throws<InvalidOperationException>(() => handler.MaxResponseHeadersLength = 1);
57 server => server.AcceptConnectionSendResponseAndCloseAsync());
62 public async Task InfiniteSingleHeader_ThrowsException()
64 await LoopbackServer.CreateServerAsync(async (server, url) =>
66 using (HttpClientHandler handler = CreateHttpClientHandler())
67 using (HttpClient client = CreateHttpClient(handler))
69 Task<HttpResponseMessage> getAsync = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
70 await server.AcceptConnectionAsync(async connection =>
72 var cts = new CancellationTokenSource();
73 Task serverTask = Task.Run(async delegate
75 await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nMyInfiniteHeader: ");
78 while (!cts.IsCancellationRequested)
80 await connection.Writer.WriteAsync(new string('s', 16000));
87 Exception e = await Assert.ThrowsAsync<HttpRequestException>(() => getAsync);
89 Assert.Contains((handler.MaxResponseHeadersLength * 1024).ToString(), e.ToString());
97 [Theory, MemberData(nameof(ResponseWithManyHeadersData))]
98 public async Task ThresholdExceeded_ThrowsException(string responseHeaders, int? maxResponseHeadersLength, bool shouldSucceed)
100 await LoopbackServer.CreateServerAsync(async (server, url) =>
102 using (HttpClientHandler handler = CreateHttpClientHandler())
103 using (HttpClient client = CreateHttpClient(handler))
105 if (maxResponseHeadersLength.HasValue)
107 handler.MaxResponseHeadersLength = maxResponseHeadersLength.Value;
109 Task<HttpResponseMessage> getAsync = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
111 await server.AcceptConnectionAsync(async connection =>
113 Task serverTask = connection.ReadRequestHeaderAndSendCustomResponseAsync(responseHeaders);
117 (await getAsync).Dispose();
122 Exception e = await Assert.ThrowsAsync<HttpRequestException>(() => getAsync);
123 Assert.Contains((handler.MaxResponseHeadersLength * 1024).ToString(), e.ToString());
124 try { await serverTask; } catch { }
131 public static IEnumerable<object[]> ResponseWithManyHeadersData
135 foreach (int? max in new int?[] { null, 1, 31, 128 })
137 int actualSize = max.HasValue ? max.Value : 64;
139 yield return new object[] { GenerateLargeResponseHeaders(actualSize * 1024 - 1), max, true }; // Small enough
140 yield return new object[] { GenerateLargeResponseHeaders(actualSize * 1024), max, true }; // Just right
141 yield return new object[] { GenerateLargeResponseHeaders(actualSize * 1024 + 1), max, false }; // Too big
146 private static string GenerateLargeResponseHeaders(int responseHeadersSizeInBytes)
148 var buffer = new StringBuilder();
149 buffer.Append("HTTP/1.1 200 OK\r\n");
150 buffer.Append("Content-Length: 0\r\n");
151 for (int i = 0; i < 24; i++)
153 buffer.Append($"Custom-{i:D4}: 1234567890123456789012345\r\n");
155 buffer.Append($"Custom-24: ");
156 buffer.Append(new string('c', responseHeadersSizeInBytes - (buffer.Length + 4)));
157 buffer.Append("\r\n\r\n");
159 string response = buffer.ToString();
160 Assert.Equal(responseHeadersSizeInBytes, response.Length);