Tips and Tricks with Flutter HTTPClient

On a recent project, I had the opportunity to use the HTTPClient to integrate with a medical device. During the integration, I learned a few things that are not immediately apparent from the docs and wanted to share what I learned with you all.

Sometimes you need to use Charles Proxy to debug what is happening. The HTTPClient ignores proxies by default. We can modify the HTTPClient to use the proxy with the following configuration:

Using a Proxy (Charles Proxy/Fiddler)

HttpClient client = HttpClient();
client.findProxy = (uri) {
  return "PROXY 192.168.1.77:3128;";
};

A more advanced configuration that will override a bad certificate check:

  static HttpClient getHttpClient() {
    HttpClient client = new HttpClient()
      ..findProxy = (uri) {
        return "PROXY 192.168.1.199:8888;";
      }
      ..badCertificateCallback =
      ((X509Certificate cert, String host, int port) => true);

    return client;
  }

Configuring Digest Access Authentication

Basic Authentication is fairly straightforward to integrate, but Digest Access Authentication takes a little more work. Below you can see a sample Digest Access Authentication configuration:

static HttpClient getHttpClient(String userName, String password) {
HttpClient client = new HttpClient()
..badCertificateCallback =
((X509Certificate cert, String host, int port) => true)
client.authenticate = (uri, scheme, realm) {
client.addCredentials(
uri, realm, new HttpClientDigestCredentials(userName, password));
return new Future.value(true);
};
return client;
}

The above snippet sets a function on the authenticate property of the client. When a resource the client is connecting to asks for authentication, we will supply HttpClientDigestCredentials to authenticate the request. I found this to work well with GET requests, but would fail with POST requests that included an attachment. This is because the client retries the request once credentials are provided, but it does not include attachments when replaying POST requests

Digest Authentication with POST

To handle the situation with Digest Authentication not working with POST, I created an authentication header that I would send with the initial POST request:

String ha1 = getHA1(userName, "websvc", password);
String ha2 = getHA2("POST", "/1.0/restrictedResource");
String response = getResponse(ha1, ha2, "3a15147c7e97b692f01a78ad53f887cd", "00000001", "SpL0Prz6");

HttpClientRequest httpClientRequest = await client.postUrl(Uri.parse(path));
httpClientRequest.headers.contentLength = csvAttachment.length;
httpClientRequest.headers.contentType = ContentType.text;
httpClientRequest.headers.add("Authorization", 'Digest username="$userName", realm="websvc", nonce="3a15147c7e97b692f01a78ad53f887cd", uri="/1.0/restrictedResource", algorithm="MD5", qop=auth, nc=00000001, cnonce="SpL0Prz6", response="$response"');

httpClientRequest.write(csvAttachment);

return await httpClientRequest.close();

// Helper methods

static String getHA1(String username, String realm, String password) {
List<int> toConvert = [];
toConvert.addAll(utf8.encode(username));
toConvert.add(58);
toConvert.addAll(realm.codeUnits);
toConvert.add(58);
toConvert.addAll(utf8.encode(password));
return md5.convert(toConvert).toString();
}

static String getHA2(String method, String url) {
List<int> toConvert = [];
toConvert.addAll(utf8.encode(method));
toConvert.add(58);
toConvert.addAll(utf8.encode(url));
return md5.convert(toConvert).toString();
}

static String getResponse(String ha1, String ha2, String nonce, String nonceCount, String cnonce) {
List<int> toConvert = [];
toConvert.addAll(utf8.encode(ha1));
toConvert.add(58);
toConvert.addAll(utf8.encode(nonce));
toConvert.add(58);
toConvert.addAll(utf8.encode(nonceCount));
toConvert.add(58);
toConvert.addAll(utf8.encode(cnonce));
toConvert.add(58);
toConvert.addAll(utf8.encode("auth"));
toConvert.add(58);
toConvert.addAll(utf8.encode(ha2));
return md5.convert(toConvert).toString();
}

Those are a few tips/tricks I picked up. Maybe they will help you along your journey. Thank you!

Leave a comment

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload the CAPTCHA.