diff options
author | Brennan Conroy <brecon@microsoft.com> | 2022-05-04 23:49:36 +0300 |
---|---|---|
committer | Brennan Conroy <brecon@microsoft.com> | 2022-05-04 23:49:36 +0300 |
commit | 4b79dc9e6ef2670d037f8d3e8964413a58961429 (patch) | |
tree | 4616ea2007296095c917bdffae773f332ff7855b | |
parent | bc42cade7ce0b1149ab9bdc30c78094b3007fd67 (diff) |
Set Content-Type more accurately in SignalR TS clientbrecon/contentType
4 files changed, 76 insertions, 29 deletions
diff --git a/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts b/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts index 2b9d6efacb..676aa698d1 100644 --- a/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts +++ b/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts @@ -1019,29 +1019,6 @@ describe("hubConnection", () => { } }); - it("populates the Content-Type header when sending XMLHttpRequest", async () => { - // Skip test on Node as this header isn't set (it was added for React-Native) - if (typeof window === "undefined") { - return; - } - const hubConnection = getConnectionBuilder(HttpTransportType.LongPolling, TESTHUB_NOWEBSOCKETS_ENDPOINT_URL) - .withHubProtocol(new JsonHubProtocol()) - .build(); - - try { - await hubConnection.start(); - - // Check what transport was used by asking the server to tell us. - expect(await hubConnection.invoke("GetActiveTransportName")).toEqual("LongPolling"); - // Check to see that the Content-Type header is set the expected value - expect(await hubConnection.invoke("GetContentTypeHeader")).toEqual("text/plain;charset=UTF-8"); - - await hubConnection.stop(); - } catch (e) { - fail(e); - } - }); - eachTransport((t) => { it("sets the user agent header", async () => { const hubConnection = getConnectionBuilder(t, TESTHUBENDPOINT_URL) diff --git a/src/SignalR/clients/ts/signalr/src/FetchHttpClient.ts b/src/SignalR/clients/ts/signalr/src/FetchHttpClient.ts index b7eef31951..9dd3610aa0 100644 --- a/src/SignalR/clients/ts/signalr/src/FetchHttpClient.ts +++ b/src/SignalR/clients/ts/signalr/src/FetchHttpClient.ts @@ -7,7 +7,7 @@ import { CookieJar } from "@types/tough-cookie"; import { AbortError, HttpError, TimeoutError } from "./Errors"; import { HttpClient, HttpRequest, HttpResponse } from "./HttpClient"; import { ILogger, LogLevel } from "./ILogger"; -import { Platform, getGlobalThis } from "./Utils"; +import { Platform, getGlobalThis, isArrayBuffer } from "./Utils"; export class FetchHttpClient extends HttpClient { private readonly _abortControllerType: { prototype: AbortController, new(): AbortController }; @@ -84,14 +84,26 @@ export class FetchHttpClient extends HttpClient { }, msTimeout); } + if (request.content === "") { + request.content = undefined; + } + if (request.content) { + // Explicitly setting the Content-Type header for React Native on Android platform. + request.headers = request.headers || {}; + if (isArrayBuffer(request.content)) { + request.headers["Content-Type"] = "application/octet-stream"; + } else { + request.headers["Content-Type"] = "text/plain;charset=UTF-8"; + } + } + let response: Response; try { response = await this._fetchType(request.url!, { - body: request.content!, + body: request.content, cache: "no-cache", credentials: request.withCredentials === true ? "include" : "same-origin", headers: { - "Content-Type": "text/plain;charset=UTF-8", "X-Requested-With": "XMLHttpRequest", ...request.headers, }, diff --git a/src/SignalR/clients/ts/signalr/src/XhrHttpClient.ts b/src/SignalR/clients/ts/signalr/src/XhrHttpClient.ts index 8812d1218f..21992813c6 100644 --- a/src/SignalR/clients/ts/signalr/src/XhrHttpClient.ts +++ b/src/SignalR/clients/ts/signalr/src/XhrHttpClient.ts @@ -4,6 +4,7 @@ import { AbortError, HttpError, TimeoutError } from "./Errors"; import { HttpClient, HttpRequest, HttpResponse } from "./HttpClient"; import { ILogger, LogLevel } from "./ILogger"; +import { isArrayBuffer } from "./Utils"; export class XhrHttpClient extends HttpClient { private readonly _logger: ILogger; @@ -33,8 +34,17 @@ export class XhrHttpClient extends HttpClient { xhr.open(request.method!, request.url!, true); xhr.withCredentials = request.withCredentials === undefined ? true : request.withCredentials; xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - // Explicitly setting the Content-Type header for React Native on Android platform. - xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); + if (request.content === "") { + request.content = undefined; + } + if (request.content) { + // Explicitly setting the Content-Type header for React Native on Android platform. + if (isArrayBuffer(request.content)) { + xhr.setRequestHeader("Content-Type", "application/octet-stream"); + } else { + xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); + } + } const headers = request.headers; if (headers) { @@ -81,7 +91,7 @@ export class XhrHttpClient extends HttpClient { reject(new TimeoutError()); }; - xhr.send(request.content || ""); + xhr.send(request.content); }); } } diff --git a/src/SignalR/clients/ts/signalr/tests/FetchHttpClient.test.ts b/src/SignalR/clients/ts/signalr/tests/FetchHttpClient.test.ts index e31c0e1274..5f7432392e 100644 --- a/src/SignalR/clients/ts/signalr/tests/FetchHttpClient.test.ts +++ b/src/SignalR/clients/ts/signalr/tests/FetchHttpClient.test.ts @@ -17,4 +17,52 @@ describe("FetchHttpClient", () => { expect(e).toEqual(new Error("error from test")); } }); + + it("sets Content-type header for plaintext", async () => { + (global.fetch as any) = (_: string, request: RequestInit) => { + expect((request.headers as any)!["Content-Type"]).toEqual("text/plain;charset=UTF-8") + throw new Error("error from test"); + }; + const httpClient = new FetchHttpClient(NullLogger.instance); + + try { + await httpClient.post("/", { content: "content" }); + } catch (e) { + expect(e).toEqual(new Error("error from test")); + } + }); + + it("sets Content-Type header for binary", async () => { + (global.fetch as any) = (_: string, request: RequestInit) => { + expect((request.headers as any)!["Content-Type"]).toEqual("application/octet-stream") + throw new Error("error from test"); + }; + const httpClient = new FetchHttpClient(NullLogger.instance); + + try { + await httpClient.post("/", { content: new ArrayBuffer(1) }); + } catch (e) { + expect(e).toEqual(new Error("error from test")); + } + }); + + it("does not set Content-Type header for empty content", async () => { + (global.fetch as any) = (_: string, request: RequestInit) => { + expect((request.headers as any)!["Content-Type"]).toBeUndefined() + throw new Error("error from test"); + }; + const httpClient = new FetchHttpClient(NullLogger.instance); + + try { + await httpClient.post("/", { content: "" }); + } catch (e) { + expect(e).toEqual(new Error("error from test")); + } + + try { + await httpClient.post("/"); + } catch (e) { + expect(e).toEqual(new Error("error from test")); + } + }); }); |