|
1 | 1 | 'use strict'
|
2 | 2 |
|
3 | 3 | const Qs = require('qs')
|
| 4 | +const qsDefaultEncoder = require('qs/lib/utils').encode |
4 | 5 | const isNode = require('detect-node')
|
5 | 6 | const ndjson = require('ndjson')
|
6 | 7 | const pump = require('pump')
|
@@ -112,7 +113,39 @@ function requestAPI (config, options, callback) {
|
112 | 113 | headers['Content-Type'] = `multipart/form-data; boundary=${stream.boundary}`
|
113 | 114 | }
|
114 | 115 |
|
115 |
| - const qs = Qs.stringify(options.qs, {arrayFormat: 'repeat'}) |
| 116 | + const qs = Qs.stringify(options.qs, { |
| 117 | + arrayFormat: 'repeat', |
| 118 | + encoder: data => { |
| 119 | + // TODO: future releases of qs will provide the default |
| 120 | + // encoder as a 2nd argument to this function; it will |
| 121 | + // no longer be necessary to import qsDefaultEncoder |
| 122 | + if (Buffer.isBuffer(data)) { |
| 123 | + let uriEncoded = '' |
| 124 | + for (const byte of data) { |
| 125 | + // https://tools.ietf.org/html/rfc3986#page-14 |
| 126 | + // ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) |
| 127 | + if ( |
| 128 | + (byte >= 0x41 && byte <= 0x5A) || |
| 129 | + (byte >= 0x61 && byte <= 0x7A) || |
| 130 | + (byte >= 0x30 && byte <= 0x39) || |
| 131 | + (byte === 0x2D) || |
| 132 | + (byte === 0x2E) || |
| 133 | + (byte === 0x5F) || |
| 134 | + (byte === 0x7E) |
| 135 | + ) { |
| 136 | + uriEncoded += String.fromCharCode(byte) |
| 137 | + } else { |
| 138 | + const hex = byte.toString(16) |
| 139 | + // String.prototype.padStart() not widely supported yet |
| 140 | + const padded = hex.length === 1 ? `0${hex}` : hex |
| 141 | + uriEncoded += `%${padded}` |
| 142 | + } |
| 143 | + } |
| 144 | + return uriEncoded |
| 145 | + } |
| 146 | + return qsDefaultEncoder(data) |
| 147 | + } |
| 148 | + }) |
116 | 149 | const req = request(config.protocol)({
|
117 | 150 | hostname: config.host,
|
118 | 151 | path: `${config['api-path']}${options.path}?${qs}`,
|
|
0 commit comments