react-native-xenon
Version:
A powerful in-app debugging tool for React Native.
129 lines (101 loc) • 3.77 kB
text/typescript
import { NETWORK_REQUEST_HEADER } from '../core/constants';
import { formatRequestMethod, frozen, getHttpInterceptorId, singleton } from '../core/utils';
import { NetworkType } from '../types';
import HttpInterceptor from './HttpInterceptor';
const originalFetch = global.fetch;
export default class FetchInterceptor extends HttpInterceptor {
enableInterception() {
if (this.isInterceptorEnabled) return;
const {
openCallback,
requestHeaderCallback,
sendCallback,
headerReceivedCallback,
responseCallback,
} = this.getCallbacks();
global.fetch = async function (input, init) {
const interceptionId = getHttpInterceptorId();
const requestHeaders = new Headers(init?.headers);
requestHeaders.append(NETWORK_REQUEST_HEADER, NetworkType.Fetch);
const requestInit: RequestInit = { ...init, headers: requestHeaders };
//#region open
const method = formatRequestMethod(init?.method);
let url: string;
switch (true) {
case input instanceof Request:
url = input.url;
break;
case input instanceof URL:
url = input.href;
break;
default:
url = input;
}
openCallback?.(interceptionId, NetworkType.Fetch, method, url);
//#endregion
//#region requestHeader
const headers = requestInit?.headers;
if (headers) {
switch (true) {
case headers instanceof Headers:
for (const [headerKey, headerValue] of headers.entries()) {
requestHeaderCallback?.(interceptionId, headerKey, headerValue);
}
break;
case Array.isArray(headers):
for (const [headerKey, headerValue] of headers) {
if (headerKey && headerValue)
requestHeaderCallback?.(interceptionId, headerKey, headerValue);
}
break;
default:
for (const key in headers) {
if (headers[key]) requestHeaderCallback?.(interceptionId, key, headers[key]);
}
break;
}
}
//#endregion
//#region send
sendCallback?.(interceptionId, Date.now(), init?.body ?? null);
//#endregion
const response = await originalFetch.call(this, input, requestInit);
const clonedResponse = response.clone();
const clonedResponseHeaders = clonedResponse.headers;
//#region headerReceived
const contentTypeString = clonedResponseHeaders.get('Content-Type');
const contentLengthString = clonedResponseHeaders.get('Content-Length');
const responseContentType = contentTypeString ? contentTypeString.split(';')[0] : undefined;
const responseSize = contentLengthString ? parseInt(contentLengthString, 10) : undefined;
const responseHeaders: Map<string, string> = new Map();
clonedResponseHeaders.forEach((headerValue: string, headerKey: string) => {
responseHeaders.set(headerKey, headerValue);
});
headerReceivedCallback?.(interceptionId, responseContentType, responseSize, responseHeaders);
//#endregion
//#region response
const responseBody: string | null = await clonedResponse.text().catch(() => null);
responseCallback?.(
interceptionId,
clonedResponse.status,
0,
Date.now(),
responseBody,
clonedResponse.url,
clonedResponse.type,
);
//#endregion
return response;
};
this.isInterceptorEnabled = true;
}
disableInterception() {
if (!this.isInterceptorEnabled) return;
this.isInterceptorEnabled = false;
global.fetch = originalFetch;
this.clearCallbacks();
}
}