1+ package com.lagradost.cloudstream3.network
2+
3+ import android.util.Log
4+ import okhttp3.Credentials
5+ import okhttp3.Interceptor
6+ import okhttp3.OkHttpClient
7+ import okhttp3.Response
8+ import java.io.IOException
9+ import java.net.ConnectException
10+ import java.net.InetSocketAddress
11+ import java.net.Proxy
12+ import java.net.SocketTimeoutException
13+ import java.util.concurrent.TimeUnit
14+
15+ class ProxyInterceptor (
16+ private val host : String ,
17+ private val port : Int ,
18+ private val proxyType : Proxy .Type = Proxy .Type .HTTP ,
19+ private val username : String? = null ,
20+ private val password : String? = null ,
21+ private val allowFallback : Boolean = false ,
22+ private val connectTimeoutSeconds : Long = 15L ,
23+ private val readTimeoutSeconds : Long = 15L
24+ ) : Interceptor {
25+
26+ companion object {
27+ private const val TAG = " ProxyDebug"
28+ }
29+
30+ init {
31+ Log .d(
32+ TAG ,
33+ " proxy setup: " + listOf (
34+ " host=$host " ,
35+ " port=$port " ,
36+ " type=${proxyType.name} " ,
37+ " timeouts=${connectTimeoutSeconds} s/${readTimeoutSeconds} s" ,
38+ " auth=${if (username != null ) " enabled" else " None" } " ,
39+ " fallback=${if (allowFallback) " Allowed" else " Disabled" } "
40+ ).joinToString(separator = " , " ) // Join only the parameters
41+ )
42+ }
43+
44+ private val proxyClient by lazy {
45+ Log .d(TAG , " Building proxy client for $host :$port " )
46+
47+ val proxy = Proxy (proxyType, InetSocketAddress (host, port))
48+ OkHttpClient .Builder ()
49+ .proxy(proxy)
50+ .connectTimeout(connectTimeoutSeconds, TimeUnit .SECONDS )
51+ .readTimeout(readTimeoutSeconds, TimeUnit .SECONDS )
52+ .apply {
53+ if (username != null && password != null ) {
54+ Log .d(TAG , " Configuring proxy credentials" )
55+ proxyAuthenticator { _, response ->
56+ Log .d(TAG , " Authenticating proxy for ${response.request.url} " )
57+ response.request.newBuilder()
58+ .header(" Proxy-Authorization" , Credentials .basic(username, password))
59+ .build()
60+ }
61+ }
62+ }
63+ .build()
64+ }
65+
66+ override fun intercept (chain : Interceptor .Chain ): Response {
67+ Log .d(TAG , " Intercepting request to ${chain.request().url.host} " )
68+
69+ return try {
70+ val response = proxyClient.newCall(chain.request()).execute()
71+
72+ Log .d(
73+ TAG ,
74+ " proxy response:" + listOf (
75+ " url=${response.request.url} " ,
76+ " status=${response.code} " ,
77+ " headers=${response.headers.size} " ,
78+ " body=${response.body?.contentLength() ? : 0 } bytes"
79+ ).joinToString(separator = " , " )
80+ )
81+
82+ when {
83+ response.code == 407 -> handleProxyAuthenticationError(chain, response)
84+ ! response.isSuccessful -> throw IOException (" HTTP ${response.code} " )
85+ else -> response
86+ }
87+ } catch (e: IOException ) {
88+ Log .d(
89+ TAG ,
90+ " proxy error:" + listOf (
91+ " type=${e.javaClass.simpleName} " ,
92+ " message=${e.message} " ,
93+ " request=${chain.request().url} "
94+ ).joinToString(separator = " , " )
95+ )
96+ handleProxyError(e, chain)
97+ }
98+ }
99+
100+ private fun handleProxyAuthenticationError (
101+ chain : Interceptor .Chain ,
102+ response : Response
103+ ): Response {
104+ response.close()
105+ Log .d(TAG , " Proxy authentication failed for $host :$port " )
106+ return if (allowFallback) {
107+ Log .d(TAG , " Attempting fallback connection" )
108+ fallback(chain)
109+ } else {
110+ throw IOException (" Proxy authentication required" )
111+ }
112+ }
113+
114+ private fun handleProxyError (e : IOException , chain : Interceptor .Chain ): Response {
115+ return when (e) {
116+ is ConnectException -> {
117+ Log .d(TAG , " Connection refused to proxy $host :$port " )
118+ if (allowFallback) fallback(chain) else throw e
119+ }
120+
121+ is SocketTimeoutException -> {
122+ Log .d(TAG , " Timeout connecting to proxy (${connectTimeoutSeconds} s)" )
123+ if (allowFallback) fallback(chain) else throw e
124+ }
125+
126+ else -> {
127+ Log .d(TAG , " Unexpected proxy error: ${e.javaClass.simpleName} " )
128+ throw e
129+ }
130+ }
131+ }
132+
133+ private fun fallback (chain : Interceptor .Chain ): Response {
134+ Log .d(TAG , " Using direct connection to ${chain.request().url.host} " )
135+ return chain.proceed(chain.request()).also { response ->
136+ Log .d(
137+ TAG ,
138+ " direct connection: " + listOf (
139+ " status=${response.code} " ,
140+ " via=${response.handshake?.tlsVersion ? : " Plaintext" } " ,
141+ " server=${response.header(" Server" ) ? : " Unknown" } "
142+ ).joinToString(separator = " , " )
143+ )
144+ }
145+ }
146+ }
0 commit comments