@algolia/requester-node-http 4.25.3 → 4.27.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.
@@ -5,6 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var http = require('http');
6
6
  var https = require('https');
7
7
  var URL = require('url');
8
+ var zlib = require('zlib');
8
9
 
9
10
  /* eslint functional/prefer-readonly-type: 0 */
10
11
  const agentOptions = { keepAlive: true };
@@ -18,6 +19,12 @@ function createNodeHttpRequester({ agent: userGlobalAgent, httpAgent: userHttpAg
18
19
  return new Promise(resolve => {
19
20
  const url = URL.parse(request.url);
20
21
  const path = url.query === null ? url.pathname : `${url.pathname}?${url.query}`;
22
+ const COMPRESSION_THRESHOLD = 750;
23
+ const acceptEncoding = request.headers['accept-encoding'];
24
+ const shouldCompress = request.data !== undefined &&
25
+ Buffer.byteLength(request.data) >= COMPRESSION_THRESHOLD &&
26
+ acceptEncoding !== undefined &&
27
+ acceptEncoding.toLowerCase().includes('gzip');
21
28
  const options = {
22
29
  ...requesterOptions,
23
30
  agent: url.protocol === 'https:' ? httpsAgent : httpAgent,
@@ -27,30 +34,62 @@ function createNodeHttpRequester({ agent: userGlobalAgent, httpAgent: userHttpAg
27
34
  headers: {
28
35
  ...(requesterOptions && requesterOptions.headers ? requesterOptions.headers : {}),
29
36
  ...request.headers,
37
+ ...(shouldCompress ? { 'content-encoding': 'gzip' } : {}),
30
38
  },
31
39
  ...(url.port !== undefined ? { port: url.port || '' } : {}),
32
40
  };
41
+ // eslint-disable-next-line functional/no-let, prefer-const
42
+ let connectTimeout;
43
+ // eslint-disable-next-line functional/no-let
44
+ let responseTimeout;
45
+ // eslint-disable-next-line functional/no-let
46
+ let gunzip;
47
+ const cleanup = () => {
48
+ clearTimeout(connectTimeout);
49
+ clearTimeout(responseTimeout);
50
+ if (gunzip) {
51
+ gunzip.destroy();
52
+ }
53
+ };
54
+ const onError = (error) => {
55
+ cleanup();
56
+ resolve({ status: 0, content: error.message, isTimedOut: false });
57
+ };
33
58
  const req = (url.protocol === 'https:' ? https : http).request(options, response => {
59
+ const contentEncoding = response.headers['content-encoding'];
60
+ const isGzipResponse = contentEncoding !== undefined && contentEncoding.toLowerCase().includes('gzip');
34
61
  // eslint-disable-next-line functional/no-let
35
62
  let contentBuffers = [];
36
- response.on('data', chunk => {
63
+ const onData = (chunk) => {
37
64
  contentBuffers = contentBuffers.concat(chunk);
38
- });
39
- response.on('end', () => {
40
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
41
- clearTimeout(connectTimeout);
42
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
43
- clearTimeout(responseTimeout);
65
+ };
66
+ const onEnd = () => {
67
+ cleanup();
44
68
  resolve({
45
69
  status: response.statusCode || 0,
46
70
  content: Buffer.concat(contentBuffers).toString(),
47
71
  isTimedOut: false,
48
72
  });
49
- });
73
+ };
74
+ response.on('error', onError);
75
+ if (isGzipResponse) {
76
+ gunzip = zlib.createGunzip();
77
+ response.pipe(gunzip);
78
+ gunzip.on('data', onData);
79
+ gunzip.on('end', onEnd);
80
+ gunzip.on('error', onError);
81
+ }
82
+ else {
83
+ response.on('data', onData);
84
+ response.on('end', onEnd);
85
+ }
50
86
  });
51
87
  const createTimeout = (timeout, content) => {
52
88
  return setTimeout(() => {
53
89
  req.abort();
90
+ if (gunzip) {
91
+ gunzip.destroy();
92
+ }
54
93
  resolve({
55
94
  status: 0,
56
95
  content,
@@ -58,22 +97,30 @@ function createNodeHttpRequester({ agent: userGlobalAgent, httpAgent: userHttpAg
58
97
  });
59
98
  }, timeout * 1000);
60
99
  };
61
- const connectTimeout = createTimeout(request.connectTimeout, 'Connection timeout');
62
- // eslint-disable-next-line functional/no-let
63
- let responseTimeout;
64
- req.on('error', error => {
65
- clearTimeout(connectTimeout);
66
- clearTimeout(responseTimeout);
67
- resolve({ status: 0, content: error.message, isTimedOut: false });
68
- });
100
+ connectTimeout = createTimeout(request.connectTimeout, 'Connection timeout');
101
+ req.on('error', onError);
69
102
  req.once('response', () => {
70
103
  clearTimeout(connectTimeout);
71
104
  responseTimeout = createTimeout(request.responseTimeout, 'Socket timeout');
72
105
  });
73
- if (request.data !== undefined) {
74
- req.write(request.data);
106
+ if (request.data !== undefined && shouldCompress) {
107
+ zlib.gzip(request.data, (error, compressedBody) => {
108
+ if (error) {
109
+ onError(error);
110
+ return;
111
+ }
112
+ req.setHeader('content-length', compressedBody.byteLength);
113
+ req.write(compressedBody);
114
+ req.end();
115
+ });
116
+ }
117
+ else {
118
+ if (request.data !== undefined) {
119
+ req.setHeader('content-length', Buffer.byteLength(request.data));
120
+ req.write(request.data);
121
+ }
122
+ req.end();
75
123
  }
76
- req.end();
77
124
  });
78
125
  },
79
126
  destroy() {
@@ -3,6 +3,7 @@ import { Agent } from 'http';
3
3
  import * as https from 'https';
4
4
  import { Agent as Agent$1 } from 'https';
5
5
  import { parse } from 'url';
6
+ import { createGunzip, gzip } from 'zlib';
6
7
 
7
8
  /* eslint functional/prefer-readonly-type: 0 */
8
9
  const agentOptions = { keepAlive: true };
@@ -16,6 +17,12 @@ function createNodeHttpRequester({ agent: userGlobalAgent, httpAgent: userHttpAg
16
17
  return new Promise(resolve => {
17
18
  const url = parse(request.url);
18
19
  const path = url.query === null ? url.pathname : `${url.pathname}?${url.query}`;
20
+ const COMPRESSION_THRESHOLD = 750;
21
+ const acceptEncoding = request.headers['accept-encoding'];
22
+ const shouldCompress = request.data !== undefined &&
23
+ Buffer.byteLength(request.data) >= COMPRESSION_THRESHOLD &&
24
+ acceptEncoding !== undefined &&
25
+ acceptEncoding.toLowerCase().includes('gzip');
19
26
  const options = {
20
27
  ...requesterOptions,
21
28
  agent: url.protocol === 'https:' ? httpsAgent : httpAgent,
@@ -25,30 +32,62 @@ function createNodeHttpRequester({ agent: userGlobalAgent, httpAgent: userHttpAg
25
32
  headers: {
26
33
  ...(requesterOptions && requesterOptions.headers ? requesterOptions.headers : {}),
27
34
  ...request.headers,
35
+ ...(shouldCompress ? { 'content-encoding': 'gzip' } : {}),
28
36
  },
29
37
  ...(url.port !== undefined ? { port: url.port || '' } : {}),
30
38
  };
39
+ // eslint-disable-next-line functional/no-let, prefer-const
40
+ let connectTimeout;
41
+ // eslint-disable-next-line functional/no-let
42
+ let responseTimeout;
43
+ // eslint-disable-next-line functional/no-let
44
+ let gunzip;
45
+ const cleanup = () => {
46
+ clearTimeout(connectTimeout);
47
+ clearTimeout(responseTimeout);
48
+ if (gunzip) {
49
+ gunzip.destroy();
50
+ }
51
+ };
52
+ const onError = (error) => {
53
+ cleanup();
54
+ resolve({ status: 0, content: error.message, isTimedOut: false });
55
+ };
31
56
  const req = (url.protocol === 'https:' ? https : http).request(options, response => {
57
+ const contentEncoding = response.headers['content-encoding'];
58
+ const isGzipResponse = contentEncoding !== undefined && contentEncoding.toLowerCase().includes('gzip');
32
59
  // eslint-disable-next-line functional/no-let
33
60
  let contentBuffers = [];
34
- response.on('data', chunk => {
61
+ const onData = (chunk) => {
35
62
  contentBuffers = contentBuffers.concat(chunk);
36
- });
37
- response.on('end', () => {
38
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
39
- clearTimeout(connectTimeout);
40
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
41
- clearTimeout(responseTimeout);
63
+ };
64
+ const onEnd = () => {
65
+ cleanup();
42
66
  resolve({
43
67
  status: response.statusCode || 0,
44
68
  content: Buffer.concat(contentBuffers).toString(),
45
69
  isTimedOut: false,
46
70
  });
47
- });
71
+ };
72
+ response.on('error', onError);
73
+ if (isGzipResponse) {
74
+ gunzip = createGunzip();
75
+ response.pipe(gunzip);
76
+ gunzip.on('data', onData);
77
+ gunzip.on('end', onEnd);
78
+ gunzip.on('error', onError);
79
+ }
80
+ else {
81
+ response.on('data', onData);
82
+ response.on('end', onEnd);
83
+ }
48
84
  });
49
85
  const createTimeout = (timeout, content) => {
50
86
  return setTimeout(() => {
51
87
  req.abort();
88
+ if (gunzip) {
89
+ gunzip.destroy();
90
+ }
52
91
  resolve({
53
92
  status: 0,
54
93
  content,
@@ -56,22 +95,30 @@ function createNodeHttpRequester({ agent: userGlobalAgent, httpAgent: userHttpAg
56
95
  });
57
96
  }, timeout * 1000);
58
97
  };
59
- const connectTimeout = createTimeout(request.connectTimeout, 'Connection timeout');
60
- // eslint-disable-next-line functional/no-let
61
- let responseTimeout;
62
- req.on('error', error => {
63
- clearTimeout(connectTimeout);
64
- clearTimeout(responseTimeout);
65
- resolve({ status: 0, content: error.message, isTimedOut: false });
66
- });
98
+ connectTimeout = createTimeout(request.connectTimeout, 'Connection timeout');
99
+ req.on('error', onError);
67
100
  req.once('response', () => {
68
101
  clearTimeout(connectTimeout);
69
102
  responseTimeout = createTimeout(request.responseTimeout, 'Socket timeout');
70
103
  });
71
- if (request.data !== undefined) {
72
- req.write(request.data);
104
+ if (request.data !== undefined && shouldCompress) {
105
+ gzip(request.data, (error, compressedBody) => {
106
+ if (error) {
107
+ onError(error);
108
+ return;
109
+ }
110
+ req.setHeader('content-length', compressedBody.byteLength);
111
+ req.write(compressedBody);
112
+ req.end();
113
+ });
114
+ }
115
+ else {
116
+ if (request.data !== undefined) {
117
+ req.setHeader('content-length', Buffer.byteLength(request.data));
118
+ req.write(request.data);
119
+ }
120
+ req.end();
73
121
  }
74
- req.end();
75
122
  });
76
123
  },
77
124
  destroy() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/requester-node-http",
3
- "version": "4.25.3",
3
+ "version": "4.27.0",
4
4
  "private": false,
5
5
  "description": "Promise-based request library for node using the native http module.",
6
6
  "repository": {
@@ -17,6 +17,6 @@
17
17
  "dist"
18
18
  ],
19
19
  "dependencies": {
20
- "@algolia/requester-common": "4.25.3"
20
+ "@algolia/requester-common": "4.27.0"
21
21
  }
22
22
  }