Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion app/src/androidTest/java/at/bitfire/icsdroid/MockServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ object MockServer {

private val queue = mutableListOf<Response>()

var lastRequestHeaders: Headers? = null
private set

val createMockEngine: (CustomCertManager, SSLContext) -> HttpClientEngine = { _, _ ->
MockEngine {
MockEngine { request ->
// record headers from the incoming request
lastRequestHeaders = request.headers

if (queue.isNotEmpty()) {
val response = lock.withLock { queue.removeAt(0) }
respond(response.content, response.status, response.headers)
Expand All @@ -34,6 +40,7 @@ object MockServer {

fun clear() {
queue.clear()
lastRequestHeaders = null
}

private fun enqueue(response: Response) {
Expand Down
57 changes: 57 additions & 0 deletions app/src/androidTest/java/at/bitfire/icsdroid/TestAppHttpClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package at.bitfire.icsdroid

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class TestAppHttpClient {

private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var client: AppHttpClient

@Before
fun setUp() {
MockServer.clear()
client = MockServer.httpClient(context)
}

@After
fun tearDown() {
MockServer.clear()
}

// Verifies that no Accept-Charset header is sent by default
@Test
fun request_doesNotContainAcceptCharsetHeader() = runBlocking {
// enqueue a simple 200 response
MockServer.enqueue(content = "ok", status = HttpStatusCode.OK)

// perform a GET request to the mock server
val uri = MockServer.uri("test")
val response: HttpResponse = client.httpClient.get(uri.toString())

assertEquals(HttpStatusCode.OK, response.status)

// retrieve the headers recorded by the mock server
val headers = MockServer.lastRequestHeaders

// Ensure headers were recorded
assertNotNull(headers)

// Assert that Accept-Charset header is not present
assertFalse(headers!!.contains(HttpHeaders.AcceptCharset))
}
}
12 changes: 12 additions & 0 deletions app/src/main/java/at/bitfire/icsdroid/AppHttpClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.UserAgent
import io.ktor.client.plugins.api.Send
import io.ktor.client.plugins.api.createClientPlugin
import io.ktor.client.plugins.cookies.HttpCookies
import io.ktor.http.HttpHeaders
import okhttp3.brotli.BrotliInterceptor
import okhttp3.internal.tls.OkHostnameVerifier
import javax.net.ssl.SSLContext
Expand Down Expand Up @@ -85,6 +88,15 @@ class AppHttpClient @AssistedInject constructor(
// Enable cookie storage - in memory, will be lost on app restart
install(HttpCookies)

// Some servers have issues with the Accept-Charset header. It is actually deprecated/not-recommended by RFC 9110 §12.5.2.
// Ktor adds it by default, so we need to manually strip it with a custom plugin.
install(createClientPlugin("RemoveAcceptCharsetHeader") {
on(Send) { request ->
request.headers.remove(HttpHeaders.AcceptCharset)
proceed(request)
}
})

// Disable redirect following, it's handled by CalendarFetcher
followRedirects = false
}
Expand Down