Skip to content

Instantly share code, notes, and snippets.

@SteveSandersonMS
Created February 20, 2018 10:54
Show Gist options
  • Save SteveSandersonMS/ba34011dd4ee8bb3021c8837ff68ef28 to your computer and use it in GitHub Desktop.
Save SteveSandersonMS/ba34011dd4ee8bb3021c8837ff68ef28 to your computer and use it in GitHub Desktop.
Manually implemented HTTP client
@using StandaloneApp.Util
@(Layout<StandaloneApp.Shared.MainLayout>())
<h1>Hello, world!</h1>
Welcome to your new app.
<button @onclick(DoRequest)>Do request</button>
<div><strong>Response: </strong>@responseText</div>
@functions {
string responseText;
private async void DoRequest()
{
responseText = await new MyHttpClient().GetStringAsync("/index.html");
StateHasChanged();
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Blazor standalone</title>
<link href="/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="/css/site.css" rel="stylesheet" />
</head>
<body>
<app>Loading...</app>
<script src="/css/bootstrap/bootstrap-native.min.js"></script>
<script type="blazor-boot"></script>
<script src="myhttpclient.js"></script>
</body>
</html>
using Microsoft.AspNetCore.Blazor.Browser.Interop;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace StandaloneApp.Util
{
public class MyHttpClient
{
static object _idLock = new object();
static int _nextRequestId = 0;
static IDictionary<int, TaskCompletionSource<HttpResponseMessage>> _pendingRequests
= new Dictionary<int, TaskCompletionSource<HttpResponseMessage>>();
public async Task<string> GetStringAsync(string requestUri)
{
var response = await GetAsync(requestUri);
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"The response status code was {response.StatusCode}");
}
return await response.Content.ReadAsStringAsync();
}
public Task<HttpResponseMessage> GetAsync(string requestUri)
{
var tcs = new TaskCompletionSource<HttpResponseMessage>();
int id;
lock (_idLock)
{
id = _nextRequestId++;
_pendingRequests.Add(id, tcs);
}
RegisteredFunction.Invoke<object>("HttpClientSend", id, requestUri);
return tcs.Task;
}
private static void ReceiveResponse(string id, string statusCode, string responseText, string errorText)
{
TaskCompletionSource<HttpResponseMessage> tcs;
var idVal = int.Parse(id);
lock (_idLock)
{
tcs = _pendingRequests[idVal];
_pendingRequests.Remove(idVal);
}
if (errorText == null)
{
tcs.SetResult(new HttpResponseMessage
{
StatusCode = (HttpStatusCode)int.Parse(statusCode),
Content = new StringContent(responseText)
});
}
else
{
tcs.SetException(new HttpRequestException(errorText));
}
}
}
}
(function () {
function dispatchResponse(id, statusCode, responseText, errorInfo) {
var method = Blazor.platform.findMethod('StandaloneApp', 'StandaloneApp.Util', 'MyHttpClient', 'ReceiveResponse');
Blazor.platform.callMethod(method, null, [
Blazor.platform.toDotNetString(id.toString()),
Blazor.platform.toDotNetString(statusCode.toString()),
responseText === null ? null : Blazor.platform.toDotNetString(responseText),
errorInfo === null ? null : Blazor.platform.toDotNetString(errorInfo.toString())
]);
}
Blazor.registerFunction('HttpClientSend', function (id, requestUri) {
fetch(requestUri).then(function (response) {
return response.text().then(function (responseText) {
dispatchResponse(id, response.status, responseText, null);
});
}).catch(function (errorInfo) { dispatchResponse(id, 0, null, errorInfo); });
});
})();
@TylerBrinkley
Copy link

@mlidbom I agree in this instance the performance of the lock is not going to be significant but in the general case it would probably be good for the runtime to optimize locks away.

@mlidbom
Copy link

mlidbom commented Feb 23, 2018

@TylerBrinkley
Sure. I would guess that such an optimization would be pretty far down the backlog though. We are talking about client code here. I would assume that other aspects needs optimization more. Assembly size is likely to be rather important. JIT support would likely utterly eclipse any locking optimization etc.
And, of course, unless there is no contention for resources between the tasks, I would focus on API surfaces for now. Not performance.

@TylerBrinkley
Copy link

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment