Repository: expo/expo
File: packages/expo/src/winter/fetch/fetch.ts (TypeScript)

Code before:
  const url = initFromRequest ? input.url : input;
  const body = initFromRequest ? input.body : init?.body || null;
  const signal = initFromRequest ? input.signal : init?.signal || undefined;
  const redirect = initFromRequest ? input.redirect : init?.redirect;
  const method = initFromRequest ? input.method : init?.method;
  const credentials = initFromRequest ? input.credentials : init?.credentials;

  let headers = normalizeHeadersInit(initFromRequest ? input.headers : init?.headers);

  let abortSubscription: AbortSubscriptionCleanupFunction | null = null;

  const response = new FetchResponse(() => {
    abortSubscription?.();
  });

  const request = new ExpoFetchModule.NativeRequest(response) as NativeRequest;

  const { body: requestBody, overriddenHeaders } = await normalizeBodyInitAsync(body);
  if (overriddenHeaders) {
    headers = overrideHeaders(headers, overriddenHeaders);
  }

  const nativeRequestInit: NativeRequestInit = {
    credentials: credentials ?? 'include',
    headers,
    method: method?.toUpperCase() ?? 'GET',
    redirect: redirect ?? 'follow',
  };

  if (signal && signal.aborted) {
    throw new FetchError('The operation was aborted.');
  }
  abortSubscription = addAbortSignalListener(signal, () => {
    request.cancel();
  });
  try {
    await request.start(`${url}`, nativeRequestInit, requestBody);
  } catch (e: unknown) {
    if (e instanceof Error) {
      throw FetchError.createFromError(e);
    } else {
      throw new FetchError(String(e));
    }
  }
  return response;
}

/**
 * A wrapper of `AbortSignal.addEventListener` that returns a cleanup function.
 */
function addAbortSignalListener(

Review comment (security):
[🐧 nit] Strictly speaking, the Fetch spec only normalizes a specific set of methods to uppercase (DELETE, GET, HEAD, OPTIONS, POST, PUT). Custom or non-standard methods should be passed as-is. This is a minor point since most servers are case-insensitive, but worth noting for spec conformance.

See [Fetch spec §5.1 normalize method](https://fetch.spec.whatwg.org/#concept-method-normalize).

Code after:
export async function fetch(
  input: string | URL | FetchRequestLike,
  init?: FetchRequestInit
): Promise<FetchResponse> {
  const initFromRequest = isRequest(input);
  const url = initFromRequest ? input.url : input;
  const body = init?.body ?? (initFromRequest ? input.body : null);
  const signal = init?.signal ?? (initFromRequest ? input.signal : undefined);
  const redirect = init?.redirect ?? (initFromRequest ? input.redirect : undefined);
  const method = init?.method ?? (initFromRequest ? input.method : undefined);
  const credentials = init?.credentials ?? (initFromRequest ? input.credentials : undefined);

  let headers = normalizeHeadersInit(
    init?.headers ?? (initFromRequest ? input.headers : undefined)
  );

  let abortSubscription: AbortSubscriptionCleanupFunction | null = null;

  const response = new FetchResponse(() => {
    abortSubscription?.();
  });

  const request = new ExpoFetchModule.NativeRequest(response) as NativeRequest;

  const { body: requestBody, overriddenHeaders } = await normalizeBodyInitAsync(body);
  if (overriddenHeaders) {
    headers = overrideHeaders(headers, overriddenHeaders);
  }

  const nativeRequestInit: NativeRequestInit = {
    credentials: credentials ?? 'include',
    headers,
    method: method != null ? normalizeMethod(method) : 'GET',
    redirect: redirect ?? 'follow',
  };

  if (signal && signal.aborted) {
    throw new FetchError('The operation was aborted.');
  }
  abortSubscription = addAbortSignalListener(signal, () => {
    request.cancel();
  });
  try {
    await request.start(`${url}`, nativeRequestInit, requestBody);
  } catch (e: unknown) {
    if (e instanceof Error) {
      throw FetchError.createFromError(e);
    } else {
      throw new FetchError(String(e));
    }
  }