Skip to content

Commit

Permalink
[browser][HTTP] remove special StringContent handling (#91463)
Browse files Browse the repository at this point in the history
  • Loading branch information
campersau authored Sep 4, 2023
1 parent 13a225f commit 7704164
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 61 deletions.
48 changes: 16 additions & 32 deletions src/libraries/Common/tests/System/Net/Http/ResponseStreamTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,41 +285,29 @@ public async Task BrowserHttpHandler_Streaming()
}

[OuterLoop]
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser))]
[InlineData(true)]
[InlineData(false)]
public async Task BrowserHttpHandler_StreamingRequest(bool useStringContent)
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser))]
public async Task BrowserHttpHandler_StreamingRequest()
{
var WebAssemblyEnableStreamingRequestKey = new HttpRequestOptionsKey<bool>("WebAssemblyEnableStreamingRequest");

var req = new HttpRequestMessage(HttpMethod.Post, Configuration.Http.Http2RemoteVerifyUploadServer);

req.Options.Set(WebAssemblyEnableStreamingRequestKey, true);

int size;
if (useStringContent)
{
string bodyContent = "Hello World";
size = bodyContent.Length;
req.Content = new StringContent(bodyContent);
}
else
{
size = 1500 * 1024 * 1024;
int remaining = size;
req.Content = new StreamContent(new DelegateStream(
readAsyncFunc: (buffer, offset, count, cancellationToken) =>
int size = 1500 * 1024 * 1024;
int remaining = size;
req.Content = new StreamContent(new DelegateStream(
readAsyncFunc: (buffer, offset, count, cancellationToken) =>
{
if (remaining > 0)
{
if (remaining > 0)
{
int send = Math.Min(remaining, count);
buffer.AsSpan(offset, send).Fill(65);
remaining -= send;
return Task.FromResult(send);
}
return Task.FromResult(0);
}));
}
int send = Math.Min(remaining, count);
buffer.AsSpan(offset, send).Fill(65);
remaining -= send;
return Task.FromResult(send);
}
return Task.FromResult(0);
}));

req.Content.Headers.Add("Content-MD5-Skip", "browser");

Expand All @@ -329,11 +317,7 @@ public async Task BrowserHttpHandler_StreamingRequest(bool useStringContent)
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(size.ToString(), Assert.Single(response.Headers.GetValues("X-HttpRequest-Body-Length")));
// Streaming requests can't set Content-Length
Assert.Equal(useStringContent, response.Headers.Contains("X-HttpRequest-Headers-ContentLength"));
if (useStringContent)
{
Assert.Equal(size.ToString(), Assert.Single(response.Headers.GetValues("X-HttpRequest-Headers-ContentLength")));
}
Assert.False(response.Headers.Contains("X-HttpRequest-Headers-ContentLength"));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,37 +212,27 @@ private static async Task<WasmFetchResponse> CallFetch(HttpRequestMessage reques
cancellationToken.ThrowIfCancellationRequested();
if (request.Content != null)
{
if (request.Content is StringContent)
bool streamingEnabled = false;
if (BrowserHttpInterop.SupportsStreamingRequest())
{
string body = await request.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(true);
request.Options.TryGetValue(EnableStreamingRequest, out streamingEnabled);
}

if (streamingEnabled)
{
Stream stream = await request.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(true);
cancellationToken.ThrowIfCancellationRequested();

promise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames, optionValues, abortController, body);
ReadableStreamPullState pullState = new ReadableStreamPullState(stream, cancellationToken);

promise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames, optionValues, abortController, ReadableStreamPull, pullState);
}
else
{
bool streamingEnabled = false;
if (BrowserHttpInterop.SupportsStreamingRequest())
{
request.Options.TryGetValue(EnableStreamingRequest, out streamingEnabled);
}

if (streamingEnabled)
{
Stream stream = await request.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(true);
cancellationToken.ThrowIfCancellationRequested();

ReadableStreamPullState pullState = new ReadableStreamPullState(stream, cancellationToken);

promise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames, optionValues, abortController, ReadableStreamPull, pullState);
}
else
{
byte[] buffer = await request.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(true);
cancellationToken.ThrowIfCancellationRequested();
byte[] buffer = await request.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(true);
cancellationToken.ThrowIfCancellationRequested();

promise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames, optionValues, abortController, buffer);
}
promise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames, optionValues, abortController, buffer);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ public static partial Task<JSObject> Fetch(
string[] headerValues,
string[] optionNames,
[JSMarshalAs<JSType.Array<JSType.Any>>] object?[] optionValues,
JSObject abortControler,
string? body = null);
JSObject abortControler);

[JSImport("INTERNAL.http_wasm_fetch_stream")]
public static partial Task<JSObject> Fetch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,33 @@ await server.AcceptConnectionAsync(async connection =>
}
}

[Fact]
public async Task HttpRequest_StringContent_WithoutMediaType()
{
using (HttpClient client = CreateHttpClient())
{
await LoopbackServer.CreateServerAsync(async (server, uri) =>
{
var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = new StringContent("Hello World", null, ((MediaTypeHeaderValue)null)!);

Task<HttpResponseMessage> requestTask = client.SendAsync(request);
await server.AcceptConnectionAsync(async connection =>
{
var requestData = await connection.ReadRequestDataAsync().ConfigureAwait(false);
#if TARGET_BROWSER
requestData = await connection.HandleCORSPreFlight(requestData);
#endif

Assert.DoesNotContain(requestData.Headers, line => line.Name.StartsWith("Content-Type"));

await connection.SendResponseAsync();
await requestTask;
});
});
}
}

[Fact]
public async Task HttpRequest_BodylessMethod_LargeContentLength()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public async Task Ctor_DefineNoEncoding_DefaultEncodingUsed()
}

[Fact]
public void Ctor_PassNullForMediaType_DefaultMediaTypeUsed()
public void Ctor_PassNullStringForMediaType_DefaultMediaTypeUsed()
{
string sourceString = "\u00C4\u00E4\u00FC\u00DC";
Encoding defaultStringEncoding = Encoding.GetEncoding("utf-8");
Expand All @@ -81,7 +81,18 @@ public void Ctor_PassNullForMediaType_DefaultMediaTypeUsed()
// If no media is passed-in, the default is used
Assert.Equal("text/plain", content.Headers.ContentType.MediaType);
}


[Fact]
public void Ctor_PassNullHeaderValueForMediaType_NoMediaTypeUsed()
{
string sourceString = "\u00C4\u00E4\u00FC\u00DC";
Encoding defaultStringEncoding = Encoding.GetEncoding("utf-8");
var content = new StringContent(sourceString, defaultStringEncoding, ((Headers.MediaTypeHeaderValue)null)!);

// If no media header value is passed-in, there is none
Assert.Null(content.Headers.ContentType);
}

[Fact]
public async Task Ctor_UseCustomMediaTypeHeaderValue_SpecificEncoding()
{
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/runtime/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export function http_wasm_fetch_bytes(url: string, header_names: string[], heade
return http_wasm_fetch(url, header_names, header_values, option_names, option_values, abort_controller, copy);
}

export function http_wasm_fetch(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, body: string | Uint8Array | ReadableStream | null): ControllablePromise<ResponseExtension> {
export function http_wasm_fetch(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, body: Uint8Array | ReadableStream | null): ControllablePromise<ResponseExtension> {
verifyEnvironment();
mono_assert(url && typeof url === "string", "expected url string");
mono_assert(header_names && header_values && Array.isArray(header_names) && Array.isArray(header_values) && header_names.length === header_values.length, "expected headerNames and headerValues arrays");
Expand Down

0 comments on commit 7704164

Please sign in to comment.