How to use HttpClient with a network trigger (XAML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

How to maintain an HttpClient network connection using ControlChannelTrigger when an app is in the background .

What you need to know

Technologies

Prerequisites

  • The following information applies to any connected or network-aware Windows Store app that depends on network connections using HttpClient to always be connected. The HttpClient object provide access to HTTP servers and other Web services. This topic applies to apps written in C++/XAML and apps using the .NET Framework 4.5 in C#, VB.NET, or managed C++ on Windows 8 and Windows Server 2012.

    This topic does not apply to apps written in JavaScript or a foreground app in JavaScript with an in-process C# or C++ binary. Background network connectivity using a network trigger with ControlChannelTrigger is not supported by a JavaScript app. For information on background tasks that apply to JavaScript apps, see Supporting your app with background tasks. For information on background network connectivity supported by a JavaScript app, see Staying connected in the background (HTML).

Instructions

Step 1: Using HttpClient with ControlChannelTrigger

Some special considerations apply when using HttpClient with ControlChannelTrigger. There are some transport-specific usage patterns and best practices that should be followed when using a HttpClient with ControlChannelTrigger. In addition, these considerations affect the way that requests to receive packets on the HttpClient are handled.

Note HttpClient using SSL is not currently supported using the network trigger feature and ControlChannelTrigger.

The following usage patterns and best practices should be followed when using HttpClient with ControlChannelTrigger:

  • The app may need to set various properties and headers on the HttpClient or HttpClientHandler object in the System.Net.Http namespace before sending the request to the specific URI.
  • An app may need to make need to an initial request to test and setup the transport properly before creating the HttpClient transport to be used with ControlChannelTrigger. Once the app determines that the transport can be properly setup, an HttpClient object can be configured as the transport object used with the ControlChannelTrigger object. This process is designed prevent some scenarios from breaking the connection established over the transport. Using SSL with a certificate, an app may require a dialog to be displayed for PIN entry or if there are multiple certificates to choose from. Proxy authentication and server authentication may be required. If the proxy or server authentication expires, the connection may be closed. One way an app can deal with these authentication expiration issues is to set a timer. When an HTTP redirect is required, it is not guaranteed that the second connection can be established reliably. An initial test request will ensure that the app can use the most up-to-date redirected URL before using the HttpClient object as the transport with the ControlChannelTrigger object.

Unlike other network transports, the HttpClient object cannot be directly passed into the UsingTransport method of the ControlChannelTrigger object. Instead, an HttpRequestMessage object must be specially constructed for use with the HttpClient object and the ControlChannelTrigger. The HttpRequestMessage object is created using the RtcRequestFactory.Create method. The HttpRequestMessage object that is created is then passed to UsingTransport method .

The following sample shows how to construct an HttpRequestMessage object for use with the HttpClient object and the ControlChannelTrigger.

using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Windows.Networking.Sockets;

public HttpRequestMessage httpRequest;
public HttpClient httpClient;
public HttpRequestMessage httpRequest;
public ControlChannelTrigger channel;
public Uri serverUri;

private void SetupHttpRequestAndSendToHttpServer()
{
    try
    {
        // For HTTP based transports that use the RTC broker, whenever we send next request, we will abort the earlier 
        // outstanding http request and start new one.
        // For example in case when http server is taking longer to reply, and keep alive trigger is fired in-between 
        // then keep alive task will abort outstanding http request and start a new request which should be finished 
        // before next keep alive task is triggered.
        if (httpRequest != null)
        {
            httpRequest.Dispose();
        }

        httpRequest = RtcRequestFactory.Create(HttpMethod.Get, serverUri);

        SendHttpRequest();
    }
        catch (Exception e)
    {
        Diag.DebugPrint("Connect failed with: " + e.ToString());
        throw;
    }
}

Some special considerations affect the way that requests to send HTTP requests on the HttpClient to initiate receiving a response are handled. In particular, when using a HttpClient with the ControlChannelTrigger, your app must use a Task for handling sends instead of the await model.

Using HttpClient, there is no synchronization with the IBackgroundTask.Run method on the background task for the ControlChannelTrigger with the return of the receive completion callback. For this reason, the app can only use the blocking HttpResponseMessage technique in the Run method and wait until the whole response is received.

Using HttpClient with ControlChannelTrigger is noticeably different from the StreamSocket, MessageWebSocket or StreamWebSocket transports . The HttpClient receive callback is delivered via a Task to the app since the HttpClient code. This means that the ControlChannelTrigger push notification task will fire as soon as the data or error is dispatched to the app. In the sample below, the code stores the responseTask returned by HttpClient.SendAsync method into global storage that the push notify task will pick up and process inline.

The following sample shows how to handle send requests on the HttpClient when used with ControlChannelTrigger.

using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Windows.Networking.Sockets;

private void SendHttpRequest()
{
    if (httpRequest == null)
    {
        throw new Exception("HttpRequest object is null");
    }

    // Tie the transport method to the controlchanneltrigger object to push enable it.
    // Note that if the transport's TCP connection is broken at a later point of time,
    // the controlchanneltrigger object can be reused to plugin a new transport by
    // calling UsingTransport API again.
    channel.UsingTransport(httpRequest);

    // Call the SendAsync function to kick start the TCP connection establishment
    // process for this http request.
    Task<HttpResponseMessage> httpResponseTask = httpClient.SendAsync(httpRequest);

    // Call WaitForPushEnabled API to make sure the TCP connection has been established, 
    // which will mean that the OS will have allocated any hardware slot for this TCP connection.
    ControlChannelTriggerStatus status = channel.WaitForPushEnabled();
    Diag.DebugPrint("WaitForPushEnabled() completed with status: " + status);
    if (status != ControlChannelTriggerStatus.HardwareSlotAllocated
        && status != ControlChannelTriggerStatus.SoftwareSlotAllocated)
    {
        throw new Exception("Hardware/Software slot not allocated");
    }

    // The HttpClient receive callback is delivered via a Task to the app. 
    // The notification task will fire as soon as the data or error is dispatched
    // Enqueue the responseTask returned by httpClient.sendAsync 
    // into a queue that the push notify task will pick up and process inline.
    AppContext.messageQueue.Enqueue(httpResponseTask);
}

The following sample shows how to read responses received on the HttpClient when used with ControlChannelTrigger.

using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public string ReadResponse(Task<HttpResponseMessage> httpResponseTask)
{
    string message = null;
    try
    {
        if (httpResponseTask.IsCanceled || httpResponseTask.IsFaulted)
        {
            Diag.DebugPrint("Task is cancelled or has failed");
            return message;
        }
        // We'll wait until we got the whole response. 
        // This is the only supported scenario for HttpClient for ControlChannelTrigger.
        HttpResponseMessage httpResponse = httpResponseTask.Result;
        if (httpResponse == null || httpResponse.Content == null)
        {
            Diag.DebugPrint("Cannot read from httpresponse, as either httpResponse or its content is null. try to reset connection.");
        }
        else
        {
            // This is likely being processed in the context of a background task and so
            // synchronously read the Content's results inline so that the Toast can be shown.
            // before we exit the Run method.
            message = httpResponse.Content.ReadAsStringAsync().Result;
        }
    }
    catch (Exception exp)
    {
        Diag.DebugPrint("Failed to read from httpresponse with error:  " + exp.ToString());
    }
    return message;
}

For more information on using HttpClient with ControlChannelTrigger, see the ControlChannelTrigger HttpClient sample.

Step 2: Previous steps

For more information on how to create a lock screen app to receive background network notifications that use network triggers, see Quickstart: Create a lock screen app that uses background network triggers.

For more information on how to use network triggers to deliver notifications to a lock screen app, see How to use network triggers to deliver notifications to a lock screen app.

For more information on how to write a background task to receive background network notifications that use network triggers, see How to write a background task for a network trigger.

For more information on how to re-establish a network trigger and a transport connection, see How to re-establish a network trigger and transport connection.

Step 3: Further steps

For more information on guidelines and checklists for using network triggers, see Guidelines and checklist for using network triggers.

Other resources

Adding support for networking

Background Networking

Badge overview

Connecting to a web service

Lock screen overview

Staying connected in the background

Supporting your app with background tasks

Tile and tile notification overview

Toast notification overview

Transferring data in the background

Troubleshooting and debugging network connections

Reference

ControlChannelTrigger

HttpClient

HttpClientHandler

System.Net.Http

Windows.ApplicationModel.Background

Windows.Networking.BackgroundTransfer

Samples

Background task sample

ControlChannelTrigger HttpClient sample

Lock screen apps sample

Push and periodic notifications client-side sample

Raw notifications sample