Add HttpUtil, refactor the network connection function
This commit is contained in:
@@ -16,6 +16,7 @@ import com.v2ray.ang.fmt.TrojanFmt
|
||||
import com.v2ray.ang.fmt.VlessFmt
|
||||
import com.v2ray.ang.fmt.VmessFmt
|
||||
import com.v2ray.ang.fmt.WireguardFmt
|
||||
import com.v2ray.ang.util.HttpUtil
|
||||
import com.v2ray.ang.util.JsonUtil
|
||||
import com.v2ray.ang.util.QRCodeDecoder
|
||||
import com.v2ray.ang.util.Utils
|
||||
@@ -346,7 +347,7 @@ object AngConfigManager {
|
||||
if (!it.second.enabled) {
|
||||
return 0
|
||||
}
|
||||
val url = Utils.idnToASCII(it.second.url)
|
||||
val url = HttpUtil.idnToASCII(it.second.url)
|
||||
if (!Utils.isValidUrl(url)) {
|
||||
return 0
|
||||
}
|
||||
@@ -354,7 +355,7 @@ object AngConfigManager {
|
||||
|
||||
var configText = try {
|
||||
val httpPort = SettingsManager.getHttpPort()
|
||||
Utils.getUrlContentWithCustomUserAgent(url, 30000, httpPort)
|
||||
HttpUtil.getUrlContentWithUserAgent(url, 30000, httpPort)
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.ANG_PACKAGE, "Update subscription: proxy not ready or other error, try……")
|
||||
//e.printStackTrace()
|
||||
@@ -362,7 +363,7 @@ object AngConfigManager {
|
||||
}
|
||||
if (configText.isEmpty()) {
|
||||
configText = try {
|
||||
Utils.getUrlContentWithCustomUserAgent(url)
|
||||
HttpUtil.getUrlContentWithUserAgent(url)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
""
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.v2ray.ang.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.ColorStateList
|
||||
@@ -39,6 +38,7 @@ import com.v2ray.ang.handler.MigrateManager
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
|
||||
import com.v2ray.ang.service.V2RayServiceManager
|
||||
import com.v2ray.ang.util.HttpUtil
|
||||
import com.v2ray.ang.util.Utils
|
||||
import com.v2ray.ang.viewmodel.MainViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -46,7 +46,6 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.drakeet.support.toast.ToastCompat
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||
private val binding by lazy {
|
||||
@@ -626,7 +625,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
}
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val configText = try {
|
||||
Utils.getUrlContentWithCustomUserAgent(url)
|
||||
HttpUtil.getUrlContentWithUserAgent(url)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
""
|
||||
|
||||
@@ -24,6 +24,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.Collator
|
||||
import com.v2ray.ang.util.HttpUtil
|
||||
|
||||
class PerAppProxyActivity : BaseActivity() {
|
||||
private val binding by lazy {
|
||||
@@ -154,7 +155,7 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
toast(R.string.msg_downloading_content)
|
||||
val url = AppConfig.androidpackagenamelistUrl
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val content = Utils.getUrlContext(url, 5000)
|
||||
val content = HttpUtil.getUrlContent(url, 5000)
|
||||
launch(Dispatchers.Main) {
|
||||
Log.d(ANG_PACKAGE, content)
|
||||
selectProxyApp(content, true)
|
||||
|
||||
@@ -20,7 +20,6 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.databinding.ActivitySubSettingBinding
|
||||
import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding
|
||||
@@ -30,6 +29,7 @@ import com.v2ray.ang.extension.toTrafficString
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.handler.SettingsManager
|
||||
import com.v2ray.ang.util.HttpUtil
|
||||
import com.v2ray.ang.util.Utils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -37,9 +37,6 @@ import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.net.URL
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
|
||||
@@ -186,8 +183,10 @@ class UserAssetActivity : BaseActivity() {
|
||||
return false
|
||||
}
|
||||
// Send URL to UserAssetUrlActivity for Processing
|
||||
startActivity(Intent(this, UserAssetUrlActivity::class.java)
|
||||
.putExtra(UserAssetUrlActivity.ASSET_URL_QRCODE, url))
|
||||
startActivity(
|
||||
Intent(this, UserAssetUrlActivity::class.java)
|
||||
.putExtra(UserAssetUrlActivity.ASSET_URL_QRCODE, url)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
@@ -229,22 +228,10 @@ class UserAssetActivity : BaseActivity() {
|
||||
private fun downloadGeo(item: AssetUrlItem, timeout: Int, httpPort: Int): Boolean {
|
||||
val targetTemp = File(extDir, item.remarks + "_temp")
|
||||
val target = File(extDir, item.remarks)
|
||||
var conn: HttpURLConnection? = null
|
||||
//Log.d(AppConfig.ANG_PACKAGE, url)
|
||||
|
||||
val conn = HttpUtil.createProxyConnection(item.url, httpPort, timeout, timeout, needStream = true) ?: return false
|
||||
try {
|
||||
conn = if (httpPort == 0) {
|
||||
URL(item.url).openConnection() as HttpURLConnection
|
||||
} else {
|
||||
URL(item.url).openConnection(
|
||||
Proxy(
|
||||
Proxy.Type.HTTP,
|
||||
InetSocketAddress(LOOPBACK, httpPort)
|
||||
)
|
||||
) as HttpURLConnection
|
||||
}
|
||||
conn.connectTimeout = timeout
|
||||
conn.readTimeout = timeout
|
||||
val inputStream = conn.inputStream
|
||||
val responseCode = conn.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
|
||||
126
V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
Normal file
126
V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
Normal file
@@ -0,0 +1,126 @@
|
||||
package com.v2ray.ang.util
|
||||
|
||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.util.Utils.encode
|
||||
import com.v2ray.ang.util.Utils.urlDecode
|
||||
import java.io.IOException
|
||||
import java.net.*
|
||||
import java.util.*
|
||||
|
||||
object HttpUtil {
|
||||
|
||||
fun idnToASCII(str: String): String {
|
||||
val url = URL(str)
|
||||
return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file).toExternalForm()
|
||||
}
|
||||
|
||||
fun getUrlContent(url: String, timeout: Int): String {
|
||||
var result: String = ""
|
||||
val conn = createProxyConnection(url, 0, timeout, timeout) ?: return result
|
||||
try {
|
||||
result = conn.inputStream.bufferedReader().readText()
|
||||
} catch (_: Exception) {
|
||||
} finally {
|
||||
conn.disconnect()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getUrlContentWithUserAgent(url: String?, timeout: Int = 30000, httpPort: Int = 0): String {
|
||||
var currentUrl = url
|
||||
var redirects = 0
|
||||
val maxRedirects = 3
|
||||
|
||||
while (redirects++ < maxRedirects) {
|
||||
if (currentUrl == null) continue
|
||||
val conn = createProxyConnection(currentUrl, httpPort, timeout, timeout) ?: continue
|
||||
conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
|
||||
conn.connect()
|
||||
|
||||
val responseCode = conn.responseCode
|
||||
when (responseCode) {
|
||||
in 300..399 -> {
|
||||
val location = conn.getHeaderField("Location")
|
||||
conn.disconnect()
|
||||
if (location.isNullOrEmpty()) {
|
||||
throw IOException("Redirect location not found")
|
||||
}
|
||||
currentUrl = location
|
||||
continue
|
||||
}
|
||||
|
||||
else -> try {
|
||||
return conn.inputStream.use { it.bufferedReader().readText() }
|
||||
} finally {
|
||||
conn.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
throw IOException("Too many redirects")
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an HttpURLConnection object connected through a proxy.
|
||||
*
|
||||
* @param urlStr The target URL address.
|
||||
* @param ip The IP address of the proxy server.
|
||||
* @param port The port of the proxy server.
|
||||
* @param connectTimeout The connection timeout in milliseconds (default is 30000 ms).
|
||||
* @param readTimeout The read timeout in milliseconds (default is 30000 ms).
|
||||
* @param needStream
|
||||
* @return Returns a configured HttpURLConnection object, or null if it fails.
|
||||
*/
|
||||
fun createProxyConnection(
|
||||
urlStr: String,
|
||||
port: Int,
|
||||
connectTimeout: Int = 30000,
|
||||
readTimeout: Int = 30000,
|
||||
needStream: Boolean = false
|
||||
): HttpURLConnection? {
|
||||
|
||||
var conn: HttpURLConnection? = null
|
||||
try {
|
||||
val url = URL(urlStr)
|
||||
// Create a connection
|
||||
conn = if (port == 0) {
|
||||
url.openConnection()
|
||||
} else {
|
||||
url.openConnection(
|
||||
Proxy(
|
||||
Proxy.Type.HTTP,
|
||||
InetSocketAddress(LOOPBACK, port)
|
||||
)
|
||||
)
|
||||
} as HttpURLConnection
|
||||
|
||||
// Set connection and read timeouts
|
||||
conn.connectTimeout = connectTimeout
|
||||
conn.readTimeout = readTimeout
|
||||
if (!needStream) {
|
||||
// Set request headers
|
||||
conn.setRequestProperty("Connection", "close")
|
||||
// Disable automatic redirects
|
||||
conn.instanceFollowRedirects = false
|
||||
// Disable caching
|
||||
conn.useCaches = false
|
||||
}
|
||||
|
||||
//Add Basic Authorization
|
||||
url.userInfo?.let {
|
||||
conn.setRequestProperty(
|
||||
"Authorization",
|
||||
"Basic ${encode(urlDecode(it))}"
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
// If an exception occurs, close the connection and return null
|
||||
conn?.disconnect()
|
||||
return null
|
||||
}
|
||||
return conn
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,14 @@ import android.os.SystemClock
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.responseLength
|
||||
import com.v2ray.ang.util.HttpUtil
|
||||
import kotlinx.coroutines.isActive
|
||||
import libv2ray.Libv2ray
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.net.Socket
|
||||
import java.net.URL
|
||||
import java.net.UnknownHostException
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
@@ -101,23 +98,9 @@ object SpeedtestUtil {
|
||||
fun testConnection(context: Context, port: Int): Pair<Long, String> {
|
||||
var result: String
|
||||
var elapsed = -1L
|
||||
var conn: HttpURLConnection? = null
|
||||
|
||||
val conn = HttpUtil.createProxyConnection(Utils.getDelayTestUrl(), port, 30000, 30000) ?: return Pair(elapsed, "")
|
||||
try {
|
||||
val url = URL(Utils.getDelayTestUrl())
|
||||
|
||||
conn = url.openConnection(
|
||||
Proxy(
|
||||
Proxy.Type.HTTP,
|
||||
InetSocketAddress(LOOPBACK, port)
|
||||
)
|
||||
) as HttpURLConnection
|
||||
conn.connectTimeout = 30000
|
||||
conn.readTimeout = 30000
|
||||
conn.setRequestProperty("Connection", "close")
|
||||
conn.instanceFollowRedirects = false
|
||||
conn.useCaches = false
|
||||
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
val code = conn.responseCode
|
||||
elapsed = SystemClock.elapsedRealtime() - start
|
||||
|
||||
@@ -21,7 +21,6 @@ import androidx.core.content.ContextCompat
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.dto.Language
|
||||
import com.v2ray.ang.extension.toast
|
||||
@@ -337,87 +336,6 @@ object Utils {
|
||||
return Base64.encodeToString(androidId.copyOf(32), Base64.NO_PADDING.or(Base64.URL_SAFE))
|
||||
}
|
||||
|
||||
fun getUrlContext(url: String, timeout: Int): String {
|
||||
var result: String
|
||||
var conn: HttpURLConnection? = null
|
||||
|
||||
try {
|
||||
conn = URL(url).openConnection() as HttpURLConnection
|
||||
conn.connectTimeout = timeout
|
||||
conn.readTimeout = timeout
|
||||
conn.setRequestProperty("Connection", "close")
|
||||
conn.instanceFollowRedirects = false
|
||||
conn.useCaches = false
|
||||
//val code = conn.responseCode
|
||||
result = conn.inputStream.bufferedReader().readText()
|
||||
} catch (e: Exception) {
|
||||
result = ""
|
||||
} finally {
|
||||
conn?.disconnect()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getUrlContentWithCustomUserAgent(
|
||||
urlStr: String?,
|
||||
timeout: Int = 30000,
|
||||
httpPort: Int = 0
|
||||
): String {
|
||||
var currentUrl = urlStr
|
||||
var redirects = 0
|
||||
val maxRedirects = 5
|
||||
|
||||
while (redirects < maxRedirects) {
|
||||
val url = URL(currentUrl)
|
||||
val conn = if (httpPort == 0) {
|
||||
url.openConnection()
|
||||
} else {
|
||||
url.openConnection(
|
||||
Proxy(
|
||||
Proxy.Type.HTTP,
|
||||
InetSocketAddress(LOOPBACK, httpPort)
|
||||
)
|
||||
)
|
||||
} as HttpURLConnection
|
||||
|
||||
conn.connectTimeout = timeout
|
||||
conn.readTimeout = timeout
|
||||
conn.setRequestProperty("Connection", "close")
|
||||
conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
|
||||
url.userInfo?.let {
|
||||
conn.setRequestProperty(
|
||||
"Authorization",
|
||||
"Basic ${encode(urlDecode(it))}"
|
||||
)
|
||||
}
|
||||
conn.useCaches = false
|
||||
conn.instanceFollowRedirects = false
|
||||
|
||||
conn.connect()
|
||||
|
||||
val responseCode = conn.responseCode
|
||||
when (responseCode) {
|
||||
in 300..399 -> {
|
||||
val location = conn.getHeaderField("Location")
|
||||
conn.disconnect()
|
||||
if (location.isNullOrEmpty()) {
|
||||
throw IOException("Redirect location not found")
|
||||
}
|
||||
currentUrl = location
|
||||
redirects++
|
||||
continue
|
||||
}
|
||||
else -> try {
|
||||
return conn.inputStream.use { it.bufferedReader().readText() }
|
||||
} finally {
|
||||
conn.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
throw IOException("Too many redirects")
|
||||
}
|
||||
|
||||
fun getDarkModeStatus(context: Context): Boolean {
|
||||
return context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK != UI_MODE_NIGHT_NO
|
||||
}
|
||||
@@ -477,12 +395,6 @@ object Utils {
|
||||
return str?.replace(" ", "")
|
||||
}
|
||||
|
||||
fun idnToASCII(str: String): String {
|
||||
val url = URL(str)
|
||||
return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file)
|
||||
.toExternalForm()
|
||||
}
|
||||
|
||||
fun isTv(context: Context): Boolean =
|
||||
context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user