UNPKG

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.

482 lines (415 loc) 12.2 kB
/** * Cache Interceptor * 缓存拦截器实现 */ import type { Interceptor, HttpRequestOptions, HttpResponse, HttpError, } from '../definitions'; import type { CacheOptions, CacheStrategy, CacheEntry, CacheInterceptorOptions, OfflineOptions, } from '../types/cache'; import { createCacheManager, type CacheManager } from './cache'; /** * 缓存拦截器类 */ export class CacheInterceptor implements Interceptor { private cacheManager: CacheManager; private options: CacheInterceptorOptions; private offlineOptions?: OfflineOptions; constructor(options?: CacheInterceptorOptions, offlineOptions?: 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: HttpRequestOptions): Promise<HttpRequestOptions> { // 如果缓存未启用,直接返回 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: HttpResponse): Promise<HttpResponse> { // 如果缓存未启用,直接返回 if (!this.options.enabled) { return response; } // 检查是否应该排除此请求 const config = (response as any).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: CacheEntry = { 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: HttpError): Promise<void | HttpResponse> { // 检查是否是缓存命中的特殊错误 if ((error as any).__cacheHit) { return (error as any).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; } /** * 生成缓存键 */ private generateCacheKey(config: HttpRequestOptions): string { 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}`; } /** * 检查是否应该排除此请求 */ private shouldExclude(config: HttpRequestOptions): boolean { 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; } /** * 将缓存条目转换为响应 */ private entryToResponse(entry: CacheEntry): HttpResponse { 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 || '', }; } /** * 计算数据大小 */ private calculateSize(data: any): number { try { return new Blob([JSON.stringify(data)]).size; } catch { return JSON.stringify(data).length; } } /** * 压缩数据 */ private async compressData(data: any): Promise<any> { // 简单的压缩实现(实际项目中可以使用 pako 等库) try { const jsonString = JSON.stringify(data); // 这里可以集成 CompressionStream API 或其他压缩库 return jsonString; } catch (error) { console.error('[Cache] Compression error:', error); return data; } } /** * 解压数据 */ private decompressData(data: any): any { // 简单的解压实现 try { if (typeof data === 'string') { return JSON.parse(data); } return data; } catch (error) { console.error('[Cache] Decompression error:', error); return data; } } /** * 后台重新验证 */ private async backgroundRevalidate(config: HttpRequestOptions, cacheKey: string): Promise<void> { try { // 这里需要实际的HTTP请求实现 // 在实际使用中,这会调用原始的请求方法 console.log('[Cache] Background revalidation for:', cacheKey); } catch (error) { console.error('[Cache] Background revalidation failed:', error); } } /** * 检查是否离线 */ private async checkOffline(): Promise<boolean> { if (this.offlineOptions?.isOffline) { return this.offlineOptions.isOffline(); } // 默认使用 navigator.onLine return typeof navigator !== 'undefined' ? !navigator.onLine : false; } /** * 处理离线请求 */ private async handleOfflineRequest(config: HttpRequestOptions): Promise<HttpRequestOptions> { 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; } /** * 处理离线错误 */ private async handleOfflineError(error: HttpError): Promise<HttpResponse> { 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(): CacheManager { return this.cacheManager; } /** * 清空缓存 */ async clearCache(): Promise<void> { await this.cacheManager.clear(); } /** * 获取缓存统计 */ async getStats() { return this.cacheManager.getStats(); } /** * 清理过期缓存 */ async cleanup(): Promise<number> { return this.cacheManager.cleanup(); } } /** * 创建缓存拦截器的便捷函数 */ export function createCacheInterceptor( options?: CacheInterceptorOptions, offlineOptions?: OfflineOptions ): CacheInterceptor { return new CacheInterceptor(options, offlineOptions); } /** * 预设的缓存策略 */ export const CacheStrategies = { /** * 网络优先策略 * 优先从网络获取,失败时使用缓存 */ networkFirst: (maxAge: number = 300000): CacheInterceptorOptions => ({ strategy: 'network-first', maxAge, enabled: true, }), /** * 缓存优先策略 * 优先使用缓存,缓存不存在时从网络获取 */ cacheFirst: (maxAge: number = 300000): CacheInterceptorOptions => ({ strategy: 'cache-first', maxAge, enabled: true, }), /** * 仅网络策略 * 总是从网络获取,不使用缓存 */ networkOnly: (): CacheInterceptorOptions => ({ strategy: 'network-only', enabled: false, }), /** * 仅缓存策略 * 只使用缓存,不发起网络请求 */ cacheOnly: (maxAge: number = 300000): CacheInterceptorOptions => ({ strategy: 'cache-only', maxAge, enabled: true, }), /** * 陈旧重新验证策略 * 立即返回缓存,同时在后台更新 */ staleWhileRevalidate: (maxAge: number = 300000): CacheInterceptorOptions => ({ strategy: 'stale-while-revalidate', maxAge, enabled: true, }), };