Http2Tests.cs 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  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. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Http;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using Microsoft.AspNetCore.Http;
  12. using Microsoft.AspNetCore.Http.Features;
  13. using Microsoft.AspNetCore.Http2Cat;
  14. using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
  15. using Microsoft.AspNetCore.Testing;
  16. using Microsoft.Extensions.Hosting;
  17. using Microsoft.Extensions.Logging;
  18. using Microsoft.Net.Http.Headers;
  19. using Xunit;
  20. namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
  21. {
  22. public class Http2Tests
  23. {
  24. [ConditionalFact]
  25. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  26. public async Task EmptyResponse_200()
  27. {
  28. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  29. {
  30. var feature = httpContext.Features.Get<IHttpUpgradeFeature>();
  31. Assert.False(feature.IsUpgradableRequest);
  32. Assert.False(httpContext.Request.CanHaveBody());
  33. // Default 200
  34. return Task.CompletedTask;
  35. });
  36. await new HostBuilder()
  37. .UseHttp2Cat(address, async h2Connection =>
  38. {
  39. await h2Connection.InitializeConnectionAsync();
  40. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  41. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  42. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  43. {
  44. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  45. });
  46. var dataFrame = await h2Connection.ReceiveFrameAsync();
  47. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  48. h2Connection.Logger.LogInformation("Connection stopped.");
  49. })
  50. .Build().RunAsync();
  51. }
  52. [ConditionalTheory]
  53. [InlineData("POST")]
  54. [InlineData("PUT")]
  55. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  56. public async Task RequestWithoutData_LengthRequired_Rejected(string method)
  57. {
  58. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  59. {
  60. throw new NotImplementedException();
  61. });
  62. await new HostBuilder()
  63. .UseHttp2Cat(address, async h2Connection =>
  64. {
  65. await h2Connection.InitializeConnectionAsync();
  66. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  67. var headers = new[]
  68. {
  69. new KeyValuePair<string, string>(HeaderNames.Method, method),
  70. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  71. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  72. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  73. };
  74. await h2Connection.StartStreamAsync(1, headers, endStream: true);
  75. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  76. {
  77. Assert.Equal("411", decodedHeaders[HeaderNames.Status]);
  78. });
  79. var dataFrame = await h2Connection.ReceiveFrameAsync();
  80. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 344);
  81. dataFrame = await h2Connection.ReceiveFrameAsync();
  82. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  83. h2Connection.Logger.LogInformation("Connection stopped.");
  84. })
  85. .Build().RunAsync();
  86. }
  87. [ConditionalTheory]
  88. [InlineData("GET")]
  89. [InlineData("HEAD")]
  90. [InlineData("PATCH")]
  91. [InlineData("DELETE")]
  92. [InlineData("CUSTOM")]
  93. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  94. public async Task RequestWithoutData_Success(string method)
  95. {
  96. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  97. {
  98. Assert.True(HttpMethods.Equals(method, httpContext.Request.Method));
  99. Assert.False(httpContext.Request.CanHaveBody());
  100. Assert.Null(httpContext.Request.ContentLength);
  101. Assert.False(httpContext.Request.Headers.ContainsKey(HeaderNames.TransferEncoding));
  102. return Task.CompletedTask;
  103. });
  104. await new HostBuilder()
  105. .UseHttp2Cat(address, async h2Connection =>
  106. {
  107. await h2Connection.InitializeConnectionAsync();
  108. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  109. var headers = new[]
  110. {
  111. new KeyValuePair<string, string>(HeaderNames.Method, method),
  112. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  113. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  114. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  115. };
  116. await h2Connection.StartStreamAsync(1, headers, endStream: true);
  117. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  118. {
  119. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  120. });
  121. var dataFrame = await h2Connection.ReceiveFrameAsync();
  122. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  123. h2Connection.Logger.LogInformation("Connection stopped.");
  124. })
  125. .Build().RunAsync();
  126. }
  127. [ConditionalTheory]
  128. [InlineData("GET")]
  129. // [InlineData("HEAD")] Reset with code HTTP_1_1_REQUIRED
  130. [InlineData("POST")]
  131. [InlineData("PUT")]
  132. [InlineData("PATCH")]
  133. [InlineData("DELETE")]
  134. [InlineData("CUSTOM")]
  135. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  136. public async Task RequestWithDataAndContentLength_Success(string method)
  137. {
  138. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  139. {
  140. Assert.True(HttpMethods.Equals(method, httpContext.Request.Method));
  141. Assert.True(httpContext.Request.CanHaveBody());
  142. Assert.Equal(11, httpContext.Request.ContentLength);
  143. Assert.False(httpContext.Request.Headers.ContainsKey(HeaderNames.TransferEncoding));
  144. return httpContext.Request.Body.CopyToAsync(httpContext.Response.Body);
  145. });
  146. await new HostBuilder()
  147. .UseHttp2Cat(address, async h2Connection =>
  148. {
  149. await h2Connection.InitializeConnectionAsync();
  150. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  151. var headers = new[]
  152. {
  153. new KeyValuePair<string, string>(HeaderNames.Method, method),
  154. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  155. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  156. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  157. new KeyValuePair<string, string>(HeaderNames.ContentLength, "11"),
  158. };
  159. await h2Connection.StartStreamAsync(1, headers, endStream: false);
  160. await h2Connection.SendDataAsync(1, Encoding.UTF8.GetBytes("Hello World"), endStream: true);
  161. // Http.Sys no longer sends a window update here on later versions.
  162. if (Environment.OSVersion.Version < new Version(10, 0, 19041, 0))
  163. {
  164. var windowUpdate = await h2Connection.ReceiveFrameAsync();
  165. Assert.Equal(Http2FrameType.WINDOW_UPDATE, windowUpdate.Type);
  166. }
  167. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  168. {
  169. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  170. });
  171. var dataFrame = await h2Connection.ReceiveFrameAsync();
  172. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);
  173. Assert.Equal("Hello World", Encoding.UTF8.GetString(dataFrame.Payload.Span));
  174. dataFrame = await h2Connection.ReceiveFrameAsync();
  175. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  176. h2Connection.Logger.LogInformation("Connection stopped.");
  177. })
  178. .Build().RunAsync();
  179. }
  180. [ConditionalTheory]
  181. [InlineData("GET")]
  182. // [InlineData("HEAD")] Reset with code HTTP_1_1_REQUIRED
  183. [InlineData("POST")]
  184. [InlineData("PUT")]
  185. [InlineData("PATCH")]
  186. [InlineData("DELETE")]
  187. [InlineData("CUSTOM")]
  188. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  189. public async Task RequestWithDataAndNoContentLength_Success(string method)
  190. {
  191. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  192. {
  193. Assert.True(HttpMethods.Equals(method, httpContext.Request.Method));
  194. Assert.True(httpContext.Request.CanHaveBody());
  195. Assert.Null(httpContext.Request.ContentLength);
  196. // The client didn't send this header, Http.Sys added it for back compat with HTTP/1.1.
  197. Assert.Equal("chunked", httpContext.Request.Headers[HeaderNames.TransferEncoding]);
  198. return httpContext.Request.Body.CopyToAsync(httpContext.Response.Body);
  199. });
  200. await new HostBuilder()
  201. .UseHttp2Cat(address, async h2Connection =>
  202. {
  203. await h2Connection.InitializeConnectionAsync();
  204. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  205. var headers = new[]
  206. {
  207. new KeyValuePair<string, string>(HeaderNames.Method, method),
  208. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  209. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  210. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  211. };
  212. await h2Connection.StartStreamAsync(1, headers, endStream: false);
  213. await h2Connection.SendDataAsync(1, Encoding.UTF8.GetBytes("Hello World"), endStream: true);
  214. // Http.Sys no longer sends a window update here on later versions.
  215. if (Environment.OSVersion.Version < new Version(10, 0, 19041, 0))
  216. {
  217. var windowUpdate = await h2Connection.ReceiveFrameAsync();
  218. Assert.Equal(Http2FrameType.WINDOW_UPDATE, windowUpdate.Type);
  219. }
  220. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  221. {
  222. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  223. });
  224. var dataFrame = await h2Connection.ReceiveFrameAsync();
  225. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);
  226. Assert.Equal("Hello World", Encoding.UTF8.GetString(dataFrame.Payload.Span));
  227. dataFrame = await h2Connection.ReceiveFrameAsync();
  228. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  229. h2Connection.Logger.LogInformation("Connection stopped.");
  230. })
  231. .Build().RunAsync();
  232. }
  233. [ConditionalFact]
  234. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  235. public async Task ResponseWithData_Success()
  236. {
  237. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  238. {
  239. return httpContext.Response.WriteAsync("Hello World");
  240. });
  241. await new HostBuilder()
  242. .UseHttp2Cat(address, async h2Connection =>
  243. {
  244. await h2Connection.InitializeConnectionAsync();
  245. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  246. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  247. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  248. {
  249. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  250. });
  251. var dataFrame = await h2Connection.ReceiveFrameAsync();
  252. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);
  253. dataFrame = await h2Connection.ReceiveFrameAsync();
  254. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  255. h2Connection.Logger.LogInformation("Connection stopped.");
  256. })
  257. .Build().RunAsync();
  258. }
  259. [ConditionalFact(Skip = "https://github.com/dotnet/aspnetcore/issues/17420")]
  260. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  261. [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H1, SkipReason = "This is last version without GoAway support")]
  262. public async Task ConnectionClose_NoOSSupport_NoGoAway()
  263. {
  264. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  265. {
  266. httpContext.Response.Headers[HeaderNames.Connection] = "close";
  267. return Task.FromResult(0);
  268. });
  269. await new HostBuilder()
  270. .UseHttp2Cat(address, async h2Connection =>
  271. {
  272. await h2Connection.InitializeConnectionAsync();
  273. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  274. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  275. await h2Connection.ReceiveHeadersAsync(1, endStream: true, decodedHeaders =>
  276. {
  277. // HTTP/2 filters out the connection header
  278. Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
  279. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  280. });
  281. // Send and receive a second request to ensure there is no GoAway frame on the wire yet.
  282. await h2Connection.StartStreamAsync(3, Http2Utilities.BrowserRequestHeaders, endStream: true);
  283. await h2Connection.ReceiveHeadersAsync(3, endStream: true, decodedHeaders =>
  284. {
  285. // HTTP/2 filters out the connection header
  286. Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
  287. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  288. });
  289. await h2Connection.StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
  290. h2Connection.Logger.LogInformation("Connection stopped.");
  291. })
  292. .Build().RunAsync();
  293. }
  294. [ConditionalFact]
  295. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H2, SkipReason = "GoAway support was added in Win10_19H2.")]
  296. public async Task ConnectionClose_OSSupport_SendsGoAway()
  297. {
  298. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  299. {
  300. httpContext.Response.Headers[HeaderNames.Connection] = "close";
  301. return Task.FromResult(0);
  302. });
  303. await new HostBuilder()
  304. .UseHttp2Cat(address, async h2Connection =>
  305. {
  306. await h2Connection.InitializeConnectionAsync();
  307. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  308. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  309. var goAwayFrame = await h2Connection.ReceiveFrameAsync();
  310. h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);
  311. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  312. {
  313. // HTTP/2 filters out the connection header
  314. Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
  315. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  316. });
  317. var dataFrame = await h2Connection.ReceiveFrameAsync();
  318. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  319. // Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams.
  320. h2Connection.Logger.LogInformation("Connection stopped.");
  321. })
  322. .Build().RunAsync();
  323. }
  324. [ConditionalFact]
  325. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H2, SkipReason = "GoAway support was added in Win10_19H2.")]
  326. public async Task ConnectionClose_AdditionalRequests_ReceivesSecondGoAway()
  327. {
  328. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  329. {
  330. httpContext.Response.Headers[HeaderNames.Connection] = "close";
  331. return Task.FromResult(0);
  332. });
  333. await new HostBuilder()
  334. .UseHttp2Cat(address, async h2Connection =>
  335. {
  336. await h2Connection.InitializeConnectionAsync();
  337. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  338. var streamId = 1;
  339. await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true);
  340. var goAwayFrame = await h2Connection.ReceiveFrameAsync();
  341. h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);
  342. await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
  343. {
  344. // HTTP/2 filters out the connection header
  345. Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
  346. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  347. });
  348. var dataFrame = await h2Connection.ReceiveFrameAsync();
  349. Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
  350. // Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams.
  351. for (var i = 1; i < 200; i++)
  352. {
  353. streamId = 1 + (i * 2); // Odds.
  354. await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true);
  355. await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
  356. {
  357. // HTTP/2 filters out the connection header
  358. Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
  359. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  360. });
  361. dataFrame = await h2Connection.ReceiveFrameAsync();
  362. Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
  363. }
  364. streamId = 1 + (200 * 2); // Odds.
  365. await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true);
  366. // Final GoAway
  367. goAwayFrame = await h2Connection.ReceiveFrameAsync();
  368. h2Connection.VerifyGoAway(goAwayFrame, streamId, Http2ErrorCode.NO_ERROR);
  369. // Normal response
  370. await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
  371. {
  372. // HTTP/2 filters out the connection header
  373. Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
  374. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  375. });
  376. dataFrame = await h2Connection.ReceiveFrameAsync();
  377. Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
  378. h2Connection.Logger.LogInformation("Connection stopped.");
  379. })
  380. .Build().RunAsync();
  381. }
  382. [ConditionalFact]
  383. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  384. public async Task AppException_BeforeResponseHeaders_500()
  385. {
  386. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  387. {
  388. throw new Exception("Application exception");
  389. });
  390. await new HostBuilder()
  391. .UseHttp2Cat(address, async h2Connection =>
  392. {
  393. await h2Connection.InitializeConnectionAsync();
  394. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  395. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  396. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  397. {
  398. Assert.Equal("500", decodedHeaders[HeaderNames.Status]);
  399. });
  400. var dataFrame = await h2Connection.ReceiveFrameAsync();
  401. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  402. h2Connection.Logger.LogInformation("Connection stopped.");
  403. })
  404. .Build().RunAsync();
  405. }
  406. [ConditionalFact]
  407. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  408. [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_20H1, SkipReason = "This is last version without custom Reset support")]
  409. public async Task AppException_AfterHeaders_PriorOSVersions_ResetCancel()
  410. {
  411. using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
  412. {
  413. await httpContext.Response.Body.FlushAsync();
  414. throw new Exception("Application exception");
  415. });
  416. await new HostBuilder()
  417. .UseHttp2Cat(address, async h2Connection =>
  418. {
  419. await h2Connection.InitializeConnectionAsync();
  420. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  421. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  422. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  423. {
  424. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  425. });
  426. var resetFrame = await h2Connection.ReceiveFrameAsync();
  427. Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, Http2ErrorCode.CANCEL);
  428. h2Connection.Logger.LogInformation("Connection stopped.");
  429. })
  430. .Build().RunAsync();
  431. }
  432. [ConditionalFact]
  433. [MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Custom Reset support was added in Win10_20H2.")]
  434. public async Task AppException_AfterHeaders_ResetInternalError()
  435. {
  436. using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
  437. {
  438. await httpContext.Response.Body.FlushAsync();
  439. throw new Exception("Application exception");
  440. });
  441. await new HostBuilder()
  442. .UseHttp2Cat(address, async h2Connection =>
  443. {
  444. await h2Connection.InitializeConnectionAsync();
  445. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  446. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  447. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  448. {
  449. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  450. });
  451. var frame = await h2Connection.ReceiveFrameAsync();
  452. Http2Utilities.VerifyResetFrame(frame, expectedStreamId: 1, Http2ErrorCode.INTERNAL_ERROR);
  453. h2Connection.Logger.LogInformation("Connection stopped.");
  454. })
  455. .Build().RunAsync();
  456. }
  457. [ConditionalFact]
  458. public async Task Reset_Http1_NotSupported()
  459. {
  460. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  461. {
  462. Assert.Equal("HTTP/1.1", httpContext.Request.Protocol);
  463. var feature = httpContext.Features.Get<IHttpResetFeature>();
  464. Assert.Null(feature);
  465. return httpContext.Response.WriteAsync("Hello World");
  466. });
  467. var handler = new HttpClientHandler();
  468. handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
  469. using HttpClient client = new HttpClient(handler);
  470. client.DefaultRequestVersion = HttpVersion.Version11;
  471. var response = await client.GetStringAsync(address);
  472. Assert.Equal("Hello World", response);
  473. }
  474. [ConditionalFact]
  475. [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
  476. [MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_20H1, SkipReason = "This is last version without Reset support")]
  477. public async Task Reset_PriorOSVersions_NotSupported()
  478. {
  479. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  480. {
  481. Assert.Equal("HTTP/2", httpContext.Request.Protocol);
  482. var feature = httpContext.Features.Get<IHttpResetFeature>();
  483. Assert.Null(feature);
  484. return httpContext.Response.WriteAsync("Hello World");
  485. });
  486. var handler = new HttpClientHandler();
  487. handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
  488. using HttpClient client = new HttpClient(handler);
  489. client.DefaultRequestVersion = HttpVersion.Version20;
  490. var response = await client.GetStringAsync(address);
  491. Assert.Equal("Hello World", response);
  492. }
  493. [ConditionalFact]
  494. [MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
  495. public async Task Reset_BeforeResponse_Resets()
  496. {
  497. var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  498. using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
  499. {
  500. try
  501. {
  502. Assert.Equal("HTTP/2", httpContext.Request.Protocol);
  503. var feature = httpContext.Features.Get<IHttpResetFeature>();
  504. Assert.NotNull(feature);
  505. feature.Reset(1111); // Custom
  506. appResult.SetResult(0);
  507. }
  508. catch (Exception ex)
  509. {
  510. appResult.SetException(ex);
  511. }
  512. return Task.FromResult(0);
  513. });
  514. await new HostBuilder()
  515. .UseHttp2Cat(address, async h2Connection =>
  516. {
  517. await h2Connection.InitializeConnectionAsync();
  518. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  519. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  520. var resetFrame = await h2Connection.ReceiveFrameAsync();
  521. Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
  522. // Any app errors?
  523. Assert.Equal(0, await appResult.Task.DefaultTimeout());
  524. h2Connection.Logger.LogInformation("Connection stopped.");
  525. })
  526. .Build().RunAsync();
  527. }
  528. [ConditionalFact]
  529. [MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
  530. public async Task Reset_AfterResponseHeaders_Resets()
  531. {
  532. var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  533. using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
  534. {
  535. try
  536. {
  537. Assert.Equal("HTTP/2", httpContext.Request.Protocol);
  538. var feature = httpContext.Features.Get<IHttpResetFeature>();
  539. Assert.NotNull(feature);
  540. await httpContext.Response.Body.FlushAsync();
  541. feature.Reset(1111); // Custom
  542. appResult.SetResult(0);
  543. }
  544. catch (Exception ex)
  545. {
  546. appResult.SetException(ex);
  547. }
  548. });
  549. await new HostBuilder()
  550. .UseHttp2Cat(address, async h2Connection =>
  551. {
  552. await h2Connection.InitializeConnectionAsync();
  553. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  554. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  555. // Any app errors?
  556. Assert.Equal(0, await appResult.Task.DefaultTimeout());
  557. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  558. {
  559. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  560. });
  561. var resetFrame = await h2Connection.ReceiveFrameAsync();
  562. Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
  563. h2Connection.Logger.LogInformation("Connection stopped.");
  564. })
  565. .Build().RunAsync();
  566. }
  567. [ConditionalFact]
  568. [MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
  569. public async Task Reset_DurringResponseBody_Resets()
  570. {
  571. var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  572. using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
  573. {
  574. try
  575. {
  576. Assert.Equal("HTTP/2", httpContext.Request.Protocol);
  577. var feature = httpContext.Features.Get<IHttpResetFeature>();
  578. Assert.NotNull(feature);
  579. await httpContext.Response.WriteAsync("Hello World");
  580. feature.Reset(1111); // Custom
  581. appResult.SetResult(0);
  582. }
  583. catch (Exception ex)
  584. {
  585. appResult.SetException(ex);
  586. }
  587. });
  588. await new HostBuilder()
  589. .UseHttp2Cat(address, async h2Connection =>
  590. {
  591. await h2Connection.InitializeConnectionAsync();
  592. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  593. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  594. // Any app errors?
  595. Assert.Equal(0, await appResult.Task.DefaultTimeout());
  596. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  597. {
  598. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  599. });
  600. var dataFrame = await h2Connection.ReceiveFrameAsync();
  601. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);
  602. var resetFrame = await h2Connection.ReceiveFrameAsync();
  603. Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
  604. h2Connection.Logger.LogInformation("Connection stopped.");
  605. })
  606. .Build().RunAsync();
  607. }
  608. [ConditionalFact]
  609. [MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
  610. public async Task Reset_AfterCompleteAsync_NoReset()
  611. {
  612. var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  613. using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
  614. {
  615. try
  616. {
  617. Assert.Equal("HTTP/2", httpContext.Request.Protocol);
  618. var feature = httpContext.Features.Get<IHttpResetFeature>();
  619. Assert.NotNull(feature);
  620. await httpContext.Response.WriteAsync("Hello World");
  621. await httpContext.Response.CompleteAsync();
  622. // The request and response are fully complete, the reset doesn't get sent.
  623. feature.Reset(1111);
  624. appResult.SetResult(0);
  625. }
  626. catch (Exception ex)
  627. {
  628. appResult.SetException(ex);
  629. }
  630. });
  631. await new HostBuilder()
  632. .UseHttp2Cat(address, async h2Connection =>
  633. {
  634. await h2Connection.InitializeConnectionAsync();
  635. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  636. await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
  637. // Any app errors?
  638. Assert.Equal(0, await appResult.Task.DefaultTimeout());
  639. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  640. {
  641. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  642. });
  643. var dataFrame = await h2Connection.ReceiveFrameAsync();
  644. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);
  645. dataFrame = await h2Connection.ReceiveFrameAsync();
  646. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  647. h2Connection.Logger.LogInformation("Connection stopped.");
  648. })
  649. .Build().RunAsync();
  650. }
  651. [ConditionalFact]
  652. [MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
  653. public async Task Reset_BeforeRequestBody_Resets()
  654. {
  655. var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  656. using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
  657. {
  658. try
  659. {
  660. Assert.Equal("HTTP/2", httpContext.Request.Protocol);
  661. var feature = httpContext.Features.Get<IHttpResetFeature>();
  662. Assert.NotNull(feature);
  663. var readTask = httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
  664. feature.Reset(1111);
  665. await Assert.ThrowsAsync<IOException>(() => readTask);
  666. appResult.SetResult(0);
  667. }
  668. catch (Exception ex)
  669. {
  670. appResult.SetException(ex);
  671. }
  672. });
  673. await new HostBuilder()
  674. .UseHttp2Cat(address, async h2Connection =>
  675. {
  676. await h2Connection.InitializeConnectionAsync();
  677. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  678. await h2Connection.StartStreamAsync(1, Http2Utilities.PostRequestHeaders, endStream: false);
  679. // Any app errors?
  680. Assert.Equal(0, await appResult.Task.DefaultTimeout());
  681. var resetFrame = await h2Connection.ReceiveFrameAsync();
  682. Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
  683. h2Connection.Logger.LogInformation("Connection stopped.");
  684. })
  685. .Build().RunAsync();
  686. }
  687. [ConditionalFact]
  688. [MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
  689. public async Task Reset_DurringRequestBody_Resets()
  690. {
  691. var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  692. using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
  693. {
  694. try
  695. {
  696. Assert.Equal("HTTP/2", httpContext.Request.Protocol);
  697. var feature = httpContext.Features.Get<IHttpResetFeature>();
  698. Assert.NotNull(feature);
  699. var read = await httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
  700. Assert.Equal(10, read);
  701. var readTask = httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
  702. feature.Reset(1111);
  703. await Assert.ThrowsAsync<IOException>(() => readTask);
  704. appResult.SetResult(0);
  705. }
  706. catch (Exception ex)
  707. {
  708. appResult.SetException(ex);
  709. }
  710. });
  711. await new HostBuilder()
  712. .UseHttp2Cat(address, async h2Connection =>
  713. {
  714. await h2Connection.InitializeConnectionAsync();
  715. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  716. await h2Connection.StartStreamAsync(1, Http2Utilities.PostRequestHeaders, endStream: false);
  717. await h2Connection.SendDataAsync(1, new byte[10], endStream: false);
  718. // Any app errors?
  719. Assert.Equal(0, await appResult.Task.DefaultTimeout());
  720. var resetFrame = await h2Connection.ReceiveFrameAsync();
  721. Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
  722. h2Connection.Logger.LogInformation("Connection stopped.");
  723. })
  724. .Build().RunAsync();
  725. }
  726. [ConditionalFact]
  727. [MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
  728. public async Task Reset_CompleteAsyncDurringRequestBody_Resets()
  729. {
  730. var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  731. using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
  732. {
  733. try
  734. {
  735. Assert.Equal("HTTP/2", httpContext.Request.Protocol);
  736. var feature = httpContext.Features.Get<IHttpResetFeature>();
  737. Assert.NotNull(feature);
  738. var read = await httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
  739. Assert.Equal(10, read);
  740. var readTask = httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
  741. await httpContext.Response.CompleteAsync();
  742. feature.Reset((int)Http2ErrorCode.NO_ERROR); // GRPC does this
  743. await Assert.ThrowsAsync<IOException>(() => readTask);
  744. appResult.SetResult(0);
  745. }
  746. catch (Exception ex)
  747. {
  748. appResult.SetException(ex);
  749. }
  750. });
  751. await new HostBuilder()
  752. .UseHttp2Cat(address, async h2Connection =>
  753. {
  754. await h2Connection.InitializeConnectionAsync();
  755. h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
  756. await h2Connection.StartStreamAsync(1, Http2Utilities.PostRequestHeaders, endStream: false);
  757. await h2Connection.SendDataAsync(1, new byte[10], endStream: false);
  758. // Any app errors?
  759. Assert.Equal(0, await appResult.Task.DefaultTimeout());
  760. await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
  761. {
  762. Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
  763. });
  764. var dataFrame = await h2Connection.ReceiveFrameAsync();
  765. Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
  766. var resetFrame = await h2Connection.ReceiveFrameAsync();
  767. Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.NO_ERROR);
  768. h2Connection.Logger.LogInformation("Connection stopped.");
  769. })
  770. .Build().RunAsync();
  771. }
  772. }
  773. }