Skip to content

Instantly share code, notes, and snippets.

@uzumaki-narut0
Created April 12, 2020 12:52
Show Gist options
  • Save uzumaki-narut0/e8d7b51ca7cc02b932316719803a8dd0 to your computer and use it in GitHub Desktop.
Save uzumaki-narut0/e8d7b51ca7cc02b932316719803a8dd0 to your computer and use it in GitHub Desktop.
/**
* Simulates a network download of a particular number of bytes over an optional maximum amount of time
* and returns information about the ending state.
*
* See https://hpbn.co/building-blocks-of-tcp/#three-way-handshake and
* https://hpbn.co/transport-layer-security-tls/#tls-handshake for details.
*
* @param {number} bytesToDownload
* @param {DownloadOptions} [options]
* @return {DownloadResults}
*/
simulateDownloadUntil(bytesToDownload, options) {
const {timeAlreadyElapsed = 0, maximumTimeToElapse = Infinity, dnsResolutionTime = 0} =
options || {};
if (this._warmed && this._h2) {
bytesToDownload -= this._h2OverflowBytesDownloaded;
}
const twoWayLatency = this._rtt; // 150 ms
const oneWayLatency = twoWayLatency / 2; // 75 ms
const maximumCongestionWindow = this._computeMaximumCongestionWindowInSegments(); // 20
let handshakeAndRequest = oneWayLatency; // 75ms
if (!this._warmed) {
handshakeAndRequest =
// DNS lookup
dnsResolutionTime + // 169
// SYN
oneWayLatency +
// SYN ACK
oneWayLatency +
// ACK + initial request
oneWayLatency +
// ClientHello/ServerHello assuming TLS False Start is enabled (https://istlsfastyet.com/#server-performance).
(this._ssl ? twoWayLatency : 0); // 150
}
let roundTrips = Math.ceil(handshakeAndRequest / twoWayLatency); // 3
let timeToFirstByte = handshakeAndRequest + this._serverLatency + oneWayLatency; // 544 ms + Server response time
if (this._warmed && this._h2) timeToFirstByte = 0;
const timeElapsedForTTFB = Math.max(timeToFirstByte - timeAlreadyElapsed, 0);
const maximumDownloadTimeToElapse = maximumTimeToElapse - timeElapsedForTTFB;
let congestionWindow = Math.min(this._congestionWindow, maximumCongestionWindow); // 10
let totalBytesDownloaded = 0;
if (timeElapsedForTTFB > 0) {
totalBytesDownloaded = congestionWindow * TCP_SEGMENT_SIZE; // 1460 * 10
} else {
roundTrips = 0;
}
let downloadTimeElapsed = 0;
let bytesRemaining = bytesToDownload - totalBytesDownloaded;
while (bytesRemaining > 0 && downloadTimeElapsed <= maximumDownloadTimeToElapse) {
roundTrips++;
downloadTimeElapsed += twoWayLatency;
congestionWindow = Math.max(Math.min(maximumCongestionWindow, congestionWindow * 2), 1);
const bytesDownloadedInWindow = congestionWindow * TCP_SEGMENT_SIZE; // 1460 * 20
totalBytesDownloaded += bytesDownloadedInWindow;
bytesRemaining -= bytesDownloadedInWindow;
}
const timeElapsed = timeElapsedForTTFB + downloadTimeElapsed;
const extraBytesDownloaded = this._h2 ? Math.max(totalBytesDownloaded - bytesToDownload, 0) : 0;
const bytesDownloaded = Math.max(Math.min(totalBytesDownloaded, bytesToDownload), 0);
return {
roundTrips,
timeElapsed,
bytesDownloaded,
extraBytesDownloaded,
congestionWindow,
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment