@@ -1828,3 +1828,303 @@ test('beforeError hook can redact sensitive headers for ERR_UNSUPPORTED_PROTOCOL
18281828
18291829 t . is ( error ?. options . headers . authorization , '<redacted>' ) ;
18301830} ) ;
1831+
1832+ test ( 'beforeRetry can reassign plain stream body' , withServer , async ( t , server , got ) => {
1833+ const { Readable : readable } = await import ( 'node:stream' ) ;
1834+ let requestCount = 0 ;
1835+ const testData = 'Hello, Got!' ;
1836+
1837+ server . post ( '/retry' , async ( request , response ) => {
1838+ requestCount ++ ;
1839+ let body = '' ;
1840+ for await ( const chunk of request ) {
1841+ body += String ( chunk ) ;
1842+ }
1843+
1844+ // First request fails, second succeeds
1845+ if ( requestCount === 1 ) {
1846+ response . statusCode = 500 ;
1847+ response . end ( 'Server Error' ) ;
1848+ } else {
1849+ response . statusCode = 200 ;
1850+ response . end ( `Received: ${ body } ` ) ;
1851+ }
1852+ } ) ;
1853+
1854+ // Factory function to create fresh streams
1855+ const createStream = ( ) => readable . from ( [ testData ] ) ;
1856+
1857+ const response = await got . post ( 'retry' , {
1858+ body : createStream ( ) ,
1859+ retry : {
1860+ limit : 1 ,
1861+ methods : [ 'POST' ] ,
1862+ } ,
1863+ hooks : {
1864+ beforeRetry : [
1865+ ( { options} ) => {
1866+ // Reassign with a fresh stream - this previously failed
1867+ options . body = createStream ( ) ;
1868+ } ,
1869+ ] ,
1870+ } ,
1871+ } ) ;
1872+
1873+ t . is ( response . statusCode , 200 ) ;
1874+ t . is ( response . body , `Received: ${ testData } ` ) ;
1875+ t . is ( requestCount , 2 ) ;
1876+ } ) ;
1877+
1878+ test ( 'beforeRetry destroys old stream when reassigning body' , withServer , async ( t , server , got ) => {
1879+ const { Readable : readable } = await import ( 'node:stream' ) ;
1880+ let requestCount = 0 ;
1881+ const testData = 'Stream data' ;
1882+
1883+ server . post ( '/retry' , async ( request , response ) => {
1884+ requestCount ++ ;
1885+ let body = '' ;
1886+ for await ( const chunk of request ) {
1887+ body += String ( chunk ) ;
1888+ }
1889+
1890+ // First request fails, second succeeds
1891+ if ( requestCount === 1 ) {
1892+ response . statusCode = 500 ;
1893+ response . end ( 'Server Error' ) ;
1894+ } else {
1895+ response . statusCode = 200 ;
1896+ response . end ( `Received: ${ body } ` ) ;
1897+ }
1898+ } ) ;
1899+
1900+ const createStream = ( ) => readable . from ( [ testData ] ) ;
1901+ const firstStream = createStream ( ) ;
1902+ let oldStreamDestroyed = false ;
1903+
1904+ // Monitor when the old stream is destroyed
1905+ firstStream . on ( 'close' , ( ) => {
1906+ oldStreamDestroyed = true ;
1907+ } ) ;
1908+
1909+ await got . post ( 'retry' , {
1910+ body : firstStream ,
1911+ retry : {
1912+ limit : 1 ,
1913+ methods : [ 'POST' ] ,
1914+ } ,
1915+ hooks : {
1916+ beforeRetry : [
1917+ ( { options} ) => {
1918+ // Reassign with a fresh stream
1919+ options . body = createStream ( ) ;
1920+ } ,
1921+ ] ,
1922+ } ,
1923+ } ) ;
1924+
1925+ t . true ( oldStreamDestroyed , 'Old stream should be destroyed to prevent memory leak' ) ;
1926+ t . is ( requestCount , 2 ) ;
1927+ } ) ;
1928+
1929+ test ( 'beforeRetry handles multiple retries with stream reassignment' , withServer , async ( t , server , got ) => {
1930+ const { Readable : readable } = await import ( 'node:stream' ) ;
1931+ let requestCount = 0 ;
1932+ const testData = 'Multi-retry data' ;
1933+
1934+ server . post ( '/retry' , async ( request , response ) => {
1935+ requestCount ++ ;
1936+ let body = '' ;
1937+ for await ( const chunk of request ) {
1938+ body += String ( chunk ) ;
1939+ }
1940+
1941+ // First two requests fail, third succeeds
1942+ if ( requestCount < 3 ) {
1943+ response . statusCode = 500 ;
1944+ response . end ( 'Server Error' ) ;
1945+ } else {
1946+ response . statusCode = 200 ;
1947+ response . end ( `Received: ${ body } ` ) ;
1948+ }
1949+ } ) ;
1950+
1951+ const createStream = ( ) => readable . from ( [ testData ] ) ;
1952+ const destroyedStreams : number [ ] = [ ] ;
1953+ let streamId = 0 ;
1954+
1955+ const response = await got . post ( 'retry' , {
1956+ body : createStream ( ) ,
1957+ retry : {
1958+ limit : 2 ,
1959+ methods : [ 'POST' ] ,
1960+ } ,
1961+ hooks : {
1962+ beforeRetry : [
1963+ ( { options} ) => {
1964+ const currentStreamId = ++ streamId ;
1965+ const newStream = createStream ( ) ;
1966+ newStream . on ( 'close' , ( ) => {
1967+ destroyedStreams . push ( currentStreamId ) ;
1968+ } ) ;
1969+ options . body = newStream ;
1970+ } ,
1971+ ] ,
1972+ } ,
1973+ } ) ;
1974+
1975+ t . is ( response . statusCode , 200 ) ;
1976+ t . is ( response . body , `Received: ${ testData } ` ) ;
1977+ t . is ( requestCount , 3 ) ;
1978+ t . is ( destroyedStreams . length , 2 , 'All old streams should be destroyed' ) ;
1979+ } ) ;
1980+
1981+ test ( 'beforeRetry handles non-stream body reassignment' , withServer , async ( t , server , got ) => {
1982+ let requestCount = 0 ;
1983+
1984+ server . post ( '/retry' , async ( request , response ) => {
1985+ requestCount ++ ;
1986+ let body = '' ;
1987+ for await ( const chunk of request ) {
1988+ body += String ( chunk ) ;
1989+ }
1990+
1991+ if ( requestCount === 1 ) {
1992+ response . statusCode = 500 ;
1993+ response . end ( 'Server Error' ) ;
1994+ } else {
1995+ response . statusCode = 200 ;
1996+ response . end ( `Received: ${ body } ` ) ;
1997+ }
1998+ } ) ;
1999+
2000+ const response = await got . post ( 'retry' , {
2001+ body : 'initial body' ,
2002+ retry : {
2003+ limit : 1 ,
2004+ methods : [ 'POST' ] ,
2005+ } ,
2006+ hooks : {
2007+ beforeRetry : [
2008+ ( { options} ) => {
2009+ // Reassign with a different string body
2010+ options . body = 'retried body' ;
2011+ } ,
2012+ ] ,
2013+ } ,
2014+ } ) ;
2015+
2016+ t . is ( response . statusCode , 200 ) ;
2017+ t . is ( response . body , 'Received: retried body' ) ;
2018+ t . is ( requestCount , 2 ) ;
2019+ } ) ;
2020+
2021+ test ( 'beforeRetry handles body set to undefined' , withServer , async ( t , server , got ) => {
2022+ const { Readable : readable } = await import ( 'node:stream' ) ;
2023+ let requestCount = 0 ;
2024+
2025+ server . post ( '/retry' , async ( _request , response ) => {
2026+ requestCount ++ ;
2027+ // First request fails, second succeeds (with no body)
2028+ if ( requestCount === 1 ) {
2029+ response . statusCode = 500 ;
2030+ response . end ( 'Error' ) ;
2031+ } else {
2032+ response . statusCode = 200 ;
2033+ response . end ( 'Success' ) ;
2034+ }
2035+ } ) ;
2036+
2037+ await got . post ( 'retry' , {
2038+ body : readable . from ( [ 'initial' ] ) ,
2039+ retry : {
2040+ limit : 1 ,
2041+ methods : [ 'POST' ] ,
2042+ } ,
2043+ hooks : {
2044+ beforeRetry : [
2045+ ( { options} ) => {
2046+ options . body = undefined ;
2047+ } ,
2048+ ] ,
2049+ } ,
2050+ } ) ;
2051+
2052+ t . is ( requestCount , 2 ) ;
2053+ } ) ;
2054+
2055+ test ( 'beforeRetry handles body from undefined to stream' , withServer , async ( t , server , got ) => {
2056+ const { Readable : readable } = await import ( 'node:stream' ) ;
2057+ let requestCount = 0 ;
2058+
2059+ server . post ( '/retry' , async ( request , response ) => {
2060+ requestCount ++ ;
2061+ let body = '' ;
2062+ for await ( const chunk of request ) {
2063+ body += String ( chunk ) ;
2064+ }
2065+
2066+ if ( requestCount === 1 ) {
2067+ response . statusCode = 500 ;
2068+ response . end ( 'Error' ) ;
2069+ } else {
2070+ response . statusCode = 200 ;
2071+ response . end ( `Got: ${ body } ` ) ;
2072+ }
2073+ } ) ;
2074+
2075+ const response = await got . post ( 'retry' , {
2076+ retry : {
2077+ limit : 1 ,
2078+ methods : [ 'POST' ] ,
2079+ } ,
2080+ hooks : {
2081+ beforeRetry : [
2082+ ( { options} ) => {
2083+ options . body = readable . from ( [ 'stream-data' ] ) ;
2084+ } ,
2085+ ] ,
2086+ } ,
2087+ } ) ;
2088+
2089+ t . is ( response . body , 'Got: stream-data' ) ;
2090+ t . is ( requestCount , 2 ) ;
2091+ } ) ;
2092+
2093+ test ( 'beforeRetry handles stream to Buffer conversion' , withServer , async ( t , server , got ) => {
2094+ const { Readable : readable } = await import ( 'node:stream' ) ;
2095+ let requestCount = 0 ;
2096+
2097+ server . post ( '/retry' , async ( request , response ) => {
2098+ requestCount ++ ;
2099+ let body = '' ;
2100+ for await ( const chunk of request ) {
2101+ body += String ( chunk ) ;
2102+ }
2103+
2104+ if ( requestCount === 1 ) {
2105+ response . statusCode = 500 ;
2106+ response . end ( 'Error' ) ;
2107+ } else {
2108+ response . statusCode = 200 ;
2109+ response . end ( `Got: ${ body } ` ) ;
2110+ }
2111+ } ) ;
2112+
2113+ const response = await got . post ( 'retry' , {
2114+ body : readable . from ( [ 'initial' ] ) ,
2115+ retry : {
2116+ limit : 1 ,
2117+ methods : [ 'POST' ] ,
2118+ } ,
2119+ hooks : {
2120+ beforeRetry : [
2121+ ( { options} ) => {
2122+ options . body = Buffer . from ( 'buffer-data' ) ;
2123+ } ,
2124+ ] ,
2125+ } ,
2126+ } ) ;
2127+
2128+ t . is ( response . body , 'Got: buffer-data' ) ;
2129+ t . is ( requestCount , 2 ) ;
2130+ } ) ;
0 commit comments