350b8a9d34a9f38490a1db9e2e3f664bba36fa0e
[platform/upstream/dotnet/runtime.git] /
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.
4
5 using System.Collections.Generic;
6 using System.Net.Test.Common;
7 using System.Text;
8 using System.Threading;
9 using System.Threading.Tasks;
10 using Xunit;
11 using Xunit.Abstractions;
12
13 namespace System.Net.Http.Functional.Tests
14 {
15     using Configuration = System.Net.Test.Common.Configuration;
16
17     public abstract class HttpClientHandler_MaxResponseHeadersLength_Test : HttpClientHandlerTestBase
18     {
19         public HttpClientHandler_MaxResponseHeadersLength_Test(ITestOutputHelper output) : base(output) { }
20
21         [Theory]
22         [InlineData(0)]
23         [InlineData(-1)]
24         public void InvalidValue_ThrowsException(int invalidValue)
25         {
26             using (HttpClientHandler handler = CreateHttpClientHandler())
27             {
28                 AssertExtensions.Throws<ArgumentOutOfRangeException>("value", () => handler.MaxResponseHeadersLength = invalidValue);
29             }
30         }
31
32         [Theory]
33         [InlineData(1)]
34         [InlineData(65)]
35         [InlineData(int.MaxValue)]
36         public void ValidValue_SetGet_Roundtrips(int validValue)
37         {
38             using (HttpClientHandler handler = CreateHttpClientHandler())
39             {
40                 handler.MaxResponseHeadersLength = validValue;
41                 Assert.Equal(validValue, handler.MaxResponseHeadersLength);
42             }
43         }
44
45         [Fact]
46         public async Task SetAfterUse_Throws()
47         {
48             await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
49             {
50                 using HttpClientHandler handler = CreateHttpClientHandler();
51                 using HttpClient client = CreateHttpClient(handler);
52
53                 handler.MaxResponseHeadersLength = 1;
54                 (await client.GetStreamAsync(uri)).Dispose();
55                 Assert.Throws<InvalidOperationException>(() => handler.MaxResponseHeadersLength = 1);
56             },
57             server => server.AcceptConnectionSendResponseAndCloseAsync());
58         }
59
60         [OuterLoop]
61         [Fact]
62         public async Task InfiniteSingleHeader_ThrowsException()
63         {
64             await LoopbackServer.CreateServerAsync(async (server, url) =>
65             {
66                 using (HttpClientHandler handler = CreateHttpClientHandler())
67                 using (HttpClient client = CreateHttpClient(handler))
68                 {
69                     Task<HttpResponseMessage> getAsync = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
70                     await server.AcceptConnectionAsync(async connection =>
71                     {
72                         var cts = new CancellationTokenSource();
73                         Task serverTask = Task.Run(async delegate
74                         {
75                             await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nMyInfiniteHeader: ");
76                             try
77                             {
78                                 while (!cts.IsCancellationRequested)
79                                 {
80                                     await connection.Writer.WriteAsync(new string('s', 16000));
81                                     await Task.Delay(1);
82                                 }
83                             }
84                             catch { }
85                         });
86
87                         Exception e = await Assert.ThrowsAsync<HttpRequestException>(() => getAsync);
88                         cts.Cancel();
89                         Assert.Contains((handler.MaxResponseHeadersLength * 1024).ToString(), e.ToString());
90                         await serverTask;
91                     });
92                 }
93             });
94         }
95
96         [OuterLoop]
97         [Theory, MemberData(nameof(ResponseWithManyHeadersData))]
98         public async Task ThresholdExceeded_ThrowsException(string responseHeaders, int? maxResponseHeadersLength, bool shouldSucceed)
99         {
100             await LoopbackServer.CreateServerAsync(async (server, url) =>
101             {
102                 using (HttpClientHandler handler = CreateHttpClientHandler())
103                 using (HttpClient client = CreateHttpClient(handler))
104                 {
105                     if (maxResponseHeadersLength.HasValue)
106                     {
107                         handler.MaxResponseHeadersLength = maxResponseHeadersLength.Value;
108                     }
109                     Task<HttpResponseMessage> getAsync = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
110
111                     await server.AcceptConnectionAsync(async connection =>
112                     {
113                         Task serverTask = connection.ReadRequestHeaderAndSendCustomResponseAsync(responseHeaders);
114
115                         if (shouldSucceed)
116                         {
117                             (await getAsync).Dispose();
118                             await serverTask;
119                         }
120                         else
121                         {
122                             Exception e = await Assert.ThrowsAsync<HttpRequestException>(() => getAsync);
123                             Assert.Contains((handler.MaxResponseHeadersLength * 1024).ToString(), e.ToString());
124                             try { await serverTask; } catch { }
125                         }
126                     });
127                 }
128             });
129         }
130
131         public static IEnumerable<object[]> ResponseWithManyHeadersData
132         {
133             get
134             {
135                 foreach (int? max in new int?[] { null, 1, 31, 128 })
136                 {
137                     int actualSize = max.HasValue ? max.Value : 64;
138
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
142                 }
143             }
144         }
145
146         private static string GenerateLargeResponseHeaders(int responseHeadersSizeInBytes)
147         {
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++)
152             {
153                 buffer.Append($"Custom-{i:D4}: 1234567890123456789012345\r\n");
154             }
155             buffer.Append($"Custom-24: ");
156             buffer.Append(new string('c', responseHeadersSizeInBytes - (buffer.Length + 4)));
157             buffer.Append("\r\n\r\n");
158
159             string response = buffer.ToString();
160             Assert.Equal(responseHeadersSizeInBytes, response.Length);
161             return response;
162         }
163     }
164 }