Network tracking and interception

📘

Network tracking and interception SDK support

Network tracking and interception is supported in Android SDK version 2.2.2 and up. For information on updating your SDK, see Android integration.

Tracking network requests

To manually track network requests:

val networkRequest = SmartlookNetworkRequest(
  duration = 1000,
  url = URL("https://www.example.com"),
  method = "POST",
  protocol = "http/1.1",
  initiator = "OkHttp",
  status = SmartlookNetworkRequest.Status.OK,
  statusCode = 200,
  cached = false,
  requestBody = "{\"example\":\"body\"}",
  responseBody = "{\"example\":\"body\"}",
  requestHeaders = mutableMapOf("sample" to mutableListOf("header")),
  responseHeaders = mutableMapOf()
)

Smartlook.instance.trackNetworkRequest(networkRequest)
SmartlookNetworkRequest networkRequest = new SmartlookNetworkRequest(
    1000,
    new URL("https://www.example.com"),
    "POST",
    "http/1.1",
    "OkHttp",
    SmartlookNetworkRequest.Status.OK,
    200,
    false,
    "{\"example\":\"body\"}",
    "{\"example\":\"body\"}",
    new HashMap<String, List<String>>() {{
        put("sample", new ArrayList<String>() {{
            add("header");
        }});
    }},
    new HashMap<>()
);

Smartlook.getInstance().trackNetworkRequest(networkRequest);

Network request parameters

Network request parametersDescription
startUnix timestamp marking the start of request
durationDuration of the request
urlRequest url
methodHTTP method
protocolProtocol used to execute the request (e.g. "http/1.1")
initiatorThe technology/framework used to execute the request (e.g. "OkHttp")
statusRequest was successful or failed
statusCodeHTTP status code
cached = TRUEShown if the response body was obtained from cache
requestBodyString representation of the request body. Maximum allowed number of characters is 10000 (every character beyond this limit is dropped)
responseBodyString representation of response body. Maximum allowed number of characters is 10000 (every character beyond this limit is dropped)
requestHeadersMap of request headers
responseHeadersMap of response headers

Network interceptor

A network interceptor lets you monitor and track REST requests as they flow between a client application and a server.

The Smartlook SDK for Android currently provides a network interceptor for OkHttp.

OkHttp network interceptor

Installation

SmartlookOkHttpInterceptor is distributed as a separate package and needs to be included alongside smartlook-analytics.

Your module Gradle file:

// smartlook-analytics dependency
implementation 'com.smartlook.android:smartlook-analytics:<<current-android-2-0-sdk-version>>'

//smartlook-analytics-okhttp dependency
implementation 'com.smartlook.android:smartlook-analytics-okhttp:<<current-android-2-0-sdk-version>>'

📘

Android SDK integration

If you don't have the smartlook-analyticsdependency included in your module Gradle file, see Android integration.

Default network interception

Interceptors can be used without any configuration using SmartlookOkHttpInterceptor:

val client = OkHttpClient.Builder()
  .addSmartlookInterceptor(SmartlookOkHttpInterceptor())
  .build()
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpExtKt.addSmartlookInterceptor(builder, new SmartlookOkHttpInterceptor());
OkHttpClient client = builder.build();

What data is captured

Due to Smartlook Privacy by design, not all data is captured. The SmartlookOkHttpInterceptor captures data that is intercepted by default.

The following data is captured by default:

  • All basic data: start, duration, url, method, protocol, initiator, status, statusCode, cached

The following headers are considered safe:

  • A-IM, Accept, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Language, Access-Control-Request-Method, Access-Control-Request-Headers, Cache-Control, Connection, Content-Encoding, Content-Length, Content-MD5, Content-Type, Cookie, Date, Expect, Forwarded, From, Host, HTTP2-Settings, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Max-Forwards, Origin, Pragma, Prefer, Range, Referrer, TE, Trailer, Transfer-Encoding, User-Agent, Upgrade, Via, Warning

The following data isn't captured:

  • requestBody and responseBody because they can contain sensitive data

Non-binary body interceptor

SmartlookNonBinaryBodyInterceptor replaces requestBody and responseBody with String representations of the original bodies if the content-type is one of the following:

  • text/plain
  • text/csv
  • application/xml,
  • text/xml,
  • application/json,
  • application/ld+json,
  • text/x-markdown
val client = OkHttpClient.Builder()
  .addSmartlookInterceptor(SmartlookNonBinaryBodyInterceptor())
  .build()
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpExtKt.addSmartlookInterceptor(builder, new SmartlookNonBinaryBodyInterceptor());
OkHttpClient client = builder.build();

Headers interceptor

You can decide which headers to capture with SmartlookHeadersInterceptor:

val masks = setOf("Example-Header", "Accept.*")

val client = OkHttpClient.Builder()
  .addSmartlookInterceptor(SmartlookHeadersInterceptor(masks))
  .build()
Set<String> masks = Set.of("Example-Header", "Accept.*");

OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpExtKt.addSmartlookInterceptor(builder, new SmartlookHeadersInterceptor(masks));
OkHttpClient client = builder.build();

As you can see in the example, SmartlookHeadersInterceptor lets you define set of allowed header names. Regular expressions can be used too, meaning that headers like Accept-Charset, Accept-Encoding, and Accept-Language can be captured.

Mask URL interceptor

You can filter sensitive parts of URLs with SmartlookMaskUrlInterceptor:

val masks = setOf(
  SmartlookMaskUrlInterceptor.Mask(
    regexPattern = "(secret=)[^&]+(&*)",
    replaceWith = "$1<hidden>$2"
  )
)

val client = OkHttpClient.Builder()
  .addSmartlookInterceptor(SmartlookMaskUrlInterceptor(masks))
  .build()
Set<SmartlookMaskUrlInterceptor.Mask> masks = Set.of(
  new SmartlookMaskUrlInterceptor.Mask(
    "(secret=)[^&]+(&*)",
    "$1<hidden>$2"
  )
);

OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpExtKt.addSmartlookInterceptor(builder, new SmartlookMaskUrlInterceptor(masks));
OkHttpClient client = builder.build();

SmartlookMaskUrlInterceptor.Mask has two parameters:

  • regexPattern/ regex—Regular expression used to define parts of URL that should be masked. It can be defined as a String pattern or Regex.
  • replaceWith—A String used to mask/replace matched URL part.

An example URL with masking using the code example:

// URL before masking
https://www.example.com/sample?secret=token&test=param

// URL after masking
https://www.example.com/sample?secret=<hidden>&test=param

Mask body interceptor

You can mask sensitive parts of the request and response bodies using SmartlookMaskBodyInterceptor:

val masks = setOf(
  SmartlookMaskBodyInterceptor.Mask(
    "\"sensitive\"[:]\\s(\".*\")",
    "sensitive: <hidden>"
  )
)

val client = OkHttpClient.Builder()
  .addSmartlookInterceptor(SmartlookMaskBodyInterceptor(masks))
  .build()
Set<SmartlookMaskBodyInterceptor.Mask> masks = Set.of(
  new SmartlookMaskBodyInterceptor.Mask(
    "\"sensitive\"[:]\\s(\".*\")",
    "sensitive: <hidden>"
  )
);

OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpExtKt.addSmartlookInterceptor(builder, new SmartlookMaskBodyInterceptor(masks));
OkHttpClient client = builder.build();

SmartlookMaskBodyInterceptor.Mask has two parameters:

  • regexPattern/ regex—Regular expression used to define parts of the body that should be masked. It can be defined as a String pattern or Regex.
  • replaceWith—A String used to mask/replace matched body parts.

An example body with masking using the code example:

// Body before masking
{
  "sample": "json",
  "sensitive": "password"
}

// Body after masking
{
  "sample": "json",
  "sensitive": <hidden>
}

Custom network interceptor

If none of the above interceptors suit your needs, you can define a custom interceptor:

class CustomInterceptor: SmartlookOkHttpInterceptor() {
  override fun onIntercept(
    original: SmartlookChain,
    intercepted: SmartlookNetworkRequest
  ): SmartlookNetworkRequest? {

    // Process original and modify intercepted

    return intercepted
  }
}
public static class CustomInterceptor extends SmartlookOkHttpInterceptor {
  @Override
  public SmartlookNetworkRequest onIntercept(
    @NonNull SmartlookChain original,
    @NonNull SmartlookNetworkRequest intercepted
  ) {
    // Process original and modify intercepted
    return intercepted;
  }
}

The onIntercept function has the following parameters:

  • original—Contains all request data:
    • chain—Standard OkHttp intercepted request stored in Interceptor.Chain. Because the Smartlook SDK already processed this chain, do not call the proceed() function. If you want to read the response, use the second parameter processedResponse.
    • processedResponse—Response data stored in standard OkHttp Response.
  • intercepted—Contains previously intercepted data. If this is the first interceptor, it contains data that is intercepted by default.

The return type is SmartlookNetworkRequest? if you return:

  • null—This request will not be tracked.
  • SmartlookNetworkRequest—Instance with data that you want to track that the request will be tracked.

Interceptor chaining

Multiple interceptors can be used to process the intercepted request:

val client = OkHttpClient.Builder()
  .addSmartlookInterceptor(InterceptorA())
  .addSmartlookInterceptor(InterceptorB())
  .addSmartlookInterceptor(InterceptorC())
  .build()
OkHttpClient.Builder builder = new OkHttpClient.Builder();

OkHttpExtKt.addSmartlookInterceptor(builder, new InterceptorA());
OkHttpExtKt.addSmartlookInterceptor(builder, new InterceptorB());
OkHttpExtKt.addSmartlookInterceptor(builder, new InterceptorC());

OkHttpClient client = builder.build();

📘

Interceptor order

When there are multiple interceptors, interceptors are used in the order they are entered. In the above example, InterceptorA is the first and InterceptorC is the last.