diff options
Diffstat (limited to 'deps/npm/lib/utils/npm-registry-client/get.js')
-rw-r--r-- | deps/npm/lib/utils/npm-registry-client/get.js | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/deps/npm/lib/utils/npm-registry-client/get.js b/deps/npm/lib/utils/npm-registry-client/get.js new file mode 100644 index 0000000000..062244df3a --- /dev/null +++ b/deps/npm/lib/utils/npm-registry-client/get.js @@ -0,0 +1,171 @@ + +module.exports = get + +var GET = require("./request.js").GET + , fs = require("graceful-fs") + , npm = require("../../npm.js") + , path = require("path") + , log = require("../log.js") + , mkdir = require("../mkdir-p.js") + , cacheStat = null + +function get (project, version, timeout, nofollow, staleOk, cb) { + if (typeof cb !== "function") cb = staleOk, staleOk = false + if (typeof cb !== "function") cb = nofollow, nofollow = false + if (typeof cb !== "function") cb = timeout, timeout = -1 + if (typeof cb !== "function") cb = version, version = null + if (typeof cb !== "function") cb = project, project = null + if (typeof cb !== "function") { + throw new Error("No callback provided to registry.get") + } + + if ( process.env.COMP_CWORD !== undefined + && process.env.COMP_LINE !== undefined + && process.env.COMP_POINT !== undefined + ) timeout = Math.max(timeout, 60000) + + var uri = [] + uri.push(project || "") + if (version) uri.push(version) + uri = uri.join("/") + + // /-/all is special. + // It uses timestamp-based caching and partial updates, + // because it is a monster. + if (uri === "/-/all") { + return requestAll(cb) + } + + var cache = path.join(npm.cache, uri, ".cache.json") + fs.stat(cache, function (er, stat) { + if (!er) fs.readFile(cache, function (er, data) { + try { data = JSON.parse(data) } + catch (ex) { data = null } + get_(uri, timeout, cache, stat, data, nofollow, staleOk, cb) + }) + else get_(uri, timeout, cache, null, null, nofollow, staleOk, cb) + }) +} + +function requestAll (cb) { + var cache = path.join(npm.cache, "/-/all", ".cache.json") + + mkdir(path.join(npm.cache, "-", "all"), function (er) { + fs.readFile(cache, function (er, data) { + if (er) return requestAll_(0, {}, cb) + try { + data = JSON.parse(data) + } catch (ex) { + fs.writeFile(cache, "{}", function (er) { + if (er) return cb(new Error("Broken cache. " + +"Please run 'npm cache clean' " + +"and try again.")) + return requestAll_(0, {}, cb) + }) + } + var t = +data._updated || 0 + requestAll_(t, data, cb) + }) + }) +} + +function requestAll_ (c, data, cb) { + // use the cache and update in the background if it's not too old + if (Date.now() - c < 60000) { + cb(null, data) + cb = function () {} + } + + var uri = "/-/all/since?stale=update_after&startkey=" + c + + if (c === 0) { + log.warn("Building the index for the first time, please be patient") + uri = "/-/all" + } + + var cache = path.join(npm.cache, "-/all", ".cache.json") + GET(uri, function (er, updates, _, res) { + if (er) return cb(er, data) + var headers = res.headers + , updated = Date.parse(headers.date) + Object.keys(updates).forEach(function (p) { + data[p] = updates[p] + }) + data._updated = updated + fs.writeFile( cache, JSON.stringify(data) + , function (er) { + delete data._updated + return cb(er, data) + }) + }) +} + +function get_ (uri, timeout, cache, stat, data, nofollow, staleOk, cb) { + var etag + if (data && data._etag) etag = data._etag + if (timeout && timeout > 0 && stat && data) { + if ((Date.now() - stat.mtime.getTime())/1000 < timeout) { + log.verbose("not expired, no request", "registry.get " +uri) + delete data._etag + return cb(null, data, JSON.stringify(data), {statusCode:304}) + } + if (staleOk) { + log.verbose("staleOk, background update", "registry.get " +uri) + delete data._etag + process.nextTick(cb.bind( null, null, data, JSON.stringify(data) + , {statusCode: 304} )) + cb = function () {} + } + } + + GET(uri, etag, nofollow, function (er, remoteData, raw, response) { + if (response) { + log.silly([response.statusCode, response.headers], "get cb") + if (response.statusCode === 304 && etag) { + remoteData = data + log.verbose(uri+" from cache", "etag") + } + } + + data = remoteData + if (er) return cb(er, data, raw, response) + + // just give the write the old college try. if it fails, whatever. + function saved () { + delete data._etag + cb(er, data, raw, response) + } + + saveToCache(cache, data, saved) + }) +} + +function saveToCache (cache, data, saved) { + if (cacheStat) { + return saveToCache_(cache, data, cacheStat.uid, cacheStat.gid, saved) + } + fs.stat(npm.cache, function (er, st) { + if (er) { + return fs.stat(process.env.HOME || "", function (er, st) { + // if this fails, oh well. + if (er) return saved() + cacheStat = st + return saveToCache(cache, data, saved) + }) + } + cacheStat = st || { uid: null, gid: null } + return saveToCache(cache, data, saved) + }) +} + +function saveToCache_ (cache, data, uid, gid, saved) { + mkdir(path.dirname(cache), npm.modes.exec, uid, gid, function (er) { + if (er) return saved() + fs.writeFile(cache, JSON.stringify(data), function (er) { + if (er || uid === null || gid === null) { + return saved() + } + fs.chown(cache, uid, gid, saved) + }) + }) +} |