Implement HTTP client test

Fix #479
This commit is contained in:
Mygod
2015-12-15 19:39:57 +08:00
parent 3108306b6e
commit 62a572ee3a
5 changed files with 99 additions and 7 deletions
+5
View File
@@ -78,6 +78,11 @@
<plurals name="bytes">
<item quantity="other">字节</item>
</plurals>
<string name="connection_test_pending">点击检查是否接入互联网。</string>
<string name="connection_test_testing">测试中……</string>
<string name="connection_test_available">已接入互联网。(%d 毫秒)</string>
<string name="connection_test_error">失败:%s</string>
<string name="connection_test_error_status_code">状态码无效。(#%d)</string>
<!-- profile -->
<string name="add_profile_dialog">为影梭添加此配置文件?</string>
+5
View File
@@ -19,6 +19,11 @@
<item quantity="one">Byte</item>
<item quantity="other">Bytes</item>
</plurals>
<string name="connection_test_pending">Tap to check Internet connection availability.</string>
<string name="connection_test_testing">Testing…</string>
<string name="connection_test_available">Internet connection available. (time=%dms)</string>
<string name="connection_test_error">Fail: %s</string>
<string name="connection_test_error_status_code">Invalid status code. (#%d)</string>
<!-- proxy category -->
<string name="proxy_cat">Server Settings</string>
@@ -152,6 +152,8 @@ class Shadowsocks
var prepared = false
var currentProfile = new Profile
var vpnEnabled = -1
var trafficCache: String = _
var connectionTestResult: String = _
// Services
var currentServiceName = classOf[ShadowsocksNatService].getName
@@ -199,14 +201,17 @@ class Shadowsocks
})
}
def trafficUpdated(txRate: String, rxRate: String, txTotal: String, rxTotal: String) {
val trafficStat = getString(R.string.stat_summary)
.formatLocal(Locale.ENGLISH, txRate, rxRate, txTotal, rxTotal)
handler.post(() => {
preferences.findPreference(Key.stat).setSummary(trafficStat)
})
trafficCache = getString(R.string.stat_summary).formatLocal(Locale.ENGLISH, txRate, rxRate, txTotal, rxTotal)
handler.post(updateTraffic)
}
}
def updateTraffic(): Unit = if (trafficCache == null) callback.trafficUpdated(TrafficMonitor.getTxRate,
TrafficMonitor.getRxRate, TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal) else {
if (connectionTestResult == null) connectionTestResult = getString(R.string.connection_test_pending)
preferences.stat.setSummary(trafficCache + '\n' + connectionTestResult)
}
def attachService: Unit = attachService(callback)
override def onServiceConnected() {
@@ -485,8 +490,7 @@ class Shadowsocks
// Check if current profile changed
if (ShadowsocksApplication.profileId != currentProfile.id) reloadProfile()
callback.trafficUpdated(TrafficMonitor.getTxRate, TrafficMonitor.getRxRate,
TrafficMonitor.getTxTotal, TrafficMonitor.getRxTotal)
updateTraffic()
}
private def setPreferenceEnabled(enabled: Boolean) {
@@ -1,5 +1,8 @@
package com.github.shadowsocks
import java.lang.System.currentTimeMillis
import java.lang.{Long => jLong}
import java.net.{HttpURLConnection, URL}
import java.util.Locale
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
@@ -9,20 +12,60 @@ import android.os.{Build, Bundle}
import android.preference.{Preference, PreferenceFragment, SwitchPreference}
import android.support.v7.app.AlertDialog
import android.webkit.{WebView, WebViewClient}
import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils.Key
// TODO: Move related logic here
class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChangeListener {
private def activity = getActivity.asInstanceOf[Shadowsocks]
lazy val natSwitch = findPreference(Key.isNAT).asInstanceOf[SwitchPreference]
var stat: Preference = _
private var isProxyApps: SwitchPreference = _
private var testCount: Int = _
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.pref_all)
getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this)
stat = findPreference(Key.stat)
stat.setOnPreferenceClickListener(_ => {
val id = synchronized {
testCount += 1
activity.connectionTestResult = getString(R.string.connection_test_testing)
activity.updateTraffic()
testCount
}
ThrowableFuture {
// Based on: https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/connectivity/NetworkMonitor.java#640
autoDisconnect(new URL("https", "www.google.com", "/generate_204").openConnection
.asInstanceOf[HttpURLConnection]) { conn =>
conn.setInstanceFollowRedirects(false)
conn.setUseCaches(false)
if (testCount == id) {
var result: String = null
try {
val start = currentTimeMillis
conn.getInputStream
val elapsed = currentTimeMillis - start
val code = conn.getResponseCode
if (code == 204 || code == 200 && conn.getContentLength == 0)
result = getString(R.string.connection_test_available, elapsed: java.lang.Long)
else throw new Exception(getString(R.string.connection_test_error_status_code, code: Integer))
} catch {
case e: Exception => result = getString(R.string.connection_test_error, e.getMessage)
}
synchronized(if (testCount == id) {
activity.connectionTestResult = result
activity.handler.post(activity.updateTraffic)
})
}
}
}
true
})
isProxyApps = findPreference(Key.isProxyApps).asInstanceOf[SwitchPreference]
findPreference("recovery").setOnPreferenceClickListener((preference: Preference) => {
@@ -0,0 +1,35 @@
package com.github.shadowsocks.utils
/**
* @author Mygod
*/
object CloseUtils {
type Disconnectable = {
def disconnect()
}
def autoClose[A <: AutoCloseable, B](x: => A)(block: A => B): B = {
var a: Option[A] = None
try {
a = Some(x)
block(a.get)
} finally if (a.nonEmpty) try {
val v = a.get
if (v ne null) v.close
} catch {
case ignore: Exception =>
}
}
def autoDisconnect[A <: Disconnectable, B](x: => A)(block: A => B): B = {
var a: Option[A] = None
try {
a = Some(x)
block(a.get)
} finally if (a.nonEmpty) try {
val v = a.get
if (v ne null) v.disconnect
} catch {
case ignore: Exception =>
}
}
}