uhpacktest1.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. (*
  2. * Test program for pascal HPack for http2
  3. *
  4. * This test code uses sample headers from https://github.com/http2jp/hpack-test-case
  5. * to test decoding of available samples and then reencode and decode again
  6. * using plain only, indexing only, huffman only, and both at same time.
  7. *
  8. * The JSON parsing adds around a 15% speed penalty.
  9. *
  10. *)
  11. unit uhpacktest1;
  12. {$mode objfpc}{$H+}
  13. {$DEFINE QUIET}
  14. {$DEFINE FULL_QUIET}
  15. {$IFDEF FULL_QUIET}
  16. {$DEFINE QUIET}
  17. {$ENDIF}
  18. interface
  19. uses
  20. Classes, SysUtils, fpcunit, testregistry, uhpack, fpjson, jsonparser, jsonscanner;
  21. type
  22. { THPackTestCaseCycle }
  23. THPackTestCaseCycle= class(TTestCase)
  24. private
  25. HPDecoder: THPackDecoder;
  26. HPIntfDecoderPlain: THPackDecoder;
  27. HPIntfDecoderPlainIndexed: THPackDecoder;
  28. HPIntfDecoderHuffman: THPackDecoder;
  29. HPIntfDecoderHuffmanIndexed: THPackDecoder;
  30. HPIntfEncoderPlain: THPackEncoder;
  31. HPIntfEncoderPlainIndexed: THPackEncoder;
  32. HPIntfEncoderHuffman: THPackEncoder;
  33. HPIntfEncoderHuffmanIndexed: THPackEncoder;
  34. SequenceCounter: integer;
  35. StoryCounter: integer;
  36. GroupsCounter: integer;
  37. WireBytes: integer;
  38. DecodedBytes: integer;
  39. procedure TestThisSequence(const aGroup: integer; const aStory: integer; const aJSon: TJSONData);
  40. procedure TestCaseStory(const aGroup: integer; const aStory: integer; const aJSon: TJSONData);
  41. procedure RunSampleHeadersTest;
  42. protected
  43. function GetTestName: string; override;
  44. published
  45. procedure TestHookUp;
  46. end;
  47. { THPackTestDecoder }
  48. THPackTestDecoder= class(TTestCase)
  49. private
  50. HPDecoder: THPackDecoder;
  51. DummyDecoder: THPackDecoder;
  52. DummyEncoder: THPackEncoder;
  53. protected
  54. procedure SetUp; override;
  55. procedure TearDown; override;
  56. published
  57. procedure VerifyIncompleteIndexRead;
  58. procedure InvalidTableIndexZero;
  59. procedure IndexShiftOverflow;
  60. procedure DynamicTableSizeUpdate;
  61. procedure DynamicTableSizeUpdateRequired;
  62. procedure IllegalDynamicTableSizeUpdate;
  63. procedure MaxDynamicTableSizeSignOverflow;
  64. procedure ReduceMaxDynamicTableSize;
  65. procedure TooLargeDynamicTableSizeUpdate;
  66. procedure MissingDynamicTableSizeUpdate;
  67. procedure LiteralWithIncrementalIndexingWithEmptyName;
  68. procedure LiteralWithIncrementalIndexingCompleteEviction;
  69. procedure LiteralWithIncrementalIndexingWithLargeName;
  70. procedure LiteralWithIncrementalIndexingWithLargeValue;
  71. procedure LiteralWithoutIndexingWithEmptyName;
  72. procedure LiteralWithoutIndexingWithLargeName;
  73. procedure LiteralWithoutIndexingWithLargeValue;
  74. procedure LiteralNeverIndexedWithEmptyName;
  75. procedure LiteralNeverIndexedWithLargeName;
  76. procedure LiteralNeverIndexedWithLargeValue;
  77. end;
  78. implementation
  79. function HexToBinString(aHex: RawByteString): RawByteString;
  80. var
  81. j: integer;
  82. t: integer;
  83. begin
  84. t:=0;
  85. for j := 1 to Length(aHex) do begin
  86. if (aHex[j] in ['a'..'f','A'..'F','0'..'9']) then begin
  87. inc(t);
  88. if t<>j then begin
  89. aHex[t]:=aHex[j];
  90. end;
  91. end else begin
  92. if (aHex[j]<>#32) and (aHex[j]<>'-') then begin
  93. Raise Exception.Create('Internal: Invalid hex format character');
  94. end;
  95. end;
  96. end;
  97. if t<>j then SetLength(aHex,t);
  98. if t mod 2 <>0 then begin
  99. Raise Exception.Create('Internal: Invalid hex chars count (odd)');
  100. end;
  101. SetLength(Result,Length(aHex) div 2);
  102. HexToBin(@aHex[1],@Result[1],Length(Result));
  103. end;
  104. function BinStringToHex(const aBinString: string): string;
  105. begin
  106. Result:='';
  107. SetLength(Result,Length(aBinString)*2);
  108. BinToHex(@aBinString[1],@Result[1],Length(aBinString));
  109. end;
  110. function ErrorHeader(const aString: string): string;
  111. begin
  112. if Length(aString)<38 then begin
  113. Result:='**'+aString+StringOfChar('*',38-Length(aString));
  114. end else begin
  115. Result:='**'+aString+'**';
  116. end;
  117. end;
  118. { THPackTestDecoder }
  119. procedure THPackTestDecoder.SetUp;
  120. begin
  121. //Setup 2 dummy encoder & decoder to avoid multiple
  122. //creation of internal tables. This should be fixed some
  123. //way in the future.
  124. DummyDecoder:=THPackDecoder.Create;
  125. DummyEncoder:=THPackEncoder.Create;
  126. inherited SetUp;
  127. end;
  128. procedure THPackTestDecoder.TearDown;
  129. begin
  130. FreeAndNil(DummyEncoder);
  131. FreeAndNil(DummyDecoder);
  132. inherited TearDown;
  133. end;
  134. procedure THPackTestDecoder.VerifyIncompleteIndexRead;
  135. var
  136. Data: TStringStream;
  137. begin
  138. Data:=TStringStream.Create(HexToBinString('FFF0'));
  139. HPDecoder:=THPackDecoder.Create;
  140. try
  141. HPDecoder.Decode(Data);
  142. AssertEquals(Data.Size-Data.Position,1);
  143. HPDecoder.Decode(Data);
  144. AssertEquals(Data.Size-Data.Position,1);
  145. finally
  146. Data.Free;
  147. FreeAndNil(HPDecoder);
  148. end;
  149. end;
  150. procedure THPackTestDecoder.InvalidTableIndexZero;
  151. begin
  152. HPDecoder:=THPackDecoder.Create;
  153. try
  154. try
  155. HPDecoder.Decode(HexToBinString('80'));
  156. FAIL('Exception missing');
  157. except
  158. on e: Exception do begin
  159. if not (e is THPACKException) then begin
  160. Raise;
  161. end;
  162. end;
  163. end;
  164. finally
  165. FreeAndNil(HPDecoder);
  166. end;
  167. end;
  168. procedure THPackTestDecoder.IndexShiftOverflow;
  169. begin
  170. HPDecoder:=THPackDecoder.Create;
  171. try
  172. try
  173. HPDecoder.Decode(HexToBinString('FF8080808008'));
  174. FAIL('Exception missing');
  175. except
  176. on e: Exception do begin
  177. if not (e is THPACKException) then begin
  178. Raise;
  179. end;
  180. end;
  181. end;
  182. finally
  183. FreeAndNil(HPDecoder);
  184. end;
  185. end;
  186. procedure THPackTestDecoder.DynamicTableSizeUpdate;
  187. begin
  188. HPDecoder:=THPackDecoder.Create;
  189. try
  190. HPDecoder.Decode(HexToBinString('20'));
  191. AssertEquals(0,HPDecoder.GetMaxHeaderTableSize);
  192. HPDecoder.Decode(HexToBinString('3FE11F'));
  193. assertEquals(4096, HPDecoder.GetMaxHeaderTableSize);
  194. finally
  195. FreeAndNil(HPDecoder);
  196. end;
  197. end;
  198. procedure THPackTestDecoder.DynamicTableSizeUpdateRequired;
  199. begin
  200. HPDecoder:=THPackDecoder.Create;
  201. try
  202. HPDecoder.SetMaxHeaderTableSize(32);
  203. HPDecoder.Decode(HexToBinString('3F00'));
  204. assertEquals(31, HPDecoder.GetMaxHeaderTableSize);
  205. finally
  206. FreeAndNil(HPDecoder);
  207. end;
  208. end;
  209. procedure THPackTestDecoder.IllegalDynamicTableSizeUpdate;
  210. begin
  211. HPDecoder:=THPackDecoder.Create;
  212. try
  213. try
  214. HPDecoder.Decode(HexToBinString('3FE21F'));
  215. FAIL('Exception missing');
  216. except
  217. on e: Exception do begin
  218. if not (e is THPACKException) then begin
  219. raise;
  220. end;
  221. end;
  222. end;
  223. finally
  224. FreeAndNil(HPDecoder);
  225. end;
  226. end;
  227. procedure THPackTestDecoder.MaxDynamicTableSizeSignOverflow;
  228. begin
  229. HPDecoder:=THPackDecoder.Create;
  230. try
  231. try
  232. HPDecoder.Decode(HexToBinString('3FE1FFFFFF07'));
  233. except
  234. on e: Exception do begin
  235. if not (e is THPACKException) then begin
  236. raise;
  237. end;
  238. end;
  239. end;
  240. finally
  241. FreeAndNil(HPDecoder);
  242. end;
  243. end;
  244. procedure THPackTestDecoder.ReduceMaxDynamicTableSize;
  245. begin
  246. HPDecoder:=THPackDecoder.Create;
  247. try
  248. HPDecoder.SetMaxHeaderTableSize(0);
  249. AssertEquals(0, HPDecoder.GetMaxHeaderTableSize());
  250. HPDecoder.Decode(HexToBinString('2081'));
  251. AssertEquals(0, HPDecoder.GetMaxHeaderTableSize());
  252. finally
  253. FreeAndNil(HPDecoder);
  254. end;
  255. end;
  256. procedure THPackTestDecoder.TooLargeDynamicTableSizeUpdate;
  257. begin
  258. HPDecoder:=THPackDecoder.Create;
  259. try
  260. HPDecoder.SetMaxHeaderTableSize(0);
  261. AssertEquals(0, HPDecoder.GetMaxHeaderTableSize());
  262. try
  263. HPDecoder.Decode(HexToBinString('21'));
  264. FAIL('Exception missing');
  265. except
  266. on E:Exception do begin
  267. if not (e is THPACKException) then begin
  268. raise;
  269. end;
  270. end;
  271. end;
  272. finally
  273. FreeAndNil(HPDecoder);
  274. end;
  275. end;
  276. procedure THPackTestDecoder.MissingDynamicTableSizeUpdate;
  277. begin
  278. HPDecoder:=THPackDecoder.Create;
  279. try
  280. HPDecoder.SetMaxHeaderTableSize(0);
  281. AssertEquals(0, HPDecoder.GetMaxHeaderTableSize());
  282. try
  283. HPDecoder.Decode(HexToBinString('81'));
  284. FAIL('Exception missing');
  285. except
  286. on E:Exception do begin
  287. if not (e is THPACKException) then begin
  288. raise;
  289. end;
  290. end;
  291. end;
  292. finally
  293. FreeAndNil(HPDecoder);
  294. end;
  295. end;
  296. procedure THPackTestDecoder.LiteralWithIncrementalIndexingWithEmptyName;
  297. begin
  298. HPDecoder:=THPackDecoder.Create;
  299. try
  300. try
  301. HPDecoder.Decode(HexToBinString('000005')+'value');
  302. FAIL('Exception missing');
  303. except
  304. on E:Exception do begin
  305. if not (e is THPACKException) then begin
  306. raise;
  307. end;
  308. end;
  309. end;
  310. finally
  311. FreeAndNil(HPDecoder);
  312. end;
  313. end;
  314. procedure THPackTestDecoder.LiteralWithIncrementalIndexingCompleteEviction;
  315. begin
  316. HPDecoder:=THPackDecoder.Create;
  317. try
  318. HPDecoder.Decode(HexToBinString('4004')+'name'+HexToBinString('05')+'value');
  319. AssertFalse(HPDecoder.EndHeaderBlockTruncated);
  320. HPDecoder.Decode(HexToBinString('417F811F')+StringOfChar('a',4096));
  321. AssertFalse(HPDecoder.EndHeaderBlockTruncated);
  322. HPDecoder.Decode(HexToBinString('4004')+'name'+ HexToBinString('05')+'value'+HexToBinString('BE'));
  323. AssertEquals('name',HPDecoder.DecodedHeaders[0]^.HeaderName);
  324. AssertEquals('value',HPDecoder.DecodedHeaders[0]^.HeaderValue);
  325. AssertEquals('name',HPDecoder.DecodedHeaders[1]^.HeaderName);
  326. AssertEquals('value',HPDecoder.DecodedHeaders[1]^.HeaderValue);
  327. finally
  328. FreeAndNil(HPDecoder);
  329. end;
  330. end;
  331. procedure THPackTestDecoder.LiteralWithIncrementalIndexingWithLargeName;
  332. begin
  333. HPDecoder:=THPackDecoder.Create;
  334. try
  335. HPDecoder.Decode(HexToBinString('417F811F')+StringOfChar('a',16384)+HexToBinString('00'));
  336. // Verify header block is reported as truncated
  337. AssertTrue(HPDecoder.EndHeaderBlockTruncated);
  338. // Verify next header is inserted at index 62
  339. HPDecoder.Decode(HexToBinString('4004')+'name'+ HexToBinString('05')+'value'+HexToBinString('BE'));
  340. AssertEquals('name',HPDecoder.DecodedHeaders[0]^.HeaderName);
  341. AssertEquals('value',HPDecoder.DecodedHeaders[0]^.HeaderValue);
  342. AssertEquals('name',HPDecoder.DecodedHeaders[1]^.HeaderName);
  343. AssertEquals('value',HPDecoder.DecodedHeaders[1]^.HeaderValue);
  344. finally
  345. FreeAndNil(HPDecoder);
  346. end;
  347. end;
  348. procedure THPackTestDecoder.LiteralWithIncrementalIndexingWithLargeValue;
  349. begin
  350. HPDecoder:=THPackDecoder.Create;
  351. try
  352. HPDecoder.Decode(HexToBinString('4004')+'name'+HexToBinString('7F813F')+StringOfChar('a',8192));
  353. // Verify header block is reported as truncated
  354. AssertTrue(HPDecoder.EndHeaderBlockTruncated);
  355. // Verify next header is inserted at index 62
  356. HPDecoder.Decode(HexToBinString('4004')+'name'+ HexToBinString('05')+'value'+HexToBinString('BE'));
  357. AssertEquals('name',HPDecoder.DecodedHeaders[0]^.HeaderName);
  358. AssertEquals('value',HPDecoder.DecodedHeaders[0]^.HeaderValue);
  359. AssertEquals('name',HPDecoder.DecodedHeaders[1]^.HeaderName);
  360. AssertEquals('value',HPDecoder.DecodedHeaders[1]^.HeaderValue);
  361. finally
  362. FreeAndNil(HPDecoder);
  363. end;
  364. end;
  365. procedure THPackTestDecoder.LiteralWithoutIndexingWithEmptyName;
  366. begin
  367. HPDecoder:=THPackDecoder.Create;
  368. try
  369. try
  370. HPDecoder.Decode(HexToBinString('000005')+'value');
  371. FAIL('Exception missing');
  372. except
  373. on E:Exception do begin
  374. if not (e is THPACKException) then begin
  375. raise;
  376. end;
  377. end;
  378. end;
  379. finally
  380. FreeAndNil(HPDecoder);
  381. end;
  382. end;
  383. procedure THPackTestDecoder.LiteralWithoutIndexingWithLargeName;
  384. begin
  385. HPDecoder:=THPackDecoder.Create;
  386. try
  387. HPDecoder.Decode(HexToBinString('007F817F')+StringOfChar('a',16384)+HexToBinString('00'));
  388. // Verify header block is reported as truncated
  389. AssertTrue(HPDecoder.EndHeaderBlockTruncated);
  390. try
  391. HPDecoder.Decode(HexToBinString('BE'));
  392. FAIL('Exception missing');
  393. except
  394. on E:Exception do begin
  395. if not (e is THPACKException) then begin
  396. raise;
  397. end;
  398. end;
  399. end;
  400. finally
  401. FreeAndNil(HPDecoder);
  402. end;
  403. end;
  404. procedure THPackTestDecoder.LiteralWithoutIndexingWithLargeValue;
  405. begin
  406. HPDecoder:=THPackDecoder.Create;
  407. try
  408. HPDecoder.Decode(HexToBinString('0004')+'name'+HexToBinString('7F813F')+StringOfChar('a',8192));
  409. // Verify header block is reported as truncated
  410. AssertTrue(HPDecoder.EndHeaderBlockTruncated);
  411. try
  412. HPDecoder.Decode(HexToBinString('BE'));
  413. FAIL('Exception missing');
  414. except
  415. on E:Exception do begin
  416. if not (e is THPACKException) then begin
  417. raise;
  418. end;
  419. end;
  420. end;
  421. finally
  422. FreeAndNil(HPDecoder);
  423. end;
  424. end;
  425. procedure THPackTestDecoder.LiteralNeverIndexedWithEmptyName;
  426. begin
  427. HPDecoder:=THPackDecoder.Create;
  428. try
  429. try
  430. HPDecoder.Decode(HexToBinString('100005')+'value');
  431. FAIL('Exception missing');
  432. except
  433. on E:Exception do begin
  434. if not (e is THPACKException) then begin
  435. raise;
  436. end;
  437. end;
  438. end;
  439. finally
  440. FreeAndNil(HPDecoder);
  441. end;
  442. end;
  443. procedure THPackTestDecoder.LiteralNeverIndexedWithLargeName;
  444. begin
  445. HPDecoder:=THPackDecoder.Create;
  446. try
  447. HPDecoder.Decode(HexToBinString('107F817F')+StringOfChar('a',16384)+HexToBinString('00'));
  448. // Verify header block is reported as truncated
  449. AssertTrue(HPDecoder.EndHeaderBlockTruncated);
  450. try
  451. HPDecoder.Decode(HexToBinString('BE'));
  452. FAIL('Exception missing');
  453. except
  454. on E:Exception do begin
  455. if not (e is THPACKException) then begin
  456. raise;
  457. end;
  458. end;
  459. end;
  460. finally
  461. FreeAndNil(HPDecoder);
  462. end;
  463. end;
  464. procedure THPackTestDecoder.LiteralNeverIndexedWithLargeValue;
  465. begin
  466. HPDecoder:=THPackDecoder.Create;
  467. try
  468. HPDecoder.Decode(HexToBinString('1004')+'name'+HexToBinString('7F813F')+StringOfChar('a',8192));
  469. // Verify header block is reported as truncated
  470. AssertTrue(HPDecoder.EndHeaderBlockTruncated);
  471. try
  472. HPDecoder.Decode(HexToBinString('BE'));
  473. FAIL('Exception missing');
  474. except
  475. on E:Exception do begin
  476. if not (e is THPACKException) then begin
  477. raise;
  478. end;
  479. end;
  480. end;
  481. finally
  482. FreeAndNil(HPDecoder);
  483. end;
  484. end;
  485. procedure THPackTestCaseCycle.TestHookUp;
  486. begin
  487. RunSampleHeadersTest;
  488. end;
  489. function THPackTestCaseCycle.GetTestName: string;
  490. begin
  491. Result:='Sample headers cycled';
  492. end;
  493. procedure THPackTestCaseCycle.TestThisSequence(const aGroup: integer; const aStory: integer; const aJSon: TJSONData);
  494. var
  495. HeadersPath: TJSonData;
  496. HexWire: string;
  497. BinWire: RawByteString;
  498. BinWire2: RawByteString;
  499. Sequence: integer;
  500. ExpectedHeaders: THPackHeaderTextList;
  501. j, HeaderTableSize: integer;
  502. lName,lValue: string;
  503. TestPassed: integer;
  504. function GetInteger(const aPath: string; const aOptional: Boolean=false): integer;
  505. var
  506. tmp: TJSonData;
  507. begin
  508. tmp:=aJSon.FindPath(aPath);
  509. if Assigned(tmp) then begin
  510. Result:=tmp.AsInteger;
  511. end else begin
  512. if not aOptional then begin
  513. Raise Exception.Create('Missing '+aPath);
  514. end else begin
  515. Result:=-1;
  516. end;
  517. end;
  518. end;
  519. function GetString(const aPath: string): String;
  520. var
  521. tmp: TJSonData;
  522. begin
  523. tmp:=aJSon.FindPath(aPath);
  524. if Assigned(tmp) then begin
  525. Result:=tmp.AsString;
  526. end else begin
  527. Raise Exception.Create('Missing '+aPath);
  528. end;
  529. end;
  530. procedure GetHeadersPair(const aHeaders: TJSonData; out aName,aValue: string);
  531. var
  532. Enumerator: TBaseJSONEnumerator;
  533. begin
  534. aName:='';
  535. aValue:='';
  536. if aHeaders.Count<>1 then begin
  537. Raise Exception.Create('Unexpected headers count = '+aHeaders.AsJSON);
  538. end;
  539. Enumerator:=aHeaders.GetEnumerator;
  540. try
  541. if Assigned(Enumerator) then begin
  542. if Enumerator.MoveNext then begin
  543. aName:=Enumerator.Current.Key;
  544. aValue:=Enumerator.Current.Value.AsString;
  545. if Enumerator.MoveNext then begin
  546. Raise Exception.Create('Too many header parts, expected A=B');
  547. end;
  548. Exit;
  549. end;
  550. end;
  551. Raise Exception.Create('Unexpected reach');
  552. finally
  553. Enumerator.Free;
  554. end;
  555. end;
  556. function EncodeHeaders(const aEncoder: THPackEncoder; const aHeadersList: THPackHeaderTextList): String;
  557. var
  558. OutStream: TStringStream;
  559. j: integer;
  560. begin
  561. Result:='';
  562. OutStream:=TStringStream.Create('');
  563. try
  564. for j := 0 to Pred(aHeadersList.Count) do begin
  565. aEncoder.EncodeHeader(OutStream,aHeadersList[j]^.HeaderName,aHeadersList[j]^.HeaderValue,aHeadersList[j]^.IsSensitive);
  566. end;
  567. Result:=OutStream.DataString;
  568. finally
  569. FreeAndNil(OutStream);
  570. end;
  571. end;
  572. begin
  573. TestPassed:=0;
  574. Sequence:=GetInteger('seqno');
  575. HexWire:=GetString('wire');
  576. HeaderTableSize:=GetInteger('header_table_size',true);
  577. if HeaderTableSize=-1 then begin
  578. HeaderTableSize:=HPACK_MAX_HEADER_TABLE_SIZE;
  579. end;
  580. if HeaderTableSize<>HPDecoder.GetMaxHeaderTableSize then begin
  581. {$IFNDEF QUIET}
  582. writeln('Max header table size changed from ',HPDecoder.GetMaxHeaderTableSize,' to ',HeaderTableSize);
  583. {$ENDIF}
  584. HPDecoder.SetMaxHeaderTableSize(HeaderTableSize);
  585. end;
  586. ExpectedHeaders:=THPackHeaderTextList.Create;
  587. {$IFNDEF QUIET}
  588. write('SEQ: ',aGroup,'-',aStory,'-',Sequence,#13);
  589. {$ENDIF}
  590. try
  591. HeadersPath:=aJSon.FindPath('headers');
  592. if not Assigned(HeadersPath) then begin
  593. Raise Exception.Create('Missing headers');
  594. end;
  595. for j := 0 to Pred(HeadersPath.Count) do begin
  596. GetHeadersPair(HeadersPath.Items[j],lName,lValue);
  597. ExpectedHeaders.Add(lName,lValue);
  598. end;
  599. BinWire:=HexToBinString(HexWire);
  600. HPDecoder.Decode(BinWire);
  601. if HPDecoder.EndHeaderBlockTruncated then begin
  602. raise Exception.Create('FAIL EndHeaderBlock');
  603. end;
  604. if HPDecoder.DecodedHeaders.Text<>ExpectedHeaders.Text then begin
  605. raise Exception.Create('Expected headers different than decoded ones.');
  606. end;
  607. TestPassed:=1;
  608. // Now reencode with our engine and decode again, result must be the same.
  609. BinWire2:=EncodeHeaders(HPIntfEncoderPlain,ExpectedHeaders);
  610. HPIntfDecoderPlain.Decode(BinWire2);
  611. if HPIntfDecoderPlain.EndHeaderBlockTruncated then begin
  612. raise Exception.Create('FAIL EndHeaderBlock REcoded (Plain).');
  613. end;
  614. if HPIntfDecoderPlain.DecodedHeaders.Text<>ExpectedHeaders.Text then begin
  615. raise Exception.Create('Expected headers different than REcoded ones (Plain).');
  616. end;
  617. TestPassed:=2;
  618. // Now reencode with our engine and decode again, result must be the same.
  619. BinWire2:=EncodeHeaders(HPIntfEncoderPlainIndexed,ExpectedHeaders);
  620. HPIntfDecoderPlainIndexed.Decode(BinWire2);
  621. if HPIntfDecoderPlainIndexed.EndHeaderBlockTruncated then begin
  622. raise Exception.Create('FAIL EndHeaderBlock REcoded (Plain & Indexed).');
  623. end;
  624. if HPIntfDecoderPlainIndexed.DecodedHeaders.Text<>ExpectedHeaders.Text then begin
  625. raise Exception.Create('Expected headers different than REcoded ones (Plain & Indexed).');
  626. end;
  627. TestPassed:=3;
  628. // Now reencode with our engine using huffman and decode again, result must be the same.
  629. BinWire2:=EncodeHeaders(HPIntfEncoderHuffman,ExpectedHeaders);
  630. HPIntfDecoderHuffman.Decode(BinWire2);
  631. if HPIntfDecoderHuffman.EndHeaderBlockTruncated then begin
  632. raise Exception.Create('FAIL EndHeaderBlock REcoded (Huffman).');
  633. end;
  634. if HPIntfDecoderHuffman.DecodedHeaders.Text<>ExpectedHeaders.Text then begin
  635. raise Exception.Create('Expected headers different than REcoded ones (Huffman).');
  636. end;
  637. TestPassed:=4;
  638. // Now reencode with our engine using huffman & indexed and decode again, result must be the same.
  639. BinWire2:=EncodeHeaders(HPIntfEncoderHuffmanIndexed,ExpectedHeaders);
  640. HPIntfDecoderHuffmanIndexed.Decode(BinWire2);
  641. if HPIntfDecoderHuffmanIndexed.EndHeaderBlockTruncated then begin
  642. raise Exception.Create('FAIL EndHeaderBlock REcoded (Huffman & Indexed).');
  643. end;
  644. if HPIntfDecoderHuffmanIndexed.DecodedHeaders.Text<>ExpectedHeaders.Text then begin
  645. raise Exception.Create('Expected headers different than REcoded ones (Huffman & Indexed).');
  646. end;
  647. inc(DecodedBytes,Length(HPIntfDecoderHuffmanIndexed.DecodedHeaders.Text));
  648. inc(WireBytes,Length(BinWire2));
  649. TestPassed:=1000;
  650. finally
  651. if TestPassed<1000 then begin
  652. {$IFNDEF FULL_QUIET}
  653. writeln(StdErr,ErrorHeader('TEST FAIL - Section passed '+inttostr(TestPassed)));
  654. writeln(StdErr,ErrorHeader('Expected headers'));
  655. writeln(StdErr,ExpectedHeaders.Text);
  656. writeln(StdErr,ErrorHeader('Got headers'));
  657. case TestPassed of
  658. 0: writeln(StdErr,HPDecoder.DecodedHeaders.Text);
  659. 1: writeln(StdErr,HPIntfDecoderPlain.DecodedHeaders.Text);
  660. 2: writeln(StdErr,HPIntfDecoderPlainIndexed.DecodedHeaders.Text);
  661. 3: writeln(StdErr,HPIntfDecoderHuffman.DecodedHeaders.Text);
  662. 4: writeln(StdErr,HPIntfDecoderHuffmanIndexed.DecodedHeaders.Text);
  663. else
  664. writeln(StdErr,'Unknown decoder in use.');
  665. end;
  666. writeln(StdErr,ErrorHeader('Location'));
  667. writeln(StdErr,'SEQ: ',aGroup,'-',aStory,'-',Sequence);
  668. {$ENDIF}
  669. end else begin
  670. inc(SequenceCounter);
  671. end;
  672. ExpectedHeaders.Free;
  673. end;
  674. end;
  675. procedure THPackTestCaseCycle.TestCaseStory(const aGroup: integer; const aStory: integer;
  676. const aJSon: TJSONData);
  677. var
  678. JSonData: TJSONData;
  679. CaseData: TJSonData;
  680. CaseCounter,Cases: integer;
  681. TestPass: Boolean;
  682. begin
  683. TestPass:=false;
  684. JSonData:=ajSon.FindPath('description');
  685. if Assigned(JSonData) then begin
  686. {$IFNDEF QUIET}
  687. writeln(JSonData.AsString);
  688. {$ENDIF}
  689. end;
  690. JSonData:=ajSon.FindPath('cases');
  691. if Assigned(JSonData) then begin
  692. Cases:=JSonData.Count;
  693. {$IFNDEF QUIET}
  694. writeln('Sequences in case ',Cases);
  695. {$ENDIF}
  696. HPDecoder:=THPackDecoder.Create(HPACK_MAX_HEADER_SIZE,HPACK_MAX_HEADER_TABLE_SIZE);
  697. // This encoders, decoders are for cycle compress, decompress tests.
  698. HPIntfDecoderPlain:=THPackDecoder.Create(HPACK_MAX_HEADER_SIZE,HPACK_MAX_HEADER_TABLE_SIZE);
  699. HPIntfDecoderPlainIndexed:=THPackDecoder.Create(HPACK_MAX_HEADER_SIZE,HPACK_MAX_HEADER_TABLE_SIZE);
  700. HPIntfDecoderHuffman:=THPackDecoder.Create(HPACK_MAX_HEADER_SIZE,HPACK_MAX_HEADER_TABLE_SIZE);
  701. HPIntfDecoderHuffmanIndexed:=THPackDecoder.Create(HPACK_MAX_HEADER_SIZE,HPACK_MAX_HEADER_TABLE_SIZE);
  702. HPIntfEncoderPlain:=THPackEncoder.Create(HPACK_MAX_HEADER_TABLE_SIZE,false,false,true);
  703. HPIntfEncoderPlainIndexed:=THPackEncoder.Create(HPACK_MAX_HEADER_TABLE_SIZE,true,false,true);
  704. HPIntfEncoderHuffman:=THPackEncoder.Create(HPACK_MAX_HEADER_TABLE_SIZE,false,true,false);
  705. HPIntfEncoderHuffmanIndexed:=THPackEncoder.Create(HPACK_MAX_HEADER_TABLE_SIZE,true,true,false);
  706. try
  707. CaseCounter:=0;
  708. while CaseCounter<Cases do begin
  709. CaseData:=JSonData.Items[CaseCounter];
  710. TestThisSequence(aGroup,aStory,CaseData);
  711. inc(CaseCounter);
  712. end;
  713. TestPass:=true;
  714. finally
  715. if not TestPass then begin
  716. {$IFNDEF FULL_QUIET}
  717. writeln(StdErr,ErrorHeader('Sequence failed'));
  718. writeln(StdErr,'Seq expected: ',CaseCounter);
  719. {$ENDIF}
  720. end else begin
  721. inc(StoryCounter);
  722. end;
  723. FreeAndNil(HPDecoder);
  724. FreeAndNil(HPIntfDecoderPlain);
  725. FreeAndNil(HPIntfDecoderPlainIndexed);
  726. FreeAndNil(HPIntfDecoderHuffman);
  727. FreeAndNil(HPIntfDecoderHuffmanIndexed);
  728. FreeAndNil(HPIntfEncoderPlain);
  729. FreeAndNil(HPIntfEncoderPlainIndexed);
  730. FreeAndNil(HPIntfEncoderHuffman);
  731. FreeAndNil(HPIntfEncoderHuffmanIndexed);
  732. end;
  733. end;
  734. end;
  735. procedure THPackTestCaseCycle.RunSampleHeadersTest;
  736. const
  737. TestCaseBase: string ='hpack-test-case-master'+PathDelim;
  738. TestCaseGroups: array [0..10] of string =
  739. (
  740. 'go-hpack',
  741. 'haskell-http2-linear',
  742. 'haskell-http2-linear-huffman',
  743. 'haskell-http2-naive',
  744. 'haskell-http2-naive-huffman',
  745. 'haskell-http2-static',
  746. 'haskell-http2-static-huffman',
  747. 'nghttp2',
  748. 'nghttp2-16384-4096',
  749. 'nghttp2-change-table-size',
  750. 'node-http2-hpack'
  751. );
  752. TestCaseStoryMask: string ='story_%.2d.json';
  753. var
  754. TheFile: string;
  755. JSonParser: TJSONParser;
  756. JSonData: TJSonData;
  757. MyStream: TFileStream;
  758. j: integer;
  759. FolderCounter: integer;
  760. FailCounter: Integer=0;
  761. ElapsedTime: QWord;
  762. begin
  763. SequenceCounter:=0;
  764. StoryCounter:=0;
  765. GroupsCounter:=0;
  766. WireBytes:=0;
  767. DecodedBytes:=0;
  768. ElapsedTime:=GetTickCount64;
  769. FolderCounter:=0;
  770. while FolderCounter<=High(TestCaseGroups) do begin
  771. j:=0;
  772. while true do begin
  773. TheFile:=IncludeTrailingPathDelimiter(TestCaseBase)+IncludeTrailingPathDelimiter(TestCaseGroups[FolderCounter])+format(TestCaseStoryMask,[j]);
  774. if not FileExists(TheFile) then begin
  775. break;
  776. end;
  777. MyStream:=TFileStream.Create(TheFile,fmOpenRead or fmShareDenyWrite);
  778. JSonParser:=TJSONParser.Create(MyStream,[]);
  779. JSonData:=JSonParser.Parse;
  780. {$IFNDEF QUIET}
  781. writeln('Check story ',Thefile);
  782. {$ENDIF}
  783. try
  784. try
  785. TestCaseStory(FolderCounter,j,JSonData);
  786. finally
  787. FreeAndNil(JSonData);
  788. FreeAndNil(JSonParser);
  789. FreeAndNil(MyStream);
  790. end;
  791. except
  792. on e: exception do begin
  793. {$IFNDEF FULL_QUIET}
  794. writeln(StdErr,ErrorHeader('Story failed'));
  795. writeln(StdErr,TheFile);
  796. writeln(StdErr,ErrorHeader('Fail condition'));
  797. writeln(StdErr,e.Message);
  798. inc(FailCounter);
  799. {$ENDIF}
  800. break;
  801. end;
  802. end;
  803. inc(j);
  804. end;
  805. inc(GroupsCounter);
  806. inc(FolderCounter);
  807. end;
  808. ElapsedTime:=GetTickCount64-ElapsedTime;
  809. {$IFNDEF QUIET}
  810. writeln;
  811. writeln;
  812. {$ENDIF}
  813. {$IFNDEF FULL_QUIET}
  814. writeln(ErrorHeader('Summary'));
  815. writeln('Groups: ',GroupsCounter);
  816. writeln('Stories: ',StoryCounter);
  817. writeln('Sequences: ',SequenceCounter);
  818. writeln('Time: ',ElapsedTime/1000:1:3,' seconds.');
  819. writeln('Wire bytes / Decoded bytes: ',WireBytes,' / ',DecodedBytes);
  820. writeln('Compression ratio: ',WireBytes/DecodedBytes:1:3);
  821. writeln('Failed tests: ',FailCounter);
  822. {$ENDIF}
  823. if FailCounter>0 then begin
  824. Fail('Failed cycle tests: %d',[FailCounter]);
  825. end;
  826. end;
  827. initialization
  828. RegisterTest(THPackTestCaseCycle);
  829. RegisterTest(THPackTestDecoder);
  830. end.