Network Client

In this section, you will find information about Beagle’s network client and how to modify it.


This interface defines how the services requests are configured, and to use it, you need to create a class that implements a HttpClient interface.

You can add headers to your requests, define method request, body response, data response, run cryptography, etc. See:

interface HttpClient {

    fun execute(
        request: RequestData,
        onSuccess: (responseData: ResponseData) -> Unit,
        onError: (responseData: ResponseData) -> Unit
    ): RequestCall

In the execute method, you can create the rules for your network layer, causing Beagle to recognize its rule.

requestRequestDatavRequestData is the class for configuring http requests.
onSuccess(responseData: ResponseData) -> UnitHigher-Order Functions responsible for the return of success
onError(responseData: ResponseData) -> UnitHigher-Order Functions responsible for error return


RequestData is the class for configuring http requests.

uriURIDefines the endpoint that returns the screen or component you wish to display.
methodHttpMethodIt is an ENUM class that defines which HTTP operation you wish to do. It works as a HTTP REQUEST METHOD and it is set as GET by default.
headersMap<String, String>It is used when you need to send data via an HTTP header.
bodyStringIt is set default as null and it just needs to be implemented when you need to send a HTTP messages asbody data.


It is an ENUM and the values are:

GETThe GET method requests a representation of a specific resource. Requests using the method GET must return only data.
POSTThe POST method is used to submit an entity to a specific resource, frenquetly causing a change in the resource state or colateral effects on the server.
PUTThe PUT method replaces all the current representation of the target resources with the data of the request.
DELETEThe DELETE method removes a specific resource.
HEADThe HEAD method request an answer the same way the GET method does, however without a response body.
PATCHThe PATCH method is used to apply partial modifications in a resource.


ResponseData is used to return data made by the request.

statusCodeIntReturns the response code returned by the remote HTTP server.
dataByteArrayResponse body returned from request.
headersMap<String, String>It is used when you need to send data via an HTTP header.
statusTextStringReturns the response message returned by the remote HTTP server.

Create a custom network client

To create this class, follow the next steps:

Step 1: Add the dependencies

Locate the file build.gradle(Module:app) , open it and scroll the page until you find the code block dependencies { }.

  1. Copy and paste the line below inside the dependencies:

    • implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9’
  2. Press Sync now to synchronize the Gradle again.

This dependency is necessary, because the class that implements HttpClient will import some of its configuration.

Step 2: Create a CoroutineDispatchers object

Create an object and choose a name for it, for example, CoroutineDispatchers.

This object is responsible for configuring CoroutineDispatchers and will dictate which thread the tasks will run:

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers

internal object CoroutineDispatchers {

    init {

    lateinit var IO: CoroutineDispatcher
    lateinit var Main: CoroutineDispatcher
    lateinit var Default: CoroutineDispatcher

    fun reset() {
        IO = Dispatchers.IO
        Main = Dispatchers.Main
        Default = Dispatchers.Default

Step 3: Create a HttpURLConnectionExtensions file

Create a file and choose a name for it, for example,HttpURLConnectionExtensions. This file is responsible for containing methods returning the HttpURLConnection rule, so you will use these methods in the HttpClientDefault class:

import java.lang.Exception

internal fun HttpURLConnection.getSafeResponseCode(): Int? {
    return getMessageFormatted { this.responseCode }

internal fun HttpURLConnection.getSafeResponseMessage(): String? {
    return getMessageFormatted { this.responseMessage }

internal fun HttpURLConnection.getSafeError(): ByteArray? {
    return getMessageFormatted { this.errorStream.readBytes() }

internal typealias GetData<T> = () -> T

internal fun <T> getMessageFormatted(getData: GetData<T>): T? {
    return try {
    } catch (exception: Exception) {

Step 4: Create a HttpClientDefault class

Create a class and choose a name for it, for example HttpClientDefault.

HttpClientDefault class defines how the services requests are configured. To use it, you need to create a class that implements the HttpClient interface.

This configuration is long, so copy and paste the class below. You may modify it later:

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch

typealias OnSuccess = (responseData: ResponseData) -> Unit
typealias OnError = (responseData: ResponseData) -> Unit

class HttpClientDefault : HttpClient, CoroutineScope {
  private val job = Job()
  override val coroutineContext = job + CoroutineDispatchers.IO
  override fun execute(
    request: RequestData,
    onSuccess: OnSuccess,
    onError: OnError
  ): RequestCall {
    if (getOrDeleteOrHeadHasData(request)) {
      onError(ResponseData(-1, data = byteArrayOf()))
      return createRequestCall()
    launch {
      try {
        val responseData = doHttpRequest(request)
      } catch (ex: BeagleApiException) {
    return createRequestCall()
  private fun getOrDeleteOrHeadHasData(request: RequestData): Boolean {
    val method = request.httpAdditionalData.method
    val body = request.httpAdditionalData.body
    return (method == HttpMethod.GET ||
      method == HttpMethod.DELETE ||
      method == HttpMethod.HEAD) &&
      body != null
  private fun doHttpRequest(
    request: RequestData
  ): ResponseData {
    val urlConnection: HttpURLConnection
    try {
      val uri = URI(request.url)
      urlConnection = uri.toURL().openConnection() as HttpURLConnection
    } catch (e: Exception) {
      throw BeagleApiException(ResponseData(-1, data = byteArrayOf()), request)
    request.httpAdditionalData.headers?.forEach {
      urlConnection.setRequestProperty(it.key, it.value)
    addRequestMethod(urlConnection, method)
    request.httpAdditionalData.body?.run {
      setRequestBody(urlConnection, request)
    try {
      return createResponseData(urlConnection)
    } catch (e: Exception) {
      throw tryFormatException(urlConnection, request)
    } finally {
  private fun tryFormatException(
    urlConnection: HttpURLConnection,
    request: RequestData
  ): BeagleApiException {
    val response = urlConnection.getSafeError() ?: byteArrayOf()
    val statusCode = urlConnection.getSafeResponseCode()
    val statusText = urlConnection.getSafeResponseMessage()
    val responseData = ResponseData(
      statusCode = statusCode,
      data = response,
      statusText = statusText,
    return BeagleApiException(responseData, request)
  private fun addRequestMethod(urlConnection: HttpURLConnection, method: HttpMethod) {
    val methodValue = method.toString()
    if (method == HttpMethod.PATCH || method == HttpMethod.HEAD) {
      urlConnection.setRequestProperty("X-HTTP-Method-Override", methodValue)
      urlConnection.requestMethod = "POST"
    } else {
      urlConnection.requestMethod = methodValue
  private fun setRequestBody(urlConnection: HttpURLConnection, request: RequestData) {
    try {
      urlConnection.doOutput = true
    } catch (e: Exception) {
      throw BeagleApiException(ResponseData(-1, data = byteArrayOf()), request)
  private fun createResponseData(urlConnection: HttpURLConnection): ResponseData {
    return ResponseData(
      statusCode = urlConnection.responseCode,
      statusText = urlConnection.responseMessage,
      headers = urlConnection.headerFields.filter { it.key != null }.map {
        val headerValue = it.value.toString()
          .replace("[", "")
          .replace("]", "")
        it.key to headerValue
      data = try {
      } catch (e: EOFException) {
  private fun createRequestCall() = object : RequestCall {
    override fun cancel() {

Step 5: Create a HttpClientFactoryDefault class

Create a class and choose a name for it, for example HttpClientFactoryDefault.

HttpClientFactoryDefault class is responsible for creating the HttpClient instance for Beagle. This class is particularly useful when you need to configure or pass parameters in your HttpClient instantiation.

To use it:

  • Create a class that implements the HttpClientFactory interface.

As an example, you can copy and paste the class below. You may modify it later:


class AppHttpClientFactory: HttpClientFactory {
    override fun create(): HttpClient {
        return HttpClientDefault()

Last modified September 17, 2021: Reviewed Android section (#773) (e4c75e98)