Skip to content

Commit 42b4883

Browse files
authored
Merge pull request #186 from hotwired/offline-caching-redirects
When using offline caching, don't mask HTTP redirects
2 parents f2105b1 + c17ccef commit 42b4883

File tree

3 files changed

+79
-26
lines changed

3 files changed

+79
-26
lines changed

turbo/src/main/kotlin/dev/hotwire/turbo/http/TurboHttpRepository.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ internal class TurboHttpRepository(private val coroutineScope: CoroutineScope) {
2828

2929
data class Result(
3030
val response: WebResourceResponse?,
31-
val offline: Boolean
31+
val offline: Boolean,
32+
val redirectToLocation: String? = null
3233
)
3334

3435
internal fun preCache(
@@ -74,15 +75,26 @@ internal class TurboHttpRepository(private val coroutineScope: CoroutineScope) {
7475
return try {
7576
val response = issueRequest(resourceRequest)
7677

78+
// Cache based on the response's request url, which may have been a redirect
79+
val responseUrl = response?.request?.url.toString()
80+
val isRedirect = url != responseUrl
81+
7782
// Let the app cache the response
7883
val resourceResponse = resourceResponse(response)
7984
val cachedResponse = resourceResponse?.let {
80-
requestHandler.cacheResponse(url, it)
85+
requestHandler.cacheResponse(responseUrl, it)
8186
}
8287

83-
Result(cachedResponse ?: resourceResponse, false)
88+
Result(
89+
response = cachedResponse ?: resourceResponse,
90+
offline = false,
91+
redirectToLocation = if (isRedirect) responseUrl else null
92+
)
8493
} catch (e: IOException) {
85-
Result(requestHandler.getCachedResponse(url, allowStaleResponse = true), true)
94+
Result(
95+
response = requestHandler.getCachedResponse(url, allowStaleResponse = true),
96+
offline = true
97+
)
8698
}
8799
}
88100

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package dev.hotwire.turbo.http
2+
3+
import android.webkit.WebResourceRequest
4+
import android.webkit.WebResourceResponse
5+
import dev.hotwire.turbo.session.TurboSession
6+
import dev.hotwire.turbo.util.logEvent
7+
8+
internal class TurboWebViewRequestInterceptor(val session: TurboSession) {
9+
private val offlineRequestHandler get() = session.offlineRequestHandler
10+
private val httpRepository get() = session.httpRepository
11+
private val currentVisit get() = session.currentVisit
12+
13+
fun interceptRequest(request: WebResourceRequest): WebResourceResponse? {
14+
val requestHandler = offlineRequestHandler ?: return null
15+
16+
if (!shouldInterceptRequest(request)) {
17+
return null
18+
}
19+
20+
val url = request.url.toString()
21+
val isCurrentVisitRequest = url == currentVisit?.location
22+
val result = httpRepository.fetch(requestHandler, request)
23+
24+
return if (isCurrentVisitRequest) {
25+
logCurrentVisitResult(url, result)
26+
currentVisit?.completedOffline = result.offline
27+
28+
// If the request resulted in a redirect, don't return the response. This
29+
// lets the WebView handle the request/response and Turbo can see the redirect,
30+
// so a redirect "replace" visit can be proposed.
31+
when (result.redirectToLocation) {
32+
null -> result.response
33+
else -> null
34+
}
35+
} else {
36+
result.response
37+
}
38+
}
39+
40+
private fun shouldInterceptRequest(request: WebResourceRequest): Boolean {
41+
return request.method.equals("GET", ignoreCase = true) &&
42+
request.url.scheme?.startsWith("HTTP", ignoreCase = true) == true
43+
}
44+
45+
private fun logCurrentVisitResult(url: String, result: TurboHttpRepository.Result) {
46+
logEvent(
47+
"location" to url,
48+
"redirectToLocation" to result.redirectToLocation.toString(),
49+
"statusCode" to (result.response?.statusCode ?: "<none>"),
50+
"completedOffline" to result.offline
51+
)
52+
}
53+
54+
private fun logEvent(vararg params: Pair<String, Any>) {
55+
val attributes = params.toMutableList().apply {
56+
add(0, "session" to session.sessionName)
57+
}
58+
logEvent("interceptRequest", attributes)
59+
}
60+
}

turbo/src/main/kotlin/dev/hotwire/turbo/session/TurboSession.kt

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ import androidx.webkit.WebViewFeature.*
1717
import dev.hotwire.turbo.config.TurboPathConfiguration
1818
import dev.hotwire.turbo.config.screenshotsEnabled
1919
import dev.hotwire.turbo.delegates.TurboFileChooserDelegate
20-
import dev.hotwire.turbo.http.TurboHttpClient
21-
import dev.hotwire.turbo.http.TurboHttpRepository
22-
import dev.hotwire.turbo.http.TurboOfflineRequestHandler
23-
import dev.hotwire.turbo.http.TurboPreCacheRequest
20+
import dev.hotwire.turbo.http.*
2421
import dev.hotwire.turbo.nav.TurboNavDestination
2522
import dev.hotwire.turbo.util.*
2623
import dev.hotwire.turbo.views.TurboWebView
@@ -54,6 +51,7 @@ class TurboSession internal constructor(
5451
internal var restorationIdentifiers = SparseArray<String>()
5552
internal val context: Context = activity.applicationContext
5653
internal val httpRepository = TurboHttpRepository(activity.lifecycleScope)
54+
internal val requestInterceptor = TurboWebViewRequestInterceptor(this)
5755
internal val fileChooserDelegate = TurboFileChooserDelegate(this)
5856

5957
// User accessible
@@ -648,24 +646,7 @@ class TurboSession internal constructor(
648646
}
649647

650648
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
651-
val requestHandler = offlineRequestHandler ?: return null
652-
653-
if (!request.method.equals("GET", ignoreCase = true) ||
654-
request.url.scheme?.startsWith("HTTP", ignoreCase = true) != true
655-
) {
656-
return null
657-
}
658-
659-
val url = request.url.toString()
660-
val result = httpRepository.fetch(requestHandler, request)
661-
662-
currentVisit?.let { visit ->
663-
if (visit.location == url) {
664-
visit.completedOffline = result.offline
665-
}
666-
}
667-
668-
return result.response
649+
return requestInterceptor.interceptRequest(request)
669650
}
670651

671652
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceErrorCompat) {

0 commit comments

Comments
 (0)