diff --git a/docs/transport.asciidoc b/docs/transport.asciidoc index f4fc722ce..308e7098e 100644 --- a/docs/transport.asciidoc +++ b/docs/transport.asciidoc @@ -32,3 +32,10 @@ class MyTransport extends Transport { } ---- +==== Supported content types + +- `application/json`, in this case the transport will return a plain JavaScript object +- `text/plain`, in this case the transport will return a plain string +- `application/vnd.mapbox-vector-tile`, in this case the transport will return a Buffer +- `application/vnd.elasticsearch+json`, in this case the transport will return a plain JavaScript object + diff --git a/lib/Transport.d.ts b/lib/Transport.d.ts index 912dd96da..2313bf23f 100644 --- a/lib/Transport.d.ts +++ b/lib/Transport.d.ts @@ -155,8 +155,8 @@ export default class Transport { _nextSniff: number; _isSniffing: boolean; constructor(opts: TransportOptions); - request(params: TransportRequestParams, options?: TransportRequestOptions): TransportRequestPromise; - request(params: TransportRequestParams, options?: TransportRequestOptions, callback?: (err: ApiError, result: ApiResponse) => void): TransportRequestCallback; + request, TContext = Context>(params: TransportRequestParams, options?: TransportRequestOptions): TransportRequestPromise>; + request, TContext = Context>(params: TransportRequestParams, options?: TransportRequestOptions, callback?: (err: ApiError, result: ApiResponse) => void): TransportRequestCallback; getConnection(opts: TransportGetConnectionOptions): Connection | null; sniff(opts?: TransportSniffOptions, callback?: (...args: any[]) => void): void; } diff --git a/lib/Transport.js b/lib/Transport.js index cd2d7584c..3e6d9fd3d 100644 --- a/lib/Transport.js +++ b/lib/Transport.js @@ -237,6 +237,7 @@ class Transport { const contentEncoding = (result.headers['content-encoding'] || '').toLowerCase() const isCompressed = contentEncoding.indexOf('gzip') > -1 || contentEncoding.indexOf('deflate') > -1 + const isVectorTile = (result.headers['content-type'] || '').indexOf('application/vnd.mapbox-vector-tile') > -1 /* istanbul ignore else */ if (result.headers['content-length'] !== undefined) { @@ -255,8 +256,9 @@ class Transport { } // if the response is compressed, we must handle it // as buffer for allowing decompression later - let payload = isCompressed ? [] : '' - const onData = isCompressed + // while if it's a vector tile, we should return it as buffer + let payload = isCompressed || isVectorTile ? [] : '' + const onData = isCompressed || isVectorTile ? chunk => { payload.push(chunk) } : chunk => { payload += chunk } const onEnd = err => { @@ -272,7 +274,7 @@ class Transport { if (isCompressed) { unzip(Buffer.concat(payload), onBody) } else { - onBody(null, payload) + onBody(null, isVectorTile ? Buffer.concat(payload) : payload) } } @@ -281,7 +283,7 @@ class Transport { onEnd(new Error('Response aborted while reading the body')) } - if (!isCompressed) { + if (!isCompressed && !isVectorTile) { response.setEncoding('utf8') } @@ -297,7 +299,9 @@ class Transport { this.emit('response', err, result) return callback(err, result) } - if (Buffer.isBuffer(payload)) { + + const isVectorTile = (result.headers['content-type'] || '').indexOf('application/vnd.mapbox-vector-tile') > -1 + if (Buffer.isBuffer(payload) && !isVectorTile) { payload = payload.toString() } const isHead = params.method === 'HEAD' diff --git a/test/unit/transport.test.js b/test/unit/transport.test.js index c617bc3af..e1a6aaa9f 100644 --- a/test/unit/transport.test.js +++ b/test/unit/transport.test.js @@ -2689,3 +2689,76 @@ test('The callback with a sync error should be called in the next tick - ndjson' t.type(transportReturn.catch, 'function') t.type(transportReturn.abort, 'function') }) + +test('Support mapbox vector tile', t => { + t.plan(2) + const mvtContent = 'GoMCCgRtZXRhEikSFAAAAQACAQMBBAAFAgYDBwAIBAkAGAMiDwkAgEAagEAAAP8//z8ADxoOX3NoYXJkcy5mYWlsZWQaD19zaGFyZHMuc2tpcHBlZBoSX3NoYXJkcy5zdWNjZXNzZnVsGg1fc2hhcmRzLnRvdGFsGhlhZ2dyZWdhdGlvbnMuX2NvdW50LmNvdW50GhdhZ2dyZWdhdGlvbnMuX2NvdW50LnN1bRoTaGl0cy50b3RhbC5yZWxhdGlvbhoQaGl0cy50b3RhbC52YWx1ZRoJdGltZWRfb3V0GgR0b29rIgIwACICMAIiCRkAAAAAAAAAACIECgJlcSICOAAogCB4Ag==' + + function handler (req, res) { + res.setHeader('Content-Type', 'application/vnd.mapbox-vector-tile') + res.end(Buffer.from(mvtContent, 'base64')) + } + + buildServer(handler, ({ port }, server) => { + const pool = new ConnectionPool({ Connection }) + pool.addConnection(`http://localhost:${port}`) + + const transport = new Transport({ + emit: () => {}, + connectionPool: pool, + serializer: new Serializer(), + maxRetries: 3, + requestTimeout: 30000, + sniffInterval: false, + sniffOnStart: false + }) + skipProductCheck(transport) + + transport.request({ + method: 'GET', + path: '/hello' + }, (err, { body }) => { + t.error(err) + t.same(body.toString('base64'), Buffer.from(mvtContent, 'base64').toString('base64')) + server.stop() + }) + }) +}) + +test('Compressed mapbox vector tile', t => { + t.plan(2) + const mvtContent = 'GoMCCgRtZXRhEikSFAAAAQACAQMBBAAFAgYDBwAIBAkAGAMiDwkAgEAagEAAAP8//z8ADxoOX3NoYXJkcy5mYWlsZWQaD19zaGFyZHMuc2tpcHBlZBoSX3NoYXJkcy5zdWNjZXNzZnVsGg1fc2hhcmRzLnRvdGFsGhlhZ2dyZWdhdGlvbnMuX2NvdW50LmNvdW50GhdhZ2dyZWdhdGlvbnMuX2NvdW50LnN1bRoTaGl0cy50b3RhbC5yZWxhdGlvbhoQaGl0cy50b3RhbC52YWx1ZRoJdGltZWRfb3V0GgR0b29rIgIwACICMAIiCRkAAAAAAAAAACIECgJlcSICOAAogCB4Ag==' + + function handler (req, res) { + const body = gzipSync(Buffer.from(mvtContent, 'base64')) + res.setHeader('Content-Type', 'application/vnd.mapbox-vector-tile') + res.setHeader('Content-Encoding', 'gzip') + res.setHeader('Content-Length', Buffer.byteLength(body)) + res.end(body) + } + + buildServer(handler, ({ port }, server) => { + const pool = new ConnectionPool({ Connection }) + pool.addConnection(`http://localhost:${port}`) + + const transport = new Transport({ + emit: () => {}, + connectionPool: pool, + serializer: new Serializer(), + maxRetries: 3, + requestTimeout: 30000, + sniffInterval: false, + sniffOnStart: false + }) + skipProductCheck(transport) + + transport.request({ + method: 'GET', + path: '/hello' + }, (err, { body }) => { + t.error(err) + t.same(body.toString('base64'), Buffer.from(mvtContent, 'base64').toString('base64')) + server.stop() + }) + }) +})