Windows 8 Metro Runtime comes with many innovations to be used by us developers. There are quite a number of new features also regarding the Web and Http namespaces.
Imagine you were dealing with a long lasting http communication scenario. It involves an HttpRequest and a response that takes quite a while to download. In windows runtime spirit, you want to have a download progress (since this falls into the determinate progress category).
If we were not dealing with a custom HttpRequest, the perfect fit for this scenario would actually be the Background Transfer Api. However, you want to have the IAsyncOperationWithProgress using the HttpClient . (i.e. You want to have an awaitable operation that gives you progress updates and that you can easily cancel with a cancellation token.
So we start by defining our function signature:
public static IAsyncOperationWithProgress<string, int> GetStringAsyncWithProgress(this System.Net.Http.HttpClient client, HttpRequestMessage request, CancellationToken cancelToken)
public
static
IAsyncOperationWithProgress<
string
,
int
> GetStringAsyncWithProgress(
this
System.Net.Http.HttpClient client, HttpRequestMessage request, CancellationToken cancelToken)
We define it as an extension method to HttpClient. The function takes in two parameters:
We could have as well declared the progress callback function here as a parameter, but this is not a very common pattern. In our quest, we will be using the AsynInfo.Run<TResult,TProgress> method to create our IAsyncOperationWithProgress. For this scenario, result type would be string (i.e. we are downloading a string) and let’s say the progress type is integer (i.e. the number of bytes downloaded already). This method has a single parameter (so-called task provider) that is the delegate to create and start the task. In our delegate method (from AsyncInfo.Run<TResult, TProgress>) The started task is represented by the Windows Runtime asynchronous action that is returned. The function is passed a cancellation token that the task can monitor to be notified of cancellation requests, and an interface for reporting progress; you can ignore either or both of these arguments if your task does not support progress reporting or cancellation. So if we were to create a stub, it would look something like (Task.Run part is just a place holder):
var operation = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancelToken);
return
AsyncInfo.Run<
>((token, progress) =>
{
if
(cancelToken != CancellationToken.None) token = cancelToken;
// TODO: Do something here and return Task<string>
Task.Run(() =>
.Empty);
});
So we create the send request operation (however, not executing it), and returning our async operation with progress. Crucial point here is that the completion option for the http request is set to ResponseHeadersRead, meaning, the operation should complete as soon as the headers are received from the remote server. We want to handle the body of the response with our task provider. Notice that if the cancellation token that comes as a parameter from our method is not an empty one, we are assigning it to the current synchronization context’s token.
Next step is to actually declare our task provider. Under normal circumstances, the task provider would simply use a cancellation token (i.e. of type CancellationToken) and the progress callback (i.e. of type IProgress<int> in our case). However, we also need the reference to the send request operation.
async Task<
> GetStringTaskProvider(Task<HttpResponseMessage> httpOperation, CancellationToken token, IProgress<
> progressCallback)
var responseBuffer =
new
byte
[500];
// Execute the http request and get the initial response
// NOTE: We might receive a network error here
var httpInitialResponse = await httpOperation;
using
(var responseStream = await httpInitialResponse.Content.ReadAsStreamAsync())
read;
do
read = await responseStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
result += Encoding.UTF8.GetString(responseBuffer, 0, read);
offset += read;
progressCallback.Report(offset);
}
while
(read != 0);
result;
One thing we forgot is the cancellation token. In each read sequence (i.e. in the while loop), we can actually check if there is any signal from the source about the cancellation.
(token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
GetStringTaskProvider(operation, token, progress);
To use the implemented extension method we need first to implement the progress handler function (i.e. Action<int>):
// We declare our progress callback action
Action<
> reportProgress = (progresss) =>
Debug.WriteLine(
"\t{0:o}\tCurrent Progress {1} bytes"
, DateTime.Now, progresss);
};
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
// TODO: Do something on the UI (e.g. update text, progressbar, etc.)
var client =
System.Net.Http.HttpClient();
// We prepare the HttpRequest to be sent
HttpRequestMessage request =
HttpRequestMessage(HttpMethod.Get, resourceAddress);
m_CancellationSource =
CancellationTokenSource();
// Get the token from the cancellation source
var token = m_CancellationSource.Token;
var operationWithProgress = client.GetStringAsyncWithProgress(request, token);
// We assign the progress action we defined
operationWithProgress.Progress = (result, progress) => reportProgress(progress);
// You can as well set a Timeout value
//m_CancellationSource.CancelAfter(2000);
var response = await operationWithProgress;
.Format(
"\t{0:o}\tStarting Write of {1} bytes"
, DateTime.Now, bytes.Length));
(written != bytes.Length)
var bytesToWrite = bytes.Length - written > 1000 ? 1000 : bytes.Length - written;
Response.Buffer =
false
;
Response.BufferOutput =
Response.OutputStream.Write(bytes, written, bytesToWrite);
Response.OutputStream.Flush();
written += bytesToWrite;
"\t{0:o}\tWritten {1} bytes"
, DateTime.Now, written));
Thread.Sleep(50);
"\t{0:o}\tEnd Write of {1} bytes"
Can Bilgin edited Revision 9. Comment: Added the code samples
Can Bilgin edited Revision 6. Comment: Some additional content added, code changes to come
Can Bilgin edited Revision 5. Comment: code block styling and hyperlinks
Can Bilgin edited Revision 4. Comment: Trying to correct the code display
Revision: edited tags