Skip to content

Commit

Permalink
feat(gzip): add opts.gzip convenience opt
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Jul 27, 2018
1 parent 9287951 commit 340abe0
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 3 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,15 @@ packages.
See also [`opts.retry`](#opts-retry) to provide all retry options as a single
object.

##### <a name="opts-gzip"></a> `opts.gzip`

* Type: Boolean
* Default: false

If true, `npm-registry-fetch` will set the `Content-Encoding` header to `gzip`
and use `zlib.gzip()` or `zlib.createGzip()` to gzip-encode
[`opts.body`](#opts-body).

##### <a name="opts-headers"></a> `opts.headers`

* Type: Object
Expand Down
3 changes: 2 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = figgyPudding({
'fetch-retry-maxtimeout': {},
'fetch-retry-mintimeout': {},
'gid': {},
'gzip': {},
'headers': {},
'https-proxy': {},
'integrity': {},
Expand Down Expand Up @@ -56,7 +57,7 @@ module.exports = figgyPudding({
'prefer-online': {},
'projectScope': {},
'project-scope': 'projectScope',
'Promise': {},
'Promise': {default: () => Promise},
'proxy': {},
'query': {},
'refer': {},
Expand Down
17 changes: 15 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const fetch = require('make-fetch-happen')
const npa = require('npm-package-arg')
const qs = require('querystring')
const url = require('url')
const zlib = require('zlib')

module.exports = regFetch
function regFetch (uri, opts) {
Expand Down Expand Up @@ -38,6 +39,18 @@ function regFetch (uri, opts) {
} else if (body && !headers['content-type']) {
headers['content-type'] = 'application/octet-stream'
}
if (opts.gzip) {
headers['content-encoding'] = 'gzip'
if (bodyIsStream) {
const gz = zlib.createGzip()
body.on('error', err => gz.emit('error', err))
body = body.pipe(gz)
} else {
body = new opts.Promise((resolve, reject) => {
zlib.gzip(body, (err, gz) => err ? reject(err) : resolve(gz))
})
}
}
if (opts.get('query')) {
let q = opts.get('query')
if (typeof q === 'string') {
Expand All @@ -51,7 +64,7 @@ function regFetch (uri, opts) {
)
uri = url.format(parsed)
}
return fetch(uri, {
return opts.Promise.resolve(body).then(body => fetch(uri, {
agent: opts.get('agent'),
algorithms: opts.get('algorithms'),
body,
Expand Down Expand Up @@ -82,7 +95,7 @@ function regFetch (uri, opts) {
gid: opts.get('gid')
}).then(res => checkResponse(
opts.get('method') || 'GET', res, registry, startTime, opts
))
)))
}

module.exports.json = fetchJSON
Expand Down
93 changes: 93 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const silentLog = require('../silentlog.js')
const ssri = require('ssri')
const test = require('tap').test
const tnock = require('./util/tnock.js')
const zlib = require('zlib')

const fetch = require('../index.js')

Expand Down Expand Up @@ -117,6 +118,98 @@ test('stream body param', t => {
.then(json => t.deepEqual(json, {hello: 'world'}))
})

test('JSON body param', t => {
tnock(t, OPTS.registry)
.matchHeader('content-type', ctype => {
t.equal(ctype[0], 'application/json', 'content-type automatically set')
return ctype[0] === 'application/json'
})
.matchHeader('content-encoding', enc => {
t.equal(enc[0], 'gzip', 'content-encoding automatically set')
return enc[0] === 'gzip'
})
.post('/hello')
// NOTE: can't really test the body itself here because nock freaks out.
.reply(200)
const opts = Object.assign({
method: 'POST',
body: {hello: 'world'},
gzip: true
}, OPTS)
return fetch('/hello', opts)
.then(res => {
t.equal(res.status, 200, 'request succeeded')
})
})

test('gzip + buffer body param', t => {
tnock(t, OPTS.registry)
.matchHeader('content-type', ctype => {
t.equal(ctype[0], 'application/octet-stream', 'content-type automatically set')
return ctype[0] === 'application/octet-stream'
})
.matchHeader('content-encoding', enc => {
t.equal(enc[0], 'gzip', 'content-encoding automatically set')
return enc[0] === 'gzip'
})
.post('/hello')
.reply(200, (uri, reqBody) => {
reqBody = zlib.gunzipSync(Buffer.from(reqBody, 'hex'))
t.deepEqual(
Buffer.from(reqBody, 'utf8').toString('utf8'),
'hello',
'got the JSON version of the body'
)
return reqBody
})
const opts = Object.assign({
method: 'POST',
body: Buffer.from('hello', 'utf8'),
gzip: true
}, OPTS)
return fetch('/hello', opts)
.then(res => {
t.equal(res.status, 200)
return res.buffer()
})
.then(buf =>
t.deepEqual(buf, Buffer.from('hello', 'utf8'), 'got response')
)
})

test('gzip + stream body param', t => {
tnock(t, OPTS.registry)
.matchHeader('content-type', ctype => {
t.equal(ctype[0], 'application/octet-stream', 'content-type automatically set')
return ctype[0] === 'application/octet-stream'
})
.matchHeader('content-encoding', enc => {
t.equal(enc[0], 'gzip', 'content-encoding automatically set')
return enc[0] === 'gzip'
})
.post('/hello')
.reply(200, (uri, reqBody) => {
reqBody = zlib.gunzipSync(Buffer.from(reqBody, 'hex'))
t.deepEqual(JSON.parse(reqBody.toString('utf8')), {
hello: 'world'
}, 'got the stringified version of the body')
return reqBody
})
const stream = new PassThrough()
setImmediate(() => stream.end(JSON.stringify({hello: 'world'})))
const opts = Object.assign({
method: 'POST',
body: stream,
gzip: true
}, OPTS)
return fetch('/hello', opts)
.then(res => {
t.equal(res.status, 200)
return res.json()
})
.then(json => t.deepEqual(json, {hello: 'world'}))
})

test('query strings', t => {
tnock(t, OPTS.registry)
.get('/hello?hi=there&who=wor%20ld')
Expand Down

0 comments on commit 340abe0

Please sign in to comment.