I am having immense difficulty figuring out how to set a request timeout using the Windows.Web.HttpClient made available through WinRT. The application I am working on is a JavaScript-type Universal Windows app. "client-side", the JavaScript has access to use a variety of namespaces that one would normally utilize if programming in C#. Members of these namespaces can be accessed off the window object like so:
const {
HttpRequestMessage,
HttpMethod,
HttpClient,
} = R.pathOr({}, ['Windows', 'Web', 'Http'], window);
The goal of the code I will post below is to do a simple reach-ability check on a URL before the app redirects there, and in case someone wonders why I have not just done so using the more standard means in JavaScript (ex: XMLHttpRequest), it is because CORS sometimes fails those requests. Since I work for a large company, it is neither easy to request the proper access-control-allow-origin headers be added everywhere, and besides that, we have testers that are side-loading onto devices, running proxies, etc...
That said, although I was able to locate documentation with some relevance for the task at hand, the examples are all in C# and I have noticed that not everything on the JavaScript side of things aligns perfectly with this documentation.
A Stack Overflow user asked a very similar question here and the accepted solution was to use CancellationTokenSource. However, so far as I know, the C# solution to this problem does not apply to the JavaScript side of things, but I did come up with a near-solution where I am able to cancel the task using setTimeout. Please note that I have edited out some lines of code here for readability.
const isReachable = async (url) => (
new Promise((resolve) => {
const message = new HttpRequestMessage();
message.method = new HttpMethod('HEAD');
message.requestUri = new Uri(url);
const client = new HttpClient();
const task = client.sendRequestAsync(message);
const timeout = setTimeout(() => task.cancel(), 5000);
task.done(
(result) => { // Success
clearTimeout(timeout);
resolve(true);
},
(result) => { // Failure
clearTimeout(timeout);
resolve(false);
},
);
})
);
The above code seemed to work well on it's own, but then I noticed that the arguments being passed into the callback functions (arg named response) were not what I expected. I expected something at least similar to an XHR response, but what I get instead seems to be either a success or error response for the task. The problem is, I do need to get the status code and failure reason so to log it for our reports (that code is not illustrated).
This was my next (partial) attempt and apparently, if I await the response, then I actually do get an object similar enough to an XHR response to be used for that purpose. However, since I am awaiting the task here, I'm not storing it in a variable so that it can be canceled after my timing out.
const urlIsReachable = async (url = '') => (
new Promise(async (resolve) => {
const request = new HttpRequestMessage();
request.method = new HttpMethod('HEAD');
request.requestUri = new Uri(url);
const client = new HttpClient();
const response = await client.sendRequestAsync(request);
const { statusCode: status } = response;
if (status === 200) {
resolve(true);
return;
}
resolve(false);
})
);
In conclusion, I didn't think something as simple as setting a timeout threshold would be such a pain, but I have not been able to locate a way to do so.
According to Setting timeout values with WinJS.xhr or HttpClient(HTML):
When you use XMLHttpRequest, you can set time-out values directly, but you cannot do this when you use Windows.Web.Http.HttpClient
So it is not possible to set a timeout when you are using Windows.Web.Http.HttpClient.
but then I noticed that the arguments being passed into the callback functions (arg named response) were not what I expected.....The problem is, I do need to get the status code and failure reason so to log it for our reports (that code is not illustrated)
That's because what you are getting is not an xhr response but a WinRT Windows.Web.Http.HttpResponseMessage object you can use result.asyncOpType to see that. If you want to get the error message, simply use result.message in your onError callback:
task.done(
(result) => { // Success
clearTimeout(timeout);
resolve(true);
},
(result) => { // Failure
let msg=result.message;//message is 'Canceled' if timeout callback's called
clearTimeout(timeout);
resolve(false);
},
);