diff --git a/src/api/middleware.js b/src/api/middleware.js index 28e67ba3e..313278783 100644 --- a/src/api/middleware.js +++ b/src/api/middleware.js @@ -6,7 +6,7 @@ import { validate_package as utilValidatePackage, isObject, ErrorCode} from '../lib/utils'; -import {HEADERS} from '../lib/constants'; +import {API_ERROR, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER} from '../lib/constants'; import {stringToMD5} from '../lib/crypto-utils'; import type {$ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth} from '../../types'; import type {Config} from '@verdaccio/types'; @@ -57,9 +57,9 @@ export function validatePackage(req: $RequestExtend, res: $ResponseExtend, next: export function media(expect: string) { return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) { - if (req.headers['content-type'] !== expect) { - next( ErrorCode.getCode(415, 'wrong content-type, expect: ' + expect - + ', got: '+req.headers['content-type']) ); + if (req.headers[HEADER_TYPE.CONTENT_TYPE] !== expect) { + next( ErrorCode.getCode(HTTP_STATUS.UNSUPORTED_MEDIA, 'wrong content-type, expect: ' + expect + + ', got: '+req.headers[HEADER_TYPE.CONTENT_TYPE]) ); } else { next(); } @@ -89,7 +89,7 @@ export function anti_loop(config: Config) { for (let i=0; i= 400 && err.status < 600) { + if (err.status && err.status >= HTTP_STATUS.BAD_REQUEST && err.status < 600) { if (_.isNil(res.headersSent) === false) { res.status(err.status); - next({error: err.message || 'unknown error'}); + next({error: err.message || API_ERROR.UNKNOWN_ERROR}); } } else { - Logger.logger.error( {err: err} - , 'unexpected error: @{!err.message}\n@{err.stack}'); + Logger.logger.error( {err: err}, 'unexpected error: @{!err.message}\n@{err.stack}'); if (!res.status || !res.send) { Logger.logger.error('this is an error in express.js, please report this'); res.destroy(); } else if (!res.headersSent) { - res.status(500); - next({error: 'internal server error'}); + res.status(HTTP_STATUS.INTERNAL_ERROR); + next({error: API_ERROR.INTERNAL_SERVER_ERROR}); } else { // socket should be already closed } } }; + next(); } diff --git a/src/lib/constants.js b/src/lib/constants.js index 152530be2..164e851ff 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -9,6 +9,8 @@ export const HEADERS = { export const HEADER_TYPE = { CONTENT_ENCODING: 'content-encoding', + CONTENT_TYPE: 'content-type', + CONTENT_LENGTH: 'content-length', ACCEPT_ENCODING: 'accept-encoding', }; @@ -22,25 +24,33 @@ export const DEFAULT_REGISTRY = 'https://registry.npmjs.org/'; export const HTTP_STATUS = { - INTERNAL_ERROR: 500, - SERVICE_UNAVAILABLE: 503, OK: 200, CREATED: 201, + MULTIPLE_CHOICES: 300, BAD_REQUEST: 400, UNAUTHORIZED: 401, FORBIDDEN: 403, NOT_FOUND: 404, CONFLICT: 409, + UNSUPORTED_MEDIA: 415, BAD_DATA: 422, + INTERNAL_ERROR: 500, + SERVICE_UNAVAILABLE: 503, + LOOP_DETECTED: 508, }; export const PORT_SERVER_1 = '55551'; export const PORT_SERVER_2 = '55552'; -export const PORT_SERVER_3 = '55551'; +export const PORT_SERVER_3 = '55553'; -export const PACKAGE_ERROR = { +export const API_ERROR = { NO_PACKAGE: 'no such package available', NOT_ALLOWED: 'not allowed to access package', + INTERNAL_SERVER_ERROR: 'internal server error', + UNKNOWN_ERROR: 'unknown error', + NOT_PACKAGE_UPLINK: 'package doesn\'t exist on uplink', + CONTENT_MISMATCH: 'content length mismatch', + NOT_FILE_UPLINK: 'file doesn\'t exist on uplink', }; export const DEFAULT_NO_README = 'ERROR: No README data found!'; diff --git a/src/lib/storage.js b/src/lib/storage.js index 76b34dc44..e0745aad7 100644 --- a/src/lib/storage.js +++ b/src/lib/storage.js @@ -6,23 +6,24 @@ import async from 'async'; import Stream from 'stream'; import ProxyStorage from './up-storage'; import Search from './search'; +import {API_ERROR, HTTP_STATUS} from './constants'; import LocalStorage from './local-storage'; import {ReadTarball} from '@verdaccio/streams'; import {checkPackageLocal, publishPackage, checkPackageRemote, cleanUpLinksRef, - mergeUplinkTimeIntoLocal, generatePackageTemplate} from './storage-utils'; +mergeUplinkTimeIntoLocal, generatePackageTemplate} from './storage-utils'; import {setupUpLinks, updateVersionsHiddenUpLink} from './uplink-util'; import {mergeVersions} from './metadata-utils'; import {ErrorCode, normalizeDistTags, validate_metadata, isObject, DIST_TAGS} from './utils'; import type {IStorage, IProxy, IStorageHandler, ProxyList, StringValue} from '../../types'; import type { - Versions, - Package, - Config, - MergeTags, - Version, - DistFile, - Callback, - Logger, +Versions, +Package, +Config, +MergeTags, +Version, +DistFile, +Callback, +Logger, } from '@verdaccio/types'; import type {IReadTarball, IUploadTarball} from '@verdaccio/streams'; @@ -146,7 +147,7 @@ class Storage implements IStorageHandler { let localStream: any = self.localStorage.getTarball(name, filename); let isOpen = false; localStream.on('error', (err) => { - if (isOpen || err.status !== 404) { + if (isOpen || err.status !== HTTP_STATUS.NOT_FOUND) { return readStream.emit('error', err); } @@ -273,7 +274,7 @@ class Storage implements IStorageHandler { */ getPackage(options: any) { this.localStorage.getPackageMetadata(options.name, (err, data) => { - if (err && (!err.status || err.status >= 500)) { + if (err && (!err.status || err.status >= HTTP_STATUS.INTERNAL_ERROR)) { // report internal errors right away return options.callback(err); } @@ -478,7 +479,7 @@ class Storage implements IStorageHandler { }, (err: Error, upLinksErrors: any) => { assert(!err && Array.isArray(upLinksErrors)); if (!exists) { - return callback( ErrorCode.getNotFound('no such package available') + return callback( ErrorCode.getNotFound(API_ERROR.NO_PACKAGE) , null , upLinksErrors ); } diff --git a/src/lib/up-storage.js b/src/lib/up-storage.js index f389d68ef..83d3c93fd 100644 --- a/src/lib/up-storage.js +++ b/src/lib/up-storage.js @@ -8,7 +8,7 @@ import Stream from 'stream'; import URL from 'url'; import {parseInterval, isObject, ErrorCode, buildToken} from './utils'; import {ReadTarball} from '@verdaccio/streams'; -import {ERROR_CODE, TOKEN_BASIC, TOKEN_BEARER, HEADERS} from './constants'; +import {ERROR_CODE, TOKEN_BASIC, TOKEN_BEARER, HEADERS, HTTP_STATUS, API_ERROR, HEADER_TYPE} from './constants'; import type { Config, UpLinkConf, @@ -407,10 +407,10 @@ class ProxyStorage implements IProxy { if (err) { return callback(err); } - if (res.statusCode === 404) { - return callback( ErrorCode.getNotFound('package doesn\'t exist on uplink')); + if (res.statusCode === HTTP_STATUS.NOT_FOUND) { + return callback( ErrorCode.getNotFound(API_ERROR.NOT_PACKAGE_UPLINK)); } - if (!(res.statusCode >= 200 && res.statusCode < 300)) { + if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) { const error = ErrorCode.getInternalError(`bad status code: ${res.statusCode}`); // $FlowFixMe error.remoteStatus = res.statusCode; @@ -440,15 +440,15 @@ class ProxyStorage implements IProxy { }); readStream.on('response', function(res: any) { - if (res.statusCode === 404) { - return stream.emit('error', ErrorCode.getNotFound('file doesn\'t exist on uplink')); + if (res.statusCode === HTTP_STATUS.NOT_FOUND) { + return stream.emit('error', ErrorCode.getNotFound(API_ERROR.NOT_FILE_UPLINK)); } - if (!(res.statusCode >= 200 && res.statusCode < 300)) { + if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) { return stream.emit('error', ErrorCode.getInternalError(`bad uplink status code: ${res.statusCode}`)); } - if (res.headers['content-length']) { - expected_length = res.headers['content-length']; - stream.emit('content-length', res.headers['content-length']); + if (res.headers[HEADER_TYPE.CONTENT_LENGTH]) { + expected_length = res.headers[HEADER_TYPE.CONTENT_LENGTH]; + stream.emit(HEADER_TYPE.CONTENT_LENGTH, res.headers[HEADER_TYPE.CONTENT_LENGTH]); } readStream.pipe(stream); @@ -465,7 +465,7 @@ class ProxyStorage implements IProxy { current_length += data.length; } if (expected_length && current_length != expected_length) { - stream.emit('error', ErrorCode.getInternalError('content length mismatch')); + stream.emit('error', ErrorCode.getInternalError(API_ERROR.CONTENT_MISMATCH)); } }); return stream; @@ -500,7 +500,7 @@ class ProxyStorage implements IProxy { // See https://github.com/request/request#requestoptions-callback // Request library will not decode gzip stream. let jsonStream; - if (res.headers['content-encoding'] === 'gzip') { + if (res.headers[HEADER_TYPE.CONTENT_ENCODING] === HEADERS.GZIP) { jsonStream = res.pipe(zlib.createUnzip()); } else { jsonStream = res; diff --git a/src/lib/utils.js b/src/lib/utils.js index 477e3348a..a16823fd7 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -10,7 +10,7 @@ import _ from 'lodash'; import asciidoctor from 'asciidoctor.js'; import createError from 'http-errors'; import marked from 'marked'; -import {HTTP_STATUS} from './constants'; +import {HTTP_STATUS, API_ERROR} from './constants'; import type {Package} from '@verdaccio/types'; import type {$Request} from 'express'; @@ -359,7 +359,7 @@ const ErrorCode = { return createError(HTTP_STATUS.SERVICE_UNAVAILABLE, message); }, getNotFound: (customMessage?: string) => { - return createError(HTTP_STATUS.NOT_FOUND, customMessage || 'no such package available'); + return createError(HTTP_STATUS.NOT_FOUND, customMessage || API_ERROR.NO_PACKAGE); }, getCode: (statusCode: number, customMessage: string) => { return createError(statusCode, customMessage); diff --git a/test/functional/fixtures/package.js b/test/functional/fixtures/package.js index 12676ac85..0b9b65542 100644 --- a/test/functional/fixtures/package.js +++ b/test/functional/fixtures/package.js @@ -1,13 +1,16 @@ import {PORT_SERVER_1} from "../../../src/lib/constants"; -module.exports = function(name, version = '0.0.0', port = PORT_SERVER_1, domain= `http://localhost:${port}`) { +export const FILE_NAME = 'blahblah'; + +module.exports = function(name, version = '0.0.0', port = PORT_SERVER_1, domain= `http://localhost:${port}`, + fileName = 'blahblah', readme = 'this is a readme') { return { - name: name, - version: version, - readme: "this is a readme", + name, + version, + readme, dist: { shasum: 'fake', - tarball: `${domain}/${encodeURIComponent(name)}/-/blahblah`, + tarball: `${domain}/${encodeURIComponent(name)}/-/${fileName}`, }, }; }; diff --git a/test/functional/lib/environment.js b/test/functional/lib/environment.js index 4d4a4bdb1..959916085 100644 --- a/test/functional/lib/environment.js +++ b/test/functional/lib/environment.js @@ -8,6 +8,7 @@ import VerdaccioProcess from "../../lib/server_process"; import Server from "../../lib/server"; import ExpressServer from "./simple_server"; import type {IServerBridge} from '../../types'; +import {PORT_SERVER_1, PORT_SERVER_2, PORT_SERVER_3} from "../../../src/lib/constants"; const EXPRESS_PORT = 55550; @@ -34,17 +35,17 @@ class FunctionalEnvironment extends NodeEnvironment { const pathStore = path.join(__dirname, '../store'); const listServers = [ { - port: 55551, + port: PORT_SERVER_1, config: '/config-1.yaml', storage: '/test-storage' }, { - port: 55552, + port: PORT_SERVER_2, config: '/config-2.yaml', storage: '/test-storage2' }, { - port: 55553, + port: PORT_SERVER_3, config: '/config-3.yaml', storage: '/test-storage3' } diff --git a/test/functional/plugins/auth.js b/test/functional/plugins/auth.js index 9ead67086..c0d101663 100644 --- a/test/functional/plugins/auth.js +++ b/test/functional/plugins/auth.js @@ -1,4 +1,4 @@ -import {HTTP_STATUS, PACKAGE_ERROR} from "../../../src/lib/constants"; +import {HTTP_STATUS, API_ERROR} from "../../../src/lib/constants"; export default function(server2) { // credentials @@ -70,19 +70,19 @@ export default function(server2) { test(`should fails (404) on access ${UNEXISTING_PKG_NAME}`, () => { return server2.getPackage(UNEXISTING_PKG_NAME) .status(HTTP_STATUS.NOT_FOUND) - .body_error(PACKAGE_ERROR.NO_PACKAGE); + .body_error(API_ERROR.NO_PACKAGE); }); test(`should fails (403) access ${ONLY_ACCESS_BY_USER_2}`, () => { return server2.getPackage(ONLY_ACCESS_BY_USER_2) .status(HTTP_STATUS.FORBIDDEN) - .body_error(PACKAGE_ERROR.NOT_ALLOWED); + .body_error(API_ERROR.NOT_ALLOWED); }); test(`should fails (404) access ${AUTH_PKG_ACCESS_NAME}`, () => { return server2.getPackage(AUTH_PKG_ACCESS_NAME) .status(HTTP_STATUS.NOT_FOUND) - .body_error(PACKAGE_ERROR.NO_PACKAGE); + .body_error(API_ERROR.NO_PACKAGE); }); }); @@ -96,19 +96,19 @@ export default function(server2) { test(`should fails (403) on access ${UNEXISTING_PKG_NAME}`, () => { return server2.getPackage(UNEXISTING_PKG_NAME) .status(HTTP_STATUS.FORBIDDEN) - .body_error(PACKAGE_ERROR.NOT_ALLOWED); + .body_error(API_ERROR.NOT_ALLOWED); }); test(`should fails (403) on access ${DENY_PKG_NAME}`, () => { return server2.getPackage(DENY_PKG_NAME) .status(HTTP_STATUS.FORBIDDEN) - .body_error(PACKAGE_ERROR.NOT_ALLOWED); + .body_error(API_ERROR.NOT_ALLOWED); }); test(`should fails (404) access ${AUTH_PKG_ACCESS_NAME}`, () => { return server2.getPackage(AUTH_PKG_ACCESS_NAME) .status(HTTP_STATUS.NOT_FOUND) - .body_error(PACKAGE_ERROR.NO_PACKAGE); + .body_error(API_ERROR.NO_PACKAGE); }); }); diff --git a/test/functional/sanity/mirror.js b/test/functional/sanity/mirror.js index 9474ad4ab..ea09d3d08 100644 --- a/test/functional/sanity/mirror.js +++ b/test/functional/sanity/mirror.js @@ -1,58 +1,76 @@ -import assert from 'assert'; import {readFile} from '../lib/test.utils'; +import {HTTP_STATUS} from "../../../src/lib/constants"; +import generatePkg from '../fixtures/package'; const getBinary = () => readFile('../fixtures/binary'); export default function (server, server2) { - test('testing anti-loop', () => { - return server2.getPackage('testloop') - .status(404) - .body_error(/no such package/); - }) + describe('anti-loop testing', () => { + test('testing anti-loop', () => { + return server2.getPackage('testloop').status(HTTP_STATUS.NOT_FOUND) + .body_error(/no such package/); + }); + }); - ;['fwd'].forEach(function (pkg) { - let prefix = pkg + ': '; - pkg = 'test' + pkg; + describe('mirror', () => { + const pkgFileName = 'blahblah'; + const pkgList = ['pkg1', 'pkg2', 'pkg3']; - describe(pkg, () => { - beforeAll(function () { - return server.putPackage(pkg, require('../fixtures/package')(pkg)) - .status(201) - .body_ok(/created new package/); - }); + pkgList.forEach(function (pkg) { + let prefix = pkg; + pkg = 'test-mirror-' + pkg; - test(prefix + 'creating new package', () => {}); - - describe(pkg, () => { + describe(`testing mirror for ${pkg}`, () => { beforeAll(function () { - return server.putVersion(pkg, '0.1.1', require('../fixtures/package')(pkg)) - .status(201) - .body_ok(/published/); + return server2.putPackage(pkg, generatePkg(pkg)) + .status(HTTP_STATUS.CREATED) + .body_ok(/created new package/); }); - test(prefix + 'uploading new package version', () => {}); + test(prefix + 'creating new package', () => {}); - test(prefix + 'uploading incomplete tarball', () => { - return server.putTarballIncomplete(pkg, pkg + '.bad', getBinary(), 3000); - }); - - describe('tarball', () => { + describe(pkg, () => { beforeAll(function () { - return server.putTarball(pkg, pkg + '.file', getBinary()) - .status(201) - .body_ok(/.*/); + return server2.putVersion(pkg, '0.1.1', generatePkg(pkg)) + .status(HTTP_STATUS.CREATED) + .body_ok(/published/); }); - test(prefix + 'uploading new tarball', () => { + test(`should ${prefix} uploading new package version`, () => {}); + + test(`${prefix} uploading incomplete tarball`, () => { + return server2.putTarballIncomplete(pkg, pkg + '.bad', getBinary(), 3000); }); - test(prefix + 'downloading tarball from server1', () => { - return server.getTarball(pkg, pkg + '.file') - .status(200) - .then(function (body) { - assert.deepEqual(body, getBinary()); - }); + describe('should put a tarball', () => { + beforeAll(function () { + return server2.putTarball(pkg, pkgFileName, getBinary()) + .status(HTTP_STATUS.CREATED) + .body_ok(/.*/); + }); + + test(`should ${prefix} uploading new tarball`, () => {}); + + test(`should ${prefix} downloading tarball from server2`, () => { + return server2.getTarball(pkg, pkgFileName) + .status(HTTP_STATUS.OK) + .then(function (body) { + expect(body).toEqual(getBinary()); + }); + }); + + test('testing mirror server1', () => { + return server.getPackage(pkg).status(HTTP_STATUS.OK); + }); + + test(`should ${prefix} downloading tarball from server1`, () => { + return server.getTarball(pkg, pkgFileName) + .status(HTTP_STATUS.OK) + .then(function (body) { + expect(body).toEqual(getBinary()); + }); + }); }); }); }); diff --git a/test/functional/scenarios/gh29.js b/test/functional/scenarios/gh29.js index 87dd6ba34..4d71ebd8e 100644 --- a/test/functional/scenarios/gh29.js +++ b/test/functional/scenarios/gh29.js @@ -11,19 +11,18 @@ function readfile(filePath) { const binary = '../fixtures/binary'; const pkgName = 'testpkg-gh29'; -const pkgContent = 'blahblah'; +const pkgFileName = 'blahblah'; export default function (server, server2) { - - test('downloading non-existent tarball #1 / srv2', () => { - return server2.getTarball(pkgName, pkgContent) - .status(HTTP_STATUS.NOT_FOUND) - .body_error(/no such package/); + describe('pkg-gh29 #1', () => { + test('downloading non-existent tarball #1 / srv2', () => { + return server2.getTarball(pkgName, pkgFileName) + .status(HTTP_STATUS.NOT_FOUND) + .body_error(/no such package/); + }); }); - describe('pkg-gh29', () => { - - + describe('pkg-gh29 #2', () => { beforeAll(function() { return server.putPackage(pkgName, require('../fixtures/package')(pkgName)) .status(HTTP_STATUS.CREATED) @@ -33,14 +32,14 @@ export default function (server, server2) { test('creating new package / srv1', () => {}); test('downloading non-existent tarball #2 / srv2', () => { - return server2.getTarball(pkgName, pkgContent) + return server2.getTarball(pkgName, pkgFileName) .status(HTTP_STATUS.NOT_FOUND) .body_error(/no such file available/); }); describe('tarball', () => { beforeAll(function() { - return server.putTarball(pkgName, pkgContent, readfile(binary)) + return server.putTarball(pkgName, pkgFileName, readfile(binary)) .status(HTTP_STATUS.CREATED) .body_ok(/.*/); }); @@ -59,7 +58,7 @@ export default function (server, server2) { test('uploading new package version / srv1', () => {}); test('downloading newly created tarball / srv2', () => { - return server2.getTarball(pkgName, pkgContent) + return server2.getTarball(pkgName, pkgFileName) .status(HTTP_STATUS.OK) .then(function(body) { expect(body).toEqual(readfile(binary)); diff --git a/test/functional/store/config-1.yaml b/test/functional/store/config-1.yaml index 5c8a2cb04..94814b81b 100644 --- a/test/functional/store/config-1.yaml +++ b/test/functional/store/config-1.yaml @@ -34,12 +34,13 @@ packages: publish: $all proxy: server2 - 'testfwd*': + ## mirror.js + 'test-mirror-*': access: $all publish: $all proxy: server2 - proxy_publish: server2 + ## mirror.js 'testloop': access: $all publish: $all diff --git a/test/functional/store/config-2.yaml b/test/functional/store/config-2.yaml index 61d0ee2ee..df61f25f8 100644 --- a/test/functional/store/config-2.yaml +++ b/test/functional/store/config-2.yaml @@ -39,7 +39,11 @@ packages: publish: $all proxy: server1 - 'testfwd': + 'test-fwd': + access: $all + publish: $all + + 'test-mirror-fwdw*': access: $all publish: $all diff --git a/test/lib/request.js b/test/lib/request.js index 81f90aa60..5cbfb8bc3 100644 --- a/test/lib/request.js +++ b/test/lib/request.js @@ -1,8 +1,8 @@ // @flow +import _ from 'lodash'; import assert from 'assert'; import request from 'request'; -import _ from 'lodash'; import type {IRequestPromise} from '../types'; const requestData = Symbol('smart_request_data'); @@ -54,7 +54,6 @@ export class PromiseAssert extends Promise implements IRequestPromise{ const selfData = this[requestData]; return injectResponse(this, this.then(function(body) { - // console.log("======>smartRequest body_error://", body); try { if (_.isRegExp(expected)) { assert(body.error.match(expected), body.error + ' doesn\'t match ' + expected); diff --git a/test/unit/api/api.spec.js b/test/unit/api/api.spec.js index d7103ce9c..bd1c75963 100644 --- a/test/unit/api/api.spec.js +++ b/test/unit/api/api.spec.js @@ -8,7 +8,7 @@ import publishMetadata from '../partials/publish-api'; import forbiddenPlace from '../partials/forbidden-place'; import Config from '../../../src/lib/config'; import endPointAPI from '../../../src/api/index'; -import {HEADERS} from '../../../src/lib/constants'; +import {HEADERS, API_ERROR} from '../../../src/lib/constants'; require('../../../src/lib/logger').setup([]); const credentials = { name: 'Jota', password: 'secretPass' }; @@ -595,7 +595,7 @@ describe('endpoint unit test', () => { .expect(200) .expect('Content-Type', 'text/plain; charset=utf-8') .end(function(err, res) { - expect(res.body.error).toMatch('no such package available'); + expect(res.body.error).toMatch(API_ERROR.NO_PACKAGE); done(); }); });