GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

Add field that says when a bridge was last tested.

This patch fixes #6
parent f393cbed
......@@ -56,6 +56,7 @@ The service responds with the following JSON:
"bridge_results": {
"BRIDGE_LINE_1": {
"functional": BOOL,
"last_tested": "STRING",
"error": "STRING", (only present if "functional" is false)
},
...
......@@ -68,10 +69,12 @@ The service responds with the following JSON:
}
In a nutshell, the "bridge_results" dictionary maps bridge lines (as they were
provided in the request) to a dictionary consisting of two keys: "functional"
provided in the request) to a dictionary consisting of three keys: "functional"
is set to "true" if tor could fetch the bridge's descriptor. If tor was unable
to fetch the bridge's descriptor, "functional" is set to "false" and the
"error" key maps to an error string.
"error" key maps to an error string. The key "last_tested" maps to a string
representation (in ISO 8601 format) of the UTC time and date the bridge was
last tested.
In addition to the "bridge_results" dictionary, the response may contain an
optional "error" key if the entire test failed (e.g. if bridgestrap failed to
......@@ -90,10 +93,12 @@ Here are a few examples:
{
"bridge_results": {
"obfs4 1.2.3.4:1234 cert=fJRlJc0T7i2Qkw3SyLQq+M6iTGs9ghLHK65LBy/MQewXJpNOKFq63Om1JHVkLlrmEBbX1w iat-mode=0": {
"functional": true
"functional": true,
"last_tested": "2020-11-12T19:42:16.736853122Z"
},
"1.2.3.4:1234": {
"functional": false,
"last_tested": "2020-11-10T09:44:45.877531581Z",
"error": "timed out waiting for bridge descriptor"
}
},
......@@ -104,6 +109,7 @@ Here are a few examples:
"bridge_results": {
"1.2.3.4:1234 1234567890ABCDEF1234567890ABCDEF12345678": {
"functional": false,
"last_tested": "2020-11-10T09:44:45.877531581Z",
"error": "timed out waiting for bridge descriptor"
}
},
......
......@@ -117,8 +117,9 @@ func (tc *TestCache) IsCached(bridgeLine string) *CacheEntry {
return r
}
// AddEntry adds an entry for the given bridge and test result to our cache.
func (tc *TestCache) AddEntry(bridgeLine string, result error) {
// AddEntry adds an entry for the given bridge, test result, and test time to
// our cache.
func (tc *TestCache) AddEntry(bridgeLine string, result error, lastTested time.Time) {
addrPort, err := bridgeLineToAddrPort(bridgeLine)
if err != nil {
......@@ -132,6 +133,6 @@ func (tc *TestCache) AddEntry(bridgeLine string, result error) {
errorStr = result.Error()
}
cacheMutex.Lock()
(*tc)[addrPort] = &CacheEntry{errorStr, time.Now()}
(*tc)[addrPort] = &CacheEntry{errorStr, lastTested}
cacheMutex.Unlock()
}
......@@ -21,14 +21,14 @@ func TestCacheFunctions(t *testing.T) {
t.Errorf("Cache is empty but marks bridge line as existing.")
}
cache.AddEntry(bridgeLine, nil)
cache.AddEntry(bridgeLine, nil, time.Now().UTC())
e = cache.IsCached(bridgeLine)
if e == nil {
t.Errorf("Could not retrieve existing element from cache.")
}
testError := fmt.Errorf("bridge is on fire")
cache.AddEntry(bridgeLine, testError)
cache.AddEntry(bridgeLine, testError, time.Now().UTC())
e = cache.IsCached(bridgeLine)
if e.Error != testError.Error() {
t.Errorf("Got test result %q but expected %q.", e.Error, testError)
......@@ -45,7 +45,7 @@ func TestCacheExpiration(t *testing.T) {
cache[bridgeLine1] = &CacheEntry{"", expiry}
bridgeLine2 := "2.2.2.2:2222"
cache[bridgeLine2] = &CacheEntry{"", time.Now()}
cache[bridgeLine2] = &CacheEntry{"", time.Now().UTC()}
e := cache.IsCached(bridgeLine1)
if e != nil {
......@@ -72,7 +72,7 @@ func BenchmarkIsCached(b *testing.B) {
numCacheEntries := 10000
cache := make(TestCache)
for i := 0; i < numCacheEntries; i++ {
cache.AddEntry(getRandAddrPort(), getRandError())
cache.AddEntry(getRandAddrPort(), getRandError(), time.Now().UTC())
}
// How long does it take to iterate over numCacheEntries cache entries?
......@@ -86,8 +86,8 @@ func TestCacheSerialisation(t *testing.T) {
cache := make(TestCache)
testError := fmt.Errorf("foo")
cache.AddEntry("1.1.1.1:1", testError)
cache.AddEntry("2.2.2.2:2", fmt.Errorf("bar"))
cache.AddEntry("1.1.1.1:1", testError, time.Now().UTC())
cache.AddEntry("2.2.2.2:2", fmt.Errorf("bar"), time.Now().UTC())
tmpFh, err := ioutil.TempFile(os.TempDir(), "cache-file-")
if err != nil {
......@@ -132,7 +132,7 @@ func TestCacheConcurrency(t *testing.T) {
byte((i>>16)&0xff),
byte((i>>8)&0xff),
byte(i&0xff))
cache.AddEntry(fmt.Sprintf("%s:1234", ipAddr.String()), nil)
cache.AddEntry(fmt.Sprintf("%s:1234", ipAddr.String()), nil, time.Now().UTC())
}
doneWriting <- true
}()
......
......@@ -20,8 +20,9 @@ var FailurePage string
// BridgeTest represents the result of a bridge test, sent back to the client
// as JSON object.
type BridgeTest struct {
Functional bool `json:"functional"`
Error string `json:"error,omitempty"`
Functional bool `json:"functional"`
LastTested time.Time `json:"last_tested"`
Error string `json:"error,omitempty"`
}
// TestResult represents the result of a test.
......@@ -98,8 +99,11 @@ func testBridgeLines(bridgeLines []string) *TestResult {
for _, bridgeLine := range bridgeLines {
if entry := cache.IsCached(bridgeLine); entry != nil {
numCached++
result.Bridges[bridgeLine] = &BridgeTest{Functional: entry.Error == "",
Error: entry.Error}
result.Bridges[bridgeLine] = &BridgeTest{
Functional: entry.Error == "",
LastTested: entry.Time,
Error: entry.Error,
}
} else {
remainingBridgeLines = append(remainingBridgeLines, bridgeLine)
}
......@@ -116,7 +120,7 @@ func testBridgeLines(bridgeLines []string) *TestResult {
// Cache partial test results and add them to our existing result object.
for bridgeLine, bridgeTest := range partialResult.Bridges {
cache.AddEntry(bridgeLine, errors.New(bridgeTest.Error))
cache.AddEntry(bridgeLine, errors.New(bridgeTest.Error), bridgeTest.LastTested)
result.Bridges[bridgeLine] = bridgeTest
}
} else {
......
......@@ -236,10 +236,17 @@ func (c *TorContext) TestBridgeLines(bridgeLines []string) *TestResult {
parser.Feed(line)
if parser.State == BridgeStateSuccess {
log.Printf("Setting %s to 'true'", bridgeLine)
result.Bridges[bridgeLine] = &BridgeTest{Functional: true}
result.Bridges[bridgeLine] = &BridgeTest{
Functional: true,
LastTested: time.Now().UTC(),
}
} else if parser.State == BridgeStateFailure {
log.Printf("Setting %s to 'false'", bridgeLine)
result.Bridges[bridgeLine] = &BridgeTest{Functional: false, Error: parser.Reason}
result.Bridges[bridgeLine] = &BridgeTest{
Functional: false,
Error: parser.Reason,
LastTested: time.Now().UTC(),
}
}
}
......@@ -254,8 +261,11 @@ func (c *TorContext) TestBridgeLines(bridgeLines []string) *TestResult {
// Mark whatever bridge results we're missing as nonfunctional.
for _, bridgeLine := range bridgeLines {
if _, exists := result.Bridges[bridgeLine]; !exists {
result.Bridges[bridgeLine] = &BridgeTest{Functional: false,
Error: "timed out waiting for bridge descriptor"}
result.Bridges[bridgeLine] = &BridgeTest{
Functional: false,
Error: "timed out waiting for bridge descriptor",
LastTested: time.Now().UTC(),
}
}
}
return result
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment