2

I have an App which repeatedly gets some data from an API. That works really good when I have a wifi connection. But with mobile data my request either times out or takes like 20 seconds. Which doesn't make sense because if I just type the API url in the android browser it almost immediately loads up. Also the API is a non https api I don't know if that matters. Any idea what could cause this? My Ktor Client:

object KtorClient {

val ktorClient = HttpClient(Android) {
    install(DefaultRequest) {
        header("apiKey", "apiKey")
    }
    install(ContentNegotiation) {
        json()
    }
    defaultRequest {
        contentType(ContentType.Application.Json)
        accept(ContentType.Application.Json)
    }
}

}

My product repository:

object ProductRepository {

const val URL = "API"
const val LATEST_APK_URI = "$URL/apk"

suspend fun getProducts() = ktorClient.get("$URL/products").body<Map<String, List<ProductItem>>>().map {
    it.key to it.value.sortedBy { item -> item.done }
}.toMap() 

}

My viewmodel:

class ProductViewModel : ViewModel() {

val productFlow = mutableStateMapOf<String, List<ProductItem>>()
val errorFlow = MutableStateFlow<Error?>(null)
val lastRefreshFlow = MutableStateFlow<DateTimeTz?>(null)

fun refreshProducts(context: Context) {
    viewModelScope.launch {
        kotlin.runCatching {
            ProductRepository.getProducts()
        }.onSuccess {
            productFlow.clear()
            productFlow.putAll(it)
            lastRefreshFlow.value = DateTime.nowLocal()
            //updateOrder(context)
            context.cacheFile.updateCache(productFlow)
        }.onFailure {
            errorFlow.value = SilentError("Konnte Produkte nicht neu laden", it)
        }
    }
}

}
Jan T.
  • 43
  • 6

1 Answers1

1

The browser adds extra headers to the request which are used by DDOS protection mechanisms which usually delay or simply reject the request depending on various criteria like the client's IP. Sometimes you can't even make the request to a server IP without specifying the hostname in a header (presumably because the IP is of a proxy server doing the DDOS filtering and not the end server). It might be that the proxy doesn't trust the mobile carrier's IPs as much as the ones of the VPNs or broadband ISPs. You may use a packet/traffic inspection app on the device to analyse the request made by the browser and try to replicate it (copy the headers) in your own requests.


The server/cloud is more likely to cause the delay than the actual code of the webapp. Try using different mobile operators to see if they all cause the delay. Also check the actual IPs and their associated country. Using traceroute to see the delays in your request might help you pinpointing the problem.

If you're using a domain not a raw IP you might also want to check if the DNS resolution causes the delay although it's unlikely to cause significant and repeated delays because it caches queries.

And last but not least check the HTTP library you use client side, it might do some weird 'optimizations' and request queuing which could get messed up especially if you change connection while the app is running (volley is known to cause quite a grief and cache responses in its default setup). Try using a raw HttpUrlConnection or whatever is most basic in your android stack to see if that changes anything.


It's weird the DNS doesn't cache the domain/IP pair after the first query so that subsequent queries are faster, probably something about the domain that instructs it not to. As far as I know you can't do much about the DNS over cellular, as it's managed by the carrier so the alternative would be to define and use a VPN with its own DNS. If that seems quite over the top or inconvenient you could also try to make a direct DNS query (or check if this Java library can be used) in your app and use the resolved IP for subsequent requests in that session.

You can also have a look at finding current DNS in Android and sending a UDP DNS query

Crispert
  • 1,102
  • 7
  • 13
  • 1
    I tried to copy all the headers of the browsers with the user agent etc. but it didn't change anything. Also, the API is mine – Jan T. May 09 '22 at 23:06
  • Okay so I tried to use the raw IP, and it immediately loads up. No delay or anything. – Jan T. May 10 '22 at 12:14
  • Any idea on how to fix that dns problem then? – Jan T. May 10 '22 at 12:29
  • 1
    Made a direct dns query at the end thanks! – Jan T. May 11 '22 at 10:53
  • 1
    For anyone wondering, made a ktor plugin for that: https://github.com/jan-tennert/DnsPlugin – Jan T. May 11 '22 at 14:23
  • Hey @JanT.! I came across this because I am running into a similar issue with Ktor’s HttpClient. Did you ever get to the bottom of this and get it resolved without requiring your own plugin that, basically, takes DNS away from Ktor? Cheers. – Bink May 21 '23 at 01:39