Http2Utilities.cs 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  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.Buffers;
  5. using System.Buffers.Binary;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. using System.IO.Pipelines;
  9. using System.Linq;
  10. using System.Net.Http;
  11. using System.Net.Http.HPack;
  12. using System.Runtime.CompilerServices;
  13. using System.Text;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using Microsoft.AspNetCore.Connections;
  17. using Microsoft.AspNetCore.Http;
  18. using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
  19. using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
  20. using Microsoft.AspNetCore.Testing;
  21. using Microsoft.Extensions.Logging;
  22. using Microsoft.Net.Http.Headers;
  23. using IHttpHeadersHandler = System.Net.Http.IHttpHeadersHandler;
  24. namespace Microsoft.AspNetCore.Http2Cat
  25. {
  26. internal class Http2Utilities : IHttpHeadersHandler
  27. {
  28. public static ReadOnlySpan<byte> ClientPreface => new byte[24] { (byte)'P', (byte)'R', (byte)'I', (byte)' ', (byte)'*', (byte)' ', (byte)'H', (byte)'T', (byte)'T', (byte)'P', (byte)'/', (byte)'2', (byte)'.', (byte)'0', (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n', (byte)'S', (byte)'M', (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' };
  29. public const int MaxRequestHeaderFieldSize = 16 * 1024;
  30. public static readonly string FourKHeaderValue = new string('a', 4096);
  31. private static readonly Encoding HeaderValueEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
  32. public static readonly IEnumerable<KeyValuePair<string, string>> BrowserRequestHeaders = new[]
  33. {
  34. new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
  35. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  36. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  37. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:443"),
  38. new KeyValuePair<string, string>("user-agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"),
  39. new KeyValuePair<string, string>("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
  40. new KeyValuePair<string, string>("accept-language", "en-US,en;q=0.5"),
  41. new KeyValuePair<string, string>("accept-encoding", "gzip, deflate, br"),
  42. new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
  43. };
  44. public static readonly IEnumerable<KeyValuePair<string, string>> BrowserRequestHeadersHttp = new[]
  45. {
  46. new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
  47. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  48. new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
  49. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  50. new KeyValuePair<string, string>("user-agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"),
  51. new KeyValuePair<string, string>("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
  52. new KeyValuePair<string, string>("accept-language", "en-US,en;q=0.5"),
  53. new KeyValuePair<string, string>("accept-encoding", "gzip, deflate, br"),
  54. new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
  55. };
  56. public static readonly IEnumerable<KeyValuePair<string, string>> PostRequestHeaders = new[]
  57. {
  58. new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
  59. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  60. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  61. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  62. };
  63. public static readonly IEnumerable<KeyValuePair<string, string>> ExpectContinueRequestHeaders = new[]
  64. {
  65. new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
  66. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  67. new KeyValuePair<string, string>(HeaderNames.Authority, "127.0.0.1"),
  68. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  69. new KeyValuePair<string, string>("expect", "100-continue"),
  70. };
  71. public static readonly IEnumerable<KeyValuePair<string, string>> RequestTrailers = new[]
  72. {
  73. new KeyValuePair<string, string>("trailer-one", "1"),
  74. new KeyValuePair<string, string>("trailer-two", "2"),
  75. };
  76. public static readonly IEnumerable<KeyValuePair<string, string>> OneContinuationRequestHeaders = new[]
  77. {
  78. new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
  79. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  80. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  81. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  82. new KeyValuePair<string, string>("a", FourKHeaderValue),
  83. new KeyValuePair<string, string>("b", FourKHeaderValue),
  84. new KeyValuePair<string, string>("c", FourKHeaderValue),
  85. new KeyValuePair<string, string>("d", FourKHeaderValue)
  86. };
  87. public static readonly IEnumerable<KeyValuePair<string, string>> TwoContinuationsRequestHeaders = new[]
  88. {
  89. new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
  90. new KeyValuePair<string, string>(HeaderNames.Path, "/"),
  91. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  92. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  93. new KeyValuePair<string, string>("a", FourKHeaderValue),
  94. new KeyValuePair<string, string>("b", FourKHeaderValue),
  95. new KeyValuePair<string, string>("c", FourKHeaderValue),
  96. new KeyValuePair<string, string>("d", FourKHeaderValue),
  97. new KeyValuePair<string, string>("e", FourKHeaderValue),
  98. new KeyValuePair<string, string>("f", FourKHeaderValue),
  99. new KeyValuePair<string, string>("g", FourKHeaderValue),
  100. };
  101. public static IEnumerable<KeyValuePair<string, string>> ReadRateRequestHeaders(int expectedBytes) => new[]
  102. {
  103. new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
  104. new KeyValuePair<string, string>(HeaderNames.Path, "/" + expectedBytes),
  105. new KeyValuePair<string, string>(HeaderNames.Scheme, "https"),
  106. new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
  107. };
  108. public static readonly byte[] _helloBytes = Encoding.ASCII.GetBytes("hello");
  109. public static readonly byte[] _worldBytes = Encoding.ASCII.GetBytes("world");
  110. public static readonly byte[] _helloWorldBytes = Encoding.ASCII.GetBytes("hello, world");
  111. public static readonly byte[] _noData = Array.Empty<byte>();
  112. public static readonly byte[] _maxData = Encoding.ASCII.GetBytes(new string('a', Http2PeerSettings.MinAllowedMaxFrameSize));
  113. internal readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
  114. internal readonly HPackDecoder _hpackDecoder;
  115. private readonly byte[] _headerEncodingBuffer = new byte[Http2PeerSettings.MinAllowedMaxFrameSize];
  116. public readonly Dictionary<string, string> _decodedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  117. internal DuplexPipe.DuplexPipePair _pair;
  118. public long _bytesReceived;
  119. public Http2Utilities(ConnectionContext clientConnectionContext, ILogger logger, CancellationToken stopToken)
  120. {
  121. _hpackDecoder = new HPackDecoder((int)_clientSettings.HeaderTableSize, MaxRequestHeaderFieldSize);
  122. _pair = new DuplexPipe.DuplexPipePair(transport: null, application: clientConnectionContext.Transport);
  123. Logger = logger;
  124. StopToken = stopToken;
  125. }
  126. public ILogger Logger { get; }
  127. public CancellationToken StopToken { get; }
  128. void IHttpHeadersHandler.OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
  129. {
  130. _decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetAsciiOrUTF8StringNonNullCharacters(HeaderValueEncoding);
  131. }
  132. void IHttpHeadersHandler.OnHeadersComplete(bool endStream) { }
  133. public async Task InitializeConnectionAsync(int expectedSettingsCount = 3)
  134. {
  135. await SendPreambleAsync().ConfigureAwait(false);
  136. await SendSettingsAsync();
  137. await ExpectAsync(Http2FrameType.SETTINGS,
  138. withLength: expectedSettingsCount * Http2FrameReader.SettingSize,
  139. withFlags: 0,
  140. withStreamId: 0);
  141. await ExpectAsync(Http2FrameType.WINDOW_UPDATE,
  142. withLength: 4,
  143. withFlags: 0,
  144. withStreamId: 0);
  145. await ExpectAsync(Http2FrameType.SETTINGS,
  146. withLength: 0,
  147. withFlags: (byte)Http2SettingsFrameFlags.ACK,
  148. withStreamId: 0);
  149. }
  150. public Task StartStreamAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, bool endStream)
  151. {
  152. var writableBuffer = _pair.Application.Output;
  153. var frame = new Http2Frame();
  154. frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
  155. var buffer = _headerEncodingBuffer.AsSpan();
  156. var headersEnumerator = GetHeadersEnumerator(headers);
  157. var done = HPackHeaderWriter.BeginEncodeHeaders(headersEnumerator, buffer, out var length);
  158. frame.PayloadLength = length;
  159. if (done)
  160. {
  161. frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
  162. }
  163. if (endStream)
  164. {
  165. frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
  166. }
  167. WriteHeader(frame, writableBuffer);
  168. writableBuffer.Write(buffer.Slice(0, length));
  169. while (!done)
  170. {
  171. frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
  172. done = HPackHeaderWriter.ContinueEncodeHeaders(headersEnumerator, buffer, out length);
  173. frame.PayloadLength = length;
  174. if (done)
  175. {
  176. frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
  177. }
  178. WriteHeader(frame, writableBuffer);
  179. writableBuffer.Write(buffer.Slice(0, length));
  180. }
  181. return FlushAsync(writableBuffer);
  182. }
  183. private static IEnumerator<KeyValuePair<string, string>> GetHeadersEnumerator(IEnumerable<KeyValuePair<string, string>> headers)
  184. {
  185. var headersEnumerator = headers.GetEnumerator();
  186. return headersEnumerator;
  187. }
  188. internal Dictionary<string, string> DecodeHeaders(Http2FrameWithPayload frame, bool endHeaders = false)
  189. {
  190. Assert.Equal(Http2FrameType.HEADERS, frame.Type);
  191. _hpackDecoder.Decode(frame.PayloadSequence, endHeaders, handler: this);
  192. return _decodedHeaders;
  193. }
  194. internal void ResetHeaders()
  195. {
  196. _decodedHeaders.Clear();
  197. }
  198. /* https://tools.ietf.org/html/rfc7540#section-4.1
  199. +-----------------------------------------------+
  200. | Length (24) |
  201. +---------------+---------------+---------------+
  202. | Type (8) | Flags (8) |
  203. +-+-------------+---------------+-------------------------------+
  204. |R| Stream Identifier (31) |
  205. +=+=============================================================+
  206. | Frame Payload (0...) ...
  207. +---------------------------------------------------------------+
  208. */
  209. internal static void WriteHeader(Http2Frame frame, PipeWriter output)
  210. {
  211. var buffer = output.GetSpan(Http2FrameReader.HeaderLength);
  212. Bitshifter.WriteUInt24BigEndian(buffer, (uint)frame.PayloadLength);
  213. buffer = buffer.Slice(3);
  214. buffer[0] = (byte)frame.Type;
  215. buffer[1] = frame.Flags;
  216. buffer = buffer.Slice(2);
  217. Bitshifter.WriteUInt31BigEndian(buffer, (uint)frame.StreamId, preserveHighestBit: false);
  218. output.Advance(Http2FrameReader.HeaderLength);
  219. }
  220. /* https://tools.ietf.org/html/rfc7540#section-6.2
  221. +---------------+
  222. |Pad Length? (8)|
  223. +-+-------------+-----------------------------------------------+
  224. | Header Block Fragment (*) ...
  225. +---------------------------------------------------------------+
  226. | Padding (*) ...
  227. +---------------------------------------------------------------+
  228. */
  229. public Task SendHeadersWithPaddingAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte padLength, bool endStream)
  230. {
  231. var writableBuffer = _pair.Application.Output;
  232. var frame = new Http2Frame();
  233. frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED, streamId);
  234. frame.HeadersPadLength = padLength;
  235. var extendedHeaderLength = 1; // Padding length field
  236. var buffer = _headerEncodingBuffer.AsSpan();
  237. var extendedHeader = buffer.Slice(0, extendedHeaderLength);
  238. extendedHeader[0] = padLength;
  239. var payload = buffer.Slice(extendedHeaderLength, buffer.Length - padLength - extendedHeaderLength);
  240. HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out var length);
  241. var padding = buffer.Slice(extendedHeaderLength + length, padLength);
  242. padding.Clear();
  243. frame.PayloadLength = extendedHeaderLength + length + padLength;
  244. if (endStream)
  245. {
  246. frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
  247. }
  248. WriteHeader(frame, writableBuffer);
  249. writableBuffer.Write(buffer.Slice(0, frame.PayloadLength));
  250. return FlushAsync(writableBuffer);
  251. }
  252. /* https://tools.ietf.org/html/rfc7540#section-6.2
  253. +-+-------------+-----------------------------------------------+
  254. |E| Stream Dependency? (31) |
  255. +-+-------------+-----------------------------------------------+
  256. | Weight? (8) |
  257. +-+-------------+-----------------------------------------------+
  258. | Header Block Fragment (*) ...
  259. +---------------------------------------------------------------+
  260. */
  261. public Task SendHeadersWithPriorityAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte priority, int streamDependency, bool endStream)
  262. {
  263. var writableBuffer = _pair.Application.Output;
  264. var frame = new Http2Frame();
  265. frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PRIORITY, streamId);
  266. frame.HeadersPriorityWeight = priority;
  267. frame.HeadersStreamDependency = streamDependency;
  268. var extendedHeaderLength = 5; // stream dependency + weight
  269. var buffer = _headerEncodingBuffer.AsSpan();
  270. var extendedHeader = buffer.Slice(0, extendedHeaderLength);
  271. Bitshifter.WriteUInt31BigEndian(extendedHeader, (uint)streamDependency);
  272. extendedHeader[4] = priority;
  273. var payload = buffer.Slice(extendedHeaderLength);
  274. HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out var length);
  275. frame.PayloadLength = extendedHeaderLength + length;
  276. if (endStream)
  277. {
  278. frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
  279. }
  280. WriteHeader(frame, writableBuffer);
  281. writableBuffer.Write(buffer.Slice(0, frame.PayloadLength));
  282. return FlushAsync(writableBuffer);
  283. }
  284. /* https://tools.ietf.org/html/rfc7540#section-6.2
  285. +---------------+
  286. |Pad Length? (8)|
  287. +-+-------------+-----------------------------------------------+
  288. |E| Stream Dependency? (31) |
  289. +-+-------------+-----------------------------------------------+
  290. | Weight? (8) |
  291. +-+-------------+-----------------------------------------------+
  292. | Header Block Fragment (*) ...
  293. +---------------------------------------------------------------+
  294. | Padding (*) ...
  295. +---------------------------------------------------------------+
  296. */
  297. public Task SendHeadersWithPaddingAndPriorityAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte padLength, byte priority, int streamDependency, bool endStream)
  298. {
  299. var writableBuffer = _pair.Application.Output;
  300. var frame = new Http2Frame();
  301. frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED | Http2HeadersFrameFlags.PRIORITY, streamId);
  302. frame.HeadersPadLength = padLength;
  303. frame.HeadersPriorityWeight = priority;
  304. frame.HeadersStreamDependency = streamDependency;
  305. var extendedHeaderLength = 6; // pad length + stream dependency + weight
  306. var buffer = _headerEncodingBuffer.AsSpan();
  307. var extendedHeader = buffer.Slice(0, extendedHeaderLength);
  308. extendedHeader[0] = padLength;
  309. Bitshifter.WriteUInt31BigEndian(extendedHeader.Slice(1), (uint)streamDependency);
  310. extendedHeader[5] = priority;
  311. var payload = buffer.Slice(extendedHeaderLength, buffer.Length - padLength - extendedHeaderLength);
  312. HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out var length);
  313. var padding = buffer.Slice(extendedHeaderLength + length, padLength);
  314. padding.Clear();
  315. frame.PayloadLength = extendedHeaderLength + length + padLength;
  316. if (endStream)
  317. {
  318. frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
  319. }
  320. WriteHeader(frame, writableBuffer);
  321. writableBuffer.Write(buffer.Slice(0, frame.PayloadLength));
  322. return FlushAsync(writableBuffer);
  323. }
  324. public Task SendAsync(ReadOnlySpan<byte> span)
  325. {
  326. var writableBuffer = _pair.Application.Output;
  327. writableBuffer.Write(span);
  328. return FlushAsync(writableBuffer);
  329. }
  330. public static async Task FlushAsync(PipeWriter writableBuffer)
  331. {
  332. await writableBuffer.FlushAsync().AsTask().DefaultTimeout();
  333. }
  334. public Task SendPreambleAsync() => SendAsync(ClientPreface);
  335. public async Task SendSettingsAsync()
  336. {
  337. var writableBuffer = _pair.Application.Output;
  338. var frame = new Http2Frame();
  339. frame.PrepareSettings(Http2SettingsFrameFlags.NONE);
  340. var settings = _clientSettings.GetNonProtocolDefaults();
  341. var payload = new byte[settings.Count * Http2FrameReader.SettingSize];
  342. frame.PayloadLength = payload.Length;
  343. WriteSettings(settings, payload);
  344. WriteHeader(frame, writableBuffer);
  345. await SendAsync(payload);
  346. }
  347. internal static void WriteSettings(IList<Http2PeerSetting> settings, Span<byte> destination)
  348. {
  349. foreach (var setting in settings)
  350. {
  351. BinaryPrimitives.WriteUInt16BigEndian(destination, (ushort)setting.Parameter);
  352. BinaryPrimitives.WriteUInt32BigEndian(destination.Slice(2), setting.Value);
  353. destination = destination.Slice(Http2FrameReader.SettingSize);
  354. }
  355. }
  356. public async Task SendSettingsAckWithInvalidLengthAsync(int length)
  357. {
  358. var writableBuffer = _pair.Application.Output;
  359. var frame = new Http2Frame();
  360. frame.PrepareSettings(Http2SettingsFrameFlags.ACK);
  361. frame.PayloadLength = length;
  362. WriteHeader(frame, writableBuffer);
  363. await SendAsync(new byte[length]);
  364. }
  365. public async Task SendSettingsWithInvalidStreamIdAsync(int streamId)
  366. {
  367. var writableBuffer = _pair.Application.Output;
  368. var frame = new Http2Frame();
  369. frame.PrepareSettings(Http2SettingsFrameFlags.NONE);
  370. frame.StreamId = streamId;
  371. var settings = _clientSettings.GetNonProtocolDefaults();
  372. var payload = new byte[settings.Count * Http2FrameReader.SettingSize];
  373. frame.PayloadLength = payload.Length;
  374. WriteSettings(settings, payload);
  375. WriteHeader(frame, writableBuffer);
  376. await SendAsync(payload);
  377. }
  378. public async Task SendSettingsWithInvalidLengthAsync(int length)
  379. {
  380. var writableBuffer = _pair.Application.Output;
  381. var frame = new Http2Frame();
  382. frame.PrepareSettings(Http2SettingsFrameFlags.NONE);
  383. frame.PayloadLength = length;
  384. var payload = new byte[length];
  385. WriteHeader(frame, writableBuffer);
  386. await SendAsync(payload);
  387. }
  388. internal async Task SendSettingsWithInvalidParameterValueAsync(Http2SettingsParameter parameter, uint value)
  389. {
  390. var writableBuffer = _pair.Application.Output;
  391. var frame = new Http2Frame();
  392. frame.PrepareSettings(Http2SettingsFrameFlags.NONE);
  393. frame.PayloadLength = 6;
  394. var payload = new byte[Http2FrameReader.SettingSize];
  395. payload[0] = (byte)((ushort)parameter >> 8);
  396. payload[1] = (byte)(ushort)parameter;
  397. payload[2] = (byte)(value >> 24);
  398. payload[3] = (byte)(value >> 16);
  399. payload[4] = (byte)(value >> 8);
  400. payload[5] = (byte)value;
  401. WriteHeader(frame, writableBuffer);
  402. await SendAsync(payload);
  403. }
  404. public Task SendPushPromiseFrameAsync()
  405. {
  406. var writableBuffer = _pair.Application.Output;
  407. var frame = new Http2Frame();
  408. frame.PayloadLength = 0;
  409. frame.Type = Http2FrameType.PUSH_PROMISE;
  410. frame.StreamId = 1;
  411. WriteHeader(frame, writableBuffer);
  412. return FlushAsync(writableBuffer);
  413. }
  414. internal async Task<bool> SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, IEnumerable<KeyValuePair<string, string>> headers)
  415. {
  416. var outputWriter = _pair.Application.Output;
  417. var frame = new Http2Frame();
  418. frame.PrepareHeaders(flags, streamId);
  419. var buffer = _headerEncodingBuffer.AsMemory();
  420. var done = HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), buffer.Span, out var length);
  421. frame.PayloadLength = length;
  422. WriteHeader(frame, outputWriter);
  423. await SendAsync(buffer.Span.Slice(0, length));
  424. return done;
  425. }
  426. internal async Task SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, byte[] headerBlock)
  427. {
  428. var outputWriter = _pair.Application.Output;
  429. var frame = new Http2Frame();
  430. frame.PrepareHeaders(flags, streamId);
  431. frame.PayloadLength = headerBlock.Length;
  432. WriteHeader(frame, outputWriter);
  433. await SendAsync(headerBlock);
  434. }
  435. public async Task SendInvalidHeadersFrameAsync(int streamId, int payloadLength, byte padLength)
  436. {
  437. Assert.True(padLength >= payloadLength, $"{nameof(padLength)} must be greater than or equal to {nameof(payloadLength)} to create an invalid frame.");
  438. var outputWriter = _pair.Application.Output;
  439. var frame = new Http2Frame();
  440. frame.PrepareHeaders(Http2HeadersFrameFlags.PADDED, streamId);
  441. frame.PayloadLength = payloadLength;
  442. var payload = new byte[payloadLength];
  443. if (payloadLength > 0)
  444. {
  445. payload[0] = padLength;
  446. }
  447. WriteHeader(frame, outputWriter);
  448. await SendAsync(payload);
  449. }
  450. public async Task SendIncompleteHeadersFrameAsync(int streamId)
  451. {
  452. var outputWriter = _pair.Application.Output;
  453. var frame = new Http2Frame();
  454. frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS, streamId);
  455. frame.PayloadLength = 3;
  456. var payload = new byte[3];
  457. // Set up an incomplete Literal Header Field w/ Incremental Indexing frame,
  458. // with an incomplete new name
  459. payload[0] = 0;
  460. payload[1] = 2;
  461. payload[2] = (byte)'a';
  462. WriteHeader(frame, outputWriter);
  463. await SendAsync(payload);
  464. }
  465. internal async Task<bool> SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags, IEnumerator<KeyValuePair<string, string>> headersEnumerator)
  466. {
  467. var outputWriter = _pair.Application.Output;
  468. var frame = new Http2Frame();
  469. frame.PrepareContinuation(flags, streamId);
  470. var buffer = _headerEncodingBuffer.AsMemory();
  471. var done = HPackHeaderWriter.ContinueEncodeHeaders(headersEnumerator, buffer.Span, out var length);
  472. frame.PayloadLength = length;
  473. WriteHeader(frame, outputWriter);
  474. await SendAsync(buffer.Span.Slice(0, length));
  475. return done;
  476. }
  477. internal async Task SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags, byte[] payload)
  478. {
  479. var outputWriter = _pair.Application.Output;
  480. var frame = new Http2Frame();
  481. frame.PrepareContinuation(flags, streamId);
  482. frame.PayloadLength = payload.Length;
  483. WriteHeader(frame, outputWriter);
  484. await SendAsync(payload);
  485. }
  486. internal async Task<bool> SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags, IEnumerable<KeyValuePair<string, string>> headers)
  487. {
  488. var outputWriter = _pair.Application.Output;
  489. var frame = new Http2Frame();
  490. frame.PrepareContinuation(flags, streamId);
  491. var buffer = _headerEncodingBuffer.AsMemory();
  492. var done = HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), buffer.Span, out var length);
  493. frame.PayloadLength = length;
  494. WriteHeader(frame, outputWriter);
  495. await SendAsync(buffer.Span.Slice(0, length));
  496. return done;
  497. }
  498. internal Task SendEmptyContinuationFrameAsync(int streamId, Http2ContinuationFrameFlags flags)
  499. {
  500. var outputWriter = _pair.Application.Output;
  501. var frame = new Http2Frame();
  502. frame.PrepareContinuation(flags, streamId);
  503. frame.PayloadLength = 0;
  504. WriteHeader(frame, outputWriter);
  505. return FlushAsync(outputWriter);
  506. }
  507. public async Task SendIncompleteContinuationFrameAsync(int streamId)
  508. {
  509. var outputWriter = _pair.Application.Output;
  510. var frame = new Http2Frame();
  511. frame.PrepareContinuation(Http2ContinuationFrameFlags.END_HEADERS, streamId);
  512. frame.PayloadLength = 3;
  513. var payload = new byte[3];
  514. // Set up an incomplete Literal Header Field w/ Incremental Indexing frame,
  515. // with an incomplete new name
  516. payload[0] = 0;
  517. payload[1] = 2;
  518. payload[2] = (byte)'a';
  519. WriteHeader(frame, outputWriter);
  520. await SendAsync(payload);
  521. }
  522. public Task SendDataAsync(int streamId, Memory<byte> data, bool endStream)
  523. {
  524. var outputWriter = _pair.Application.Output;
  525. var frame = new Http2Frame();
  526. frame.PrepareData(streamId);
  527. frame.PayloadLength = data.Length;
  528. frame.DataFlags = endStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE;
  529. WriteHeader(frame, outputWriter);
  530. return SendAsync(data.Span);
  531. }
  532. public async Task SendDataWithPaddingAsync(int streamId, Memory<byte> data, byte padLength, bool endStream)
  533. {
  534. var outputWriter = _pair.Application.Output;
  535. var frame = new Http2Frame();
  536. frame.PrepareData(streamId, padLength);
  537. frame.PayloadLength = data.Length + 1 + padLength;
  538. if (endStream)
  539. {
  540. frame.DataFlags |= Http2DataFrameFlags.END_STREAM;
  541. }
  542. WriteHeader(frame, outputWriter);
  543. outputWriter.GetSpan(1)[0] = padLength;
  544. outputWriter.Advance(1);
  545. await SendAsync(data.Span);
  546. await SendAsync(new byte[padLength]);
  547. }
  548. public Task SendInvalidDataFrameAsync(int streamId, int frameLength, byte padLength)
  549. {
  550. Assert.True(padLength >= frameLength, $"{nameof(padLength)} must be greater than or equal to {nameof(frameLength)} to create an invalid frame.");
  551. var outputWriter = _pair.Application.Output;
  552. var frame = new Http2Frame();
  553. frame.PrepareData(streamId);
  554. frame.DataFlags = Http2DataFrameFlags.PADDED;
  555. frame.PayloadLength = frameLength;
  556. var payload = new byte[frameLength];
  557. if (frameLength > 0)
  558. {
  559. payload[0] = padLength;
  560. }
  561. WriteHeader(frame, outputWriter);
  562. return SendAsync(payload);
  563. }
  564. internal Task SendPingAsync(Http2PingFrameFlags flags)
  565. {
  566. var outputWriter = _pair.Application.Output;
  567. var pingFrame = new Http2Frame();
  568. pingFrame.PreparePing(flags);
  569. WriteHeader(pingFrame, outputWriter);
  570. return SendAsync(new byte[8]); // Empty payload
  571. }
  572. public Task SendPingWithInvalidLengthAsync(int length)
  573. {
  574. var outputWriter = _pair.Application.Output;
  575. var pingFrame = new Http2Frame();
  576. pingFrame.PreparePing(Http2PingFrameFlags.NONE);
  577. pingFrame.PayloadLength = length;
  578. WriteHeader(pingFrame, outputWriter);
  579. return SendAsync(new byte[length]);
  580. }
  581. public Task SendPingWithInvalidStreamIdAsync(int streamId)
  582. {
  583. Assert.NotEqual(0, streamId);
  584. var outputWriter = _pair.Application.Output;
  585. var pingFrame = new Http2Frame();
  586. pingFrame.PreparePing(Http2PingFrameFlags.NONE);
  587. pingFrame.StreamId = streamId;
  588. WriteHeader(pingFrame, outputWriter);
  589. return SendAsync(new byte[pingFrame.PayloadLength]);
  590. }
  591. /* https://tools.ietf.org/html/rfc7540#section-6.3
  592. +-+-------------------------------------------------------------+
  593. |E| Stream Dependency (31) |
  594. +-+-------------+-----------------------------------------------+
  595. | Weight (8) |
  596. +-+-------------+
  597. */
  598. public Task SendPriorityAsync(int streamId, int streamDependency = 0)
  599. {
  600. var outputWriter = _pair.Application.Output;
  601. var priorityFrame = new Http2Frame();
  602. priorityFrame.PreparePriority(streamId, streamDependency: streamDependency, exclusive: false, weight: 0);
  603. var payload = new byte[priorityFrame.PayloadLength].AsSpan();
  604. Bitshifter.WriteUInt31BigEndian(payload, (uint)streamDependency);
  605. payload[4] = 0; // Weight
  606. WriteHeader(priorityFrame, outputWriter);
  607. return SendAsync(payload);
  608. }
  609. public Task SendInvalidPriorityFrameAsync(int streamId, int length)
  610. {
  611. var outputWriter = _pair.Application.Output;
  612. var priorityFrame = new Http2Frame();
  613. priorityFrame.PreparePriority(streamId, streamDependency: 0, exclusive: false, weight: 0);
  614. priorityFrame.PayloadLength = length;
  615. WriteHeader(priorityFrame, outputWriter);
  616. return SendAsync(new byte[length]);
  617. }
  618. /* https://tools.ietf.org/html/rfc7540#section-6.4
  619. +---------------------------------------------------------------+
  620. | Error Code (32) |
  621. +---------------------------------------------------------------+
  622. */
  623. public Task SendRstStreamAsync(int streamId)
  624. {
  625. var outputWriter = _pair.Application.Output;
  626. var rstStreamFrame = new Http2Frame();
  627. rstStreamFrame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
  628. var payload = new byte[rstStreamFrame.PayloadLength];
  629. BinaryPrimitives.WriteUInt32BigEndian(payload, (uint)Http2ErrorCode.CANCEL);
  630. WriteHeader(rstStreamFrame, outputWriter);
  631. return SendAsync(payload);
  632. }
  633. public Task SendInvalidRstStreamFrameAsync(int streamId, int length)
  634. {
  635. var outputWriter = _pair.Application.Output;
  636. var frame = new Http2Frame();
  637. frame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
  638. frame.PayloadLength = length;
  639. WriteHeader(frame, outputWriter);
  640. return SendAsync(new byte[length]);
  641. }
  642. public Task SendGoAwayAsync()
  643. {
  644. var outputWriter = _pair.Application.Output;
  645. var frame = new Http2Frame();
  646. frame.PrepareGoAway(0, Http2ErrorCode.NO_ERROR);
  647. WriteHeader(frame, outputWriter);
  648. return SendAsync(new byte[frame.PayloadLength]);
  649. }
  650. public Task SendInvalidGoAwayFrameAsync()
  651. {
  652. var outputWriter = _pair.Application.Output;
  653. var frame = new Http2Frame();
  654. frame.PrepareGoAway(0, Http2ErrorCode.NO_ERROR);
  655. frame.StreamId = 1;
  656. WriteHeader(frame, outputWriter);
  657. return SendAsync(new byte[frame.PayloadLength]);
  658. }
  659. public Task SendWindowUpdateAsync(int streamId, int sizeIncrement)
  660. {
  661. var outputWriter = _pair.Application.Output;
  662. var frame = new Http2Frame();
  663. frame.PrepareWindowUpdate(streamId, sizeIncrement);
  664. WriteHeader(frame, outputWriter);
  665. var buffer = outputWriter.GetSpan(4);
  666. BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)sizeIncrement);
  667. outputWriter.Advance(4);
  668. return FlushAsync(outputWriter);
  669. }
  670. public Task SendInvalidWindowUpdateAsync(int streamId, int sizeIncrement, int length)
  671. {
  672. var outputWriter = _pair.Application.Output;
  673. var frame = new Http2Frame();
  674. frame.PrepareWindowUpdate(streamId, sizeIncrement);
  675. frame.PayloadLength = length;
  676. WriteHeader(frame, outputWriter);
  677. return SendAsync(new byte[length]);
  678. }
  679. public Task SendUnknownFrameTypeAsync(int streamId, int frameType)
  680. {
  681. var outputWriter = _pair.Application.Output;
  682. var frame = new Http2Frame();
  683. frame.StreamId = streamId;
  684. frame.Type = (Http2FrameType)frameType;
  685. frame.PayloadLength = 0;
  686. WriteHeader(frame, outputWriter);
  687. return FlushAsync(outputWriter);
  688. }
  689. internal async Task<Http2FrameWithPayload> ReceiveFrameAsync(uint maxFrameSize = Http2PeerSettings.DefaultMaxFrameSize)
  690. {
  691. var frame = new Http2FrameWithPayload();
  692. while (true)
  693. {
  694. var result = await _pair.Application.Input.ReadAsync().AsTask().DefaultTimeout();
  695. var buffer = result.Buffer;
  696. var consumed = buffer.Start;
  697. var examined = buffer.Start;
  698. try
  699. {
  700. Assert.True(buffer.Length > 0);
  701. if (Http2FrameReader.TryReadFrame(ref buffer, frame, maxFrameSize, out var framePayload))
  702. {
  703. consumed = examined = framePayload.End;
  704. frame.Payload = framePayload.ToArray();
  705. return frame;
  706. }
  707. else
  708. {
  709. examined = buffer.End;
  710. }
  711. if (result.IsCompleted)
  712. {
  713. throw new IOException("The reader completed without returning a frame.");
  714. }
  715. }
  716. finally
  717. {
  718. _bytesReceived += buffer.Slice(buffer.Start, consumed).Length;
  719. _pair.Application.Input.AdvanceTo(consumed, examined);
  720. }
  721. }
  722. }
  723. internal async Task<Http2FrameWithPayload> ExpectAsync(Http2FrameType type, int withLength, byte withFlags, int withStreamId)
  724. {
  725. var frame = await ReceiveFrameAsync((uint)withLength);
  726. Assert.Equal(type, frame.Type);
  727. Assert.Equal(withLength, frame.PayloadLength);
  728. Assert.Equal(withFlags, frame.Flags);
  729. Assert.Equal(withStreamId, frame.StreamId);
  730. return frame;
  731. }
  732. public async Task StopConnectionAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
  733. {
  734. await SendGoAwayAsync();
  735. await WaitForConnectionStopAsync(expectedLastStreamId, ignoreNonGoAwayFrames);
  736. _pair.Application.Output.Complete();
  737. }
  738. public Task WaitForConnectionStopAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
  739. {
  740. return WaitForConnectionErrorAsync<Exception>(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR);
  741. }
  742. internal Task ReceiveHeadersAsync(int expectedStreamId, Action<IDictionary<string, string>> verifyHeaders = null)
  743. => ReceiveHeadersAsync(expectedStreamId, endStream: false, verifyHeaders);
  744. internal async Task ReceiveHeadersAsync(int expectedStreamId, bool endStream = false, Action<IDictionary<string, string>> verifyHeaders = null)
  745. {
  746. var headersFrame = await ReceiveFrameAsync();
  747. Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
  748. Assert.Equal(expectedStreamId, headersFrame.StreamId);
  749. Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0);
  750. Assert.Equal(endStream, (headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) != 0);
  751. Logger.LogInformation("Received headers in a single frame.");
  752. ResetHeaders();
  753. DecodeHeaders(headersFrame);
  754. verifyHeaders?.Invoke(_decodedHeaders);
  755. }
  756. internal static void VerifyDataFrame(Http2Frame frame, int expectedStreamId, bool endOfStream, int length)
  757. {
  758. Assert.Equal(Http2FrameType.DATA, frame.Type);
  759. Assert.Equal(expectedStreamId, frame.StreamId);
  760. Assert.Equal(endOfStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE, frame.DataFlags);
  761. Assert.Equal(length, frame.PayloadLength);
  762. }
  763. internal void VerifyGoAway(Http2Frame frame, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
  764. {
  765. Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
  766. Assert.Equal(8, frame.PayloadLength);
  767. Assert.Equal(0, frame.Flags);
  768. Assert.Equal(0, frame.StreamId);
  769. Assert.Equal(expectedLastStreamId, frame.GoAwayLastStreamId);
  770. Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
  771. }
  772. internal static void VerifyResetFrame(Http2Frame frame, int expectedStreamId, Http2ErrorCode expectedErrorCode)
  773. {
  774. Assert.Equal(Http2FrameType.RST_STREAM, frame.Type);
  775. Assert.Equal(expectedStreamId, frame.StreamId);
  776. Assert.Equal(expectedErrorCode, frame.RstStreamErrorCode);
  777. Assert.Equal(4, frame.PayloadLength);
  778. Assert.Equal(0, frame.Flags);
  779. }
  780. internal async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
  781. where TException : Exception
  782. {
  783. await WaitForConnectionErrorAsyncDoNotCloseTransport<TException>(ignoreNonGoAwayFrames, expectedLastStreamId, expectedErrorCode);
  784. _pair.Application.Output.Complete();
  785. }
  786. internal async Task WaitForConnectionErrorAsyncDoNotCloseTransport<TException>(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
  787. where TException : Exception
  788. {
  789. var frame = await ReceiveFrameAsync();
  790. if (ignoreNonGoAwayFrames)
  791. {
  792. while (frame.Type != Http2FrameType.GOAWAY)
  793. {
  794. frame = await ReceiveFrameAsync();
  795. }
  796. }
  797. VerifyGoAway(frame, expectedLastStreamId, expectedErrorCode);
  798. }
  799. internal async Task WaitForStreamErrorAsync(int expectedStreamId, Http2ErrorCode expectedErrorCode)
  800. {
  801. var frame = await ReceiveFrameAsync();
  802. Assert.Equal(Http2FrameType.RST_STREAM, frame.Type);
  803. Assert.Equal(4, frame.PayloadLength);
  804. Assert.Equal(0, frame.Flags);
  805. Assert.Equal(expectedStreamId, frame.StreamId);
  806. Assert.Equal(expectedErrorCode, frame.RstStreamErrorCode);
  807. }
  808. public void OnStaticIndexedHeader(int index)
  809. {
  810. ref readonly var entry = ref H2StaticTable.Get(index - 1);
  811. ((IHttpHeadersHandler)this).OnHeader(entry.Name, entry.Value);
  812. }
  813. public void OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
  814. {
  815. ((IHttpHeadersHandler)this).OnHeader(H2StaticTable.Get(index - 1).Name, value);
  816. }
  817. internal class Http2FrameWithPayload : Http2Frame
  818. {
  819. public Http2FrameWithPayload() : base()
  820. {
  821. }
  822. // This does not contain extended headers
  823. public Memory<byte> Payload { get; set; }
  824. public ReadOnlySequence<byte> PayloadSequence => new ReadOnlySequence<byte>(Payload);
  825. }
  826. private static class Assert
  827. {
  828. public static void True(bool condition, string message = "")
  829. {
  830. if (!condition)
  831. {
  832. throw new Exception($"Assert.True failed: '{message}'");
  833. }
  834. }
  835. public static void Equal<T>(T expected, T actual)
  836. {
  837. if (!expected.Equals(actual))
  838. {
  839. throw new Exception($"Assert.Equal('{expected}', '{actual}') failed");
  840. }
  841. }
  842. public static void Equal(string expected, string actual, bool ignoreCase = false)
  843. {
  844. if (!expected.Equals(actual, ignoreCase ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))
  845. {
  846. throw new Exception($"Assert.Equal('{expected}', '{actual}') failed");
  847. }
  848. }
  849. public static void NotEqual<T>(T value1, T value2)
  850. {
  851. if (value1.Equals(value2))
  852. {
  853. throw new Exception($"Assert.NotEqual('{value1}', '{value2}') failed");
  854. }
  855. }
  856. public static void Contains<T>(IEnumerable<T> collection, T value)
  857. {
  858. if (!collection.Contains(value))
  859. {
  860. throw new Exception($"Assert.Contains(collection, '{value}') failed");
  861. }
  862. }
  863. }
  864. }
  865. }