@@ -43,12 +43,22 @@ const {
4343 getGlobalAgent,
4444} = require ( 'internal/http' ) ;
4545const { AsyncResource } = require ( 'async_hooks' ) ;
46- const { async_id_symbol } = require ( 'internal/async_hooks' ) . symbols ;
46+ const {
47+ async_id_symbol,
48+ owner_symbol,
49+ } = require ( 'internal/async_hooks' ) . symbols ;
4750const {
4851 getLazy,
4952 kEmptyObject,
5053 once,
5154} = require ( 'internal/util' ) ;
55+ const {
56+ onStreamRead,
57+ } = require ( 'internal/stream_base_commons' ) ;
58+ const {
59+ kReadBytesOrError,
60+ streamBaseState,
61+ } = internalBinding ( 'stream_wrap' ) ;
5262const {
5363 validateNumber,
5464 validateOneOf,
@@ -92,9 +102,37 @@ function freeSocketErrorListener(err) {
92102// in the TCP buffer and be silently consumed as the response for the
93103// *next* request that reuses the socket (response-queue poisoning).
94104// See: https://hackerone.com/reports/3582376
95- function freeSocketDataGuard ( ) {
96- debug ( 'DATA on FREE socket - destroying poisoned socket' ) ;
97- this . destroy ( ) ;
105+ function freeSocketOnReadGuard ( ) {
106+ const nread = streamBaseState [ kReadBytesOrError ] ;
107+ if ( nread === 0 ) return ;
108+
109+ debug ( 'READ on FREE socket - destroying poisoned socket' ) ;
110+ this [ owner_symbol ] . destroy ( ) ;
111+ }
112+
113+ function installFreeSocketDataGuard ( socket ) {
114+ if ( socket . readableLength > 0 ) {
115+ debug ( 'BUFFERED DATA on FREE socket - destroying poisoned socket' ) ;
116+ socket . destroy ( ) ;
117+ return ;
118+ }
119+
120+ const handle = socket . _handle ;
121+ if ( handle ) {
122+ handle . onread = freeSocketOnReadGuard ;
123+ if ( ! handle . reading ) {
124+ handle . reading = true ;
125+ const err = handle . readStart ( ) ;
126+ if ( err ) socket . destroy ( ) ;
127+ }
128+ }
129+ }
130+
131+ function removeFreeSocketDataGuard ( socket ) {
132+ const handle = socket . _handle ;
133+ if ( handle ?. onread === freeSocketOnReadGuard ) {
134+ handle . onread = onStreamRead ;
135+ }
98136}
99137
100138function Agent ( options ) {
@@ -207,8 +245,7 @@ function Agent(options) {
207245 this . removeSocket ( socket , options ) ;
208246
209247 socket . once ( 'error' , freeSocketErrorListener ) ;
210- socket . on ( 'data' , freeSocketDataGuard ) ;
211- socket . resume ( ) ;
248+ installFreeSocketDataGuard ( socket ) ;
212249 freeSockets . push ( socket ) ;
213250 } ) ;
214251
@@ -600,7 +637,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
600637Agent . prototype . reuseSocket = function reuseSocket ( socket , req ) {
601638 debug ( 'have free socket' ) ;
602639 socket . removeListener ( 'error' , freeSocketErrorListener ) ;
603- socket . removeListener ( 'data' , freeSocketDataGuard ) ;
640+ removeFreeSocketDataGuard ( socket ) ;
604641 req . reusedSocket = true ;
605642 socket . ref ( ) ;
606643} ;
0 commit comments