mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-01-06 22:40:26 -05:00
fix: get header by quality priority value (#3359)
* fix: get header by quality priority value * chore: disable some workflows * chore: add more tests * chore: remove some duplicated testss * chore: return right content type haders
This commit is contained in:
parent
8ac917deb2
commit
cdb80aac20
5 changed files with 97 additions and 76 deletions
70
.github/workflows/e2e-gatsbyjs-cli-workflow.yml
vendored
70
.github/workflows/e2e-gatsbyjs-cli-workflow.yml
vendored
|
@ -42,73 +42,3 @@ jobs:
|
||||||
gatsby new my-gatsby
|
gatsby new my-gatsby
|
||||||
cd my-gatsby
|
cd my-gatsby
|
||||||
npm run build
|
npm run build
|
||||||
npm7:
|
|
||||||
name: 'npm7:gatsby example'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3.0.2
|
|
||||||
|
|
||||||
- name: 'Use Node.js 14.x'
|
|
||||||
uses: actions/setup-node@v2.5.1
|
|
||||||
with:
|
|
||||||
node-version: 14.x
|
|
||||||
- name: 'install npm 7'
|
|
||||||
run: npm i -g npm@latest-7
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: yarn install
|
|
||||||
- name: 'Run verdaccio in the background'
|
|
||||||
run: |
|
|
||||||
nohup yarn node ./scripts/run-verdaccio.js --config ./scripts/e2e-config.yaml &
|
|
||||||
- name: 'Ping to verdaccio'
|
|
||||||
run: |
|
|
||||||
npm ping --registry http://localhost:4873
|
|
||||||
- name: 'Running the integration test'
|
|
||||||
run: |
|
|
||||||
source scripts/e2e-setup-ci.sh
|
|
||||||
echo "registry=http://localhost:4873
|
|
||||||
loglevel="silent"
|
|
||||||
fetch-retries=10
|
|
||||||
fetch-retry-factor=2
|
|
||||||
fetch-retry-mintimeout=10000
|
|
||||||
fetch-retry-maxtimeout=80000" > ~/.npmrc
|
|
||||||
npm config list
|
|
||||||
npm i -g gatsby
|
|
||||||
gatsby new my-gatsby
|
|
||||||
cd my-gatsby
|
|
||||||
npm run build
|
|
||||||
npm8:
|
|
||||||
name: 'npm8:gatsby example'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3.0.2
|
|
||||||
|
|
||||||
- name: 'Use Node.js 16.x'
|
|
||||||
uses: actions/setup-node@v2.5.1
|
|
||||||
with:
|
|
||||||
node-version: 16.x
|
|
||||||
- name: 'install npm 8'
|
|
||||||
run: npm i -g npm@next-8
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: yarn install
|
|
||||||
- name: 'Run verdaccio in the background'
|
|
||||||
run: |
|
|
||||||
nohup yarn node ./scripts/run-verdaccio.js --config ./scripts/e2e-config.yaml &
|
|
||||||
- name: 'Ping to verdaccio'
|
|
||||||
run: |
|
|
||||||
npm ping --registry http://localhost:4873
|
|
||||||
- name: 'Running the integration test'
|
|
||||||
run: |
|
|
||||||
source scripts/e2e-setup-ci.sh
|
|
||||||
echo "registry=http://localhost:4873
|
|
||||||
loglevel="silent"
|
|
||||||
fetch-retries=10
|
|
||||||
fetch-retry-factor=2
|
|
||||||
fetch-retry-mintimeout=10000
|
|
||||||
fetch-retry-maxtimeout=80000" > ~/.npmrc
|
|
||||||
npm config list
|
|
||||||
npm i -g gatsby
|
|
||||||
gatsby new my-gatsby
|
|
||||||
cd my-gatsby
|
|
||||||
npm run build
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ import { Config, Package } from '@verdaccio/types';
|
||||||
import { $NextFunctionVer, $RequestExtend, $ResponseExtend, IAuth, IStorageHandler } from '../../../../types';
|
import { $NextFunctionVer, $RequestExtend, $ResponseExtend, IAuth, IStorageHandler } from '../../../../types';
|
||||||
import { API_ERROR, DIST_TAGS, HEADERS } from '../../../lib/constants';
|
import { API_ERROR, DIST_TAGS, HEADERS } from '../../../lib/constants';
|
||||||
import { ErrorCode, convertDistRemoteToLocalTarballUrls, getVersion } from '../../../lib/utils';
|
import { ErrorCode, convertDistRemoteToLocalTarballUrls, getVersion } from '../../../lib/utils';
|
||||||
|
import { getByQualityPriorityValue } from '../../../utils/string';
|
||||||
import { allow } from '../../middleware';
|
import { allow } from '../../middleware';
|
||||||
|
|
||||||
const ABBREVIATED_HEADER = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*';
|
|
||||||
const downloadStream = (packageName: string, filename: string, storage: any, req: $RequestExtend, res: $ResponseExtend): void => {
|
const downloadStream = (packageName: string, filename: string, storage: any, req: $RequestExtend, res: $ResponseExtend): void => {
|
||||||
const stream = storage.getTarball(packageName, filename);
|
const stream = storage.getTarball(packageName, filename);
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
|
||||||
const can = allow(auth);
|
const can = allow(auth);
|
||||||
// TODO: anonymous user?
|
// TODO: anonymous user?
|
||||||
route.get('/:package/:version?', can('access'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
route.get('/:package/:version?', can('access'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||||
|
const abbreviated = getByQualityPriorityValue(req.get('Accept')) === 'application/vnd.npm.install-v1+json';
|
||||||
const getPackageMetaCallback = function (err, metadata: Package): void {
|
const getPackageMetaCallback = function (err, metadata: Package): void {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
|
@ -54,11 +55,18 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
|
||||||
|
|
||||||
let queryVersion = req.params.version;
|
let queryVersion = req.params.version;
|
||||||
if (_.isNil(queryVersion)) {
|
if (_.isNil(queryVersion)) {
|
||||||
|
if (abbreviated) {
|
||||||
|
res.setHeader(HEADERS.CONTENT_TYPE, 'application/vnd.npm.install-v1+json');
|
||||||
|
} else {
|
||||||
|
res.setHeader(HEADERS.CONTENT_TYPE, HEADERS.JSON);
|
||||||
|
}
|
||||||
|
|
||||||
return next(metadata);
|
return next(metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = getVersion(metadata, queryVersion);
|
let version = getVersion(metadata, queryVersion);
|
||||||
if (_.isNil(version) === false) {
|
if (_.isNil(version) === false) {
|
||||||
|
res.setHeader(HEADERS.CONTENT_TYPE, HEADERS.JSON);
|
||||||
return next(version);
|
return next(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,13 +75,14 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
|
||||||
queryVersion = metadata[DIST_TAGS][queryVersion];
|
queryVersion = metadata[DIST_TAGS][queryVersion];
|
||||||
version = getVersion(metadata, queryVersion);
|
version = getVersion(metadata, queryVersion);
|
||||||
if (_.isNil(version) === false) {
|
if (_.isNil(version) === false) {
|
||||||
|
res.setHeader(HEADERS.CONTENT_TYPE, HEADERS.JSON);
|
||||||
return next(version);
|
return next(version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return next(ErrorCode.getNotFound(`${API_ERROR.VERSION_NOT_EXIST}: ${req.params.version}`));
|
return next(ErrorCode.getNotFound(`${API_ERROR.VERSION_NOT_EXIST}: ${req.params.version}`));
|
||||||
};
|
};
|
||||||
const abbreviated = req.get('Accept') === ABBREVIATED_HEADER;
|
|
||||||
storage.getPackage({
|
storage.getPackage({
|
||||||
name: req.params.package,
|
name: req.params.package,
|
||||||
uplinksLook: true,
|
uplinksLook: true,
|
||||||
|
|
|
@ -5,3 +5,40 @@ export function spliceURL(...args: string[]): string {
|
||||||
.reduce((lastResult, current) => lastResult + current)
|
.reduce((lastResult, current) => lastResult + current)
|
||||||
.replace(/([^:])(\/)+(.)/g, `$1/$3`);
|
.replace(/([^:])(\/)+(.)/g, `$1/$3`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quality values, or q-values and q-factors, are used to describe the order
|
||||||
|
* of priority of values in a comma-separated list.
|
||||||
|
* It is a special syntax allowed in some HTTP headers and in HTML.
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Glossary/Quality_values
|
||||||
|
* @param headerValue
|
||||||
|
*/
|
||||||
|
export function getByQualityPriorityValue(headerValue: string | undefined | null): string {
|
||||||
|
if (typeof headerValue !== 'string') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const split = headerValue.split(',');
|
||||||
|
|
||||||
|
if (split.length <= 1) {
|
||||||
|
const qList = split[0].split(';');
|
||||||
|
return qList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
let [header] = split
|
||||||
|
.reduce((acc, item: string) => {
|
||||||
|
const qList = item.split(';');
|
||||||
|
if (qList.length > 1) {
|
||||||
|
const [accept, q] = qList;
|
||||||
|
const [, query] = q.split('=');
|
||||||
|
acc.push([accept.trim(), query ? query : 0]);
|
||||||
|
} else {
|
||||||
|
acc.push([qList[0], 0]);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, [] as any)
|
||||||
|
.sort(function (a, b) {
|
||||||
|
return b[1] - a[1];
|
||||||
|
});
|
||||||
|
return header[0];
|
||||||
|
}
|
||||||
|
|
|
@ -339,12 +339,40 @@ describe('endpoint unit test', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should fetch abbreviated jquery package from remote uplink', (done) => {
|
test.each(['application/json; q=1.0, application/vnd.npm.install-v1+json; q=0.9, */*', 'application/json; q=1.0; q=1.0, */*', 'application/json'])(
|
||||||
|
'should not fetch abbreviated jquery package from remote uplink with %s',
|
||||||
|
(accept, done: any) => {
|
||||||
|
request(app)
|
||||||
|
.get('/jquery')
|
||||||
|
.set('accept', accept)
|
||||||
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||||
|
.expect(HEADER_TYPE.CONTENT_ENCODING, HEADERS.GZIP)
|
||||||
|
.expect(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||||
|
.expect(HTTP_STATUS.OK)
|
||||||
|
.end(function (err, res) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
const manifest = res.body;
|
||||||
|
expect(manifest).toBeDefined();
|
||||||
|
expect(manifest.name).toMatch(/jquery/);
|
||||||
|
expect(manifest.readme).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*',
|
||||||
|
'application/vnd.npm.install-v1+json; q=1.0, */*',
|
||||||
|
'application/vnd.npm.install-v1+json',
|
||||||
|
])('should fetch abbreviated jquery package from remote uplink with %s', (accept, done: any) => {
|
||||||
request(app)
|
request(app)
|
||||||
.get('/jquery')
|
.get('/jquery')
|
||||||
.set('accept', 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*')
|
.set('accept', accept)
|
||||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
.expect(HEADER_TYPE.CONTENT_ENCODING, HEADERS.GZIP)
|
||||||
|
.expect(HEADERS.CONTENT_TYPE, 'application/vnd.npm.install-v1+json; charset=utf-8')
|
||||||
.expect(HTTP_STATUS.OK)
|
.expect(HTTP_STATUS.OK)
|
||||||
.end(function (err, res) {
|
.end(function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
validateName,
|
validateName,
|
||||||
validatePackage,
|
validatePackage,
|
||||||
} from '../../../../src/lib/utils';
|
} from '../../../../src/lib/utils';
|
||||||
import { spliceURL } from '../../../../src/utils/string';
|
import { getByQualityPriorityValue, spliceURL } from '../../../../src/utils/string';
|
||||||
import { GENERIC_AVATAR, generateGravatarUrl } from '../../../../src/utils/user';
|
import { GENERIC_AVATAR, generateGravatarUrl } from '../../../../src/utils/user';
|
||||||
import { readFile } from '../../../functional/lib/test.utils';
|
import { readFile } from '../../../functional/lib/test.utils';
|
||||||
|
|
||||||
|
@ -369,6 +369,23 @@ describe('Utilities', () => {
|
||||||
expect(isHTTPProtocol('/static/logo.png')).toBeFalsy();
|
expect(isHTTPProtocol('/static/logo.png')).toBeFalsy();
|
||||||
expect(isHTTPProtocol('F:\\static\\logo.png')).toBeFalsy();
|
expect(isHTTPProtocol('F:\\static\\logo.png')).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('getByQualityPriorityValue', () => {
|
||||||
|
expect(getByQualityPriorityValue('')).toEqual('');
|
||||||
|
expect(getByQualityPriorityValue(null)).toEqual('');
|
||||||
|
expect(getByQualityPriorityValue(undefined)).toEqual('');
|
||||||
|
expect(getByQualityPriorityValue('something')).toEqual('something');
|
||||||
|
expect(getByQualityPriorityValue('something,')).toEqual('something');
|
||||||
|
expect(getByQualityPriorityValue('0,')).toEqual('0');
|
||||||
|
expect(getByQualityPriorityValue('application/json')).toEqual('application/json');
|
||||||
|
expect(getByQualityPriorityValue('application/json; q=1')).toEqual('application/json');
|
||||||
|
expect(getByQualityPriorityValue('application/json; q=')).toEqual('application/json');
|
||||||
|
expect(getByQualityPriorityValue('application/json;')).toEqual('application/json');
|
||||||
|
expect(getByQualityPriorityValue('application/json; q=1.0, application/vnd.npm.install-v1+json; q=0.9, */*')).toEqual('application/json');
|
||||||
|
expect(getByQualityPriorityValue('application/json; q=1.0, application/vnd.npm.install-v1+json; q=, */*')).toEqual('application/json');
|
||||||
|
expect(getByQualityPriorityValue('application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.9, */*')).toEqual('application/vnd.npm.install-v1+json');
|
||||||
|
expect(getByQualityPriorityValue('application/vnd.npm.install-v1+json; q=, application/json; q=0.9, */*')).toEqual('application/json');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('User utilities', () => {
|
describe('User utilities', () => {
|
||||||
|
|
Loading…
Reference in a new issue