All Products
Search
Document Center

Alibaba Cloud DNS:Best practices for integrating Alibaba Cloud Public DNS SDK in Android WebView

Last Updated:May 24, 2023

This topic describes how to integrate Alibaba Cloud Public DNS SDK in Android WebView.

Overview

WebView is a view provided by Android that allows you to display HTML- and JavaScript-based web pages in your application. Android provides an API to configure domain name resolution and intercept network requests bound for WebView. You can use this API to intercept network requests bound for WebView and obtain the Host header field in the request URL. You can then use Alibaba Cloud Public DNS SDK for Android to resolve the domain name into an IP address and write it into the intercepted request. This topic describes the best practices for integrating Alibaba Cloud Public DNS SDK in Android WebView. You can integrate Alibaba Cloud Public DNS SDK in Android WebView when you want to establish HTTP or HTTPS connections, including HTTPS connections with the Server Name Indication (SNI) specified. The following requirements must be met:

  • The API level of the Android device on which your app is running must be 21 or greater.

  • Redirected HTTP requests cannot contain the Cookie header.

  • The requests must be GET requests.

For the complete code of integrating Alibaba Cloud Public DNS SDK in Android WebView, see the source code for a demo project.

Solution

  • Overview

public void setWebViewClient(WebViewClient client);

WebView provides the setWebViewClient method. This method allows you to set the WebViewClient that will receive notifications and requests. You can intercept network requests by calling the shouldInterceptRequest method.

public class WebViewClient{
       // API < 21
       public WebResourceResponse shouldInterceptRequest(WebView view,String url){
             .....
       } 
       // API >= 21
      public WebResourceResponse shouldInterceptRequest(WebView view,WebResourceRequest request) {
            .....
       }
     ......
 }
Important

The shouldInterceptRequest method varies based on the API level.

  • The following shouldInterceptRequest method is called when the API level is less than 21.

public WebResourceResponse shouldInterceptRequest(WebView view,String url)

Only the request URL is returned. You cannot obtain the request method, headers, and request body. If you call this method to intercept requests, WebView may fail to load all requested resources. Therefore, we recommend that you do not intercept requests on devices whose API level is less than 21.

public WebResourceResponse shouldInterceptRequest(WebView view,String url) {
      return super.shouldInterceptRequest(view, url);
}
  • The following shouldInterceptRequest method is called when the API level is 21 or greater.

public WebResourceResponse shouldInterceptRequest(WebView view, WebReso
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
     String scheme = request.getUrl().getScheme().trim();
     String method = request.getMethod();
     Map<String, String> headerFields = request.getRequestHeaders();
     // Only requests without the request body can be processed properly.
     if ((scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https"))&& method.equalsIgnoreCase("get")) {
            ......
     } else {
           return super.shouldInterceptRequest(view, reqeust);
     }
}
Important

WebResourceRequest does not encompass the method used to obtain the request body. Therefore, the shouldInterceptRequest method can be called to intercept only GET requests, but cannot be used to intercept POST requests.

Implementation

  • Provide the WebResourceResponse callback

WebResourceResponse must be returned when you call the shouldInterceptRequest method.

public WebResourceResponse(String mimeType, String encoding, InputStream data) ;

You must specify the Multipurpose Internet Mail Extensions (MIME) type, character encoding, and input stream of a request to obtain WebResourceResponse.

You can call URLConnection.getInputStream() to obtain the input stream, and call URLConnection.getContentType () to obtain the MIME type and character encoding from the ContentType of a request.

text/html;charset=utf-8
Important

You may fail to obtain the complete ContentType of some requests. In this case, you can determine whether to intercept the requests based on the following strategies:

String contentType = conn.getContentType();
String mime = getMime(contentType);
String charset = getCharset(contentType);

// Requests without the MIME type are not intercepted.
if (TextUtils.isEmpty(mime)) {
        return super.shouldInterceptRequest(view, request);
 } else {
       if (!TextUtils.isEmpty(charset)) {
             // If you can obtain both the MIME type and character encoding of a request, you can intercept this request.
             return new WebResourceResponse(mime, charset, connection.getInputStream());
       } else {
             // If you cannot obtain the character encoding of a request, you can determine whether to intercept this request based on the requested resource.
             // When a binary resource is requested, character encoding is not required. You can intercept this request.
             if (isBinaryRes(mime)) {
                   Log.e(TAG, "binary resource for " + mime);
                   return new WebResourceResponse(mime, charset, connection.getInputStream());
            }else {
                   // When a non-binary resource is requested, character encoding is required. Unencoded requests are not intercepted.
                  Log.e(TAG, "non binary resource for " + mime);
                 return super.shouldInterceptRequest(view, request);
           }
       }
  }

  • Set the Host header

public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
     ...... 
     URL url = new URL(request.getUrl().toString());
     conn = (HttpURLConnection) url.openConnection();
     // Call the API in Alibaba Cloud Public DNS SDK for Android to perform domain name resolution and obtain an IP address.
     String ip = dnsResolver.getIPV4ByHost(url.getHost());
     if (ip != null) {
           //Log.d(TAG, "get IP: " + ip + " for host: " + url.getHost() + "from pdns resolver success!");
          String newUrl = path.replaceFirst(url.getHost(), ip);
          conn = (HttpURLConnection) new URL(newUrl).openConnection(); 
          for (Map.Entry<String, String> field : headers.entrySet()) {
                // Set the Host header for the HTTP request.
               conn.setRequestProperty(field.getKey(), field.getValue( ));
          }
      }
      // conn.setRequestProperty("Host", url.getHost());
    } 
}

  • Scenarios

1. Redirection

When you attempt to intercept a GET request and the server returns an HTTP redirect response, check whether the request contains the Cookie header. If the GET request contains the Cookie header, the cookies stored in the header will change after the request is redirected. In this case, the GET request is not intercepted. If the GET request does not contain the Cookie header, the GET request is redirected and you can intercept the redirected request.

int code = conn.getResponseCode();
if (code >= 300 && code < 400) {
      if (the GET request contains the Cookie header) {
             // This request is not intercepted.
            return super.shouldInterceptRequest(view, request);
      }

      // Note that the Location key is case-sensitive. Different capitalization styles indicate different redirection modes. For example, the key may indicate that all subsequent requests are redirected to the specified IP address, or that only the current request is redirected to the specified IP address. The capitalization styles and corresponding redirection modes are determined by the server.
      String location = conn.getHeaderField("Location");
      if (location == null) {
            location = conn.getHeaderField("location");
      }

      if (!(location.startsWith("http://") || location.startsWith("https://"))) {
             // Complete the new URL.
             URL originalUrl = new URL(path);
             location = originalUrl.getProtocol() + "://"+ originalUrl.getHost() + location;
      }
      Log.e(TAG, "code:" + code + "; location:" + location + ";path" + path);
     Initiate the GET request again.
  } else {
        // redirect finish.
        Log.e(TAG, "redirect finish");
        ......
 }

2. Certificate verification for HTTPS requests

If the intercepted request is an HTTPS request, you must verify the certificate.

if (conn instanceof HttpsURLConnection) {
     final HttpsURLConnection httpsURLConnection = (HttpsURLConnection)conn;
     // Verify the certificate for an HTTPS request.
     httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
         @Override
         public boolean verify(String hostname, SSLSession session) {
             String host = httpsURLConnection.getRequestProperty("Host");
             if (null == host) {
                   host = httpsURLConnection.getURL().getHost();
             }
             return HttpsURLConnection.getDefaultHostnameVerifier().verify(host, session);
       }
   });
}

3. HTTPS+SNI

When the SNI is specified for an HTTPS request, you need to customize SSLSocket. For more information, see Connect an Android app to an IP address over HTTPS.

Important

  1. The best practices provided in this topic are applicable only in WebView on Android.

  2. For more information about how to use Alibaba Cloud Public DNS SDK for Android and the DNS resolution service, see SDK for Android developer guide.

  3. For the complete code of integrating Alibaba Cloud Public DNS SDK in Android WebView, see the source code for a demo project.