mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -05:00
fix: abbreviated headers handle quality values (#3361)
This commit is contained in:
parent
97078c9084
commit
43f32687cd
10 changed files with 111 additions and 7 deletions
8
.changeset/chilly-glasses-occur.md
Normal file
8
.changeset/chilly-glasses-occur.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
'@verdaccio/api': patch
|
||||
'@verdaccio/core': patch
|
||||
'@verdaccio/server-fastify': patch
|
||||
'@verdaccio/store': patch
|
||||
---
|
||||
|
||||
fix: abbreviated headers handle quality values
|
|
@ -3,7 +3,14 @@
|
|||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [
|
||||
["verdaccio", "@verdaccio/cli", "@verdaccio/core", "@verdaccio/config", "@verdaccio/node-api"]
|
||||
[
|
||||
"verdaccio",
|
||||
"@verdaccio/cli",
|
||||
"@verdaccio/core",
|
||||
"@verdaccio/config",
|
||||
"@verdaccio/node-api",
|
||||
"@verdaccio/ui-theme"
|
||||
]
|
||||
],
|
||||
"access": "public",
|
||||
"baseBranch": "master",
|
||||
|
|
|
@ -2,7 +2,7 @@ import buildDebug from 'debug';
|
|||
import { Router } from 'express';
|
||||
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { HEADERS, HEADER_TYPE } from '@verdaccio/core';
|
||||
import { HEADERS, HEADER_TYPE, stringUtils } from '@verdaccio/core';
|
||||
import { allow } from '@verdaccio/middleware';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
|
||||
|
@ -25,7 +25,8 @@ export default function (route: Router, auth: IAuth, storage: Storage): void {
|
|||
const name = req.params.package;
|
||||
let version = req.params.version;
|
||||
const write = req.query.write === 'true';
|
||||
const abbreviated = req.get('Accept') === Storage.ABBREVIATED_HEADER;
|
||||
const abbreviated =
|
||||
stringUtils.getByQualityPriorityValue(req.get('Accept')) === Storage.ABBREVIATED_HEADER;
|
||||
const requestOptions = {
|
||||
protocol: req.protocol,
|
||||
headers: req.headers as any,
|
||||
|
@ -43,6 +44,12 @@ export default function (route: Router, auth: IAuth, storage: Storage): void {
|
|||
version,
|
||||
requestOptions,
|
||||
});
|
||||
if (abbreviated) {
|
||||
_res.setHeader(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_INSTALL_CHARSET);
|
||||
} else {
|
||||
_res.setHeader(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
}
|
||||
|
||||
next(manifest);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
|
|
|
@ -90,7 +90,7 @@ describe('package', () => {
|
|||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.set(HEADERS.ACCEPT, Storage.ABBREVIATED_HEADER)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_INSTALL_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
expect(response.body.time).toBeDefined();
|
||||
|
|
|
@ -46,6 +46,7 @@ export const HEADERS = {
|
|||
NONE_MATCH: 'If-None-Match',
|
||||
ETAG: 'ETag',
|
||||
JSON_CHARSET: 'application/json; charset=utf-8',
|
||||
JSON_INSTALL_CHARSET: 'application/vnd.npm.install-v1+json; charset=utf-8',
|
||||
OCTET_STREAM: 'application/octet-stream; charset=utf-8',
|
||||
TEXT_CHARSET: 'text/plain; charset=utf-8',
|
||||
WWW_AUTH: 'WWW-Authenticate',
|
||||
|
|
|
@ -5,6 +5,7 @@ import * as pkgUtils from './pkg-utils';
|
|||
import * as pluginUtils from './plugin-utils';
|
||||
import * as searchUtils from './search-utils';
|
||||
import * as streamUtils from './stream-utils';
|
||||
import * as stringUtils from './string-utils';
|
||||
import * as validatioUtils from './validation-utils';
|
||||
import * as warningUtils from './warning-utils';
|
||||
|
||||
|
@ -33,6 +34,7 @@ export {
|
|||
// TODO: remove this typo
|
||||
validatioUtils,
|
||||
validationUtils,
|
||||
stringUtils,
|
||||
constants,
|
||||
pluginUtils,
|
||||
warningUtils,
|
||||
|
|
36
packages/core/core/src/string-utils.ts
Normal file
36
packages/core/core/src/string-utils.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* 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];
|
||||
}
|
40
packages/core/core/test/string-utils.spec.ts
Normal file
40
packages/core/core/test/string-utils.spec.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { stringUtils } from '../src';
|
||||
|
||||
describe('string-utils', () => {
|
||||
test('getByQualityPriorityValue', () => {
|
||||
expect(stringUtils.getByQualityPriorityValue('')).toEqual('');
|
||||
expect(stringUtils.getByQualityPriorityValue(null)).toEqual('');
|
||||
expect(stringUtils.getByQualityPriorityValue(undefined)).toEqual('');
|
||||
expect(stringUtils.getByQualityPriorityValue('something')).toEqual('something');
|
||||
expect(stringUtils.getByQualityPriorityValue('something,')).toEqual('something');
|
||||
expect(stringUtils.getByQualityPriorityValue('0,')).toEqual('0');
|
||||
expect(stringUtils.getByQualityPriorityValue('application/json')).toEqual('application/json');
|
||||
expect(stringUtils.getByQualityPriorityValue('application/json; q=1')).toEqual(
|
||||
'application/json'
|
||||
);
|
||||
expect(stringUtils.getByQualityPriorityValue('application/json; q=')).toEqual(
|
||||
'application/json'
|
||||
);
|
||||
expect(stringUtils.getByQualityPriorityValue('application/json;')).toEqual('application/json');
|
||||
expect(
|
||||
stringUtils.getByQualityPriorityValue(
|
||||
'application/json; q=1.0, application/vnd.npm.install-v1+json; q=0.9, */*'
|
||||
)
|
||||
).toEqual('application/json');
|
||||
expect(
|
||||
stringUtils.getByQualityPriorityValue(
|
||||
'application/json; q=1.0, application/vnd.npm.install-v1+json; q=, */*'
|
||||
)
|
||||
).toEqual('application/json');
|
||||
expect(
|
||||
stringUtils.getByQualityPriorityValue(
|
||||
'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.9, */*'
|
||||
)
|
||||
).toEqual('application/vnd.npm.install-v1+json');
|
||||
expect(
|
||||
stringUtils.getByQualityPriorityValue(
|
||||
'application/vnd.npm.install-v1+json; q=, application/json; q=0.9, */*'
|
||||
)
|
||||
).toEqual('application/json');
|
||||
});
|
||||
});
|
|
@ -1,6 +1,8 @@
|
|||
import buildDebug from 'debug';
|
||||
import { FastifyInstance } from 'fastify';
|
||||
|
||||
import { stringUtils } from '@verdaccio/core';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
import { Package, Version } from '@verdaccio/types';
|
||||
|
||||
const debug = buildDebug('verdaccio:fastify:api:sidebar');
|
||||
|
@ -17,7 +19,9 @@ async function manifestRoute(fastify: FastifyInstance) {
|
|||
const storage = fastify.storage;
|
||||
debug('pkg name %s ', name);
|
||||
// @ts-ignore
|
||||
const abbreviated = request.headers['accept'] === Storage.ABBREVIATED_HEADER;
|
||||
const abbreviated =
|
||||
stringUtils.getByQualityPriorityValue(request.headers['accept']) ===
|
||||
Storage.ABBREVIATED_HEADER;
|
||||
const data = await storage?.getPackageByOptions({
|
||||
name,
|
||||
// @ts-ignore
|
||||
|
|
|
@ -94,8 +94,7 @@ class Storage {
|
|||
debug('uplinks available %o', Object.keys(this.uplinks));
|
||||
}
|
||||
|
||||
static ABBREVIATED_HEADER =
|
||||
'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*';
|
||||
static ABBREVIATED_HEADER = 'application/vnd.npm.install-v1+json';
|
||||
|
||||
/**
|
||||
* Change an existing package (i.e. unpublish one version)
|
||||
|
|
Loading…
Reference in a new issue