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:
Juan Picado 2022-09-05 21:36:23 +02:00 committed by GitHub
parent 8ac917deb2
commit cdb80aac20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 97 additions and 76 deletions

View File

@ -42,73 +42,3 @@ jobs:
gatsby new my-gatsby
cd my-gatsby
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

View File

@ -6,9 +6,9 @@ import { Config, Package } from '@verdaccio/types';
import { $NextFunctionVer, $RequestExtend, $ResponseExtend, IAuth, IStorageHandler } from '../../../../types';
import { API_ERROR, DIST_TAGS, HEADERS } from '../../../lib/constants';
import { ErrorCode, convertDistRemoteToLocalTarballUrls, getVersion } from '../../../lib/utils';
import { getByQualityPriorityValue } from '../../../utils/string';
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 stream = storage.getTarball(packageName, filename);
@ -46,6 +46,7 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
const can = allow(auth);
// TODO: anonymous user?
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 {
if (err) {
return next(err);
@ -54,11 +55,18 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
let queryVersion = req.params.version;
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);
}
let version = getVersion(metadata, queryVersion);
if (_.isNil(version) === false) {
res.setHeader(HEADERS.CONTENT_TYPE, HEADERS.JSON);
return next(version);
}
@ -67,13 +75,14 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
queryVersion = metadata[DIST_TAGS][queryVersion];
version = getVersion(metadata, queryVersion);
if (_.isNil(version) === false) {
res.setHeader(HEADERS.CONTENT_TYPE, HEADERS.JSON);
return next(version);
}
}
}
return next(ErrorCode.getNotFound(`${API_ERROR.VERSION_NOT_EXIST}: ${req.params.version}`));
};
const abbreviated = req.get('Accept') === ABBREVIATED_HEADER;
storage.getPackage({
name: req.params.package,
uplinksLook: true,

View File

@ -5,3 +5,40 @@ export function spliceURL(...args: string[]): string {
.reduce((lastResult, current) => lastResult + current)
.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];
}

View File

@ -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)
.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)
.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)
.end(function (err, res) {
if (err) {

View File

@ -21,7 +21,7 @@ import {
validateName,
validatePackage,
} 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 { readFile } from '../../../functional/lib/test.utils';
@ -369,6 +369,23 @@ describe('Utilities', () => {
expect(isHTTPProtocol('/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', () => {