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.

238 lines (237 loc) 9.19 kB
import { isCrossOrigin, createInterceptorContext } from './utils'; /** * HTTP Request Manager * Handles all HTTP requests with CORS bypass and interceptor support */ export class HttpManager { constructor(proxyServerUrl) { this.proxyServerUrl = proxyServerUrl; } /** * Set custom proxy server URL */ setProxyServer(url) { this.proxyServerUrl = url; console.log(`🔧 Proxy server set to: ${url}`); } /** * Make an HTTP request with CORS bypass and interceptor support */ async request(options, interceptors) { const context = createInterceptorContext(); try { // Execute request interceptors let modifiedOptions = await this.executeRequestInterceptors(options, context, interceptors); const { url, method = 'GET', headers = {}, data, params, timeout = 30000, responseType = 'json', followRedirects = true, } = modifiedOptions; // Build URL with query parameters let requestUrl = url; if (params) { const urlParams = new URLSearchParams(params); requestUrl += (url.includes('?') ? '&' : '?') + urlParams.toString(); } // Use proxy server if available and URL is cross-origin let finalUrl = requestUrl; let fetchOptions = { method, headers, redirect: followRedirects ? 'follow' : 'manual', }; if (this.proxyServerUrl && isCrossOrigin(requestUrl)) { console.log(`🔧 Using proxy server for: ${requestUrl}`); finalUrl = `${this.proxyServerUrl}/proxy/${encodeURIComponent(requestUrl)}`; } // Create AbortController for timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); fetchOptions.signal = controller.signal; try { // Add body for methods that support it if (data && ['POST', 'PUT', 'PATCH'].includes(method)) { if (typeof data === 'string') { fetchOptions.body = data; } else { fetchOptions.body = JSON.stringify(data); if (!headers['Content-Type']) { headers['Content-Type'] = 'application/json'; } } } const response = await fetch(finalUrl, fetchOptions); clearTimeout(timeoutId); // Parse response based on responseType let responseData; switch (responseType) { case 'text': responseData = await response.text(); break; case 'blob': responseData = await response.blob(); break; case 'arraybuffer': responseData = await response.arrayBuffer(); break; case 'json': default: try { responseData = await response.json(); } catch { responseData = await response.text(); } break; } // Convert Headers to plain object const responseHeaders = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); let httpResponse = { status: response.status, statusText: response.statusText, headers: responseHeaders, data: responseData, url: response.url, }; // Execute response interceptors httpResponse = await this.executeResponseInterceptors(httpResponse, context, interceptors); return httpResponse; } catch (error) { clearTimeout(timeoutId); // Create HTTP error const httpError = { message: error instanceof Error ? error.message : 'Unknown error', config: modifiedOptions, originalError: error, }; // Try error interceptors const interceptorResult = await this.executeErrorInterceptors(httpError, context, interceptors); if (interceptorResult) { return interceptorResult; } // If proxy failed and we have a proxy server, try direct request as fallback if (this.proxyServerUrl && finalUrl.includes(this.proxyServerUrl)) { console.warn(`⚠️ Proxy request failed, trying direct request: ${error}`); return this.request({ ...options, url: requestUrl }, interceptors); } throw httpError; } } catch (error) { // Handle errors from interceptors or other sources if (error.config) { // Already an HttpError throw error; } // Create HTTP error const httpError = { message: error instanceof Error ? error.message : 'Unknown error', config: options, originalError: error, }; throw httpError; } } /** * Make a GET request */ async get(options, interceptors) { return this.request({ ...options, method: 'GET' }, interceptors); } /** * Make a POST request */ async post(options, interceptors) { return this.request({ ...options, method: 'POST' }, interceptors); } /** * Make a PUT request */ async put(options, interceptors) { return this.request({ ...options, method: 'PUT' }, interceptors); } /** * Make a PATCH request */ async patch(options, interceptors) { return this.request({ ...options, method: 'PATCH' }, interceptors); } /** * Make a DELETE request */ async delete(options, interceptors) { return this.request({ ...options, method: 'DELETE' }, interceptors); } /** * Execute request interceptors */ async executeRequestInterceptors(config, context, interceptors) { let modifiedConfig = { ...config }; for (const entry of interceptors) { if (!entry.enabled || !entry.interceptor.onRequest) { continue; } // Check scope if defined if (entry.options.scope) { const { urlPattern, methods } = entry.options.scope; if (urlPattern && !new RegExp(urlPattern).test(modifiedConfig.url)) { continue; } if (methods && modifiedConfig.method && !methods.includes(modifiedConfig.method)) { continue; } } try { modifiedConfig = await Promise.resolve(entry.interceptor.onRequest(modifiedConfig)); } catch (error) { console.error(`[Interceptor ${entry.id}] Request interceptor error:`, error); throw error; } } return modifiedConfig; } /** * Execute response interceptors */ async executeResponseInterceptors(response, context, interceptors) { let modifiedResponse = { ...response }; for (const entry of interceptors) { if (!entry.enabled || !entry.interceptor.onResponse) { continue; } try { modifiedResponse = await Promise.resolve(entry.interceptor.onResponse(modifiedResponse)); } catch (error) { console.error(`[Interceptor ${entry.id}] Response interceptor error:`, error); throw error; } } return modifiedResponse; } /** * Execute error interceptors */ async executeErrorInterceptors(error, context, interceptors) { for (const entry of interceptors) { if (!entry.enabled || !entry.interceptor.onError) { continue; } try { const result = await Promise.resolve(entry.interceptor.onError(error)); if (result) { // Interceptor returned a response, use it return result; } } catch (interceptorError) { console.error(`[Interceptor ${entry.id}] Error interceptor error:`, interceptorError); // Continue to next interceptor } } // No interceptor handled the error, return void return; } }