postman-runtime 7.30.0 → 7.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.yaml +18 -0
- package/README.md +16 -1
- package/dist/index.js +1 -1
- package/lib/authorizer/auth-interface.js +23 -2
- package/lib/authorizer/index.js +2 -1
- package/lib/authorizer/jwt.js +251 -0
- package/lib/authorizer/oauth2.js +13 -1
- package/lib/requester/browser/request.js +2 -1
- package/lib/requester/request-wrapper.js +4 -1
- package/lib/requester/requester.js +28 -1
- package/lib/requester/sse-processor.js +129 -0
- package/lib/runner/extensions/event.command.js +1 -4
- package/lib/runner/extensions/http-request.command.js +5 -1
- package/lib/runner/extract-runnable-items.js +41 -16
- package/lib/runner/index.js +3 -1
- package/lib/runner/request-helpers-presend.js +14 -8
- package/package.json +17 -13
|
@@ -8,10 +8,11 @@ var _ = require('lodash'),
|
|
|
8
8
|
* @constructs AuthInterface
|
|
9
9
|
* @param {RequestAuth} auth -
|
|
10
10
|
* @param {Object} protocolProfileBehavior - Protocol profile behaviors
|
|
11
|
+
* @param {Object} [options] - authorizer options
|
|
11
12
|
* @return {AuthInterface}
|
|
12
13
|
* @throws {Error}
|
|
13
14
|
*/
|
|
14
|
-
createAuthInterface = function (auth, protocolProfileBehavior) {
|
|
15
|
+
createAuthInterface = function (auth, protocolProfileBehavior, options) {
|
|
15
16
|
if (!(auth && auth.parameters && auth.parameters())) {
|
|
16
17
|
throw new Error('runtime~createAuthInterface: invalid auth');
|
|
17
18
|
}
|
|
@@ -34,12 +35,25 @@ createAuthInterface = function (auth, protocolProfileBehavior) {
|
|
|
34
35
|
var paramVariable;
|
|
35
36
|
|
|
36
37
|
if (_.isString(keys)) {
|
|
38
|
+
// This hacky handling here localizes the impact for refresh token in runtime, we want to avoid
|
|
39
|
+
// changing the auth handler interface in general and localizing this here allows us to refactor
|
|
40
|
+
// easily if we decide to move the oauth2 token generation flow into runtime
|
|
41
|
+
if (keys === 'refreshOAuth2Token') {
|
|
42
|
+
return options && options.refreshOAuth2Token;
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
paramVariable = auth.parameters().one(keys);
|
|
38
46
|
|
|
39
47
|
return paramVariable && paramVariable.get();
|
|
40
48
|
}
|
|
41
49
|
if (_.isArray(keys)) {
|
|
42
50
|
return _.transform(keys, function (paramObject, key) {
|
|
51
|
+
if (key === 'refreshOAuth2Token') {
|
|
52
|
+
paramObject[key] = options && options.refreshOAuth2Token;
|
|
53
|
+
|
|
54
|
+
return paramObject;
|
|
55
|
+
}
|
|
56
|
+
|
|
43
57
|
paramVariable = auth.parameters().one(key);
|
|
44
58
|
paramVariable && (paramObject[key] = paramVariable.get());
|
|
45
59
|
|
|
@@ -53,13 +67,14 @@ createAuthInterface = function (auth, protocolProfileBehavior) {
|
|
|
53
67
|
/**
|
|
54
68
|
* @param {String|Object} key -
|
|
55
69
|
* @param {*} [value] -
|
|
70
|
+
* @param {Boolean} [system] - Explicitly allow setting a param as a system param
|
|
56
71
|
* @return {AuthInterface}
|
|
57
72
|
* @example
|
|
58
73
|
* set('foo', 'bar')
|
|
59
74
|
* set({foo: 'bar', 'alpha': 'beta'})
|
|
60
75
|
* @throws {Error}
|
|
61
76
|
*/
|
|
62
|
-
set: function (key, value) {
|
|
77
|
+
set: function (key, value, system) {
|
|
63
78
|
var modifiedParams = {},
|
|
64
79
|
parameters;
|
|
65
80
|
|
|
@@ -77,6 +92,12 @@ createAuthInterface = function (auth, protocolProfileBehavior) {
|
|
|
77
92
|
_.forEach(modifiedParams, function (value, key) {
|
|
78
93
|
var param = parameters.one(key);
|
|
79
94
|
|
|
95
|
+
// If we are updating a user param inside runtime, it is now a system param for this execution and
|
|
96
|
+
// should be marked as such
|
|
97
|
+
if (system) {
|
|
98
|
+
param.system = true;
|
|
99
|
+
}
|
|
100
|
+
|
|
80
101
|
if (!param) {
|
|
81
102
|
return parameters.add({ key: key, value: value, system: true });
|
|
82
103
|
}
|
package/lib/authorizer/index.js
CHANGED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
const _ = require('lodash'),
|
|
2
|
+
jose = require('jose'),
|
|
3
|
+
|
|
4
|
+
// jwt key constants
|
|
5
|
+
BEARER_AUTH_PREFIX = 'Bearer',
|
|
6
|
+
QUERY_KEY = 'token',
|
|
7
|
+
AUTH_KEYS = {
|
|
8
|
+
ALGORITHM: 'algorithm',
|
|
9
|
+
HEADER: 'header',
|
|
10
|
+
HEADER_ALGORITHM: 'alg',
|
|
11
|
+
PAYLOAD: 'payload',
|
|
12
|
+
SECRET: 'secret',
|
|
13
|
+
IS_SECRET_BASE_64_ENCODED: 'isSecretBase64Encoded',
|
|
14
|
+
PRIVATE_KEY: 'privateKey',
|
|
15
|
+
ADD_TOKEN_TO: 'addTokenTo',
|
|
16
|
+
HEADER_PREFIX: 'headerPrefix',
|
|
17
|
+
QUERY_PARAM_KEY: 'queryParamKey'
|
|
18
|
+
},
|
|
19
|
+
ADD_TOKEN_TO_TARGETS = {
|
|
20
|
+
HEADER: 'header',
|
|
21
|
+
QUERY_PARAM: 'queryParam'
|
|
22
|
+
},
|
|
23
|
+
AUTHORIZATION = 'Authorization',
|
|
24
|
+
BASE64 = 'base64',
|
|
25
|
+
ASCII = 'ascii',
|
|
26
|
+
SPACE = ' ',
|
|
27
|
+
|
|
28
|
+
// HS Algorithms
|
|
29
|
+
HS_ALGORITHMS = {
|
|
30
|
+
HS256: 'HS256',
|
|
31
|
+
HS384: 'HS384',
|
|
32
|
+
HS512: 'HS512'
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// algorithms supported
|
|
36
|
+
ALGORITHMS_SUPPORTED = {
|
|
37
|
+
...HS_ALGORITHMS,
|
|
38
|
+
RS256: 'RS256',
|
|
39
|
+
RS384: 'RS384',
|
|
40
|
+
RS512: 'RS512',
|
|
41
|
+
PS256: 'PS256',
|
|
42
|
+
PS384: 'PS384',
|
|
43
|
+
PS512: 'PS512',
|
|
44
|
+
ES256: 'ES256',
|
|
45
|
+
ES384: 'ES384',
|
|
46
|
+
ES512: 'ES512'
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* add the JWT Token to the request in auth header or query param
|
|
51
|
+
*
|
|
52
|
+
* @param {AuthInterface} auth - auth
|
|
53
|
+
* @param {Request} request - request
|
|
54
|
+
* @param {string} jwtToken - base64encoded jwt token
|
|
55
|
+
*/
|
|
56
|
+
function addTokenToRequest (auth, request, jwtToken) {
|
|
57
|
+
const addTokenTo = auth.get(AUTH_KEYS.ADD_TOKEN_TO) || ADD_TOKEN_TO_TARGETS.HEADER,
|
|
58
|
+
queryParamKey = auth.get(AUTH_KEYS.QUERY_PARAM_KEY) || QUERY_KEY,
|
|
59
|
+
headerPrefix = auth.get(AUTH_KEYS.HEADER_PREFIX) || BEARER_AUTH_PREFIX;
|
|
60
|
+
|
|
61
|
+
if (addTokenTo === ADD_TOKEN_TO_TARGETS.HEADER) {
|
|
62
|
+
request.removeHeader(AUTHORIZATION, { ignoreCase: true });
|
|
63
|
+
|
|
64
|
+
request.addHeader({
|
|
65
|
+
key: AUTHORIZATION,
|
|
66
|
+
value: headerPrefix + SPACE + jwtToken,
|
|
67
|
+
system: true
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else if (addTokenTo === ADD_TOKEN_TO_TARGETS.QUERY_PARAM) {
|
|
71
|
+
request.url.query.remove(function (query) {
|
|
72
|
+
return query && query.key === queryParamKey;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
request.url.query.add({
|
|
76
|
+
key: queryParamKey,
|
|
77
|
+
value: jwtToken,
|
|
78
|
+
system: true
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Request auth payload structure
|
|
85
|
+
* request:{
|
|
86
|
+
* auth:{
|
|
87
|
+
* type:'jwt',
|
|
88
|
+
* jwt:{
|
|
89
|
+
* algorithm: <string> - ALGORITHMS_SUPPORTED,
|
|
90
|
+
* header: <JSON string> | JSON Object
|
|
91
|
+
* payload: <JSON string> | JSON Object
|
|
92
|
+
* secret: <string> - secret for HS algorithms
|
|
93
|
+
* isSecretBase64Encoded: <boolean> - optional property used when <secret> for HS algorithms
|
|
94
|
+
* is encoded in base64 format
|
|
95
|
+
* privateKey: <string> - PEM format private key for RS, PS, ES algorithms
|
|
96
|
+
* addTokenTo: <string> - possible values - header | queryParam,
|
|
97
|
+
* headerPrefix: <string> - prefix added before jwt token in header - Default Bearer
|
|
98
|
+
* queryParamKey: <string> - optional property added when <addTokenTo> set to [queryParam],
|
|
99
|
+
* }
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
*/
|
|
103
|
+
/**
|
|
104
|
+
* @implements {AuthHandlerInterface}
|
|
105
|
+
*/
|
|
106
|
+
module.exports = {
|
|
107
|
+
/**
|
|
108
|
+
* @property {AuthHandlerInterface~AuthManifest}
|
|
109
|
+
*/
|
|
110
|
+
manifest: {
|
|
111
|
+
info: {
|
|
112
|
+
name: 'jwt',
|
|
113
|
+
version: '1.0.0'
|
|
114
|
+
},
|
|
115
|
+
updates: [
|
|
116
|
+
{
|
|
117
|
+
property: 'Authorization',
|
|
118
|
+
type: 'header'
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
property: '*',
|
|
122
|
+
type: 'url.param'
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Initializes an item (extracts parameters from intermediate requests if any, etc)
|
|
129
|
+
* before the actual authorization step
|
|
130
|
+
*
|
|
131
|
+
* @param {AuthInterface} auth -
|
|
132
|
+
* @param {Response} response -
|
|
133
|
+
* @param {AuthHandlerInterface~authInitHookCallback} done -
|
|
134
|
+
*/
|
|
135
|
+
init: function (auth, response, done) {
|
|
136
|
+
done();
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Checks the item, and fetches any parameters that are not already provided.
|
|
141
|
+
*
|
|
142
|
+
* @param {AuthInterface} auth -
|
|
143
|
+
* @param {AuthHandlerInterface~authPreHookCallback} done -
|
|
144
|
+
*/
|
|
145
|
+
pre: function (auth, done) {
|
|
146
|
+
return done(null, true);
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Verifies whether the auth succeeded
|
|
151
|
+
*
|
|
152
|
+
* @param {AuthInterface} auth -
|
|
153
|
+
* @param {Response} response -
|
|
154
|
+
* @param {AuthHandlerInterface~authPostHookCallback} done -
|
|
155
|
+
*/
|
|
156
|
+
post: function (auth, response, done) {
|
|
157
|
+
done(null, true);
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Signs the request
|
|
162
|
+
*
|
|
163
|
+
* @param {AuthInterface} auth -
|
|
164
|
+
* @param {Request} request -
|
|
165
|
+
* @param {AuthHandlerInterface~authSignHookCallback} done -
|
|
166
|
+
*/
|
|
167
|
+
sign: function (auth, request, done) {
|
|
168
|
+
const algorithm = auth.get(AUTH_KEYS.ALGORITHM),
|
|
169
|
+
secret = auth.get(AUTH_KEYS.SECRET),
|
|
170
|
+
privateKey = auth.get(AUTH_KEYS.PRIVATE_KEY),
|
|
171
|
+
isHsAlgorithm = HS_ALGORITHMS[algorithm];
|
|
172
|
+
|
|
173
|
+
// bail out - invalid algorithm
|
|
174
|
+
if (!ALGORITHMS_SUPPORTED[algorithm]) {
|
|
175
|
+
return done(new Error('invalid algorithm'));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// bail out - secret not a valid string
|
|
179
|
+
if (isHsAlgorithm && (!secret || !_.isString(secret))) {
|
|
180
|
+
return done(new Error('Invalid secret key. Enter a valid key.'));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// bail out - private key not a valid string
|
|
184
|
+
if (!isHsAlgorithm && (!privateKey || !_.isString(privateKey))) {
|
|
185
|
+
return done(new Error('Invalid private key. Enter a valid key.'));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const rawHeader = auth.get(AUTH_KEYS.HEADER),
|
|
190
|
+
rawPayload = auth.get(AUTH_KEYS.PAYLOAD);
|
|
191
|
+
|
|
192
|
+
let header = rawHeader,
|
|
193
|
+
payload = rawPayload;
|
|
194
|
+
|
|
195
|
+
if (typeof rawHeader === 'string') {
|
|
196
|
+
const trimmedHeader = rawHeader.trim();
|
|
197
|
+
|
|
198
|
+
header = trimmedHeader && JSON.parse(trimmedHeader);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// treat root level alg as source of truth.
|
|
202
|
+
// If header is not set, use empty object.
|
|
203
|
+
header = header ? { ...header, alg: algorithm } : { alg: algorithm };
|
|
204
|
+
|
|
205
|
+
if (typeof rawPayload === 'string') {
|
|
206
|
+
const trimmedPayload = rawPayload.trim();
|
|
207
|
+
|
|
208
|
+
payload = trimmedPayload ? JSON.parse(trimmedPayload) : {};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// HS Algorithms use secret for token generation
|
|
212
|
+
if (isHsAlgorithm) {
|
|
213
|
+
const isBase64 = auth.get(AUTH_KEYS.IS_SECRET_BASE_64_ENCODED),
|
|
214
|
+
rawSecret = isBase64 ? Buffer.from(secret, BASE64).toString(ASCII) : secret;
|
|
215
|
+
|
|
216
|
+
new jose.SignJWT(payload)
|
|
217
|
+
.setProtectedHeader(header)
|
|
218
|
+
.sign(new TextEncoder().encode(rawSecret))
|
|
219
|
+
.then((token) => {
|
|
220
|
+
addTokenToRequest(auth, request, token);
|
|
221
|
+
|
|
222
|
+
return done();
|
|
223
|
+
})
|
|
224
|
+
.catch((err) => {
|
|
225
|
+
done(err);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// RS,PS,ES Algorithms use private key for token generation
|
|
230
|
+
else {
|
|
231
|
+
jose.importPKCS8(privateKey, algorithm)
|
|
232
|
+
.then((signKey) => {
|
|
233
|
+
return new jose.SignJWT(payload)
|
|
234
|
+
.setProtectedHeader(header)
|
|
235
|
+
.sign(signKey);
|
|
236
|
+
})
|
|
237
|
+
.then((token) => {
|
|
238
|
+
addTokenToRequest(auth, request, token);
|
|
239
|
+
|
|
240
|
+
return done();
|
|
241
|
+
})
|
|
242
|
+
.catch((err) => {
|
|
243
|
+
done(err);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
done(err);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
package/lib/authorizer/oauth2.js
CHANGED
|
@@ -58,7 +58,19 @@ module.exports = {
|
|
|
58
58
|
* @param {AuthHandlerInterface~authPreHookCallback} done -
|
|
59
59
|
*/
|
|
60
60
|
pre: function (auth, done) {
|
|
61
|
-
|
|
61
|
+
const id = auth.get('id'),
|
|
62
|
+
refreshOAuth2Token = auth.get('refreshOAuth2Token');
|
|
63
|
+
|
|
64
|
+
if (!(id && _.isFunction(refreshOAuth2Token))) {
|
|
65
|
+
return done(null, Boolean(auth.get('accessToken')));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
refreshOAuth2Token(id, (_, accessToken) => {
|
|
69
|
+
accessToken && auth.set('accessToken', accessToken, true);
|
|
70
|
+
|
|
71
|
+
// fallback to existing token
|
|
72
|
+
done(null, Boolean(auth.get('accessToken')));
|
|
73
|
+
});
|
|
62
74
|
},
|
|
63
75
|
|
|
64
76
|
/**
|
|
@@ -90,7 +90,7 @@ function forEachAsync (items, fn, cb) {
|
|
|
90
90
|
// request
|
|
91
91
|
//
|
|
92
92
|
|
|
93
|
-
function request(originalRequest, options, onStart, callback) {
|
|
93
|
+
function request(originalRequest, options, onStart, onData, callback) {
|
|
94
94
|
var options_onResponse = options.onResponse; // Save this for later.
|
|
95
95
|
var XHR = _.get(options, ['agents', options.url && options.url.protocol.slice(0, -1), 'agentClass']) || XMLHttpRequest;
|
|
96
96
|
|
|
@@ -115,6 +115,7 @@ function request(originalRequest, options, onStart, callback) {
|
|
|
115
115
|
return callback(new Error("options.uri must be a string"));
|
|
116
116
|
|
|
117
117
|
options.onStart = onStart
|
|
118
|
+
options.onData = onData // NOTE: not implemented
|
|
118
119
|
options.callback = callback
|
|
119
120
|
options.method = options.method || 'GET';
|
|
120
121
|
options.headers = _.reduce(options.headers || {}, function (accumulator, value, key) {
|
|
@@ -68,7 +68,7 @@ var _ = require('lodash'),
|
|
|
68
68
|
// @todo trigger console warning (using callback) if not enabled.
|
|
69
69
|
requests.enableNodeExtraCACerts();
|
|
70
70
|
|
|
71
|
-
module.exports = function (request, options, onStart, callback) {
|
|
71
|
+
module.exports = function (request, options, onStart, onData, callback) {
|
|
72
72
|
var req = {};
|
|
73
73
|
|
|
74
74
|
async.waterfall([
|
|
@@ -88,6 +88,9 @@ module.exports = function (request, options, onStart, callback) {
|
|
|
88
88
|
|
|
89
89
|
// emit responseStart event
|
|
90
90
|
request.on('response', onStart);
|
|
91
|
+
|
|
92
|
+
// emit responseData event
|
|
93
|
+
request.on('data', onData);
|
|
91
94
|
});
|
|
92
95
|
|
|
93
96
|
return req;
|
|
@@ -6,8 +6,10 @@ var _ = require('lodash'),
|
|
|
6
6
|
sdk = require('postman-collection'),
|
|
7
7
|
requests = require('./request-wrapper'),
|
|
8
8
|
dryRun = require('./dry-run'),
|
|
9
|
+
SSEProcessor = require('./sse-processor'),
|
|
9
10
|
|
|
10
11
|
RESPONSE_START_EVENT_BASE = 'response.start.',
|
|
12
|
+
RESPONSE_DATA_EVENT_BASE = 'response.data.',
|
|
11
13
|
RESPONSE_END_EVENT_BASE = 'response.end.',
|
|
12
14
|
|
|
13
15
|
RESPONSE_START = 'responseStart',
|
|
@@ -138,6 +140,10 @@ class Requester extends EventEmitter {
|
|
|
138
140
|
if (!_.isFinite(this.options.timeout)) {
|
|
139
141
|
this.options.timeout = undefined;
|
|
140
142
|
}
|
|
143
|
+
|
|
144
|
+
// Tracks server sent events
|
|
145
|
+
this.isSSEStream = false;
|
|
146
|
+
this.sseProcessor = null;
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
/**
|
|
@@ -299,6 +305,24 @@ class Requester extends EventEmitter {
|
|
|
299
305
|
};
|
|
300
306
|
},
|
|
301
307
|
|
|
308
|
+
onData = function (data) {
|
|
309
|
+
const emit = (data) => {
|
|
310
|
+
self.emit(RESPONSE_DATA_EVENT_BASE + id, data);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
if (self.isSSEStream) {
|
|
314
|
+
if (!self.sseProcessor) {
|
|
315
|
+
self.sseProcessor = new SSEProcessor(emit);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
self.sseProcessor.onData(data);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
// TODO: Enable emit for regular HTTP requests
|
|
322
|
+
// emit(data);
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
|
|
302
326
|
/**
|
|
303
327
|
* Helper function to trigger `responseStart` callback and
|
|
304
328
|
* - transform postman-request response instance to SDK Response
|
|
@@ -337,6 +361,9 @@ class Requester extends EventEmitter {
|
|
|
337
361
|
header: responseHeaders
|
|
338
362
|
});
|
|
339
363
|
|
|
364
|
+
self.isSSEStream = String(sdkResponse.headers.get('content-type'))
|
|
365
|
+
.toLowerCase() === 'text/event-stream';
|
|
366
|
+
|
|
340
367
|
// prepare history from request debug data
|
|
341
368
|
history = getExecutionHistory(_.get(response, 'request._debug'));
|
|
342
369
|
|
|
@@ -396,7 +423,7 @@ class Requester extends EventEmitter {
|
|
|
396
423
|
return onEnd(new Error(ERROR_RESTRICTED_ADDRESS + hostname));
|
|
397
424
|
}
|
|
398
425
|
|
|
399
|
-
return requests(request, requestOptions, onStart, function (err, res, resBody, debug) {
|
|
426
|
+
return requests(request, requestOptions, onStart, onData, function (err, res, resBody, debug) {
|
|
400
427
|
// prepare history from request debug data
|
|
401
428
|
var history = getExecutionHistory(debug),
|
|
402
429
|
responseTime,
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const bom = [239, 187, 191],
|
|
2
|
+
colon = 58,
|
|
3
|
+
lineFeed = 10,
|
|
4
|
+
carriageReturn = 13,
|
|
5
|
+
|
|
6
|
+
// Beyond 256KB we could not observe any gain in performance
|
|
7
|
+
maxBufferAheadAllocation = 1024 * 256;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function hasBom (buf) {
|
|
11
|
+
return bom.every((charCode, index) => {
|
|
12
|
+
return buf[index] === charCode;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Adapted from https://github.com/EventSource/eventsource
|
|
17
|
+
class SSEStream {
|
|
18
|
+
constructor (emit) {
|
|
19
|
+
this.emit = emit;
|
|
20
|
+
|
|
21
|
+
this.buf = undefined;
|
|
22
|
+
this.newBuffer = undefined;
|
|
23
|
+
this.startingPos = 0;
|
|
24
|
+
this.startingFieldLength = -1;
|
|
25
|
+
this.newBufferSize = 0;
|
|
26
|
+
this.bytesUsed = 0;
|
|
27
|
+
this.discardTrailingNewline = false;
|
|
28
|
+
|
|
29
|
+
// Accumulates the event data util a new line is encountered
|
|
30
|
+
this.eventBuffer = undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
onData (chunk) {
|
|
34
|
+
if (!this.buf) {
|
|
35
|
+
this.buf = chunk;
|
|
36
|
+
if (hasBom(this.buf)) {
|
|
37
|
+
this.buf = this.buf.slice(bom.length);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.bytesUsed = this.buf.length;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
if (chunk.length > this.buf.length - this.bytesUsed) {
|
|
44
|
+
this.newBufferSize = (this.buf.length * 2) + chunk.length;
|
|
45
|
+
|
|
46
|
+
if (this.newBufferSize > maxBufferAheadAllocation) {
|
|
47
|
+
this.newBufferSize = this.buf.length + chunk.length + maxBufferAheadAllocation;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.newBuffer = Buffer.alloc(this.newBufferSize);
|
|
51
|
+
this.buf.copy(this.newBuffer, 0, 0, this.bytesUsed);
|
|
52
|
+
this.buf = this.newBuffer;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
chunk.copy(this.buf, this.bytesUsed);
|
|
56
|
+
this.bytesUsed += chunk.length;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let pos = 0,
|
|
60
|
+
length = this.bytesUsed;
|
|
61
|
+
|
|
62
|
+
while (pos < length) {
|
|
63
|
+
if (this.discardTrailingNewline) {
|
|
64
|
+
if (this.buf[pos] === lineFeed) {
|
|
65
|
+
++pos;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.discardTrailingNewline = false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let lineLength = -1,
|
|
72
|
+
fieldLength = this.startingFieldLength,
|
|
73
|
+
c;
|
|
74
|
+
|
|
75
|
+
for (let i = this.startingPos; lineLength < 0 && i < length; ++i) {
|
|
76
|
+
c = this.buf[i];
|
|
77
|
+
|
|
78
|
+
if (c === colon) {
|
|
79
|
+
if (fieldLength < 0) {
|
|
80
|
+
fieldLength = i - pos;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (c === carriageReturn) {
|
|
84
|
+
this.discardTrailingNewline = true;
|
|
85
|
+
lineLength = i - pos;
|
|
86
|
+
}
|
|
87
|
+
else if (c === lineFeed) {
|
|
88
|
+
lineLength = i - pos;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (lineLength < 0) {
|
|
93
|
+
this.startingPos = length - pos;
|
|
94
|
+
this.startingFieldLength = fieldLength;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.startingPos = 0;
|
|
99
|
+
this.startingFieldLength = -1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Dispatch event when a new line is encountered
|
|
103
|
+
if (lineLength === 0) {
|
|
104
|
+
this.eventBuffer = Buffer.from([...(this.eventBuffer || []), ...Buffer.from([lineFeed])]);
|
|
105
|
+
|
|
106
|
+
this.emit(this.eventBuffer);
|
|
107
|
+
this.eventBuffer = undefined;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const bufToCopy = this.buf.slice(pos, pos + lineLength + 1);
|
|
111
|
+
|
|
112
|
+
this.eventBuffer = Buffer.from([...(this.eventBuffer || []), ...bufToCopy]);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pos += lineLength + 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (pos === length) {
|
|
119
|
+
this.buf = undefined;
|
|
120
|
+
this.bytesUsed = 0;
|
|
121
|
+
}
|
|
122
|
+
else if (pos > 0) {
|
|
123
|
+
this.buf = this.buf.slice(pos, this.bytesUsed);
|
|
124
|
+
this.bytesUsed = this.buf.length;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = SSEStream;
|
|
@@ -6,7 +6,7 @@ var _ = require('lodash'),
|
|
|
6
6
|
sdk = require('postman-collection'),
|
|
7
7
|
sandbox = require('postman-sandbox'),
|
|
8
8
|
serialisedError = require('serialised-error'),
|
|
9
|
-
ToughCookie = require('tough-cookie').Cookie,
|
|
9
|
+
ToughCookie = require('@postman/tough-cookie').Cookie,
|
|
10
10
|
|
|
11
11
|
createItemContext = require('../create-item-context'),
|
|
12
12
|
|
|
@@ -328,9 +328,6 @@ module.exports = {
|
|
|
328
328
|
|
|
329
329
|
!Array.isArray(args) && (args = []);
|
|
330
330
|
|
|
331
|
-
// set expected args length to make sure callback is always called
|
|
332
|
-
args.length = cookieStore[fnName].length - 1;
|
|
333
|
-
|
|
334
331
|
// there's no way cookie store can identify the difference
|
|
335
332
|
// between regular and programmatic access. So, for now
|
|
336
333
|
// we check for programmatic access using the cookieJar
|
|
@@ -13,6 +13,7 @@ var _ = require('lodash'),
|
|
|
13
13
|
RequesterPool = require('../../requester').RequesterPool,
|
|
14
14
|
|
|
15
15
|
RESPONSE_START_EVENT_BASE = 'response.start.',
|
|
16
|
+
RESPONSE_DATA_EVENT_BASE = 'response.data.',
|
|
16
17
|
RESPONSE_END_EVENT_BASE = 'response.end.';
|
|
17
18
|
|
|
18
19
|
module.exports = {
|
|
@@ -25,7 +26,7 @@ module.exports = {
|
|
|
25
26
|
|
|
26
27
|
// the http trigger is actually directly triggered by the requester
|
|
27
28
|
// todo - figure out whether we should trigger it from here rather than the requester.
|
|
28
|
-
triggers: ['beforeRequest', 'request', 'responseStart', 'io'],
|
|
29
|
+
triggers: ['beforeRequest', 'request', 'responseStart', 'responseData', 'io'],
|
|
29
30
|
|
|
30
31
|
process: {
|
|
31
32
|
/**
|
|
@@ -148,6 +149,9 @@ module.exports = {
|
|
|
148
149
|
|
|
149
150
|
requester.on(RESPONSE_END_EVENT_BASE + requestId, self.triggers.io.bind(self.triggers));
|
|
150
151
|
|
|
152
|
+
requester.on(RESPONSE_DATA_EVENT_BASE + requestId, function (data) {
|
|
153
|
+
self.triggers.responseData(context.coords, data);
|
|
154
|
+
});
|
|
151
155
|
// eslint-disable-next-line max-len
|
|
152
156
|
xhr = requester.request(requestId, item.request, context.protocolProfileBehavior, function (err, res, req, cookies, history) {
|
|
153
157
|
err = err || null;
|