I'm writing here since I haven't found the solution to my problem with the app I'm creating for testing purposes.
It is a simple app that performs HTTP GET requests, and I want it specifically to perform QUIC-HTTP/3 transactions. To do so, I'm using right now okhttp AND the Cronet interceptor that gives okhttp support for those protocols. I've seen two things that I must do (and I am doing as you will see afterwards) in the okhttp client to implement that support: first, using the Cronet interceptor, and secondly add the QUIC protocol to the list of protocols (since by default it doesn't use it, as suggested in this post). The problem is that, while I'm able to have HTTP/2 connections, the app doesn't perform or receive responses with QUIC protocol. I'm trying with pages that support that protocol: www.google.com, quic.nginx.com, etc.
So my question is if I'm missing something here that I need to add too. Here are the main parts of the code related to this:
...
fun getCronetEngine(): CronetEngine {
cronetEngine?.let { return it }
// Instala el proveedor Cronet
CronetProviderInstaller.installProvider(applicationContext)
//QUIC OPTIONS?
//Crea cronetEngine
cronetEngine = CronetEngine.Builder(applicationContext)
.enableHttpCache(1, 10 * 1024 * 1024)
.enableQuic(true)
//.addQuicHint("https://192.168.1.148", 443, 443)
.build()
return cronetEngine!!
}
...
...
fun performGetRequest(url: String): Pair<String, String> {
val cronetConfig = CronetConfig(applicationContext)
val cronetEngine = cronetConfig.getCronetEngine()
val okHttpClient = try {
OkHttpClient.Builder()
.addInterceptor(CronetInterceptor.newBuilder(cronetEngine).build())
.protocols(listOf(Protocol.QUIC, Protocol.HTTP_2, Protocol.HTTP_1_1))
.build()
} catch (e: IllegalArgumentException) {
return Pair("ERROR: ${e.message}", "")
}
val request = try {
Request.Builder()
.url(url)
.get()
.build()
}
...
}
As you can see, I'm even adding the .enableQuic(true) in the cronetEngine just in case even though is enabled by default according to the Cronet documentation.
I'm testing in an Android 11 emulator in IntelliJ (I've seen that support for TLS1.3 is in Android 10 and onwards), and I'm using the latest version of both okhttp (4.12.0) and the cronet interceptor (0.1.0)
Before that I created the app with okhttp and cronet, but for whatever reason the responses I was getting were in HTTP 1.0 (at that point I was testing if I could do HTTPS requests, without QUIC). Afterwards, I found out the problem was the URL I was doing the petitions (https://upm.es; for whatever reason my university doesn't do things right).
Since I thought the problem was my app (and the usage of okhttp) I decided to use exclusively Cronet, so I rewrote it entirely to use only Cronet, but I ended up having the same problem as the first part of the post: I was only using HTTP/2. Here is the code that I wrote with only Cronet (I even added the .addHeader("Quic-Protocol", "quic/h3"), suggested by ChatGPT I will say:
...
private val cronetConfig = CronetConfig(applicationContext)
private val cronetEngine = cronetConfig.getCronetEngine()
private var url: String = ""
private var responseBody = ""
private var protocol = ""
fun performGetRequest(url: String): Pair<String, String> {
this.url = url
val requestBuilder = cronetEngine.newUrlRequestBuilder(
url,
MyUrlRequestCallback(),
Executors.newSingleThreadExecutor()
).addHeader("Quic-Protocol", "quic/h3")
val request = requestBuilder.build()
request.start()
return Pair(protocol, responseBody)
}
inner class MyUrlRequestCallback : UrlRequest.Callback() {
private val buffer = ByteArrayOutputStream()
override fun onResponseStarted(request: UrlRequest?, info: UrlResponseInfo?) {
Log.d("HTTPClient", "Response started")
val httpStatusCode = info?.httpStatusCode
if (httpStatusCode == 200) {
// The request was fulfilled. Start reading the response.
request?.read(ByteBuffer.allocateDirect(102400))
} else if (httpStatusCode == 503) {
// The service is unavailable. You should still check if the request
// contains some data.
request?.read(ByteBuffer.allocateDirect(102400))
}
}
override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {
Log.d("HTTPClient", "Executing onReadComplete")
byteBuffer?.flip()
while (byteBuffer?.hasRemaining() == true) {
buffer.write(byteBuffer.get().toInt())
}
byteBuffer?.clear()
request?.read(byteBuffer)
Log.d("HTTPClient", "EXITING onReadComplete")
}
override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
Log.d("HTTPClient", "Request succeeded")
responseBody = buffer.toString()
Log.d("HTTPClient", "Response Body: $responseBody")
protocol = info?.negotiatedProtocol.toString()
Log.d("HTTPClient", "Protocol: $protocol")
}
override fun onFailed(request: UrlRequest?, info: UrlResponseInfo?, errorInfo: CronetException?) {
Log.e("HTTPClient", "Request failed: ${errorInfo?.toString()}")
}
override fun onRedirectReceived(request: UrlRequest?, info: UrlResponseInfo?, newLocationUrl: String?) {
Log.d("HTTPClient", "Redirect received to: $newLocationUrl")
request?.followRedirect()
}
}
...
I was using the latest versions of the dependencies of Cronet.
Edit:
As commented by @nnori, the problem was (at least partially) in the uni network. Changing that and using my phone as hotspot solved the issue with the full Cronet implementation (although the OkHttp + Cronet still didn't manage to use QUIC).
Now I have the problem of decrypting that traffic :D (for now trying with friTap with no luck).