@@ -27,6 +27,7 @@ describe(QUICStream.name, () => {
2727 randomBytes : testsUtils . randomBytes ,
2828 } ;
2929 let socketCleanMethods : ReturnType < typeof testsUtils . socketCleanupFactory > ;
30+ let createQUICStreamMock : jest . SpyInstance ;
3031
3132 const testReason = Symbol ( 'TestReason' ) ;
3233 const testCodeToReason = ( type , code ) => {
@@ -46,8 +47,10 @@ describe(QUICStream.name, () => {
4647 beforeEach ( async ( ) => {
4748 key = await testsUtils . generateKeyHMAC ( ) ;
4849 socketCleanMethods = testsUtils . socketCleanupFactory ( ) ;
50+ createQUICStreamMock = jest . spyOn ( QUICStream , 'createQUICStream' ) ;
4951 } ) ;
5052 afterEach ( async ( ) => {
53+ createQUICStreamMock . mockRestore ( ) ;
5154 await socketCleanMethods . stopSockets ( ) ;
5255 } ) ;
5356
@@ -1235,7 +1238,6 @@ describe(QUICStream.name, () => {
12351238 await client . destroy ( { force : true } ) ;
12361239 await server . stop ( { force : true } ) ;
12371240 } ) ;
1238-
12391241 test ( 'streams are allowed to end when client is destroyed with force: false' , async ( ) => {
12401242 const message = Buffer . from ( 'The Quick Brown Fox Jumped Over The Lazy Dog' ) ;
12411243 const numStreams = 10 ;
@@ -1576,4 +1578,275 @@ describe(QUICStream.name, () => {
15761578 waitResolveP ( ) ;
15771579 await waitP ;
15781580 } ) ;
1581+ test ( 'quicStream properly cleans up after cancelling without data sent' , async ( ) => {
1582+ const cancelReason = Symbol ( 'CancelReason' ) ;
1583+ const connectionEventProm =
1584+ utils . promise < events . EventQUICServerConnection > ( ) ;
1585+ const tlsConfig1 = await generateTLSConfig ( defaultType ) ;
1586+ const tlsConfig2 = await generateTLSConfig ( defaultType ) ;
1587+ const reasonConverters = testsUtils . createReasonConverters ( ) ;
1588+ const server = new QUICServer ( {
1589+ crypto : {
1590+ key,
1591+ ops : serverCrypto ,
1592+ } ,
1593+ logger : logger . getChild ( QUICServer . name ) ,
1594+ config : {
1595+ key : tlsConfig1 . leafKeyPairPEM . privateKey ,
1596+ cert : tlsConfig1 . leafCertPEM ,
1597+ verifyPeer : true ,
1598+ ca : tlsConfig2 . caCertPEM ,
1599+ } ,
1600+ ...reasonConverters ,
1601+ } ) ;
1602+ socketCleanMethods . extractSocket ( server ) ;
1603+ server . addEventListener (
1604+ events . EventQUICServerConnection . name ,
1605+ ( e : events . EventQUICServerConnection ) => connectionEventProm . resolveP ( e ) ,
1606+ ) ;
1607+ await server . start ( {
1608+ host : localhost ,
1609+ } ) ;
1610+ const client = await QUICClient . createQUICClient ( {
1611+ host : localhost ,
1612+ port : server . port ,
1613+ localHost : localhost ,
1614+ crypto : {
1615+ ops : clientCrypto ,
1616+ } ,
1617+ logger : logger . getChild ( QUICClient . name ) ,
1618+ config : {
1619+ verifyPeer : false ,
1620+ key : tlsConfig2 . leafKeyPairPEM . privateKey ,
1621+ cert : tlsConfig2 . leafCertPEM ,
1622+ } ,
1623+ ...reasonConverters ,
1624+ } ) ;
1625+ socketCleanMethods . extractSocket ( client ) ;
1626+ const serverConnection = ( await connectionEventProm . p ) . detail ;
1627+
1628+ // Do the test
1629+ const { p : serverStreamP , resolveP : serverStreamResolveP } =
1630+ utils . promise < QUICStream > ( ) ;
1631+ serverConnection . addEventListener (
1632+ events . EventQUICConnectionStream . name ,
1633+ ( event : events . EventQUICConnectionStream ) => {
1634+ serverStreamResolveP ( event . detail ) ;
1635+ } ,
1636+ ) ;
1637+ // Let's make a new stream.
1638+ const clientStream = client . connection . newStream ( ) ;
1639+ const writer = clientStream . writable . getWriter ( ) ;
1640+ // Await writer.write(message);
1641+ writer . releaseLock ( ) ;
1642+ clientStream . cancel ( cancelReason ) ;
1643+ await expect ( clientStream . readable . getReader ( ) . read ( ) ) . rejects . toBe (
1644+ cancelReason ,
1645+ ) ;
1646+ await expect ( clientStream . writable . getWriter ( ) . write ( ) ) . rejects . toBe (
1647+ cancelReason ,
1648+ ) ;
1649+
1650+ // Let's check that the server side ended
1651+ const serverStream = await serverStreamP ;
1652+ const serverReadProm = ( async ( ) => {
1653+ for await ( const _ of serverStream . readable ) {
1654+ // Just consume until stream throws
1655+ }
1656+ } ) ( ) ;
1657+ await expect ( serverReadProm ) . rejects . toBe ( cancelReason ) ;
1658+ const serverWriter = serverStream . writable . getWriter ( ) ;
1659+ // Should throw
1660+ await expect ( serverWriter . write ( Buffer . from ( 'hello' ) ) ) . rejects . toBe (
1661+ cancelReason ,
1662+ ) ;
1663+
1664+ // And client stream should've cleaned up
1665+ await testsUtils . sleep ( 100 ) ;
1666+ // Only two streams should've been created
1667+ expect ( createQUICStreamMock ) . toHaveBeenCalledTimes ( 2 ) ;
1668+
1669+ expect ( clientStream [ destroyed ] ) . toBeTrue ( ) ;
1670+ await client . destroy ( { force : true } ) ;
1671+ await server . stop ( { force : true } ) ;
1672+ } ) ;
1673+ test ( 'quicStream properly cleans up after cancelling with data sent' , async ( ) => {
1674+ const cancelReason = Symbol ( 'CancelReason' ) ;
1675+ const connectionEventProm =
1676+ utils . promise < events . EventQUICServerConnection > ( ) ;
1677+ const tlsConfig1 = await generateTLSConfig ( defaultType ) ;
1678+ const tlsConfig2 = await generateTLSConfig ( defaultType ) ;
1679+ const reasonConverters = testsUtils . createReasonConverters ( ) ;
1680+ const server = new QUICServer ( {
1681+ crypto : {
1682+ key,
1683+ ops : serverCrypto ,
1684+ } ,
1685+ logger : logger . getChild ( QUICServer . name ) ,
1686+ config : {
1687+ key : tlsConfig1 . leafKeyPairPEM . privateKey ,
1688+ cert : tlsConfig1 . leafCertPEM ,
1689+ verifyPeer : true ,
1690+ ca : tlsConfig2 . caCertPEM ,
1691+ } ,
1692+ ...reasonConverters ,
1693+ } ) ;
1694+ socketCleanMethods . extractSocket ( server ) ;
1695+ server . addEventListener (
1696+ events . EventQUICServerConnection . name ,
1697+ ( e : events . EventQUICServerConnection ) => connectionEventProm . resolveP ( e ) ,
1698+ ) ;
1699+ await server . start ( {
1700+ host : localhost ,
1701+ } ) ;
1702+ const client = await QUICClient . createQUICClient ( {
1703+ host : localhost ,
1704+ port : server . port ,
1705+ localHost : localhost ,
1706+ crypto : {
1707+ ops : clientCrypto ,
1708+ } ,
1709+ logger : logger . getChild ( QUICClient . name ) ,
1710+ config : {
1711+ verifyPeer : false ,
1712+ key : tlsConfig2 . leafKeyPairPEM . privateKey ,
1713+ cert : tlsConfig2 . leafCertPEM ,
1714+ } ,
1715+ ...reasonConverters ,
1716+ } ) ;
1717+ socketCleanMethods . extractSocket ( client ) ;
1718+ const serverConnection = ( await connectionEventProm . p ) . detail ;
1719+
1720+ // Do the test
1721+ const { p : serverStreamP , resolveP : serverStreamResolveP } =
1722+ utils . promise < QUICStream > ( ) ;
1723+ serverConnection . addEventListener (
1724+ events . EventQUICConnectionStream . name ,
1725+ ( event : events . EventQUICConnectionStream ) => {
1726+ serverStreamResolveP ( event . detail ) ;
1727+ } ,
1728+ ) ;
1729+ // Let's make a new stream.
1730+ const message = Buffer . from ( 'Hello!' ) ;
1731+ const clientStream = client . connection . newStream ( ) ;
1732+ const writer = clientStream . writable . getWriter ( ) ;
1733+ await writer . write ( message ) ;
1734+ writer . releaseLock ( ) ;
1735+ clientStream . cancel ( cancelReason ) ;
1736+ await expect ( clientStream . readable . getReader ( ) . read ( ) ) . rejects . toBe (
1737+ cancelReason ,
1738+ ) ;
1739+ await expect ( clientStream . writable . getWriter ( ) . write ( ) ) . rejects . toBe (
1740+ cancelReason ,
1741+ ) ;
1742+
1743+ // Let's check that the server side ended
1744+ const serverStream = await serverStreamP ;
1745+ const serverReadProm = ( async ( ) => {
1746+ for await ( const _ of serverStream . readable ) {
1747+ // Just consume until stream throws
1748+ }
1749+ } ) ( ) ;
1750+ await expect ( serverReadProm ) . rejects . toBe ( cancelReason ) ;
1751+ const serverWriter = serverStream . writable . getWriter ( ) ;
1752+ // Should throw
1753+ await expect ( serverWriter . write ( Buffer . from ( 'hello' ) ) ) . rejects . toBe (
1754+ cancelReason ,
1755+ ) ;
1756+
1757+ // And client stream should've cleaned up
1758+ await testsUtils . sleep ( 100 ) ;
1759+ // Only two streams should've been created
1760+ expect ( createQUICStreamMock ) . toHaveBeenCalledTimes ( 2 ) ;
1761+
1762+ expect ( clientStream [ destroyed ] ) . toBeTrue ( ) ;
1763+ await client . destroy ( { force : true } ) ;
1764+ await server . stop ( { force : true } ) ;
1765+ } ) ;
1766+ test ( 'quicStream properly cleans up after graceful end with data sent' , async ( ) => {
1767+ const connectionEventProm =
1768+ utils . promise < events . EventQUICServerConnection > ( ) ;
1769+ const tlsConfig1 = await generateTLSConfig ( defaultType ) ;
1770+ const tlsConfig2 = await generateTLSConfig ( defaultType ) ;
1771+ const reasonConverters = testsUtils . createReasonConverters ( ) ;
1772+ const server = new QUICServer ( {
1773+ crypto : {
1774+ key,
1775+ ops : serverCrypto ,
1776+ } ,
1777+ logger : logger . getChild ( QUICServer . name ) ,
1778+ config : {
1779+ key : tlsConfig1 . leafKeyPairPEM . privateKey ,
1780+ cert : tlsConfig1 . leafCertPEM ,
1781+ verifyPeer : true ,
1782+ ca : tlsConfig2 . caCertPEM ,
1783+ } ,
1784+ ...reasonConverters ,
1785+ } ) ;
1786+ socketCleanMethods . extractSocket ( server ) ;
1787+ server . addEventListener (
1788+ events . EventQUICServerConnection . name ,
1789+ ( e : events . EventQUICServerConnection ) => connectionEventProm . resolveP ( e ) ,
1790+ ) ;
1791+ await server . start ( {
1792+ host : localhost ,
1793+ } ) ;
1794+ const client = await QUICClient . createQUICClient ( {
1795+ host : localhost ,
1796+ port : server . port ,
1797+ localHost : localhost ,
1798+ crypto : {
1799+ ops : clientCrypto ,
1800+ } ,
1801+ logger : logger . getChild ( QUICClient . name ) ,
1802+ config : {
1803+ verifyPeer : false ,
1804+ key : tlsConfig2 . leafKeyPairPEM . privateKey ,
1805+ cert : tlsConfig2 . leafCertPEM ,
1806+ } ,
1807+ ...reasonConverters ,
1808+ } ) ;
1809+ socketCleanMethods . extractSocket ( client ) ;
1810+ const serverConnection = ( await connectionEventProm . p ) . detail ;
1811+
1812+ // Do the test
1813+ const { p : serverStreamP , resolveP : serverStreamResolveP } =
1814+ utils . promise < QUICStream > ( ) ;
1815+ serverConnection . addEventListener (
1816+ events . EventQUICConnectionStream . name ,
1817+ ( event : events . EventQUICConnectionStream ) => {
1818+ serverStreamResolveP ( event . detail ) ;
1819+ } ,
1820+ ) ;
1821+ // Let's make a new stream.
1822+ const message = Buffer . from ( 'Hello!' ) ;
1823+ const clientStream = client . connection . newStream ( ) ;
1824+ const writer = clientStream . writable . getWriter ( ) ;
1825+ await writer . write ( message ) ;
1826+ await writer . close ( ) ;
1827+
1828+ // Let's check that the server side ended
1829+ const serverStream = await serverStreamP ;
1830+ const serverReadP = ( async ( ) => {
1831+ const writer = serverStream . writable . getWriter ( ) ;
1832+ await writer . write ( message ) ;
1833+ await writer . close ( ) ;
1834+ for await ( const _ of serverStream . readable ) {
1835+ // Just consume until finish
1836+ }
1837+ } ) ( ) ;
1838+ for await ( const _ of clientStream . readable ) {
1839+ // Just consume until finish
1840+ }
1841+ await serverReadP ;
1842+
1843+ // And client stream should've cleaned up
1844+ await testsUtils . sleep ( 100 ) ;
1845+ // Only two streams should've been created
1846+ expect ( createQUICStreamMock ) . toHaveBeenCalledTimes ( 2 ) ;
1847+
1848+ expect ( clientStream [ destroyed ] ) . toBeTrue ( ) ;
1849+ await client . destroy ( { force : true } ) ;
1850+ await server . stop ( { force : true } ) ;
1851+ } ) ;
15791852} ) ;
0 commit comments