/
index.js
128 lines (99 loc) · 3.47 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
'use strict';
var throttle = require('throttleit');
function onRequest(context) {
// Reset dynamic stuff
context.startedAt = null;
context.state = context.request.progressState = null;
context.delayTimer && clearTimeout(context.delayTimer);
context.delayTimer = null;
}
function onResponse(context, response) {
// Mark start timestamp
context.startedAt = Date.now();
// Create state
// Also expose the state throught the request
// See https://github.com/IndigoUnited/node-request-progress/pull/2/files
context.state = context.request.progressState = {
time: {
elapsed: 0,
remaining: null
},
speed: null,
percent: null,
size: {
total: Number(response.headers[context.options.lengthHeader]) || null,
transferred: 0
}
};
// Delay the progress report
context.delayTimer = setTimeout(function () {
context.delayTimer = null;
}, context.options.delay);
}
function onData(context, data) {
context.state.size.transferred += data.length;
!context.delayTimer && context.reportState();
}
function onEnd(context) {
/* istanbul ignore if */
if (context.delayTimer) {
clearTimeout(context.delayTimer);
context.delayTimer = null;
}
context.request.progressState = context.request.progressContext = null;
}
function reportState(context) {
var state;
// Do nothing if still within the initial delay or if already finished
if (context.delayTimer || !context.request.progressState) {
return;
}
state = context.state;
state.time.elapsed = (Date.now() - context.startedAt) / 1000;
// Calculate speed only if 1s has passed
if (state.time.elapsed >= 1) {
state.speed = state.size.transferred / state.time.elapsed;
}
// Calculate percent & remaining only if we know the total size
if (state.size.total != null) {
state.percent = Math.min(state.size.transferred, state.size.total) / state.size.total;
if (state.speed != null) {
state.time.remaining = state.percent !== 1 ? (state.size.total / state.speed) - state.time.elapsed : 0;
state.time.remaining = Math.round(state.time.remaining * 1000) / 1000; // Round to 4 decimals
}
}
context.request.emit('progress', state);
}
function requestProgress(request, options) {
var context;
if (request.progressContext) {
return request;
}
if (request.response) {
throw new Error('Already got response, it\'s too late to track progress');
}
// Parse options
options = options || {};
options.throttle = options.throttle == null ? 1000 : options.throttle;
options.delay = options.delay || 0;
options.lengthHeader = options.lengthHeader || 'content-length';
// Create context
context = {};
context.request = request;
context.options = options;
context.reportState = throttle(reportState.bind(null, context), options.throttle);
// context.startedAt = null;
// context.state = null;
// context.delayTimer = null;
// Attach listeners
request
.on('request', onRequest.bind(null, context))
.on('response', function handleResponse(response) {
response.on('data', onData.bind(null, context));
return onResponse(context, response);
})
.on('end', onEnd.bind(null, context));
request.progressContext = context;
return request;
}
module.exports = requestProgress;