mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-09-01 14:57:14 +08:00
873618e99d
* chore(deps-dev): bump eslint-plugin-unicorn from 65.0.1 to 66.0.0 Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 65.0.1 to 66.0.0. - [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases) - [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v65.0.1...v66.0.0) --- updated-dependencies: - dependency-name: eslint-plugin-unicorn dependency-version: 66.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * style: fix unicorn-js/prefer-url-href * style: oxlint disable * style: fix unicorn-js/no-useless-else * style: no-unsafe-string-replacement * style: fix no-duplicate-loops * style: fix prefer-number-is-safe-integer * style: fix prefer-single-array-predicate * style: fix no-object-methods-with-collections * style: fix no-useless-boolean-cast * style: fix no-useless-template-literals * style: fix no-negated-array-predicate * style: fix prefer-direct-iteration * style: fix consistent-optional-chaining * style: fix prefer-simple-sort-comparator * style: fix prefer-global-number-constants * style: fix prefer-type-literal-last * style: fix require-array-sort-compare. * style: fix prefer-early-return * style: fix prefer-short-arrow-method * style: fix prefer-array-from-map * style: fix prefer-scoped-selector * style: fix no-global-object-property-assignment * style: fix no-unnecessary-global-this * style: fix no-unreadable-object-destructuring * style: fix no-invalid-argument-count * style: fix prefer-smaller-scope * style: fix prefer-iterator-to-array. * style: fix prefer-object-iterable-methods * style: fix typo * style: fix max-nested-calls * style: fix no-incorrect-template-string-interpolation * style: fix no-break-in-nested-loop * style: fix prefer-await * style: fix no-computed-property-existence-check * style: fix prefer-number-coercion * style: fix no-empty-file * style: update eslint unicorn v66 recommended preset * style: add unicorn/prefer-spread to oxlint-disable comments * style: fix no-array-reduce * style: delete github/no-then as it's covered by unicorn/prefer-await --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tony <TonyRL@users.noreply.github.com>
287 lines
9.9 KiB
TypeScript
287 lines
9.9 KiB
TypeScript
// oxlint-disable unicorn-js/no-global-object-property-assignment
|
|
import http from 'node:http';
|
|
import https from 'node:https';
|
|
|
|
import got from 'got';
|
|
import undici from 'undici';
|
|
import { afterAll, afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { PRESETS } from '@/utils/header-generator';
|
|
|
|
const originalGlobals = {
|
|
fetch,
|
|
Headers,
|
|
FormData,
|
|
Request,
|
|
Response,
|
|
};
|
|
const originalHttp = {
|
|
get: http.get,
|
|
request: http.request,
|
|
};
|
|
const originalHttps = {
|
|
get: https.get,
|
|
request: https.request,
|
|
};
|
|
const originalEnv = {
|
|
PROXY_URI: process.env.PROXY_URI,
|
|
PROXY_AUTH: process.env.PROXY_AUTH,
|
|
PROXY_URL_REGEX: process.env.PROXY_URL_REGEX,
|
|
};
|
|
|
|
process.env.PROXY_URI = 'http://rsshub.proxy:2333/';
|
|
process.env.PROXY_AUTH = 'rsshubtest';
|
|
process.env.PROXY_URL_REGEX = 'headers';
|
|
|
|
await import('@/utils/request-rewriter');
|
|
const { config } = await import('@/config');
|
|
const { default: ofetch } = await import('@/utils/ofetch');
|
|
|
|
const createJsonResponse = () =>
|
|
Response.json(
|
|
{ ok: true },
|
|
{
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
}
|
|
);
|
|
|
|
describe('request-rewriter', () => {
|
|
afterAll(() => {
|
|
globalThis.fetch = originalGlobals.fetch;
|
|
globalThis.Headers = originalGlobals.Headers;
|
|
globalThis.FormData = originalGlobals.FormData;
|
|
globalThis.Request = originalGlobals.Request;
|
|
globalThis.Response = originalGlobals.Response;
|
|
|
|
http.get = originalHttp.get;
|
|
http.request = originalHttp.request;
|
|
https.get = originalHttps.get;
|
|
https.request = originalHttps.request;
|
|
|
|
if (originalEnv.PROXY_URI === undefined) {
|
|
delete process.env.PROXY_URI;
|
|
} else {
|
|
process.env.PROXY_URI = originalEnv.PROXY_URI;
|
|
}
|
|
if (originalEnv.PROXY_AUTH === undefined) {
|
|
delete process.env.PROXY_AUTH;
|
|
} else {
|
|
process.env.PROXY_AUTH = originalEnv.PROXY_AUTH;
|
|
}
|
|
if (originalEnv.PROXY_URL_REGEX === undefined) {
|
|
delete process.env.PROXY_URL_REGEX;
|
|
} else {
|
|
process.env.PROXY_URL_REGEX = originalEnv.PROXY_URL_REGEX;
|
|
}
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('fetch', async () => {
|
|
const fetchSpy = vi.spyOn(undici, 'fetch').mockImplementation(() => Promise.resolve(createJsonResponse()));
|
|
|
|
try {
|
|
await (await fetch('http://rsshub.test/headers')).json();
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
// headers
|
|
const headers: Headers = fetchSpy.mock.lastCall?.[0].headers;
|
|
expect(headers.get('user-agent')).toMatch(/Chrome/);
|
|
expect(headers.get('accept')).toBeDefined();
|
|
expect(headers.get('referer')).toBe('http://rsshub.test');
|
|
expect(headers.get('sec-ch-ua')).toBeDefined();
|
|
expect(headers.get('sec-ch-ua-mobile')).toBeDefined();
|
|
expect(headers.get('sec-ch-ua-platform')).toBeDefined();
|
|
expect(headers.get('sec-fetch-site')).toBeDefined();
|
|
expect(headers.get('sec-fetch-mode')).toBeDefined();
|
|
expect(headers.get('sec-fetch-user')).toBeDefined();
|
|
expect(headers.get('sec-fetch-dest')).toBeDefined();
|
|
|
|
// proxy
|
|
const options = fetchSpy.mock.lastCall?.[1];
|
|
const agentKey = Object.getOwnPropertySymbols(options?.dispatcher).find((s) => s.description === 'proxy agent options');
|
|
const agentUri = agentKey ? options?.dispatcher?.[agentKey].uri : null;
|
|
expect(agentUri).toBe(process.env.PROXY_URI);
|
|
|
|
// proxy auth
|
|
const headersKey = Object.getOwnPropertySymbols(options?.dispatcher).find((s) => s.description === 'proxy headers');
|
|
const agentHeaders = headersKey ? options?.dispatcher?.[headersKey] : null;
|
|
expect(agentHeaders['proxy-authorization']).toBe(`Basic ${process.env.PROXY_AUTH}`);
|
|
|
|
// url regex not match
|
|
{
|
|
try {
|
|
await (await fetch('http://rsshub.test/rss')).json();
|
|
} catch {
|
|
// ignore
|
|
}
|
|
const options = fetchSpy.mock.lastCall?.[1];
|
|
expect(options?.dispatcher).toBeUndefined();
|
|
}
|
|
});
|
|
|
|
it('ofetch', async () => {
|
|
const fetchSpy = vi.spyOn(undici, 'fetch').mockImplementation(() => Promise.resolve(createJsonResponse()));
|
|
|
|
try {
|
|
await ofetch('http://rsshub.test/headers', {
|
|
retry: 0,
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
// headers
|
|
const headers: Headers = fetchSpy.mock.lastCall?.[0].headers;
|
|
expect(headers.get('user-agent')).toMatch(/Chrome/);
|
|
expect(headers.get('accept')).toBeDefined();
|
|
expect(headers.get('referer')).toBe('http://rsshub.test');
|
|
expect(headers.get('sec-ch-ua')).toBeDefined();
|
|
expect(headers.get('sec-ch-ua-mobile')).toBeDefined();
|
|
expect(headers.get('sec-ch-ua-platform')).toBeDefined();
|
|
expect(headers.get('sec-fetch-site')).toBeDefined();
|
|
expect(headers.get('sec-fetch-mode')).toBeDefined();
|
|
expect(headers.get('sec-fetch-user')).toBeDefined();
|
|
expect(headers.get('sec-fetch-dest')).toBeDefined();
|
|
|
|
// proxy
|
|
const options = fetchSpy.mock.lastCall?.[1];
|
|
const agentKey = Object.getOwnPropertySymbols(options?.dispatcher).find((s) => s.description === 'proxy agent options');
|
|
const agentUri = agentKey ? options?.dispatcher?.[agentKey].uri : null;
|
|
expect(agentUri).toBe(process.env.PROXY_URI);
|
|
|
|
// proxy auth
|
|
const headersKey = Object.getOwnPropertySymbols(options?.dispatcher).find((s) => s.description === 'proxy headers');
|
|
const agentHeaders = headersKey ? options?.dispatcher?.[headersKey] : null;
|
|
expect(agentHeaders['proxy-authorization']).toBe(`Basic ${process.env.PROXY_AUTH}`);
|
|
|
|
// url regex not match
|
|
{
|
|
try {
|
|
await ofetch('http://rsshub.test/rss', {
|
|
retry: 0,
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
const options = fetchSpy.mock.lastCall?.[1];
|
|
expect(options?.dispatcher).toBeUndefined();
|
|
}
|
|
});
|
|
|
|
it('ofetch custom ua', async () => {
|
|
const fetchSpy = vi.spyOn(undici, 'fetch').mockImplementation(() => Promise.resolve(createJsonResponse()));
|
|
const userAgent = config.trueUA;
|
|
|
|
try {
|
|
await ofetch('http://rsshub.test/headers', {
|
|
retry: 0,
|
|
headers: {
|
|
'user-agent': userAgent,
|
|
},
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
// headers
|
|
const headers: Headers = fetchSpy.mock.lastCall?.[0].headers;
|
|
expect(headers.get('user-agent')).toBe(userAgent);
|
|
});
|
|
|
|
it('ofetch header preset', async () => {
|
|
const fetchSpy = vi.spyOn(undici, 'fetch').mockImplementation(() => Promise.resolve(createJsonResponse()));
|
|
|
|
try {
|
|
await ofetch('http://rsshub.test/headers', {
|
|
retry: 0,
|
|
headerGeneratorOptions: PRESETS.MODERN_WINDOWS_CHROME,
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
// headers
|
|
const headers: Headers = fetchSpy.mock.lastCall?.[0].headers;
|
|
expect(headers.get('user-agent')).toBeDefined();
|
|
expect(headers.get('accept')).toBeDefined();
|
|
expect(headers.get('referer')).toBe('http://rsshub.test');
|
|
expect(headers.get('sec-ch-ua')).toBeDefined();
|
|
expect(headers.get('sec-ch-ua-mobile')).toBe('?0');
|
|
expect(headers.get('sec-ch-ua-platform')).toBe('"Windows"');
|
|
expect(headers.get('sec-fetch-site')).toBeDefined();
|
|
expect(headers.get('sec-fetch-mode')).toBeDefined();
|
|
expect(headers.get('sec-fetch-user')).toBeDefined();
|
|
expect(headers.get('sec-fetch-dest')).toBeDefined();
|
|
});
|
|
|
|
it('http', async () => {
|
|
const httpSpy = vi.spyOn(http, 'request');
|
|
|
|
try {
|
|
await got.get('http://rsshub.test/headers', {
|
|
headers: {
|
|
'user-agent': undefined,
|
|
accept: undefined,
|
|
},
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
// headers
|
|
const options = httpSpy.mock.lastCall?.[1];
|
|
const headers = options?.headers;
|
|
expect(headers?.['user-agent']).toMatch(/Chrome/);
|
|
expect(headers?.accept).toBeDefined();
|
|
expect(headers?.referer).toBe('http://rsshub.test');
|
|
|
|
// proxy
|
|
const agentUri = options?.agent?.proxy?.href;
|
|
expect(agentUri).toBe(process.env.PROXY_URI);
|
|
expect(options?.agent?.proxyHeaders['proxy-authorization']).toBe(`Basic ${process.env.PROXY_AUTH}`);
|
|
|
|
// url regex not match
|
|
{
|
|
try {
|
|
await got.get('http://rsshub.test/rss', {
|
|
headers: {
|
|
'user-agent': undefined,
|
|
accept: undefined,
|
|
},
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
const options = httpSpy.mock.lastCall?.[1];
|
|
expect(options?.agent).toBeUndefined();
|
|
}
|
|
});
|
|
|
|
it('rate limiter', async () => {
|
|
vi.useFakeTimers();
|
|
const fetchSpy = vi.spyOn(undici, 'fetch').mockImplementation(() => Promise.resolve(createJsonResponse()));
|
|
|
|
try {
|
|
const { default: wrappedFetch } = await import('@/utils/request-rewriter/fetch');
|
|
const time = Date.now();
|
|
const tasks = Array.from({ length: 20 }, () => wrappedFetch('http://rsshub.test/headers'));
|
|
|
|
await vi.advanceTimersByTimeAsync(3000);
|
|
await Promise.all(tasks);
|
|
|
|
expect(fetchSpy).toHaveBeenCalledTimes(20);
|
|
expect(Date.now() - time).toBeGreaterThan(1500);
|
|
} finally {
|
|
vi.useRealTimers();
|
|
fetchSpy.mockRestore();
|
|
}
|
|
}, 20000);
|
|
});
|