capacitor-cors-bypass-enhanced
Version:
Enhanced Capacitor plugin for CORS bypass with HTTP/2, HTTP/3, gRPC, GraphQL, file operations, and advanced networking features. Modular TypeScript definitions for better maintainability.
402 lines (401 loc) • 12.2 kB
JavaScript
/**
* Cache Interceptor
* 缓存拦截器实现
*/
import { createCacheManager } from './cache';
/**
* 缓存拦截器类
*/
export class CacheInterceptor {
constructor(options, offlineOptions) {
this.options = {
strategy: 'network-first',
maxAge: 5 * 60 * 1000, // 5分钟
enabled: true,
compress: false,
storage: 'memory',
...options,
};
this.offlineOptions = offlineOptions;
this.cacheManager = createCacheManager(this.options);
}
/**
* 请求拦截器
*/
async onRequest(config) {
// 如果缓存未启用,直接返回
if (!this.options.enabled) {
return config;
}
// 检查是否应该排除此请求
if (this.shouldExclude(config)) {
return config;
}
// 检查是否离线
const isOffline = await this.checkOffline();
if (isOffline && this.offlineOptions?.enabled) {
return this.handleOfflineRequest(config);
}
// 根据策略处理缓存
const strategy = this.options.strategy || 'network-first';
const cacheKey = this.generateCacheKey(config);
switch (strategy) {
case 'cache-first':
case 'cache-only':
const cached = await this.cacheManager.get(cacheKey);
if (cached) {
// 缓存命中,调用钩子
if (this.options.onCacheHit) {
await this.options.onCacheHit(cached);
}
// 返回缓存的响应(通过抛出特殊错误来中断请求)
throw {
__cacheHit: true,
response: this.entryToResponse(cached),
};
}
// cache-only 策略下,如果没有缓存则失败
if (strategy === 'cache-only') {
throw new Error('Cache miss in cache-only mode');
}
break;
case 'stale-while-revalidate':
const staleCache = await this.cacheManager.get(cacheKey);
if (staleCache) {
// 返回缓存,但在后台更新
setTimeout(() => {
this.backgroundRevalidate(config, cacheKey);
}, 0);
throw {
__cacheHit: true,
response: this.entryToResponse(staleCache),
};
}
break;
}
// 缓存未命中,调用钩子
if (this.options.onCacheMiss) {
await this.options.onCacheMiss(cacheKey);
}
return config;
}
/**
* 响应拦截器
*/
async onResponse(response) {
// 如果缓存未启用,直接返回
if (!this.options.enabled) {
return response;
}
// 检查是否应该排除此请求
const config = response.config || {};
if (this.shouldExclude(config)) {
return response;
}
// 只缓存成功的响应
if (response.status >= 200 && response.status < 300) {
const cacheKey = this.generateCacheKey(config);
// 调用缓存前钩子
let dataToCache = response.data;
if (this.options.beforeCache) {
dataToCache = await this.options.beforeCache(response);
}
// 创建缓存条目
const entry = {
key: cacheKey,
data: dataToCache,
headers: response.headers,
status: response.status,
timestamp: Date.now(),
expiresAt: Date.now() + (this.options.maxAge || 300000),
size: this.calculateSize(dataToCache),
compressed: this.options.compress,
metadata: {
url: config.url,
method: config.method || 'GET',
etag: response.headers['etag'],
lastModified: response.headers['last-modified'],
},
};
// 如果启用压缩,压缩数据
if (this.options.compress) {
entry.data = await this.compressData(entry.data);
}
// 保存到缓存
await this.cacheManager.set(cacheKey, entry);
// 调用缓存后钩子
if (this.options.afterCache) {
await this.options.afterCache(entry);
}
}
return response;
}
/**
* 错误拦截器
*/
async onError(error) {
// 检查是否是缓存命中的特殊错误
if (error.__cacheHit) {
return error.response;
}
// 如果缓存未启用,直接抛出错误
if (!this.options.enabled) {
throw error;
}
// 网络错误时,尝试使用缓存
if (!error.status || error.status >= 500) {
const cacheKey = this.generateCacheKey(error.config);
const cached = await this.cacheManager.get(cacheKey);
if (cached) {
console.log('[Cache] Using cached response due to network error');
return this.entryToResponse(cached);
}
}
// 离线模式处理
const isOffline = await this.checkOffline();
if (isOffline && this.offlineOptions?.enabled) {
return this.handleOfflineError(error);
}
throw error;
}
/**
* 生成缓存键
*/
generateCacheKey(config) {
if (this.options.keyGenerator) {
return this.options.keyGenerator(config.url, config.method || 'GET', config.params);
}
const method = config.method || 'GET';
let url = config.url;
// 如果排除查询参数
if (this.options.exclude?.query) {
url = url.split('?')[0];
}
// 包含参数
const params = config.params ? JSON.stringify(config.params) : '';
return `${method}:${url}:${params}`;
}
/**
* 检查是否应该排除此请求
*/
shouldExclude(config) {
const exclude = this.options.exclude;
if (!exclude)
return false;
// 检查HTTP方法
if (exclude.methods && config.method) {
if (exclude.methods.includes(config.method)) {
return true;
}
}
// 检查URL路径
if (exclude.paths) {
for (const pattern of exclude.paths) {
if (new RegExp(pattern).test(config.url)) {
return true;
}
}
}
return false;
}
/**
* 将缓存条目转换为响应
*/
entryToResponse(entry) {
let data = entry.data;
// 如果数据被压缩,解压
if (entry.compressed) {
data = this.decompressData(data);
}
return {
data,
status: entry.status,
statusText: 'OK',
headers: entry.headers,
url: entry.metadata?.url || '',
};
}
/**
* 计算数据大小
*/
calculateSize(data) {
try {
return new Blob([JSON.stringify(data)]).size;
}
catch {
return JSON.stringify(data).length;
}
}
/**
* 压缩数据
*/
async compressData(data) {
// 简单的压缩实现(实际项目中可以使用 pako 等库)
try {
const jsonString = JSON.stringify(data);
// 这里可以集成 CompressionStream API 或其他压缩库
return jsonString;
}
catch (error) {
console.error('[Cache] Compression error:', error);
return data;
}
}
/**
* 解压数据
*/
decompressData(data) {
// 简单的解压实现
try {
if (typeof data === 'string') {
return JSON.parse(data);
}
return data;
}
catch (error) {
console.error('[Cache] Decompression error:', error);
return data;
}
}
/**
* 后台重新验证
*/
async backgroundRevalidate(config, cacheKey) {
try {
// 这里需要实际的HTTP请求实现
// 在实际使用中,这会调用原始的请求方法
console.log('[Cache] Background revalidation for:', cacheKey);
}
catch (error) {
console.error('[Cache] Background revalidation failed:', error);
}
}
/**
* 检查是否离线
*/
async checkOffline() {
if (this.offlineOptions?.isOffline) {
return this.offlineOptions.isOffline();
}
// 默认使用 navigator.onLine
return typeof navigator !== 'undefined' ? !navigator.onLine : false;
}
/**
* 处理离线请求
*/
async handleOfflineRequest(config) {
const strategy = this.offlineOptions?.strategy || 'cache-only';
const cacheKey = this.generateCacheKey(config);
if (strategy === 'cache-only') {
const cached = await this.cacheManager.get(cacheKey);
if (cached) {
throw {
__cacheHit: true,
response: this.entryToResponse(cached),
};
}
// 如果有回退响应
if (this.offlineOptions?.fallbackResponse) {
throw {
__cacheHit: true,
response: this.offlineOptions.fallbackResponse,
};
}
throw new Error('Offline and no cache available');
}
return config;
}
/**
* 处理离线错误
*/
async handleOfflineError(error) {
const cacheKey = this.generateCacheKey(error.config);
const cached = await this.cacheManager.get(cacheKey);
if (cached) {
return this.entryToResponse(cached);
}
if (this.offlineOptions?.fallbackResponse) {
return this.offlineOptions.fallbackResponse;
}
throw error;
}
/**
* 获取缓存管理器
*/
getCacheManager() {
return this.cacheManager;
}
/**
* 清空缓存
*/
async clearCache() {
await this.cacheManager.clear();
}
/**
* 获取缓存统计
*/
async getStats() {
return this.cacheManager.getStats();
}
/**
* 清理过期缓存
*/
async cleanup() {
return this.cacheManager.cleanup();
}
}
/**
* 创建缓存拦截器的便捷函数
*/
export function createCacheInterceptor(options, offlineOptions) {
return new CacheInterceptor(options, offlineOptions);
}
/**
* 预设的缓存策略
*/
export const CacheStrategies = {
/**
* 网络优先策略
* 优先从网络获取,失败时使用缓存
*/
networkFirst: (maxAge = 300000) => ({
strategy: 'network-first',
maxAge,
enabled: true,
}),
/**
* 缓存优先策略
* 优先使用缓存,缓存不存在时从网络获取
*/
cacheFirst: (maxAge = 300000) => ({
strategy: 'cache-first',
maxAge,
enabled: true,
}),
/**
* 仅网络策略
* 总是从网络获取,不使用缓存
*/
networkOnly: () => ({
strategy: 'network-only',
enabled: false,
}),
/**
* 仅缓存策略
* 只使用缓存,不发起网络请求
*/
cacheOnly: (maxAge = 300000) => ({
strategy: 'cache-only',
maxAge,
enabled: true,
}),
/**
* 陈旧重新验证策略
* 立即返回缓存,同时在后台更新
*/
staleWhileRevalidate: (maxAge = 300000) => ({
strategy: 'stale-while-revalidate',
maxAge,
enabled: true,
}),
};