Created
February 20, 2018 10:54
-
-
Save SteveSandersonMS/ba34011dd4ee8bb3021c8837ff68ef28 to your computer and use it in GitHub Desktop.
Manually implemented HTTP client
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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); }); | |
}); | |
})(); |
@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.
@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.
👍
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I just ran a quick test and my machine takes about 50 million locks a second. In this code we are making network calls through scripting interop. The overhead of the lock is exceedingly unlikely to be even remotely relevant.