fix content type error on android
This commit is contained in:
@@ -1,8 +1,97 @@
|
||||
diff --git a/build/winter/runtime.native.d.ts b/build/winter/runtime.native.d.ts
|
||||
index 85c4227e4e37003dd3769a590e73a69874de5fac..27bd245675d09b906c2474d4da9efd073dd4f72c 100644
|
||||
index 85c4227e4e37003dd3769a590e73a69874de5fac..e389ca8edd2310df9ce29684991465c275e804c5 100644
|
||||
--- a/build/winter/runtime.native.d.ts
|
||||
+++ b/build/winter/runtime.native.d.ts
|
||||
@@ -1,3 +1,2 @@
|
||||
import 'react-native/Libraries/Core/InitializeCore';
|
||||
-import '../../types';
|
||||
//# sourceMappingURL=runtime.native.d.ts.map
|
||||
\ No newline at end of file
|
||||
diff --git a/src/winter/fetch/RequestUtils.ts b/src/winter/fetch/RequestUtils.ts
|
||||
index a93473fd2beaf2ab0f3880951d292e58e7288d10..c27911bd5cb56ba6051fb8ebf4194cced43bc0fd 100644
|
||||
--- a/src/winter/fetch/RequestUtils.ts
|
||||
+++ b/src/winter/fetch/RequestUtils.ts
|
||||
@@ -50,7 +50,11 @@ function isBlob(obj: any): obj is Blob {
|
||||
*/
|
||||
export async function normalizeBodyInitAsync(
|
||||
body: BodyInit | null | undefined
|
||||
-): Promise<{ body: Uint8Array | null; overriddenHeaders?: NativeHeadersType }> {
|
||||
+): Promise<{
|
||||
+ body: Uint8Array | null;
|
||||
+ overriddenHeaders?: NativeHeadersType;
|
||||
+ fallbackHeaders?: NativeHeadersType;
|
||||
+}> {
|
||||
if (body == null) {
|
||||
return { body: null };
|
||||
}
|
||||
@@ -71,7 +75,12 @@ export async function normalizeBodyInitAsync(
|
||||
if (body instanceof Blob || isBlob(body)) {
|
||||
return {
|
||||
body: new Uint8Array(await blobToArrayBufferAsync(body)),
|
||||
- overriddenHeaders: [['Content-Type', body.type]],
|
||||
+ /*
|
||||
+ * Per the fetch spec, a blob's type is only a default for Content-Type:
|
||||
+ * it must not replace a header the caller set explicitly, and an empty
|
||||
+ * type contributes no header at all.
|
||||
+ */
|
||||
+ fallbackHeaders: body.type ? [['Content-Type', body.type]] : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -137,6 +146,25 @@ export function overrideHeaders(
|
||||
return result;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Create a new header array by adding new headers only for keys not already
|
||||
+ * present (by case-insensitive header key). Used for body-derived defaults
|
||||
+ * that must not replace caller-provided headers.
|
||||
+ */
|
||||
+export function fillMissingHeaders(
|
||||
+ headers: NativeHeadersType,
|
||||
+ newHeaders: NativeHeadersType
|
||||
+): NativeHeadersType {
|
||||
+ const existingKeySet = new Set(headers.map(([key]) => key.toLocaleLowerCase()));
|
||||
+ const result: NativeHeadersType = [...headers];
|
||||
+ for (const [key, value] of newHeaders) {
|
||||
+ if (!existingKeySet.has(key.toLocaleLowerCase())) {
|
||||
+ result.push([key, value]);
|
||||
+ }
|
||||
+ }
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
/** Normalizes known HTTP methods to uppercase */
|
||||
export function normalizeMethod(method: string): string {
|
||||
const normalized = method.toUpperCase();
|
||||
diff --git a/src/winter/fetch/fetch.ts b/src/winter/fetch/fetch.ts
|
||||
index ac789c024c96379e239bf44903c47e6a8e540d63..4f953b845c145de02976e8162ba2f63f8bd4dfd5 100644
|
||||
--- a/src/winter/fetch/fetch.ts
|
||||
+++ b/src/winter/fetch/fetch.ts
|
||||
@@ -3,6 +3,7 @@ import { FetchError } from './FetchErrors';
|
||||
import { FetchResponse, type AbortSubscriptionCleanupFunction } from './FetchResponse';
|
||||
import type { NativeRequest, NativeRequestInit } from './NativeRequest';
|
||||
import {
|
||||
+ fillMissingHeaders,
|
||||
normalizeBodyInitAsync,
|
||||
normalizeHeadersInit,
|
||||
overrideHeaders,
|
||||
@@ -62,10 +63,17 @@ export async function fetch(
|
||||
|
||||
const request = new ExpoFetchModule.NativeRequest(response) as NativeRequest;
|
||||
|
||||
- const { body: requestBody, overriddenHeaders } = await normalizeBodyInitAsync(body);
|
||||
+ const {
|
||||
+ body: requestBody,
|
||||
+ overriddenHeaders,
|
||||
+ fallbackHeaders,
|
||||
+ } = await normalizeBodyInitAsync(body);
|
||||
if (overriddenHeaders) {
|
||||
headers = overrideHeaders(headers, overriddenHeaders);
|
||||
}
|
||||
+ if (fallbackHeaders) {
|
||||
+ headers = fillMissingHeaders(headers, fallbackHeaders);
|
||||
+ }
|
||||
|
||||
const nativeRequestInit: NativeRequestInit = {
|
||||
credentials: credentials ?? 'include',
|
||||
|
||||
@@ -24,3 +24,33 @@ import.
|
||||
|
||||
Can be removed if Expo stops referencing `./react-native-web` from the types
|
||||
loaded by the native winter runtime, or guards the augmentation to web.
|
||||
|
||||
## src/winter/fetch/RequestUtils.ts + fetch.ts - Blob body must not clobber an explicit Content-Type
|
||||
|
||||
Expo 57 installs `expo/fetch` as the global `fetch` on native
|
||||
(`src/winter/runtime.native.ts`), replacing React Native's whatwg-fetch. When
|
||||
the request body is a Blob, `normalizeBodyInitAsync` returned
|
||||
`overriddenHeaders: [['Content-Type', blob.type]]`, which `fetch.ts` applied
|
||||
*over* the caller's headers (introduced in expo/expo#33405). This is backwards
|
||||
per the fetch spec: a blob's type is only a default, used when no Content-Type
|
||||
was provided, and an empty type must contribute no header at all.
|
||||
|
||||
In this app it broke publishing posts with any image blob on Android. The
|
||||
composer uploads via `agent.uploadBlob(blob, {encoding})`; `@atproto/xrpc`
|
||||
sets `content-type: <mime>` explicitly, but the blob comes from an XHR
|
||||
`file://` read of a `.bin`-renamed jpeg (the RN#27099 workaround in
|
||||
`src/lib/api/upload-blob.ts`), for which Android's BlobModule returns an empty
|
||||
mime. expo/fetch replaced the good header with the empty blob type and the PDS
|
||||
rejected the upload with "Request encoding (Content-Type) required but not
|
||||
provided". Also silently rewrote the intended mime on every other blob upload
|
||||
(avatars, banners, caption files) even when the blob type was non-empty.
|
||||
|
||||
The patch splits body-derived headers into two channels: FormData keeps
|
||||
`overriddenHeaders` (its boundary header must win), while the Blob branch
|
||||
returns new `fallbackHeaders` (skipped entirely when `blob.type` is empty)
|
||||
that `fetch.ts` applies via `fillMissingHeaders` only for header keys the
|
||||
caller did not set. This matches browser behavior.
|
||||
|
||||
Upstream: expo/expo#33405 introduced the override; the SDK 58 Request rewrite
|
||||
(expo/expo#46630) is expected to make this spec-compliant, so re-evaluate on
|
||||
the next SDK bump. Worth filing an issue against expo/expo referencing this.
|
||||
|
||||
Reference in New Issue
Block a user