Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorForrest L Norvell <forrest@npmjs.com>2014-08-08 04:56:53 +0400
committerForrest L Norvell <forrest@npmjs.com>2014-08-08 04:56:53 +0400
commitd987707442409f75ab0fa7f346a036ea5787754a (patch)
treea3046d074805f8df0cc70939c84a68d100db75c6
parent9b318e21559546cebd6715f9366a6b058ab2d5b3 (diff)
move fetch into npm-registry-client
The primary purpose is so that everything doing network requests includes auth, if so required. Sends bearer token by default, will send HTTP Basic auth with `always-auth` set in the config.
-rw-r--r--lib/cache/add-remote-tarball.js51
-rw-r--r--lib/utils/fetch.js106
-rw-r--r--node_modules/npm-registry-client/LICENSE36
-rw-r--r--node_modules/npm-registry-client/lib/attempt.js22
-rw-r--r--node_modules/npm-registry-client/lib/authify.js25
-rw-r--r--node_modules/npm-registry-client/lib/fetch.js81
-rw-r--r--node_modules/npm-registry-client/lib/initialize.js41
-rw-r--r--node_modules/npm-registry-client/lib/request.js174
-rw-r--r--node_modules/npm-registry-client/lib/unpublish.js11
-rw-r--r--node_modules/npm-registry-client/package.json27
-rw-r--r--node_modules/npm-registry-client/test/fetch-basic.js44
-rw-r--r--node_modules/npm-registry-client/test/fetch-retries.js52
-rw-r--r--node_modules/npm-registry-client/test/request-gzip-content.js8
-rw-r--r--node_modules/npm-registry-client/test/unpublish-scoped.js59
-rw-r--r--package.json2
-rw-r--r--test/tap/cache-shasum.js3
-rw-r--r--test/tap/url-dependencies.js20
17 files changed, 470 insertions, 292 deletions
diff --git a/lib/cache/add-remote-tarball.js b/lib/cache/add-remote-tarball.js
index db9a05d82..2dbc366e4 100644
--- a/lib/cache/add-remote-tarball.js
+++ b/lib/cache/add-remote-tarball.js
@@ -4,8 +4,9 @@ var mkdir = require("mkdirp")
, path = require("path")
, sha = require("sha")
, retry = require("retry")
+ , createWriteStream = require("graceful-fs").createWriteStream
, npm = require("../npm.js")
- , fetch = require("../utils/fetch.js")
+ , registry = npm.registry
, inflight = require("inflight")
, locker = require("../utils/locker.js")
, lock = locker.lock
@@ -80,27 +81,47 @@ function addRemoteTarball_(u, tmp, shasum, cb) {
}
function fetchAndShaCheck (u, tmp, shasum, cb) {
- fetch(u, tmp, function (er, response) {
+ registry.fetch(u, null, function (er, response) {
if (er) {
log.error("fetch failed", u)
return cb(er, response)
}
- if (!shasum) {
- // Well, we weren't given a shasum, so at least sha what we have
- // in case we want to compare it to something else later
- return sha.get(tmp, function (er, shasum) {
- cb(er, response, shasum)
+ // mkdir takes a little while, so don't drop chunks!
+ response.pause()
+
+ mkdir(path.dirname(tmp), function (er) {
+ if (er) return cb(er)
+
+ var tarball = createWriteStream(tmp, { mode : npm.modes.file })
+ tarball.on("error", function (er) {
+ cb(er)
+ tarball.destroy()
})
- }
- // validate that the url we just downloaded matches the expected shasum.
- sha.check(tmp, shasum, function (er) {
- if (er && er.message) {
- // add original filename for better debuggability
- er.message = er.message + '\n' + 'From: ' + u
- }
- return cb(er, response, shasum)
+ tarball.on("finish", function () {
+ if (!shasum) {
+ // Well, we weren't given a shasum, so at least sha what we have
+ // in case we want to compare it to something else later
+ return sha.get(tmp, function (er, shasum) {
+ log.silly("fetchAndShaCheck", "shasum", shasum)
+ cb(er, response, shasum)
+ })
+ }
+
+ // validate that the url we just downloaded matches the expected shasum.
+ log.silly("fetchAndShaCheck", "shasum", shasum)
+ sha.check(tmp, shasum, function (er) {
+ if (er && er.message) {
+ // add original filename for better debuggability
+ er.message = er.message + "\n" + "From: " + u
+ }
+ return cb(er, response, shasum)
+ })
+ })
+
+ response.pipe(tarball)
+ response.resume()
})
})
}
diff --git a/lib/utils/fetch.js b/lib/utils/fetch.js
deleted file mode 100644
index f6e5166ff..000000000
--- a/lib/utils/fetch.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * Fetch an HTTP url to a local file.
- **/
-
-var request = require("request")
- , fs = require("graceful-fs")
- , npm = require("../npm.js")
- , url = require("url")
- , log = require("npmlog")
- , path = require("path")
- , mkdir = require("mkdirp")
- , chownr = require("chownr")
- , regHost
- , once = require("once")
- , crypto = require("crypto")
-
-module.exports = fetch
-
-function fetch (remote, local, headers, cb) {
- if (typeof cb !== "function") cb = headers, headers = {}
- cb = once(cb)
- log.verbose("fetch", "to=", local)
- mkdir(path.dirname(local), function (er, made) {
- if (er) return cb(er)
- fetch_(remote, local, headers, cb)
- })
-}
-
-function fetch_ (remote, local, headers, cb) {
- var fstr = fs.createWriteStream(local, { mode : npm.modes.file })
- var response = null
-
- fstr.on("error", function (er) {
- cb(er)
- fstr.destroy()
- })
-
- var req = makeRequest(remote, fstr, headers)
- req.on("response", function (res) {
- log.http(res.statusCode, remote)
- response = res
- response.resume()
- // Work around bug in node v0.10.0 where the CryptoStream
- // gets stuck and never starts reading again.
- if (process.version === "v0.10.0") {
- response.resume = function (orig) { return function() {
- var ret = orig.apply(response, arguments)
- if (response.socket.encrypted)
- response.socket.encrypted.read(0)
- return ret
- }}(response.resume)
- }
- })
-
- fstr.on("close", function () {
- var er
- if (response && response.statusCode && response.statusCode >= 400) {
- er = new Error(response.statusCode + " "
- + require("http").STATUS_CODES[response.statusCode])
- }
- cb(er, response)
- })
-}
-
-function makeRequest (remote, fstr, headers) {
- remote = url.parse(remote)
- log.http("GET", remote.href)
- regHost = regHost || url.parse(npm.config.get("registry")).host
-
- if (remote.host === regHost && npm.config.get("always-auth")) {
- remote.auth = new Buffer( npm.config.get("_auth")
- , "base64" ).toString("utf8")
- if (!remote.auth) return fstr.emit("error", new Error(
- "Auth required and none provided. Please run 'npm adduser'"))
- }
-
- var proxy
- if (remote.protocol !== "https:" || !(proxy = npm.config.get("https-proxy"))) {
- proxy = npm.config.get("proxy")
- }
-
- var sessionToken = npm.registry.sessionToken
- if (!sessionToken) {
- sessionToken = crypto.randomBytes(8).toString("hex")
- npm.registry.sessionToken = sessionToken
- }
-
- var ca = remote.host === regHost ? npm.config.get("ca") : undefined
- var opts = { url: remote
- , proxy: proxy
- , strictSSL: npm.config.get("strict-ssl")
- , rejectUnauthorized: npm.config.get("strict-ssl")
- , ca: ca
- , headers:
- { "user-agent": npm.config.get("user-agent")
- , "npm-session": sessionToken
- , referer: npm.registry.refer
- }
- }
- var req = request(opts)
- req.on("error", function (er) {
- fstr.emit("error", er)
- })
- req.pipe(fstr)
- return req
-}
diff --git a/node_modules/npm-registry-client/LICENSE b/node_modules/npm-registry-client/LICENSE
index 0c44ae716..19129e315 100644
--- a/node_modules/npm-registry-client/LICENSE
+++ b/node_modules/npm-registry-client/LICENSE
@@ -1,27 +1,15 @@
-Copyright (c) Isaac Z. Schlueter ("Author")
-All rights reserved.
+The ISC License
-The BSD License
+Copyright (c) Isaac Z. Schlueter and Contributors
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/node_modules/npm-registry-client/lib/attempt.js b/node_modules/npm-registry-client/lib/attempt.js
new file mode 100644
index 000000000..9a0c81876
--- /dev/null
+++ b/node_modules/npm-registry-client/lib/attempt.js
@@ -0,0 +1,22 @@
+var retry = require("retry")
+
+module.exports = attempt
+
+function attempt(cb) {
+ // Tuned to spread 3 attempts over about a minute.
+ // See formula at <https://github.com/tim-kos/node-retry>.
+ var operation = retry.operation({
+ retries : this.conf.get("fetch-retries") || 2,
+ factor : this.conf.get("fetch-retry-factor"),
+ minTimeout : this.conf.get("fetch-retry-mintimeout") || 10000,
+ maxTimeout : this.conf.get("fetch-retry-maxtimeout") || 60000
+ })
+
+ var client = this
+ operation.attempt(function (currentAttempt) {
+ client.log.info("retrier", "registry request attempt "+currentAttempt+
+ " at "+(new Date()).toLocaleTimeString())
+
+ cb(operation)
+ })
+}
diff --git a/node_modules/npm-registry-client/lib/authify.js b/node_modules/npm-registry-client/lib/authify.js
new file mode 100644
index 000000000..a4290fd5e
--- /dev/null
+++ b/node_modules/npm-registry-client/lib/authify.js
@@ -0,0 +1,25 @@
+var url = require("url")
+
+module.exports = authify
+
+function authify (authed, parsed, headers) {
+ var c = this.conf.getCredentialsByURI(url.format(parsed))
+
+ if (c && c.token) {
+ this.log.verbose("request", "using bearer token for auth")
+ headers.authorization = "Bearer " + c.token
+
+ return null
+ }
+
+ if (authed) {
+ if (c && c.username && c.password) {
+ parsed.auth = c.username + ":" + c.password
+ }
+ else {
+ return new Error(
+ "This request requires auth credentials. Run `npm login` and repeat the request."
+ )
+ }
+ }
+}
diff --git a/node_modules/npm-registry-client/lib/fetch.js b/node_modules/npm-registry-client/lib/fetch.js
new file mode 100644
index 000000000..826155863
--- /dev/null
+++ b/node_modules/npm-registry-client/lib/fetch.js
@@ -0,0 +1,81 @@
+var assert = require("assert")
+ , url = require("url")
+
+var request = require("request")
+ , once = require("once")
+
+module.exports = fetch
+
+function fetch (uri, headers, cb) {
+ assert(uri, "must pass resource to fetch")
+ assert(cb, "must pass callback")
+
+ if (!headers) headers = {}
+
+ cb = once(cb)
+
+ var client = this
+ this.attempt(function (operation) {
+ makeRequest.call(client, uri, headers, function (er, req) {
+ if (er) return cb(er)
+
+ req.on("error", function (err) {
+ if (operation.retry(err)) {
+ client.log.info("retry", "will retry, error on last attempt: " + err)
+ }
+ })
+
+ req.on("response", function (res) {
+ client.log.http("fetch", res.statusCode, uri)
+
+ // Only retry on 408, 5xx or no `response`.
+ var statusCode = res && res.statusCode
+
+ var timeout = statusCode === 408
+ if (timeout) er = new Error("request timed out")
+
+ var serverError = statusCode >= 500
+ if (serverError) er = new Error("server error " + statusCode)
+
+ if (er && operation.retry(er)) {
+ client.log.info("retry", "will retry, error on last attempt: " + er)
+ return
+ }
+
+ // Work around bug in node v0.10.0 where the CryptoStream
+ // gets stuck and never starts reading again.
+ res.resume()
+ if (process.version === "v0.10.0") unstick(res)
+
+ cb(null, res)
+ })
+ })
+ })
+}
+
+function unstick(response) {
+ response.resume = function (orig) { return function() {
+ var ret = orig.apply(response, arguments)
+ if (response.socket.encrypted) response.socket.encrypted.read(0)
+ return ret
+ }}(response.resume)
+}
+
+function makeRequest (remote, headers, cb) {
+ var parsed = url.parse(remote)
+ this.log.http("fetch", "GET", parsed.href)
+
+ var er = this.authify(this.conf.get("always-auth"), parsed, headers)
+ if (er) return cb(er)
+
+ var opts = this.initialize(
+ parsed,
+ "GET",
+ "application/x-tar",
+ headers
+ )
+ // always want to follow redirects for fetch
+ opts.followRedirect = true
+
+ cb(null, request(opts))
+}
diff --git a/node_modules/npm-registry-client/lib/initialize.js b/node_modules/npm-registry-client/lib/initialize.js
new file mode 100644
index 000000000..b6e89ffe9
--- /dev/null
+++ b/node_modules/npm-registry-client/lib/initialize.js
@@ -0,0 +1,41 @@
+var crypto = require("crypto")
+
+var pkg = require("../package.json")
+
+module.exports = initialize
+
+function initialize (uri, method, accept, headers) {
+ if (!this.sessionToken) {
+ this.sessionToken = crypto.randomBytes(8).toString("hex")
+ this.log.verbose("request id", this.sessionToken)
+ }
+
+ var strict = this.conf.get("strict-ssl")
+ if (strict === undefined) strict = true
+
+ var p = this.conf.get("proxy")
+ var sp = this.conf.get("https-proxy") || p
+
+ var opts = {
+ url : uri,
+ method : method,
+ headers : headers,
+ proxy : uri.protocol === "https:" ? sp : p,
+ localAddress : this.conf.get("local-address"),
+ strictSSL : strict,
+ cert : this.conf.get("cert"),
+ key : this.conf.get("key"),
+ ca : this.conf.get("ca")
+ }
+
+ headers.version = this.version || pkg.version
+ headers.accept = accept
+
+ if (this.refer) headers.referer = this.refer
+
+ headers["npm-session"] = this.sessionToken
+ headers["user-agent"] = this.conf.get("user-agent") ||
+ "node/" + process.version
+
+ return opts
+}
diff --git a/node_modules/npm-registry-client/lib/request.js b/node_modules/npm-registry-client/lib/request.js
index a67a83390..b59a60f00 100644
--- a/node_modules/npm-registry-client/lib/request.js
+++ b/node_modules/npm-registry-client/lib/request.js
@@ -1,15 +1,13 @@
-module.exports = regRequest
-
-var url = require("url")
+var assert = require("assert")
+ , url = require("url")
, zlib = require("zlib")
- , assert = require("assert")
- , rm = require("rimraf")
, Stream = require("stream").Stream
+
+var rm = require("rimraf")
, request = require("request")
- , retry = require("retry")
- , crypto = require("crypto")
+ , once = require("once")
-var pkg = require("../package.json")
+module.exports = regRequest
// npm: means
// 1. https
@@ -20,11 +18,12 @@ function regRequest (method, uri, options, cb_) {
assert(cb_, "must pass callback")
options = options || {}
- var nofollow = (typeof options.follow === 'boolean' ? !options.follow : false)
- var etag = options.etag
- var what = options.body
var parsed = url.parse(uri)
+ var where = parsed.pathname
+ var what = options.body
+ var follow = (typeof options.follow === "boolean" ? options.follow : true)
+ this.log.verbose("request", "on initialization, where is", where)
var authThis = false
if (parsed.protocol === "npm") {
@@ -32,42 +31,28 @@ function regRequest (method, uri, options, cb_) {
authThis = true
}
- var where = parsed.pathname
if (parsed.search) {
where = where + parsed.search
parsed.search = ""
}
parsed.pathname = "/"
- this.log.verbose("request", "where is", where)
-
- var registry = url.format(parsed)
- this.log.verbose("request", "registry", registry)
-
- if (!this.sessionToken) {
- this.sessionToken = crypto.randomBytes(8).toString("hex")
- this.log.verbose("request id", this.sessionToken)
- }
+ this.log.verbose("request", "after pass 1, where is", where)
// Since there are multiple places where an error could occur,
// don't let the cb be called more than once.
- var errState = null
- function cb (er) {
- if (errState) return
- if (er) errState = er
- cb_.apply(null, arguments)
- }
+ var cb = once(cb_)
if (where.match(/^\/?favicon.ico/)) {
return cb(new Error("favicon.ico isn't a package, it's a picture."))
}
var adduserChange = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)\/-rev/
- , adduserNew = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)/
- , nu = where.match(adduserNew)
- , uc = where.match(adduserChange)
- , alwaysAuth = this.conf.get("always-auth")
- , isDel = method === "DELETE"
- , isWrite = what || isDel
+ , uc = where.match(adduserChange)
+ , adduserNew = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)/
+ , nu = where.match(adduserNew)
+ , alwaysAuth = this.conf.get("always-auth")
+ , isDel = method === "DELETE"
+ , isWrite = what || isDel
// resolve to a full url on the registry
if (!where.match(/^https?:\/\//)) {
@@ -86,56 +71,20 @@ function regRequest (method, uri, options, cb_) {
return p
}).join("/")
if (q) where += "?" + q
- this.log.verbose("request", "url resolving", [registry, where])
+
+ var registry = url.format(parsed)
+ this.log.verbose("request", "resolving registry", [registry, where])
+
where = url.resolve(registry, where)
- this.log.verbose("request", "url resolved", where)
- }
- this.log.verbose("request", "where is", where)
-
- var remote = url.parse(where)
- if ((authThis || alwaysAuth || isWrite) && !nu || uc || isDel) {
- this.log.verbose("request", "setting basic auth")
-
- var c = this.conf.getCredentialsByURI(where)
- if (c) {
- if (!c.token) {
- if (c.username && c.password) {
- remote.auth = c.username + ":" + c.password
- }
- else {
- return cb(new Error(
- "This request requires auth credentials. Run `npm login` and repeat the request."
- ))
- }
- }
- else {
- this.log.verbose("request", "using bearer token for auth")
- }
- }
- else {
- return cb(new Error(
- "This request requires auth credentials. Run `npm login` and repeat the request."
- ))
- }
- }
- else {
- this.log.verbose("request", "no basic auth needed")
+ this.log.verbose("request", "after pass 2, where is", where)
}
- // Tuned to spread 3 attempts over about a minute.
- // See formula at <https://github.com/tim-kos/node-retry>.
- var operation = retry.operation({
- retries: this.conf.get("fetch-retries") || 2,
- factor: this.conf.get("fetch-retry-factor"),
- minTimeout: this.conf.get("fetch-retry-mintimeout") || 10000,
- maxTimeout: this.conf.get("fetch-retry-maxtimeout") || 60000
- })
+ var authed = (authThis || alwaysAuth || isWrite) && !nu || uc || isDel
+ if (!authed) this.log.verbose("request", "no auth needed")
var self = this
- operation.attempt(function (currentAttempt) {
- self.log.info("trying", "registry request attempt " + currentAttempt
- + " at " + (new Date()).toLocaleTimeString())
- makeRequest.call(self, method, remote, where, what, etag, nofollow
+ this.attempt(function (operation) {
+ makeRequest.call(self, method, where, what, options.etag, follow, authed
, function (er, parsed, raw, response) {
if (!er || (er.message && er.message.match(/^SSL Error/))) {
if (er)
@@ -154,55 +103,38 @@ function regRequest (method, uri, options, cb_) {
return undefined
}
if (response)
- this.log.verbose("headers", response.headers)
+ self.log.verbose("headers", response.headers)
cb.apply(null, arguments)
- }.bind(this))
- }.bind(this))
+ })
+ })
}
-function makeRequest (method, remote, where, what, etag, nofollow, cb_) {
- var cbCalled = false
- function cb () {
- if (cbCalled) return
- cbCalled = true
- cb_.apply(null, arguments)
- }
-
- var strict = this.conf.get("strict-ssl")
- if (strict === undefined) strict = true
- var opts = { url: remote
- , method: method
- , encoding: null // tell request let body be Buffer instance
- , ca: this.conf.get("ca")
- , localAddress: this.conf.get("local-address")
- , cert: this.conf.get("cert")
- , key: this.conf.get("key")
- , strictSSL: strict }
- , headers = opts.headers = {}
- if (etag) {
- this.log.verbose("etag", etag)
- headers[method === "GET" ? "if-none-match" : "if-match"] = etag
- }
+function makeRequest (method, where, what, etag, follow, authed, cb_) {
+ var cb = once(cb_)
- headers["npm-session"] = this.sessionToken
- headers.version = this.version || pkg.version
+ var parsed = url.parse(where)
+ var headers = {}
- if (this.refer) {
- headers.referer = this.refer
- }
-
- headers.accept = "application/json"
+ // metadata should be compressed
headers["accept-encoding"] = "gzip"
- headers["user-agent"] = this.conf.get("user-agent") ||
- "node/" + process.version
+ var er = this.authify(authed, parsed, headers)
+ if (er) return cb_(er)
- var c = this.conf.getCredentialsByURI(url.format(remote))
- if (c.token) headers.authorization = "Bearer " + c.token
+ var opts = this.initialize(
+ parsed,
+ method,
+ "application/json",
+ headers
+ )
- var p = this.conf.get("proxy")
- var sp = this.conf.get("https-proxy") || p
- opts.proxy = remote.protocol === "https:" ? sp : p
+ opts.followRedirect = follow
+ opts.encoding = null // tell request let body be Buffer instance
+
+ if (etag) {
+ this.log.verbose("etag", etag)
+ headers[method === "GET" ? "if-none-match" : "if-match"] = etag
+ }
// figure out wth "what" is
if (what) {
@@ -219,11 +151,7 @@ function makeRequest (method, remote, where, what, etag, nofollow, cb_) {
}
}
- if (nofollow) {
- opts.followRedirect = false
- }
-
- this.log.http(method, remote.href || "/")
+ this.log.http("request", method, parsed.href || "/")
var done = requestDone.call(this, method, where, cb)
var req = request(opts, decodeResponseBody(done))
diff --git a/node_modules/npm-registry-client/lib/unpublish.js b/node_modules/npm-registry-client/lib/unpublish.js
index 6a4ac8a19..346d537fe 100644
--- a/node_modules/npm-registry-client/lib/unpublish.js
+++ b/node_modules/npm-registry-client/lib/unpublish.js
@@ -22,7 +22,7 @@ function unpublish (uri, ver, cb) {
// remove all if no version specified
if (!ver) {
this.log.info("unpublish", "No version specified, removing all")
- return this.request("DELETE", uri+'/-rev/'+data._rev, null, cb)
+ return this.request("DELETE", uri+"/-rev/"+data._rev, null, cb)
}
var versions = data.versions || {}
@@ -72,7 +72,7 @@ function unpublish (uri, ver, cb) {
function detacher (uri, data, dist, cb) {
return function (er) {
if (er) return cb(er)
- this.get(url.resolve(uri, data.name), null, function (er, data) {
+ this.get(escape(uri, data.name), null, function (er, data) {
if (er) return cb(er)
var tb = url.parse(dist.tarball)
@@ -96,10 +96,15 @@ function detach (uri, data, path, rev, cb) {
this.log.info("detach", path)
return this.request("DELETE", url.resolve(uri, path), null, cb)
}
- this.get(url.resolve(uri, data.name), null, function (er, data) {
+ this.get(escape(uri, data.name), null, function (er, data) {
rev = data._rev
if (!rev) return cb(new Error(
"No _rev found in "+data._id))
detach.call(this, data, path, rev, cb)
}.bind(this))
}
+
+function escape (base, name) {
+ var escaped = name.replace(/\//, "%2f")
+ return url.resolve(base, escaped)
+}
diff --git a/node_modules/npm-registry-client/package.json b/node_modules/npm-registry-client/package.json
index 6acb40410..ec65c225b 100644
--- a/node_modules/npm-registry-client/package.json
+++ b/node_modules/npm-registry-client/package.json
@@ -1,8 +1,12 @@
{
- "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
"name": "npm-registry-client",
"description": "Client for the npm registry",
- "version": "3.0.5",
+ "version": "3.1.1",
"repository": {
"url": "git://github.com/isaacs/npm-registry-client"
},
@@ -13,20 +17,33 @@
"dependencies": {
"chownr": "0",
"graceful-fs": "^3.0.0",
- "mkdirp": "0.5",
+ "mkdirp": "^0.5.0",
"normalize-package-data": "0.4",
"npm-cache-filename": "^1.0.0",
+ "once": "^1.3.0",
"request": "2 >=2.25.0",
"retry": "0.6.0",
"rimraf": "2",
"semver": "2 >=2.2.1 || 3.x",
- "slide": "^1.1.3"
+ "slide": "^1.1.3",
+ "npmlog": ""
},
"devDependencies": {
+ "concat-stream": "^1.4.6",
"tap": ""
},
"optionalDependencies": {
"npmlog": ""
},
- "license": "BSD"
+ "license": "ISC",
+ "readme": "# npm-registry-client\n\nThe code that npm uses to talk to the registry.\n\nIt handles all the caching and HTTP calls.\n\n## Usage\n\n```javascript\nvar RegClient = require('npm-registry-client')\nvar client = new RegClient(config)\nvar uri = \"npm://registry.npmjs.org/npm\"\nvar options = {timeout: 1000}\n\nclient.get(uri, options, function (error, data, raw, res) {\n // error is an error if there was a problem.\n // data is the parsed data object\n // raw is the json string\n // res is the response from couch\n})\n```\n\n# Registry URLs\n\nThe registry calls take either a full URL pointing to a resource in the\nregistry, or a base URL for the registry as a whole (for the base URL, any path\nwill be ignored). In addition to `http` and `https`, `npm` URLs are allowed.\n`npm` URLs are `https` URLs with the additional restrictions that they will\nalways include authorization credentials, and the response is always registry\nmetadata (and not tarballs or other attachments).\n\n# Configuration\n\nThis program is designed to work with\n[npmconf](https://npmjs.org/package/npmconf), but you can also pass in\na plain-jane object with the appropriate configs, and it'll shim it\nfor you. Any configuration thingie that has get/set/del methods will\nalso be accepted.\n\n* `cache` **Required** {String} Path to the cache folder\n* `always-auth` {Boolean} Auth even for GET requests.\n* `auth` {String} A base64-encoded `username:password`\n* `email` {String} User's email address\n* `tag` {String} The default tag to use when publishing new packages.\n Default = `\"latest\"`\n* `ca` {String} Cerficate signing authority certificates to trust.\n* `cert` {String} Client certificate (PEM encoded). Enable access\n to servers that require client certificates\n* `key` {String} Private key (PEM encoded) for client certificate 'cert'\n* `strict-ssl` {Boolean} Whether or not to be strict with SSL\n certificates. Default = `true`\n* `user-agent` {String} User agent header to send. Default =\n `\"node/{process.version} {process.platform} {process.arch}\"`\n* `log` {Object} The logger to use. Defaults to `require(\"npmlog\")` if\n that works, otherwise logs are disabled.\n* `fetch-retries` {Number} Number of times to retry on GET failures.\n Default=2\n* `fetch-retry-factor` {Number} `factor` setting for `node-retry`. Default=10\n* `fetch-retry-mintimeout` {Number} `minTimeout` setting for `node-retry`.\n Default=10000 (10 seconds)\n* `fetch-retry-maxtimeout` {Number} `maxTimeout` setting for `node-retry`.\n Default=60000 (60 seconds)\n* `proxy` {URL} The url to proxy requests through.\n* `https-proxy` {URL} The url to proxy https requests through.\n Defaults to be the same as `proxy` if unset.\n* `_auth` {String} The base64-encoded authorization header.\n* `username` `_password` {String} Username/password to use to generate\n `_auth` if not supplied.\n* `_token` {Object} A token for use with\n [couch-login](https://npmjs.org/package/couch-login)\n\n# client.request(method, uri, options, cb)\n\n* `method` {String} HTTP method\n* `uri` {String} URI pointing to the resource to request\n* `options` {Object} Object containing optional per-request properties.\n * `what` {Stream | Buffer | String | Object} The request body. Objects\n that are not Buffers or Streams are encoded as JSON.\n * `etag` {String} The cached ETag\n * `follow` {Boolean} Follow 302/301 responses (defaults to true)\n* `cb` {Function}\n * `error` {Error | null}\n * `data` {Object} the parsed data object\n * `raw` {String} the json\n * `res` {Response Object} response from couch\n\nMake a request to the registry. All the other methods are wrappers around\n`request`.\n\n# client.adduser(base, username, password, email, cb)\n\n* `base` {String} Base registry URL\n* `username` {String}\n* `password` {String}\n* `email` {String}\n* `cb` {Function}\n\nAdd a user account to the registry, or verify the credentials.\n\n# client.deprecate(uri, version, message, cb)\n\n* `uri` {String} Full registry URI for the deprecated package\n* `version` {String} Semver version range\n* `message` {String} The message to use as a deprecation warning\n* `cb` {Function}\n\nDeprecate a version of a package in the registry.\n\n# client.bugs(uri, cb)\n\n* `uri` {String} Full registry URI for the package\n* `cb` {Function}\n\nGet the url for bugs of a package\n\n# client.get(uri, options, cb)\n\n* `uri` {String} The complete registry URI to fetch\n* `options` {Object} Object containing optional per-request properties.\n * `timeout` {Number} Duration before the request times out.\n * `follow` {Boolean} Follow 302/301 responses (defaults to true)\n * `staleOk` {Boolean} If there's cached data available, then return that\n to the callback quickly, and update the cache the background.\n\nFetches data from the registry via a GET request, saving it in the cache folder\nwith the ETag.\n\n# client.publish(uri, data, tarball, cb)\n\n* `uri` {String} The registry URI to publish to\n* `data` {Object} Package data\n* `tarball` {String | Stream} Filename or stream of the package tarball\n* `cb` {Function}\n\nPublish a package to the registry.\n\nNote that this does not create the tarball from a folder. However, it can\naccept a gzipped tar stream or a filename to a tarball.\n\n# client.star(uri, starred, cb)\n\n* `uri` {String} The complete registry URI to star\n* `starred` {Boolean} True to star the package, false to unstar it.\n* `cb` {Function}\n\nStar or unstar a package.\n\nNote that the user does not have to be the package owner to star or unstar a\npackage, though other writes do require that the user be the package owner.\n\n# client.stars(base, username, cb)\n\n* `base` {String} The base URL for the registry\n* `username` {String} Name of user to fetch starred packages for.\n* `cb` {Function}\n\nView your own or another user's starred packages.\n\n# client.tag(uri, version, tag, cb)\n\n* `uri` {String} The complete registry URI to tag\n* `version` {String} Version to tag\n* `tag` {String} Tag name to apply\n* `cb` {Function}\n\nMark a version in the `dist-tags` hash, so that `pkg@tag` will fetch the\nspecified version.\n\n# client.unpublish(uri, [ver], cb)\n\n* `uri` {String} The complete registry URI to unpublish\n* `ver` {String} version to unpublish. Leave blank to unpublish all\n versions.\n* `cb` {Function}\n\nRemove a version of a package (or all versions) from the registry. When the\nlast version us unpublished, the entire document is removed from the database.\n\n# client.upload(uri, file, [etag], [nofollow], cb)\n\n* `uri` {String} The complete registry URI to upload to\n* `file` {String | Stream} Either the filename or a readable stream\n* `etag` {String} Cache ETag\n* `nofollow` {Boolean} Do not follow 301/302 responses\n* `cb` {Function}\n\nUpload an attachment. Mostly used by `client.publish()`.\n",
+ "readmeFilename": "README.md",
+ "gitHead": "e11fcc97220fb240d859018e268edc8b69e94fcb",
+ "bugs": {
+ "url": "https://github.com/isaacs/npm-registry-client/issues"
+ },
+ "homepage": "https://github.com/isaacs/npm-registry-client",
+ "_id": "npm-registry-client@3.1.1",
+ "_shasum": "197d43a342df0b07792fdcee5b0a46c64d17c7cb",
+ "_from": "npm-registry-client@3.1.1"
}
diff --git a/node_modules/npm-registry-client/test/fetch-basic.js b/node_modules/npm-registry-client/test/fetch-basic.js
new file mode 100644
index 000000000..2ce3b212b
--- /dev/null
+++ b/node_modules/npm-registry-client/test/fetch-basic.js
@@ -0,0 +1,44 @@
+var resolve = require("path").resolve
+var createReadStream = require("graceful-fs").createReadStream
+var readFileSync = require("graceful-fs").readFileSync
+
+var tap = require("tap")
+var cat = require("concat-stream")
+
+var server = require("./lib/server.js")
+var common = require("./lib/common.js")
+
+var tgz = resolve(__dirname, "./fixtures/underscore/1.3.3/package.tgz")
+
+tap.test("basic fetch", function (t) {
+ server.expect("/underscore/-/underscore-1.3.3.tgz", function (req, res) {
+ t.equal(req.method, "GET", "got expected method")
+
+ res.writeHead(200, {
+ "content-type" : "application/x-tar",
+ "content-encoding" : "gzip"
+ })
+
+ createReadStream(tgz).pipe(res)
+ })
+
+ var client = common.freshClient()
+ client.fetch(
+ "http://localhost:1337/underscore/-/underscore-1.3.3.tgz",
+ null,
+ function (er, res) {
+ t.ifError(er, "loaded successfully")
+
+ var sink = cat(function (data) {
+ t.deepEqual(data, readFileSync(tgz))
+ t.end()
+ })
+
+ res.on("error", function (error) {
+ t.ifError(error, "no errors on stream")
+ })
+
+ res.pipe(sink)
+ }
+ )
+})
diff --git a/node_modules/npm-registry-client/test/fetch-retries.js b/node_modules/npm-registry-client/test/fetch-retries.js
new file mode 100644
index 000000000..e3e89a6ea
--- /dev/null
+++ b/node_modules/npm-registry-client/test/fetch-retries.js
@@ -0,0 +1,52 @@
+var resolve = require("path").resolve
+var createReadStream = require("graceful-fs").createReadStream
+var readFileSync = require("graceful-fs").readFileSync
+
+var tap = require("tap")
+var cat = require("concat-stream")
+
+var server = require("./lib/server.js")
+var common = require("./lib/common.js")
+
+var tgz = resolve(__dirname, "./fixtures/underscore/1.3.3/package.tgz")
+
+tap.test("fetch with timeout", function (t) {
+ server.expect("/underscore/-/underscore-1.3.3.tgz", function (req, res) {
+ t.equal(req.method, "GET", "got expected method")
+
+ res.writeHead(408)
+ res.end()
+ })
+
+ server.expect("/underscore/-/underscore-1.3.3.tgz", function (req, res) {
+ t.equal(req.method, "GET", "got expected method")
+
+ res.writeHead(200, {
+ "content-type" : "application/x-tar",
+ "content-encoding" : "gzip"
+ })
+
+ createReadStream(tgz).pipe(res)
+ })
+
+ var client = common.freshClient()
+ client.conf.set("fetch-retry-mintimeout", 100)
+ client.fetch(
+ "http://localhost:1337/underscore/-/underscore-1.3.3.tgz",
+ {},
+ function (er, res) {
+ t.ifError(er, "loaded successfully")
+
+ var sink = cat(function (data) {
+ t.deepEqual(data, readFileSync(tgz))
+ t.end()
+ })
+
+ res.on("error", function (error) {
+ t.ifError(error, "no errors on stream")
+ })
+
+ res.pipe(sink)
+ }
+ )
+})
diff --git a/node_modules/npm-registry-client/test/request-gzip-content.js b/node_modules/npm-registry-client/test/request-gzip-content.js
index 79c2e8dc0..1085bfaca 100644
--- a/node_modules/npm-registry-client/test/request-gzip-content.js
+++ b/node_modules/npm-registry-client/test/request-gzip-content.js
@@ -19,10 +19,12 @@ var pkg = {
zlib.gzip(JSON.stringify(pkg), function (err, pkgGzip) {
tap.test("request gzip package content", function (t) {
+ t.ifError(err, "example package compressed")
+
server.expect("GET", "/some-package-gzip/1.2.3", function (req, res) {
res.statusCode = 200
- res.setHeader("Content-Encoding", "gzip");
- res.setHeader("Content-Type", "application/json");
+ res.setHeader("Content-Encoding", "gzip")
+ res.setHeader("Content-Type", "application/json")
res.end(pkgGzip)
})
@@ -46,4 +48,4 @@ zlib.gzip(JSON.stringify(pkg), function (err, pkgGzip) {
t.end()
})
})
-});
+})
diff --git a/node_modules/npm-registry-client/test/unpublish-scoped.js b/node_modules/npm-registry-client/test/unpublish-scoped.js
new file mode 100644
index 000000000..0e5cb8606
--- /dev/null
+++ b/node_modules/npm-registry-client/test/unpublish-scoped.js
@@ -0,0 +1,59 @@
+var tap = require("tap")
+
+var server = require("./lib/server.js")
+var common = require("./lib/common.js")
+
+var nerfed = "//localhost:" + server.port + "/:"
+
+var configuration = {}
+configuration[nerfed + "_authToken"] = "of-glad-tidings"
+
+var client = common.freshClient(configuration)
+
+var cache = require("./fixtures/@npm/npm-registry-client/cache.json")
+
+var REV = "/-rev/213-0a1049cf56172b7d9a1184742c6477b9"
+var VERSION = "3.0.6"
+
+tap.test("unpublish a package", function (t) {
+ server.expect("GET", "/@npm%2fnpm-registry-client?write=true", function (req, res) {
+ t.equal(req.method, "GET")
+
+ res.json(cache)
+ })
+
+ server.expect("PUT", "/@npm%2fnpm-registry-client" + REV, function (req, res) {
+ t.equal(req.method, "PUT")
+
+ var b = ""
+ req.setEncoding("utf-8")
+ req.on("data", function (d) {
+ b += d
+ })
+
+ req.on("end", function () {
+ var updated = JSON.parse(b)
+ t.notOk(updated.versions[VERSION])
+ })
+
+ res.json(cache)
+ })
+
+ server.expect("GET", "/@npm%2fnpm-registry-client", function (req, res) {
+ t.equal(req.method, "GET")
+
+ res.json(cache)
+ })
+
+ server.expect("DELETE", "/@npm%2fnpm-registry-client/-/@npm%2fnpm-registry-client-" + VERSION + ".tgz" + REV, function (req, res) {
+ t.equal(req.method, "DELETE")
+
+ res.json({unpublished:true})
+ })
+
+ client.unpublish("http://localhost:1337/@npm%2fnpm-registry-client", VERSION, function (error) {
+ t.ifError(error, "no errors")
+
+ t.end()
+ })
+})
diff --git a/package.json b/package.json
index be0ade9d9..dcc493689 100644
--- a/package.json
+++ b/package.json
@@ -68,7 +68,7 @@
"npm-cache-filename": "~1.0.1",
"npm-install-checks": "~1.0.2",
"npm-package-arg": "~2.0.2",
- "npm-registry-client": "~3.0.2",
+ "npm-registry-client": "~3.1.0",
"npm-user-validate": "~0.1.0",
"npmconf": "~2.0.4",
"npmlog": "~0.1.1",
diff --git a/test/tap/cache-shasum.js b/test/tap/cache-shasum.js
index 2139d8fb7..7450d3e60 100644
--- a/test/tap/cache-shasum.js
+++ b/test/tap/cache-shasum.js
@@ -1,7 +1,6 @@
var npm = require.resolve("../../")
var test = require("tap").test
var path = require("path")
-var fs = require("fs")
var rimraf = require("rimraf")
var mkdirp = require("mkdirp")
var mr = require("npm-registry-mock")
@@ -44,7 +43,7 @@ test("compare", function(t) {
var d = path.resolve(__dirname, "cache-shasum/request")
var p = path.resolve(d, "2.27.0/package.tgz")
var r = require("./cache-shasum/localhost_1337/request/.cache.json")
- var rshasum = r.versions['2.27.0'].dist.shasum
+ var rshasum = r.versions["2.27.0"].dist.shasum
sha.get(p, function (er, pshasum) {
if (er)
throw er
diff --git a/test/tap/url-dependencies.js b/test/tap/url-dependencies.js
index 7f8cc7864..34a77f4bc 100644
--- a/test/tap/url-dependencies.js
+++ b/test/tap/url-dependencies.js
@@ -7,7 +7,7 @@ var spawn = require("child_process").spawn
var npm = require.resolve("../../bin/npm-cli.js")
var node = process.execPath
var pkg = path.resolve(__dirname, "url-dependencies")
-var common = require('../common-tap')
+var common = require("../common-tap")
var mockRoutes = {
"get": {
@@ -19,9 +19,9 @@ test("url-dependencies: download first time", function(t) {
cleanup()
performInstall(function(output){
- if(!tarballWasFetched(output)){
+ if (!tarballWasFetched(output)){
t.fail("Tarball was not fetched")
- }else{
+ } else {
t.pass("Tarball was fetched")
}
t.end()
@@ -33,9 +33,9 @@ test("url-dependencies: do not download subsequent times", function(t) {
performInstall(function(){
performInstall(function(output){
- if(tarballWasFetched(output)){
+ if (tarballWasFetched(output)){
t.fail("Tarball was fetched second time around")
- }else{
+ } else {
t.pass("Tarball was not fetched")
}
t.end()
@@ -44,7 +44,7 @@ test("url-dependencies: do not download subsequent times", function(t) {
})
function tarballWasFetched(output){
- return output.indexOf("http GET " + common.registry + "/underscore/-/underscore-1.3.1.tgz") > -1
+ return output.indexOf("http fetch GET " + common.registry + "/underscore/-/underscore-1.3.1.tgz") > -1
}
function performInstall (cb) {
@@ -53,10 +53,10 @@ function performInstall (cb) {
, child = spawn(node, [npm, "install"], {
cwd: pkg,
env: {
- npm_config_registry: common.registry,
- npm_config_cache_lock_stale: 1000,
- npm_config_cache_lock_wait: 1000,
- npm_config_loglevel: "http",
+ "npm_config_registry": common.registry,
+ "npm_config_cache_lock_stale": 1000,
+ "npm_config_cache_lock_wait": 1000,
+ "npm_config_loglevel": "http",
HOME: process.env.HOME,
Path: process.env.PATH,
PATH: process.env.PATH