@@ -1425,6 +1425,20 @@ const isDomainOrSubdomain = function isDomainOrSubdomain(destination, original)
14251425 return orig === dest || orig [ orig . length - dest . length - 1 ] === '.' && orig . endsWith ( dest ) ;
14261426} ;
14271427
1428+ /**
1429+ * isSameProtocol reports whether the two provided URLs use the same protocol.
1430+ *
1431+ * Both domains must already be in canonical form.
1432+ * @param {string|URL } original
1433+ * @param {string|URL } destination
1434+ */
1435+ const isSameProtocol = function isSameProtocol ( destination , original ) {
1436+ const orig = new URL$1 ( original ) . protocol ;
1437+ const dest = new URL$1 ( destination ) . protocol ;
1438+
1439+ return orig === dest ;
1440+ } ;
1441+
14281442/**
14291443 * Fetch function
14301444 *
@@ -1456,7 +1470,7 @@ function fetch(url, opts) {
14561470 let error = new AbortError ( 'The user aborted a request.' ) ;
14571471 reject ( error ) ;
14581472 if ( request . body && request . body instanceof Stream . Readable ) {
1459- request . body . destroy ( error ) ;
1473+ destroyStream ( request . body , error ) ;
14601474 }
14611475 if ( ! response || ! response . body ) return ;
14621476 response . body . emit ( 'error' , error ) ;
@@ -1497,9 +1511,43 @@ function fetch(url, opts) {
14971511
14981512 req . on ( 'error' , function ( err ) {
14991513 reject ( new FetchError ( `request to ${ request . url } failed, reason: ${ err . message } ` , 'system' , err ) ) ;
1514+
1515+ if ( response && response . body ) {
1516+ destroyStream ( response . body , err ) ;
1517+ }
1518+
15001519 finalize ( ) ;
15011520 } ) ;
15021521
1522+ fixResponseChunkedTransferBadEnding ( req , function ( err ) {
1523+ if ( signal && signal . aborted ) {
1524+ return ;
1525+ }
1526+
1527+ if ( response && response . body ) {
1528+ destroyStream ( response . body , err ) ;
1529+ }
1530+ } ) ;
1531+
1532+ /* c8 ignore next 18 */
1533+ if ( parseInt ( process . version . substring ( 1 ) ) < 14 ) {
1534+ // Before Node.js 14, pipeline() does not fully support async iterators and does not always
1535+ // properly handle when the socket close/end events are out of order.
1536+ req . on ( 'socket' , function ( s ) {
1537+ s . addListener ( 'close' , function ( hadError ) {
1538+ // if a data listener is still present we didn't end cleanly
1539+ const hasDataListener = s . listenerCount ( 'data' ) > 0 ;
1540+
1541+ // if end happened before close but the socket didn't emit an error, do it now
1542+ if ( response && hasDataListener && ! hadError && ! ( signal && signal . aborted ) ) {
1543+ const err = new Error ( 'Premature close' ) ;
1544+ err . code = 'ERR_STREAM_PREMATURE_CLOSE' ;
1545+ response . body . emit ( 'error' , err ) ;
1546+ }
1547+ } ) ;
1548+ } ) ;
1549+ }
1550+
15031551 req . on ( 'response' , function ( res ) {
15041552 clearTimeout ( reqTimeout ) ;
15051553
@@ -1571,7 +1619,7 @@ function fetch(url, opts) {
15711619 size : request . size
15721620 } ;
15731621
1574- if ( ! isDomainOrSubdomain ( request . url , locationURL ) ) {
1622+ if ( ! isDomainOrSubdomain ( request . url , locationURL ) || ! isSameProtocol ( request . url , locationURL ) ) {
15751623 for ( const name of [ 'authorization' , 'www-authenticate' , 'cookie' , 'cookie2' ] ) {
15761624 requestOpts . headers . delete ( name ) ;
15771625 }
@@ -1664,6 +1712,13 @@ function fetch(url, opts) {
16641712 response = new Response ( body , response_options ) ;
16651713 resolve ( response ) ;
16661714 } ) ;
1715+ raw . on ( 'end' , function ( ) {
1716+ // some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted.
1717+ if ( ! response ) {
1718+ response = new Response ( body , response_options ) ;
1719+ resolve ( response ) ;
1720+ }
1721+ } ) ;
16671722 return ;
16681723 }
16691724
@@ -1683,6 +1738,44 @@ function fetch(url, opts) {
16831738 writeToStream ( req , request ) ;
16841739 } ) ;
16851740}
1741+ function fixResponseChunkedTransferBadEnding ( request , errorCallback ) {
1742+ let socket ;
1743+
1744+ request . on ( 'socket' , function ( s ) {
1745+ socket = s ;
1746+ } ) ;
1747+
1748+ request . on ( 'response' , function ( response ) {
1749+ const headers = response . headers ;
1750+
1751+ if ( headers [ 'transfer-encoding' ] === 'chunked' && ! headers [ 'content-length' ] ) {
1752+ response . once ( 'close' , function ( hadError ) {
1753+ // tests for socket presence, as in some situations the
1754+ // the 'socket' event is not triggered for the request
1755+ // (happens in deno), avoids `TypeError`
1756+ // if a data listener is still present we didn't end cleanly
1757+ const hasDataListener = socket && socket . listenerCount ( 'data' ) > 0 ;
1758+
1759+ if ( hasDataListener && ! hadError ) {
1760+ const err = new Error ( 'Premature close' ) ;
1761+ err . code = 'ERR_STREAM_PREMATURE_CLOSE' ;
1762+ errorCallback ( err ) ;
1763+ }
1764+ } ) ;
1765+ }
1766+ } ) ;
1767+ }
1768+
1769+ function destroyStream ( stream , err ) {
1770+ if ( stream . destroy ) {
1771+ stream . destroy ( err ) ;
1772+ } else {
1773+ // node < 8
1774+ stream . emit ( 'error' , err ) ;
1775+ stream . end ( ) ;
1776+ }
1777+ }
1778+
16861779/**
16871780 * Redirect code matching
16881781 *
0 commit comments