| 
 | 1 | +'use strict';  | 
 | 2 | +const common = require('../common');  | 
 | 3 | +if (!common.hasCrypto)  | 
 | 4 | +  common.skip('missing crypto');  | 
 | 5 | +const assert = require('assert');  | 
 | 6 | +const fixtures = require('../common/fixtures');  | 
 | 7 | +const https = require('https');  | 
 | 8 | + | 
 | 9 | +// This test assesses whether long-running writes can complete  | 
 | 10 | +// or timeout because the socket is not aware that the backing  | 
 | 11 | +// stream is still writing.  | 
 | 12 | +// To simulate a slow client, we write a really large chunk and  | 
 | 13 | +// then proceed through the following cycle:  | 
 | 14 | +// 1) Receive first 'data' event and record currently written size  | 
 | 15 | +// 2) Once we've read up to currently written size recorded above,  | 
 | 16 | +//    we pause the stream and wait longer than the server timeout  | 
 | 17 | +// 3) Socket.prototype._onTimeout triggers and should confirm  | 
 | 18 | +//    that the backing stream is still active and writing  | 
 | 19 | +// 4) Our timer fires, we resume the socket and start at 1)  | 
 | 20 | + | 
 | 21 | +const minReadSize = 250000;  | 
 | 22 | +const serverTimeout = common.platformTimeout(500);  | 
 | 23 | +let offsetTimeout = common.platformTimeout(100);  | 
 | 24 | +let serverConnectionHandle;  | 
 | 25 | +let writeSize = 2000000;  | 
 | 26 | +let didReceiveData = false;  | 
 | 27 | +// this represents each cycles write size, where the cycle consists  | 
 | 28 | +// of `write > read > _onTimeout`  | 
 | 29 | +let currentWriteSize = 0;  | 
 | 30 | + | 
 | 31 | +const server = https.createServer({  | 
 | 32 | +  key: fixtures.readKey('agent1-key.pem'),  | 
 | 33 | +  cert: fixtures.readKey('agent1-cert.pem')  | 
 | 34 | +}, common.mustCall((req, res) => {  | 
 | 35 | +  const content = Buffer.alloc(writeSize, 0x44);  | 
 | 36 | + | 
 | 37 | +  res.writeHead(200, {  | 
 | 38 | +    'Content-Type': 'application/octet-stream',  | 
 | 39 | +    'Content-Length': content.length.toString(),  | 
 | 40 | +    'Vary': 'Accept-Encoding'  | 
 | 41 | +  });  | 
 | 42 | + | 
 | 43 | +  serverConnectionHandle = res.socket._handle;  | 
 | 44 | +  res.write(content);  | 
 | 45 | +  res.end();  | 
 | 46 | +}));  | 
 | 47 | +server.setTimeout(serverTimeout);  | 
 | 48 | +server.on('timeout', () => {  | 
 | 49 | +  assert.strictEqual(didReceiveData, false, 'Should not timeout');  | 
 | 50 | +});  | 
 | 51 | + | 
 | 52 | +server.listen(0, common.mustCall(() => {  | 
 | 53 | +  https.get({  | 
 | 54 | +    path: '/',  | 
 | 55 | +    port: server.address().port,  | 
 | 56 | +    rejectUnauthorized: false  | 
 | 57 | +  }, common.mustCall((res) => {  | 
 | 58 | +    const resume = () => res.resume();  | 
 | 59 | +    let receivedBufferLength = 0;  | 
 | 60 | +    let firstReceivedAt;  | 
 | 61 | +    res.on('data', common.mustCallAtLeast((buf) => {  | 
 | 62 | +      if (receivedBufferLength === 0) {  | 
 | 63 | +        currentWriteSize = Math.max(  | 
 | 64 | +          minReadSize,  | 
 | 65 | +          writeSize - serverConnectionHandle.writeQueueSize  | 
 | 66 | +        );  | 
 | 67 | +        didReceiveData = false;  | 
 | 68 | +        firstReceivedAt = Date.now();  | 
 | 69 | +      }  | 
 | 70 | +      receivedBufferLength += buf.length;  | 
 | 71 | +      if (receivedBufferLength >= currentWriteSize) {  | 
 | 72 | +        didReceiveData = true;  | 
 | 73 | +        writeSize = serverConnectionHandle.writeQueueSize;  | 
 | 74 | +        receivedBufferLength = 0;  | 
 | 75 | +        res.pause();  | 
 | 76 | +        setTimeout(  | 
 | 77 | +          resume,  | 
 | 78 | +          serverTimeout + offsetTimeout - (Date.now() - firstReceivedAt)  | 
 | 79 | +        );  | 
 | 80 | +        offsetTimeout = 0;  | 
 | 81 | +      }  | 
 | 82 | +    }, 1));  | 
 | 83 | +    res.on('end', common.mustCall(() => {  | 
 | 84 | +      server.close();  | 
 | 85 | +    }));  | 
 | 86 | +  }));  | 
 | 87 | +}));  | 
0 commit comments