Skip to content

Commit 5708ef7

Browse files
committed
fix(download): can't download when using direct access.
1 parent c10eeee commit 5708ef7

10 files changed

Lines changed: 93 additions & 17 deletions

File tree

app/src/main/java/top/apricityx/workshop/DownloadCenterManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.update
1717
import kotlinx.coroutines.launch
1818
import okhttp3.OkHttpClient
1919
import top.apricityx.workshop.steam.protocol.applyDefaultHttpTimeouts
20+
import top.apricityx.workshop.steam.protocol.applySteamHttpCompatibility
2021
import top.apricityx.workshop.workshop.DownloadEvent
2122
import top.apricityx.workshop.workshop.DownloadState
2223
import top.apricityx.workshop.workshop.WorkshopDownloadEngine
@@ -326,6 +327,7 @@ class DownloadCenterManager private constructor(
326327
}
327328
val taskClient = OkHttpClient.Builder()
328329
.applyDefaultHttpTimeouts()
330+
.applySteamHttpCompatibility()
329331
.applyAppNetworkLogging("download-task")
330332
.cookieJar(
331333
SteamWebSessionCookieJar(

app/src/main/java/top/apricityx/workshop/SteamAuthRepository.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import top.apricityx.workshop.steam.protocol.SteamPublishedFileQuery
2727
import top.apricityx.workshop.steam.protocol.SteamPublishedFileQueryResult
2828
import top.apricityx.workshop.steam.protocol.SteamWebAccessTokens
2929
import top.apricityx.workshop.steam.protocol.applyDefaultHttpTimeouts
30+
import top.apricityx.workshop.steam.protocol.applySteamHttpCompatibility
3031
import java.util.UUID
3132
import java.io.IOException
3233
import java.security.SecureRandom
@@ -92,6 +93,7 @@ class SteamAuthRepository(context: Context) {
9293
private val httpClient by lazy {
9394
OkHttpClient.Builder()
9495
.applyDefaultHttpTimeouts()
96+
.applySteamHttpCompatibility()
9597
.applyAppNetworkLogging("steam-auth")
9698
.hostnameVerifier(experimentalWorkshopDirectAccessRuntime.hostnameVerifier)
9799
.addExperimentalWorkshopDirectAccess(

app/src/main/java/top/apricityx/workshop/SteamAuthenticatedCleartextInterceptor.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class SteamAuthenticatedCleartextInterceptor(
1313
if (
1414
!request.url.isHttps &&
1515
hasAuthenticatedSteamSession() &&
16-
request.url.host.isSteamAuthenticatedTrafficDomain() &&
16+
request.url.host.isSteamWebAuthenticatedTrafficDomain() &&
1717
!allowAuthenticatedCleartextHttpProvider()
1818
) {
1919
throw SteamAuthenticatedCleartextBlockedException(request.url.host)
@@ -28,14 +28,11 @@ class SteamAuthenticatedCleartextBlockedException(
2828
"当前设置禁止带 Steam 登录态的明文 HTTP 请求:$host。可在设置里开启后重试。",
2929
)
3030

31-
internal fun String.isSteamAuthenticatedTrafficDomain(): Boolean {
31+
internal fun String.isSteamWebAuthenticatedTrafficDomain(): Boolean {
3232
val host = lowercase()
3333
return host.matchesDomainSuffix("steamcommunity.com") ||
3434
host.matchesDomainSuffix("steampowered.com") ||
35-
host.matchesDomainSuffix("steamcontent.com") ||
36-
host.matchesDomainSuffix("steam.clngaa.com") ||
37-
host.matchesDomainSuffix("dl.eccdnx.com") ||
38-
host.matchesDomainSuffix("pphimalayanrt.com")
35+
host.matchesDomainSuffix("steamcontent.com")
3936
}
4037

4138
private fun String.matchesDomainSuffix(suffix: String): Boolean =

app/src/main/java/top/apricityx/workshop/WorkshopViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import okhttp3.OkHttpClient
4040
import okhttp3.Request
4141
import top.apricityx.workshop.steam.protocol.DEFAULT_HTTP_TIMEOUT_SECONDS
4242
import top.apricityx.workshop.steam.protocol.applyDefaultHttpTimeouts
43+
import top.apricityx.workshop.steam.protocol.applySteamHttpCompatibility
4344
import top.apricityx.workshop.steam.protocol.SteamPublishedFileQuery
4445
import top.apricityx.workshop.steam.protocol.STEAM_PUBLISHED_FILE_QUERY_TYPE_RANKED_BY_TEXT_SEARCH
4546
import top.apricityx.workshop.steam.protocol.SteamGuardChallengeType
@@ -79,6 +80,7 @@ class WorkshopViewModel(
7980
createExperimentalWorkshopDirectAccessRuntime(application.filesDir)
8081
private val httpClient = OkHttpClient.Builder()
8182
.applyDefaultHttpTimeouts()
83+
.applySteamHttpCompatibility()
8284
.applyAppNetworkLogging("workshop-web")
8385
.cookieJar(steamWebCookieJar)
8486
.hostnameVerifier(experimentalWorkshopDirectAccessRuntime.hostnameVerifier)
@@ -3430,4 +3432,3 @@ private const val BAIDU_DEFAULT_TARGET_LANGUAGE = "zh"
34303432

34313433

34323434

3433-

app/src/test/java/top/apricityx/workshop/SteamAuthenticatedCleartextInterceptorTest.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ class SteamAuthenticatedCleartextInterceptorTest {
4949
allowAuthenticatedCleartextHttp = true,
5050
)
5151

52-
client.newCall(Request.Builder().url(steamHttpUrl("st.dl.eccdnx.com")).build()).execute().use { response ->
52+
client.newCall(Request.Builder().url(steamHttpUrl("api.steampowered.com")).build()).execute().use { response ->
5353
assertThat(response.isSuccessful).isTrue()
5454
}
5555

5656
val request = server.takeRequest()
57-
assertThat(request.url.host).isEqualTo("st.dl.eccdnx.com")
57+
assertThat(request.url.host).isEqualTo("api.steampowered.com")
5858
}
5959

6060
@Test
@@ -73,6 +73,22 @@ class SteamAuthenticatedCleartextInterceptorTest {
7373
assertThat(request.url.host).isEqualTo("dl.steam.clngaa.com")
7474
}
7575

76+
@Test
77+
fun allows_cdn_cleartext_requests_when_authenticated_session_is_present() {
78+
server.enqueue(MockResponse.Builder().code(200).body("ok").build())
79+
val client = testClient(
80+
hasAuthenticatedSteamSession = true,
81+
allowAuthenticatedCleartextHttp = false,
82+
)
83+
84+
client.newCall(Request.Builder().url(steamHttpUrl("st.dl.eccdnx.com")).build()).execute().use { response ->
85+
assertThat(response.isSuccessful).isTrue()
86+
}
87+
88+
val request = server.takeRequest()
89+
assertThat(request.url.host).isEqualTo("st.dl.eccdnx.com")
90+
}
91+
7692
private fun testClient(
7793
hasAuthenticatedSteamSession: Boolean,
7894
allowAuthenticatedCleartextHttp: Boolean,

steam-protocol/src/main/kotlin/top/apricityx/workshop/steam/protocol/Models.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,25 @@ data class CdnServer(
2929

3030
val supportsHttps: Boolean = normalizedHttpsSupport == "mandatory" || normalizedHttpsSupport == "optional"
3131
val requiresHttps: Boolean = normalizedHttpsSupport == "mandatory"
32-
val port: Int = if (requiresHttps) 443 else 80
33-
val secureScheme: String = if (requiresHttps) "https" else "http"
32+
val port: Int = if (supportsHttps) 443 else 80
33+
val secureScheme: String = if (supportsHttps) "https" else "http"
3434

35-
fun requestEndpoints(): List<CdnRequestEndpoint> =
36-
listOf(
35+
fun requestEndpoints(): List<CdnRequestEndpoint> = buildList {
36+
add(
3737
CdnRequestEndpoint(
3838
scheme = secureScheme,
3939
port = port,
4040
),
4141
)
42+
if (supportsHttps && !requiresHttps) {
43+
add(
44+
CdnRequestEndpoint(
45+
scheme = "http",
46+
port = 80,
47+
),
48+
)
49+
}
50+
}
4251
}
4352

4453
data class CdnRequestEndpoint(

steam-protocol/src/main/kotlin/top/apricityx/workshop/steam/protocol/OkHttpTimeouts.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package top.apricityx.workshop.steam.protocol
22

33
import java.util.concurrent.TimeUnit
44
import okhttp3.OkHttpClient
5+
import okhttp3.Protocol
56

67
const val DEFAULT_HTTP_TIMEOUT_SECONDS = 30L
78

@@ -12,9 +13,13 @@ fun OkHttpClient.Builder.applyDefaultHttpTimeouts(
1213
.readTimeout(timeoutSeconds, TimeUnit.SECONDS)
1314
.writeTimeout(timeoutSeconds, TimeUnit.SECONDS)
1415

16+
fun OkHttpClient.Builder.applySteamHttpCompatibility(): OkHttpClient.Builder =
17+
protocols(listOf(Protocol.HTTP_1_1))
18+
1519
fun newDefaultOkHttpClient(
1620
timeoutSeconds: Long = DEFAULT_HTTP_TIMEOUT_SECONDS,
1721
): OkHttpClient =
1822
OkHttpClient.Builder()
1923
.applyDefaultHttpTimeouts(timeoutSeconds)
24+
.applySteamHttpCompatibility()
2025
.build()

steam-protocol/src/test/kotlin/top/apricityx/workshop/steam/protocol/CdnServerTest.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ import org.junit.Test
55

66
class CdnServerTest {
77
@Test
8-
fun `optional https support follows reference client and uses http only`() {
8+
fun `optional https support prefers https and keeps http fallback`() {
99
val server = testServer(httpsSupport = "optional")
1010

1111
assertThat(server.supportsHttps).isTrue()
1212
assertThat(server.requiresHttps).isFalse()
13-
assertThat(server.secureScheme).isEqualTo("http")
14-
assertThat(server.port).isEqualTo(80)
13+
assertThat(server.secureScheme).isEqualTo("https")
14+
assertThat(server.port).isEqualTo(443)
1515
assertThat(server.requestEndpoints()).containsExactly(
16+
CdnRequestEndpoint(scheme = "https", port = 443),
1617
CdnRequestEndpoint(scheme = "http", port = 80),
17-
)
18+
).inOrder()
1819
}
1920

2021
@Test
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package top.apricityx.workshop.steam.protocol
2+
3+
import com.google.common.truth.Truth.assertThat
4+
import okhttp3.OkHttpClient
5+
import okhttp3.Protocol
6+
import org.junit.Test
7+
8+
class OkHttpTimeoutsTest {
9+
@Test
10+
fun `applySteamHttpCompatibility forces http1 only`() {
11+
val client = OkHttpClient.Builder()
12+
.applySteamHttpCompatibility()
13+
.build()
14+
15+
assertThat(client.protocols).containsExactly(Protocol.HTTP_1_1)
16+
}
17+
18+
@Test
19+
fun `newDefaultOkHttpClient uses steam-compatible protocols`() {
20+
val client = newDefaultOkHttpClient()
21+
22+
assertThat(client.protocols).containsExactly(Protocol.HTTP_1_1)
23+
}
24+
}

workshop-core/src/test/kotlin/top/apricityx/workshop/workshop/SteamCdnTransportTest.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,25 @@ class SteamCdnTransportTest {
8989
assertThat(url.encodedQuery).isEqualTo("token=abc")
9090
}
9191

92+
@Test
93+
fun `buildRequestUrl prefers https endpoint when cdn advertises optional https`() {
94+
val server = testServer(
95+
host = "origin.example.net",
96+
httpsSupport = "optional",
97+
)
98+
99+
val url = transport.buildRequestUrl(
100+
server = server,
101+
endpoint = server.requestEndpoints().first(),
102+
path = "depot/646570/manifest/1/5/999",
103+
query = null,
104+
proxyServer = null,
105+
)
106+
107+
assertThat(url.scheme).isEqualTo("https")
108+
assertThat(url.port).isEqualTo(443)
109+
}
110+
92111
private fun testServer(
93112
host: String,
94113
vHost: String = host,

0 commit comments

Comments
 (0)