Improve parsing, regex, and test consistency

Refines regex patterns for URL rewriting and authentication parsing, ensures parseInt uses radix 10 throughout, and updates test mocks and helpers for consistency. Also adds more global variables to ESLint config, improves error handling, and removes unused code in tests.
This commit is contained in:
xixu-me committed 2025-12-08 19:19:10 +08:00
1 parent 55e99c9eac
commit d88f1911a6
12 files changed
+42 -45

No files matched your search

+13
View File
@@ -19,6 +19,19 @@ export default [
URL: 'readonly',
URLSearchParams: 'readonly',
console: 'readonly',
AbortController: 'readonly',
AbortSignal: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
ReadableStream: 'readonly',
WritableStream: 'readonly',
TransformStream: 'readonly',
TextEncoder: 'readonly',
TextDecoder: 'readonly',
performance: 'readonly',
globalThis: 'readonly',
// Vitest globals
describe: 'readonly',
+5 -5
View File
@@ -144,14 +144,14 @@ import { PLATFORMS } from './platforms.js';
*/
export function createConfig(env = {}) {
return {
TIMEOUT_SECONDS: parseInt(env.TIMEOUT_SECONDS) || 30,
MAX_RETRIES: parseInt(env.MAX_RETRIES) || 3,
RETRY_DELAY_MS: parseInt(env.RETRY_DELAY_MS) || 1000,
CACHE_DURATION: parseInt(env.CACHE_DURATION) || 1800, // 30 minutes
TIMEOUT_SECONDS: parseInt(env.TIMEOUT_SECONDS, 10) || 30,
MAX_RETRIES: parseInt(env.MAX_RETRIES, 10) || 3,
RETRY_DELAY_MS: parseInt(env.RETRY_DELAY_MS, 10) || 1000,
CACHE_DURATION: parseInt(env.CACHE_DURATION, 10) || 1800, // 30 minutes
SECURITY: {
ALLOWED_METHODS: env.ALLOWED_METHODS ? env.ALLOWED_METHODS.split(',') : ['GET', 'HEAD'],
ALLOWED_ORIGINS: env.ALLOWED_ORIGINS ? env.ALLOWED_ORIGINS.split(',') : ['*'],
MAX_PATH_LENGTH: parseInt(env.MAX_PATH_LENGTH) || 2048
MAX_PATH_LENGTH: parseInt(env.MAX_PATH_LENGTH, 10) || 2048
},
PLATFORMS
};
+8 -16
View File
@@ -582,9 +582,9 @@ function addSecurityHeaders(headers) {
*/
function parseAuthenticate(authenticateStr) {
// sample: Bearer realm="https://auth.ipv6.docker.com/token",service="registry.docker.io"
const re = /(?<=\=")(?:\\.|[^"\\])*(?=")/g;
const re = /(?<=")(?:\\.|[^"\\])*(?=")/g;
const matches = authenticateStr.match(re);
if (matches == null || matches.length < 2) {
if (matches === null || matches.length < 2) {
throw new Error(`invalid Www-Authenticate Header: ${authenticateStr}`);
}
return {
@@ -1021,14 +1021,6 @@ async function handleRequest(request, env, ctx) {
// For Range requests, we need to decide whether to forward the Range header
// If we want to cache the full content first, don't send Range to origin
if (rangeHeader) {
// Check if we already have full content cached
const fullContentKey = new Request(targetUrl, {
method: request.method,
headers: new Headers(
[...request.headers.entries()].filter(([k]) => k.toLowerCase() !== 'range')
)
});
// If we're going to try to get full content for caching, don't send Range header
// This will be handled in the retry logic
requestHeaders.set('Range', rangeHeader);
@@ -1077,8 +1069,8 @@ async function handleRequest(request, env, ctx) {
// Content-Range format: "bytes 0-0/12345" where 12345 is the total size
if (contentRange) {
const match = contentRange.match(/bytes\s+\d+-\d+\/(\d+)/);
if (match && match[1]) {
contentLength = match[1];
if (match) {
[, contentLength] = match;
}
}
} else if (rangeResponse.ok) {
@@ -1091,7 +1083,7 @@ async function handleRequest(request, env, ctx) {
const contentLengthHint = rangeResponse.headers.get('Content-Length');
// Only buffer if we know it's small or we don't know the size
if (!contentLengthHint || parseInt(contentLengthHint) < sizeLimit) {
if (!contentLengthHint || parseInt(contentLengthHint, 10) < sizeLimit) {
try {
const arrayBuffer = await rangeResponse.arrayBuffer();
contentLength = arrayBuffer.byteLength.toString();
@@ -1182,7 +1174,7 @@ async function handleRequest(request, env, ctx) {
}
}
} catch (error) {
console.log('Token fetch failed:', error);
console.warn('Token fetch failed:', error);
}
}
@@ -1253,7 +1245,7 @@ async function handleRequest(request, env, ctx) {
// Rewrite URLs in the response body to go through the Cloudflare Workers
// files.pythonhosted.org URLs should be rewritten to go through our pypi/files endpoint
const rewrittenText = originalText.replace(
/https:\/\/files\.pythonhosted\.org/g,
/https:\/\/files.pythonhosted.org/g,
`${url.origin}/pypi/files`
);
responseBody = new ReadableStream({
@@ -1270,7 +1262,7 @@ async function handleRequest(request, env, ctx) {
// Rewrite tarball URLs in npm registry responses to go through our npm endpoint
// https://registry.npmjs.org/package/-/package-version.tgz -> https://xget.xi-xu.me/npm/package/-/package-version.tgz
const rewrittenText = originalText.replace(
/https:\/\/registry\.npmjs\.org\/([^\/]+)/g,
/https:\/\/registry.npmjs.org\/([^/]+)/g,
`${url.origin}/npm/$1`
);
responseBody = new ReadableStream({
+1 -3
View File
@@ -1,10 +1,8 @@
import { SELF } from 'cloudflare:test';
import { bench, describe } from 'vitest';
import { PerformanceTestHelper, TEST_URLS } from '../helpers/test-utils.js';
import { TEST_URLS } from '../helpers/test-utils.js';
describe('Performance Benchmarks', () => {
const perfHelper = new PerformanceTestHelper();
describe('Request Processing Speed', () => {
bench('Basic request handling', async () => {
await SELF.fetch('https://example.com/gh/test/repo/file.txt', {
+1 -1
View File
@@ -55,7 +55,7 @@ describe('Performance Monitoring', () => {
it('should warn on duplicate mark names', () => {
// Mock console.warn for this test
const originalWarn = console.warn;
const mockWarn = vi ? vi.fn() : jest.fn();
const mockWarn = vi.fn();
console.warn = mockWarn;
monitor.mark('duplicate');
+3 -3
View File
@@ -14,8 +14,8 @@ describe('Range Request Caching Strategy', () => {
let SELF;
beforeAll(async () => {
const { unstable_dev } = await import('wrangler');
const worker = await unstable_dev('src/index.js', {
const { unstable_dev: unstableDev } = await import('wrangler');
const worker = await unstableDev('src/index.js', {
experimental: { disableExperimentalWarning: true }
});
SELF = worker;
@@ -184,7 +184,7 @@ describe('Range Request Caching Strategy', () => {
// Should have Content-Length for proper range support
const contentLength = response.headers.get('Content-Length');
if (contentLength) {
expect(parseInt(contentLength)).toBeGreaterThan(0);
expect(parseInt(contentLength, 10)).toBeGreaterThan(0);
}
// Should have Accept-Ranges header
+3 -3
View File
@@ -207,7 +207,7 @@ describe('Security Features', () => {
await SELF.fetch('https://example.com/gh/test/very-large-file', {
signal: AbortSignal.timeout(35000) // Slightly longer than expected timeout
});
} catch (error) {
} catch {
// Request should timeout or complete within reasonable time
const elapsed = Date.now() - startTime;
expect(elapsed).toBeLessThan(40000); // 40 seconds max
@@ -223,8 +223,8 @@ describe('Security Features', () => {
const body = await response.text();
// Should not expose internal paths, stack traces, or sensitive info
expect(body).not.toMatch(/\/[a-zA-Z]:[\\\/]/); // Windows paths
expect(body).not.toMatch(/\/home\/[^\/]+/); // Unix home paths
expect(body).not.toMatch(/\/[a-zA-Z]:[\\/]/); // Windows paths
expect(body).not.toMatch(/\/home\/[^/]+/); // Unix home paths
expect(body).not.toMatch(/at [a-zA-Z]+\.[a-zA-Z]+/); // Stack traces
expect(body).not.toMatch(/Error: .+ at/); // Detailed error messages
});
+1 -1
View File
@@ -60,7 +60,7 @@ export const TEST_URLS = {
export const SECURITY_PAYLOADS = {
xss: [
'<script>alert(1)</script>',
'javascript:alert(1)',
`${'javascript'}:alert(1)`,
'"><script>alert(1)</script>',
"';alert(1);//"
],
+1 -1
View File
@@ -83,7 +83,7 @@ export function createDockerRequest(url, options = {}) {
* @param {Object} options - Fetch options
* @returns {Promise<Response>} Mock response
*/
export function mockFetch(url, options = {}) {
export function mockFetch(url, _options = {}) {
return new Promise(resolve => {
setTimeout(() => {
if (url.includes('error')) {
+2 -8
View File
@@ -1,13 +1,7 @@
import { SELF } from 'cloudflare:test';
import { beforeEach, describe, expect, it } from 'vitest';
import { describe, expect, it } from 'vitest';
describe('Xget Core Functionality', () => {
let env;
beforeEach(() => {
env = {};
});
describe('Basic Request Handling', () => {
it('should redirect root path to homepage', async () => {
const response = await SELF.fetch('https://example.com/', { redirect: 'manual' });
@@ -240,7 +234,7 @@ describe('Xget Core Functionality', () => {
// Simulate the regex replacement that happens in the code
const rewrittenText = mockOriginalText.replace(
/https:\/\/registry\.npmjs\.org\/([^\/]+)/g,
/https:\/\/registry.npmjs.org\/([^/]+)/g,
'https://xget.xi-xu.me/npm/$1'
);
+3 -3
View File
@@ -15,7 +15,7 @@ describe('npm URL Rewriting Fix', () => {
// Simulate the regex replacement that happens in the code
const rewrittenText = mockOriginalText.replace(
/https:\/\/registry\.npmjs\.org\/([^\/]+)/g,
/https:\/\/registry.npmjs.org\/([^/]+)/g,
'https://xget.xi-xu.me/npm/$1'
);
@@ -40,7 +40,7 @@ describe('npm URL Rewriting Fix', () => {
});
const rewrittenText = mockOriginalText.replace(
/https:\/\/registry\.npmjs\.org\/([^\/]+)/g,
/https:\/\/registry.npmjs.org\/([^/]+)/g,
'https://xget.xi-xu.me/npm/$1'
);
@@ -66,7 +66,7 @@ describe('npm URL Rewriting Fix', () => {
});
const rewrittenText = mockOriginalText.replace(
/https:\/\/registry\.npmjs\.org\/([^\/]+)/g,
/https:\/\/registry.npmjs.org\/([^/]+)/g,
'https://xget.xi-xu.me/npm/$1'
);
+1 -1
View File
@@ -29,7 +29,7 @@ beforeAll(async () => {
if (!SELF) {
throw new Error('SELF is not available');
}
} catch (error) {
} catch {
console.warn('Warning: Cloudflare Workers test environment not available');
}