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.

Verified Commit 893603f9 authored by Philipp Winter's avatar Philipp Winter

Merge branch 'issue/8'

parents df61aa13 ca74ead2
......@@ -10,13 +10,7 @@ import (
"time"
)
const (
// Cache test results for one week.
CacheValidity = 7 * 24 * time.Hour
)
var cacheMutex sync.Mutex
var cache TestCache = make(TestCache)
var cache *TestCache
// Regular expression that captures the address:port part of a bridge line (for
// both IPv4 and IPv6 addresses).
......@@ -33,6 +27,14 @@ type CacheEntry struct {
Time time.Time
}
type TestCache struct {
// Entries maps a bridge's addr:port tuple to a cache entry.
Entries map[string]*CacheEntry
// EntryTimeout determines how long a cache entry is valid for.
EntryTimeout time.Duration
l sync.Mutex
}
// bridgeLineToAddrPort takes a bridge line as input and returns a string
// consisting of the bridge's addr:port (for both IPv4 and IPv6 addresses).
func bridgeLineToAddrPort(bridgeLine string) (string, error) {
......@@ -45,9 +47,6 @@ func bridgeLineToAddrPort(bridgeLine string) (string, error) {
}
}
// TestCache maps a bridge's addr:port tuple to a cache entry.
type TestCache map[string]*CacheEntry
// WriteToDisk writes our test result cache to disk, allowing it to persist
// across program restarts.
func (tc *TestCache) WriteToDisk(cacheFile string) error {
......@@ -59,13 +58,13 @@ func (tc *TestCache) WriteToDisk(cacheFile string) error {
defer fh.Close()
enc := gob.NewEncoder(fh)
cacheMutex.Lock()
tc.l.Lock()
err = enc.Encode(*tc)
if err == nil {
log.Printf("Wrote cache with %d elements to %q.",
len(*tc), cacheFile)
len((*tc).Entries), cacheFile)
}
cacheMutex.Unlock()
tc.l.Unlock()
return err
}
......@@ -80,39 +79,39 @@ func (tc *TestCache) ReadFromDisk(cacheFile string) error {
defer fh.Close()
dec := gob.NewDecoder(fh)
cacheMutex.Lock()
tc.l.Lock()
err = dec.Decode(tc)
if err == nil {
log.Printf("Read cache with %d elements from %q.",
len(*tc), cacheFile)
len((*tc).Entries), cacheFile)
}
cacheMutex.Unlock()
tc.l.Unlock()
return err
}
// IsCached returns a cache entry if the given bridge line has been tested
// recently (as determined by CacheValidity), and nil otherwise.
// recently (as determined by EntryTimeout), and nil otherwise.
func (tc *TestCache) IsCached(bridgeLine string) *CacheEntry {
// First, prune expired cache entries.
now := time.Now().UTC()
cacheMutex.Lock()
for index, entry := range *tc {
if entry.Time.Before(now.Add(-CacheValidity)) {
delete(*tc, index)
tc.l.Lock()
for index, entry := range (*tc).Entries {
if entry.Time.Before(now.Add(-(*tc).EntryTimeout)) {
delete((*tc).Entries, index)
}
}
cacheMutex.Unlock()
tc.l.Unlock()
addrPort, err := bridgeLineToAddrPort(bridgeLine)
if err != nil {
return nil
}
cacheMutex.Lock()
var r *CacheEntry = (*tc)[addrPort]
cacheMutex.Unlock()
tc.l.Lock()
var r *CacheEntry = (*tc).Entries[addrPort]
tc.l.Unlock()
return r
}
......@@ -132,7 +131,7 @@ func (tc *TestCache) AddEntry(bridgeLine string, result error, lastTested time.T
} else {
errorStr = result.Error()
}
cacheMutex.Lock()
(*tc)[addrPort] = &CacheEntry{errorStr, lastTested}
cacheMutex.Unlock()
tc.l.Lock()
(*tc).Entries[addrPort] = &CacheEntry{errorStr, lastTested}
tc.l.Unlock()
}
......@@ -11,9 +11,16 @@ import (
"time"
)
func NewCache() *TestCache {
return &TestCache{
Entries: make(map[string]*CacheEntry),
EntryTimeout: 24 * time.Hour,
}
}
func TestCacheFunctions(t *testing.T) {
cache := make(TestCache)
cache := NewCache()
bridgeLine := "obfs4 127.0.0.1:1 cert=foo iat-mode=0"
e := cache.IsCached(bridgeLine)
......@@ -35,10 +42,10 @@ func TestCacheFunctions(t *testing.T) {
}
// A bogus bridge line shouldn't make it into the cache.
cache = make(TestCache)
cache = NewCache()
bogusBridgeLine := "bogus-bridge-line"
cache.AddEntry(bogusBridgeLine, errors.New("bogus-error"), time.Now().UTC())
if len(cache) != 0 {
if len(cache.Entries) != 0 {
t.Errorf("Bogus bridge line made it into cache.")
}
......@@ -50,15 +57,15 @@ func TestCacheFunctions(t *testing.T) {
func TestCacheExpiration(t *testing.T) {
cache := make(TestCache)
cache := NewCache()
const shortForm = "2006-Jan-02"
expiry, _ := time.Parse(shortForm, "2000-Jan-01")
bridgeLine1 := "1.1.1.1:1111"
cache[bridgeLine1] = &CacheEntry{"", expiry}
cache.Entries[bridgeLine1] = &CacheEntry{"", expiry}
bridgeLine2 := "2.2.2.2:2222"
cache[bridgeLine2] = &CacheEntry{"", time.Now().UTC()}
cache.Entries[bridgeLine2] = &CacheEntry{"", time.Now().UTC()}
e := cache.IsCached(bridgeLine1)
if e != nil {
......@@ -83,7 +90,7 @@ func BenchmarkIsCached(b *testing.B) {
}
numCacheEntries := 10000
cache := make(TestCache)
cache := NewCache()
for i := 0; i < numCacheEntries; i++ {
cache.AddEntry(getRandAddrPort(), getRandError(), time.Now().UTC())
}
......@@ -97,7 +104,7 @@ func BenchmarkIsCached(b *testing.B) {
func TestCacheSerialisation(t *testing.T) {
cache := make(TestCache)
cache := NewCache()
testError := fmt.Errorf("foo")
cache.AddEntry("1.1.1.1:1", testError, time.Now().UTC())
cache.AddEntry("2.2.2.2:2", fmt.Errorf("bar"), time.Now().UTC())
......@@ -117,8 +124,8 @@ func TestCacheSerialisation(t *testing.T) {
t.Errorf("Failed to read cache from disk: %s", err)
}
if len(cache) != 2 {
t.Errorf("Cache supposed to contain but two elements but has %d.", len(cache))
if len(cache.Entries) != 2 {
t.Errorf("Cache supposed to contain but two elements but has %d.", len(cache.Entries))
}
e1 := cache.IsCached("1.1.1.1:1")
......@@ -140,7 +147,7 @@ func TestCacheSerialisation(t *testing.T) {
func TestCacheConcurrency(t *testing.T) {
cache := make(TestCache)
cache := NewCache()
max := 10000
doneReading := make(chan bool)
doneWriting := make(chan bool)
......
......@@ -94,7 +94,7 @@ func printPrettyCache() {
var shortError string
var numFunctional int
for bridgeLine, cacheEntry := range cache {
for bridgeLine, cacheEntry := range cache.Entries {
shortError = cacheEntry.Error
maxChars := 50
if len(cacheEntry.Error) > maxChars {
......@@ -105,9 +105,10 @@ func printPrettyCache() {
}
fmt.Printf("%-22s %-50s %s\n", bridgeLine, shortError, cacheEntry.Time)
}
if len(cache) > 0 {
cacheLen := len(cache.Entries)
if len(cache.Entries) > 0 {
log.Printf("Found %d (%.2f%%) out of %d functional.\n", numFunctional,
float64(numFunctional)/float64(len(cache))*100.0, len(cache))
float64(numFunctional)/float64(cacheLen)*100.0, cacheLen)
}
}
......@@ -120,7 +121,7 @@ func main() {
var cacheFile string
var templatesDir string
var torBinary string
var testTimeout int
var testTimeout, cacheTimeout int
var logFile string
flag.StringVar(&addr, "addr", ":5000", "Address to listen on.")
......@@ -134,7 +135,8 @@ func main() {
flag.StringVar(&templatesDir, "templates", "templates", "Path to directory that contains our web templates.")
flag.StringVar(&torBinary, "tor", "tor", "Path to tor executable.")
flag.StringVar(&logFile, "log", "", "File to write logs to.")
flag.IntVar(&testTimeout, "timeout", 60, "Test timeout in seconds.")
flag.IntVar(&testTimeout, "test-timeout", 60, "Test timeout in seconds.")
flag.IntVar(&cacheTimeout, "cache-timeout", 24, "Cache timeout in hours.")
flag.Parse()
if showVersion {
......@@ -179,6 +181,12 @@ func main() {
return
}
cache = &TestCache{
Entries: make(map[string]*CacheEntry),
EntryTimeout: time.Duration(cacheTimeout) * time.Hour,
}
log.Printf("Setting cache timeout to %s.", cache.EntryTimeout)
TorTestTimeout = time.Duration(testTimeout) * time.Second
log.Printf("Setting Tor test timeout to %s.", TorTestTimeout)
torCtx = &TorContext{TorBinary: torBinary}
......
......@@ -3,6 +3,7 @@ package main
import (
"bytes"
"testing"
"time"
)
func TestWriteConfigToTorrc(t *testing.T) {
......@@ -54,6 +55,7 @@ func TestBridgeTest(t *testing.T) {
defaultBridge2 := "obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0"
bogusBridge := "127.0.0.1:1"
TorTestTimeout = time.Minute
torCtx = &TorContext{TorBinary: "tor"}
if err := torCtx.Start(); err != nil {
t.Fatalf("Failed to start tor: %s", err)
......
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