From cacd78eadcb9242da7f926af0e63567ce3df0a83 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 3 Oct 2015 15:34:36 -0700 Subject: [PATCH] add utp to multiaddr-net --- Godeps/Godeps.json | 16 +- .../src/github.com/anacrolix/jitter/jitter.go | 12 + .../github.com/anacrolix/missinggo/addr.go | 72 + .../anacrolix/missinggo/addr_test.go | 17 + .../anacrolix/missinggo/args/args.go | 15 + .../github.com/anacrolix/missinggo/atime.go | 11 + .../anacrolix/missinggo/atime_darwin.go | 12 + .../anacrolix/missinggo/atime_freebsd.go | 12 + .../anacrolix/missinggo/atime_linux.go | 12 + .../anacrolix/missinggo/atime_windows.go | 12 + .../anacrolix/missinggo/castslice.go | 17 + .../anacrolix/missinggo/cmd/go-env/main.go | 12 + .../anacrolix/missinggo/cmd/nop/main.go | 3 + .../missinggo/cmd/query-escape/main.go | 11 + .../missinggo/cmd/query-unescape/main.go | 11 + .../github.com/anacrolix/missinggo/copy.go | 32 + .../anacrolix/missinggo/copy_test.go | 89 + .../github.com/anacrolix/missinggo/croak.go | 18 + .../src/github.com/anacrolix/missinggo/doc.go | 3 + .../anacrolix/missinggo/expvarIndentMap.go | 35 + .../anacrolix/missinggo/filecache/cache.go | 269 +++ .../missinggo/filecache/cache_test.go | 84 + .../anacrolix/missinggo/filecache/file.go | 117 ++ .../anacrolix/missinggo/filecache/lruitems.go | 94 ++ .../missinggo/filecache/lruitems_test.go | 22 + .../anacrolix/missinggo/httpcontentrange.go | 38 + .../missinggo/httpcontentrange_test.go | 27 + .../anacrolix/missinggo/httpfile/httpfile.go | 222 +++ .../anacrolix/missinggo/httpgzip.go | 47 + .../anacrolix/missinggo/httpresponsestatus.go | 60 + .../anacrolix/missinggo/itertools/groupby.go | 81 + .../missinggo/itertools/groupby_test.go | 31 + .../anacrolix/missinggo/itertools/iterator.go | 41 + .../missinggo/itertools/iterator_test.go | 17 + .../src/github.com/anacrolix/missinggo/net.go | 26 + .../github.com/anacrolix/missinggo/path.go | 20 + .../anacrolix/missinggo/path_test.go | 17 + .../anacrolix/missinggo/perf/mutex.go | 48 + .../anacrolix/missinggo/perf/perf.go | 92 ++ .../anacrolix/missinggo/perf/perf_test.go | 52 + .../anacrolix/missinggo/pubsub/pubsub.go | 92 ++ .../anacrolix/missinggo/pubsub/pubsub_test.go | 74 + .../src/github.com/anacrolix/missinggo/rle.go | 46 + .../anacrolix/missinggo/rle_test.go | 20 + .../anacrolix/missinggo/singleflight.go | 39 + .../github.com/anacrolix/missinggo/sync.go | 11 + .../src/github.com/anacrolix/missinggo/url.go | 30 + .../github.com/anacrolix/missinggo/wolf.go | 51 + .../anacrolix/missinggo/wolf_test.go | 30 + .../src/github.com/anacrolix/utp/.utp.go.swp | Bin 0 -> 86016 bytes .../src/github.com/anacrolix/utp/LICENSE | 362 ++++ .../src/github.com/anacrolix/utp/README.md | 19 + .../github.com/anacrolix/utp/cmd/ucat/ucat.go | 66 + .../src/github.com/anacrolix/utp/pingpong | 61 + .../src/github.com/anacrolix/utp/utp.go | 1461 +++++++++++++++++ .../src/github.com/anacrolix/utp/utp_test.go | 411 +++++ .../src/github.com/bradfitz/iter/.gitignore | 1 + .../src/github.com/bradfitz/iter/README.txt | 1 + .../src/github.com/bradfitz/iter/iter.go | 17 + .../src/github.com/bradfitz/iter/iter_test.go | 29 + .../src/github.com/h2so5/utp/.gitignore | 26 - .../src/github.com/h2so5/utp/.travis.yml | 9 - .../src/github.com/h2so5/utp/LICENSE | 21 - .../src/github.com/h2so5/utp/README.md | 28 - .../src/github.com/h2so5/utp/addr.go | 42 - .../src/github.com/h2so5/utp/base.go | 315 ---- .../src/github.com/h2so5/utp/base_test.go | 308 ---- .../github.com/h2so5/utp/benchmark/main.go | 296 ---- .../src/github.com/h2so5/utp/buffer.go | 469 ------ .../src/github.com/h2so5/utp/buffer_test.go | 177 -- .../src/github.com/h2so5/utp/conn.go | 555 ------- .../src/github.com/h2so5/utp/conn_test.go | 87 - .../src/github.com/h2so5/utp/dial.go | 101 -- .../src/github.com/h2so5/utp/dial_test.go | 52 - .../src/github.com/h2so5/utp/listener.go | 146 -- .../src/github.com/h2so5/utp/listener_test.go | 68 - .../src/github.com/h2so5/utp/log.go | 50 - .../src/github.com/h2so5/utp/packet.go | 198 --- .../src/github.com/h2so5/utp/packet_test.go | 64 - .../src/github.com/h2so5/utp/ucat/.gitignore | 3 - .../src/github.com/h2so5/utp/ucat/Makefile | 34 - .../github.com/h2so5/utp/ucat/test_simple.sh | 49 - .../src/github.com/h2so5/utp/ucat/ucat.go | 192 --- .../src/github.com/h2so5/utp/utp.go | 47 - convert.go | 4 +- convert_test.go | 10 +- multiaddr/multiaddr.go | 2 +- net.go | 32 +- net_test.go | 6 +- utp/utp_util.go | 105 ++ 90 files changed, 4774 insertions(+), 3370 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/anacrolix/jitter/jitter.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/addr.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/addr_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/args/args.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/atime.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_darwin.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_linux.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_windows.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/castslice.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/go-env/main.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/nop/main.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-escape/main.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-unescape/main.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/copy.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/copy_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/croak.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/doc.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/expvarIndentMap.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/file.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/httpfile/httpfile.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/httpgzip.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/httpresponsestatus.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/net.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/path.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/path_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/mutex.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/rle.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/rle_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/singleflight.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/sync.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/url.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf_test.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/utp/.utp.go.swp create mode 100644 Godeps/_workspace/src/github.com/anacrolix/utp/LICENSE create mode 100644 Godeps/_workspace/src/github.com/anacrolix/utp/README.md create mode 100644 Godeps/_workspace/src/github.com/anacrolix/utp/cmd/ucat/ucat.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/utp/pingpong create mode 100644 Godeps/_workspace/src/github.com/anacrolix/utp/utp.go create mode 100644 Godeps/_workspace/src/github.com/anacrolix/utp/utp_test.go create mode 100644 Godeps/_workspace/src/github.com/bradfitz/iter/.gitignore create mode 100644 Godeps/_workspace/src/github.com/bradfitz/iter/README.txt create mode 100644 Godeps/_workspace/src/github.com/bradfitz/iter/iter.go create mode 100644 Godeps/_workspace/src/github.com/bradfitz/iter/iter_test.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/.gitignore delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/README.md delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/addr.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/base.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/base_test.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/buffer.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/conn.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/dial.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/listener.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/log.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/packet.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go delete mode 100644 Godeps/_workspace/src/github.com/h2so5/utp/utp.go create mode 100644 utp/utp_util.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 3df340b..9cbebc8 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -6,8 +6,20 @@ ], "Deps": [ { - "ImportPath": "github.com/h2so5/utp", - "Rev": "5288a05e1781334589c4b8806bcfb1e69f5b5d63" + "ImportPath": "github.com/anacrolix/jitter", + "Rev": "2ea5c18645100745b24e9f5cfc9b3f6f7eac51ef" + }, + { + "ImportPath": "github.com/anacrolix/missinggo", + "Rev": "4e1ca5963308863b56c31863f60c394a7365ec29" + }, + { + "ImportPath": "github.com/anacrolix/utp", + "Rev": "0bb24de92c268452fb9106ca4fb9302442ca0dee" + }, + { + "ImportPath": "github.com/bradfitz/iter", + "Rev": "454541ec3da2a73fc34fd049b19ee5777bf19345" }, { "ImportPath": "github.com/jbenet/go-base58", diff --git a/Godeps/_workspace/src/github.com/anacrolix/jitter/jitter.go b/Godeps/_workspace/src/github.com/anacrolix/jitter/jitter.go new file mode 100644 index 0000000..7f0f39b --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/jitter/jitter.go @@ -0,0 +1,12 @@ +package jitter + +import ( + "math/rand" + "time" +) + +func Duration(average, plusMinus time.Duration) (ret time.Duration) { + ret = average - plusMinus + ret += time.Duration(rand.Int63n(2*int64(plusMinus) + 1)) + return +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/addr.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/addr.go new file mode 100644 index 0000000..7d7a4f8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/addr.go @@ -0,0 +1,72 @@ +package missinggo + +import ( + "net" + "strconv" + "strings" +) + +type HostMaybePort struct { + Host string + Port int + NoPort bool +} + +func (me HostMaybePort) String() string { + if me.NoPort { + return me.Host + } + return net.JoinHostPort(me.Host, strconv.FormatInt(int64(me.Port), 10)) +} + +func SplitHostPort(hostport string) (ret HostMaybePort) { + host, port, err := net.SplitHostPort(hostport) + if err != nil { + if strings.Contains(err.Error(), "missing port") { + ret.Host = hostport + ret.NoPort = true + return + } + panic(err) + } + i64, err := strconv.ParseInt(port, 0, 0) + ret.Host = host + ret.Port = int(i64) + if err != nil { + ret.NoPort = true + } + return +} + +// Extracts the port as an integer from an address string. +func AddrPort(addr net.Addr) int { + switch raw := addr.(type) { + case *net.UDPAddr: + return raw.Port + default: + _, port, err := net.SplitHostPort(addr.String()) + if err != nil { + panic(err) + } + i64, err := strconv.ParseInt(port, 0, 0) + if err != nil { + panic(err) + } + return int(i64) + } +} + +func AddrIP(addr net.Addr) net.IP { + switch raw := addr.(type) { + case *net.UDPAddr: + return raw.IP + case *net.TCPAddr: + return raw.IP + default: + host, _, err := net.SplitHostPort(addr.String()) + if err != nil { + panic(err) + } + return net.ParseIP(host) + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/addr_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/addr_test.go new file mode 100644 index 0000000..96ffee0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/addr_test.go @@ -0,0 +1,17 @@ +package missinggo + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSplitHostPort(t *testing.T) { + assert.EqualValues(t, HostMaybePort{"a", 1, false}, SplitHostPort("a:1")) + assert.EqualValues(t, HostMaybePort{"a", 0, true}, SplitHostPort("a")) +} + +func TestHostMaybePortString(t *testing.T) { + assert.EqualValues(t, "a:1", (HostMaybePort{"a", 1, false}).String()) + assert.EqualValues(t, "a", (HostMaybePort{"a", 0, true}).String()) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/args/args.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/args/args.go new file mode 100644 index 0000000..8f0d614 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/args/args.go @@ -0,0 +1,15 @@ +package args + +import ( + "flag" + "fmt" + "os" +) + +func Parse() { + flag.Parse() + if flag.NArg() != 0 { + fmt.Fprintf(os.Stderr, "unexpected positional arguments\n") + os.Exit(2) + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime.go new file mode 100644 index 0000000..41ede58 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime.go @@ -0,0 +1,11 @@ +package missinggo + +import ( + "os" + "time" +) + +// Extracts the access time from the FileInfo internals. +func FileInfoAccessTime(fi os.FileInfo) time.Time { + return fileInfoAccessTime(fi) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_darwin.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_darwin.go new file mode 100644 index 0000000..5ccbc43 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_darwin.go @@ -0,0 +1,12 @@ +package missinggo + +import ( + "os" + "syscall" + "time" +) + +func fileInfoAccessTime(fi os.FileInfo) time.Time { + ts := fi.Sys().(*syscall.Stat_t).Atimespec + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_freebsd.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_freebsd.go new file mode 100644 index 0000000..5ccbc43 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_freebsd.go @@ -0,0 +1,12 @@ +package missinggo + +import ( + "os" + "syscall" + "time" +) + +func fileInfoAccessTime(fi os.FileInfo) time.Time { + ts := fi.Sys().(*syscall.Stat_t).Atimespec + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_linux.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_linux.go new file mode 100644 index 0000000..e599d8b --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_linux.go @@ -0,0 +1,12 @@ +package missinggo + +import ( + "os" + "syscall" + "time" +) + +func fileInfoAccessTime(fi os.FileInfo) time.Time { + ts := fi.Sys().(*syscall.Stat_t).Atim + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_windows.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_windows.go new file mode 100644 index 0000000..bedf8c3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_windows.go @@ -0,0 +1,12 @@ +package missinggo + +import ( + "os" + "syscall" + "time" +) + +func fileInfoAccessTime(fi os.FileInfo) time.Time { + ts := fi.Sys().(syscall.Win32FileAttributeData).LastAccessTime + return time.Unix(0, int64(ts.Nanoseconds())) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/castslice.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/castslice.go new file mode 100644 index 0000000..5370c3e --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/castslice.go @@ -0,0 +1,17 @@ +package missinggo + +import ( + "reflect" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/bradfitz/iter" +) + +func ConvertToSliceOfEmptyInterface(slice interface{}) (ret []interface{}) { + v := reflect.ValueOf(slice) + l := v.Len() + ret = make([]interface{}, 0, l) + for i := range iter.N(v.Len()) { + ret = append(ret, v.Index(i).Interface()) + } + return +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/go-env/main.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/go-env/main.go new file mode 100644 index 0000000..66a736e --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/go-env/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + for _, v := range os.Environ() { + fmt.Printf("%s\n", v) + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/nop/main.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/nop/main.go new file mode 100644 index 0000000..38dd16d --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/nop/main.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-escape/main.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-escape/main.go new file mode 100644 index 0000000..cddd9d9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-escape/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "net/url" + "os" +) + +func main() { + fmt.Println(url.QueryEscape(os.Args[1])) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-unescape/main.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-unescape/main.go new file mode 100644 index 0000000..3099cc8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-unescape/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "net/url" + "os" +) + +func main() { + fmt.Println(url.QueryUnescape(os.Args[1])) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/copy.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/copy.go new file mode 100644 index 0000000..79aa58f --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/copy.go @@ -0,0 +1,32 @@ +package missinggo + +import ( + "fmt" + "reflect" +) + +func CopyExact(dest interface{}, src interface{}) { + dV := reflect.ValueOf(dest) + sV := reflect.ValueOf(src) + if dV.Kind() == reflect.Ptr { + dV = dV.Elem() + } + if dV.Kind() == reflect.Array && !dV.CanAddr() { + panic(fmt.Sprintf("dest not addressable: %T", dest)) + } + if sV.Kind() == reflect.Ptr { + sV = sV.Elem() + } + if sV.Kind() == reflect.String { + sV = sV.Convert(reflect.SliceOf(dV.Type().Elem())) + } + if !sV.IsValid() { + panic("invalid source, probably nil") + } + if dV.Len() != sV.Len() { + panic(fmt.Sprintf("dest len (%d) != src len (%d)", dV.Len(), sV.Len())) + } + if dV.Len() != reflect.Copy(dV, sV) { + panic("dammit") + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/copy_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/copy_test.go new file mode 100644 index 0000000..190f2a1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/copy_test.go @@ -0,0 +1,89 @@ +package missinggo + +import ( + "bytes" + "strings" + "testing" +) + +func TestCopyToArray(t *testing.T) { + var arr [3]byte + bb := []byte{1, 2, 3} + CopyExact(&arr, bb) + if !bytes.Equal(arr[:], bb) { + t.FailNow() + } +} + +func TestCopyToSlicedArray(t *testing.T) { + var arr [5]byte + CopyExact(arr[:], "hello") + if !bytes.Equal(arr[:], []byte("hello")) { + t.FailNow() + } +} + +func TestCopyDestNotAddr(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.FailNow() + } + t.Log(r) + }() + var arr [3]byte + CopyExact(arr, "nope") +} + +func TestCopyLenMismatch(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.FailNow() + } + t.Log(r) + }() + CopyExact(make([]byte, 2), "abc") +} + +func TestCopySrcString(t *testing.T) { + dest := make([]byte, 3) + CopyExact(dest, "lol") + if string(dest) != "lol" { + t.FailNow() + } + func() { + defer func() { + r := recover() + if r == nil { + t.FailNow() + } + }() + CopyExact(dest, "rofl") + }() + var arr [5]byte + CopyExact(&arr, interface{}("hello")) + if string(arr[:]) != "hello" { + t.FailNow() + } +} + +func TestCopySrcNilInterface(t *testing.T) { + var arr [3]byte + defer func() { + r := recover().(string) + if !strings.Contains(r, "invalid source") { + t.FailNow() + } + }() + CopyExact(&arr, nil) +} + +func TestCopySrcPtr(t *testing.T) { + var bigDst [1024]byte + var bigSrc [1024]byte = [1024]byte{'h', 'i'} + CopyExact(&bigDst, &bigSrc) + if !bytes.Equal(bigDst[:], bigSrc[:]) { + t.FailNow() + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/croak.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/croak.go new file mode 100644 index 0000000..ded86ef --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/croak.go @@ -0,0 +1,18 @@ +package missinggo + +import ( + "fmt" + "os" +) + +func Unchomp(s string) string { + if len(s) > 0 && s[len(s)-1] == '\n' { + return s + } + return s + "\n" +} + +func Fatal(msg interface{}) { + os.Stderr.WriteString(Unchomp(fmt.Sprint(msg))) + os.Exit(1) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/doc.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/doc.go new file mode 100644 index 0000000..9b8aa8d --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/doc.go @@ -0,0 +1,3 @@ +// Package missinggo contains miscellaneous helpers used in many of anacrolix' +// projects. +package missinggo diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/expvarIndentMap.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/expvarIndentMap.go new file mode 100644 index 0000000..c995299 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/expvarIndentMap.go @@ -0,0 +1,35 @@ +package missinggo + +import ( + "bytes" + "expvar" + "fmt" +) + +type IndentMap struct { + expvar.Map +} + +var _ expvar.Var = &IndentMap{} + +func NewExpvarIndentMap(name string) *IndentMap { + v := new(IndentMap) + v.Init() + expvar.Publish(name, v) + return v +} + +func (v *IndentMap) String() string { + var b bytes.Buffer + fmt.Fprintf(&b, "{") + first := true + v.Do(func(kv expvar.KeyValue) { + if !first { + fmt.Fprintf(&b, ",") + } + fmt.Fprintf(&b, "\n\t%q: %v", kv.Key, kv.Value) + first = false + }) + fmt.Fprintf(&b, "}") + return b.String() +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache.go new file mode 100644 index 0000000..43c95bd --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache.go @@ -0,0 +1,269 @@ +package filecache + +import ( + "errors" + "log" + "os" + "path" + "path/filepath" + "sync" + "time" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" +) + +type Cache struct { + mu sync.Mutex + capacity int64 + filled int64 + items *lruItems + paths map[string]ItemInfo + root string +} + +type CacheInfo struct { + Capacity int64 + Filled int64 + NumItems int +} + +type ItemInfo struct { + Accessed time.Time + Size int64 + Path string +} + +// Calls the function for every item known to the cache. The ItemInfo should +// not be modified. +func (me *Cache) WalkItems(cb func(ItemInfo)) { + me.mu.Lock() + defer me.mu.Unlock() + for e := me.items.Front(); e != nil; e = e.Next() { + cb(e.Value().(ItemInfo)) + } +} + +func (me *Cache) Info() (ret CacheInfo) { + me.mu.Lock() + defer me.mu.Unlock() + ret.Capacity = me.capacity + ret.Filled = me.filled + ret.NumItems = len(me.paths) + return +} + +func (me *Cache) SetCapacity(capacity int64) { + me.mu.Lock() + defer me.mu.Unlock() + me.capacity = capacity +} + +func NewCache(root string) (ret *Cache, err error) { + if !filepath.IsAbs(root) { + err = errors.New("root is not an absolute filepath") + return + } + ret = &Cache{ + root: root, + capacity: -1, // unlimited + } + ret.mu.Lock() + go func() { + defer ret.mu.Unlock() + ret.rescan() + }() + return +} + +// An empty return path is an error. +func sanitizePath(p string) (ret string) { + if p == "" { + return + } + ret = path.Clean("/" + p) + if ret[0] == '/' { + ret = ret[1:] + } + return +} + +// Leaf is a descendent of root. +func pruneEmptyDirs(root string, leaf string) (err error) { + rootInfo, err := os.Stat(root) + if err != nil { + return + } + for { + var leafInfo os.FileInfo + leafInfo, err = os.Stat(leaf) + if os.IsNotExist(err) { + goto parent + } + if err != nil { + return + } + if !leafInfo.IsDir() { + return + } + if os.SameFile(rootInfo, leafInfo) { + return + } + if os.Remove(leaf) != nil { + return + } + parent: + leaf = filepath.Dir(leaf) + } +} + +func (me *Cache) Remove(path string) (err error) { + path = sanitizePath(path) + me.mu.Lock() + defer me.mu.Unlock() + err = me.remove(path) + return +} + +var ( + ErrBadPath = errors.New("bad path") + ErrIsDir = errors.New("is directory") +) + +func (me *Cache) OpenFile(path string, flag int) (ret *File, err error) { + path = sanitizePath(path) + if path == "" { + err = ErrIsDir + return + } + f, err := os.OpenFile(me.realpath(path), flag, 0644) + if flag&os.O_CREATE != 0 && os.IsNotExist(err) { + os.MkdirAll(me.root, 0755) + os.MkdirAll(filepath.Dir(me.realpath(path)), 0755) + f, err = os.OpenFile(me.realpath(path), flag, 0644) + if err != nil { + me.pruneEmptyDirs(path) + } + } + if err != nil { + return + } + ret = &File{ + c: me, + path: path, + f: f, + } + me.mu.Lock() + go func() { + defer me.mu.Unlock() + me.statItem(path, time.Now()) + }() + return +} + +func (me *Cache) rescan() { + me.filled = 0 + me.items = newLRUItems() + me.paths = make(map[string]ItemInfo) + err := filepath.Walk(me.root, func(path string, info os.FileInfo, err error) error { + if os.IsNotExist(err) { + return nil + } + if err != nil { + return err + } + if info.IsDir() { + return nil + } + path, err = filepath.Rel(me.root, path) + if err != nil { + log.Print(err) + return nil + } + me.statItem(path, time.Time{}) + return nil + }) + if err != nil { + panic(err) + } +} + +func (me *Cache) insertItem(i ItemInfo) { + me.items.Insert(i) +} + +func (me *Cache) removeInfo(path string) (ret ItemInfo, ok bool) { + ret, ok = me.paths[path] + if !ok { + return + } + if !me.items.Remove(ret) { + panic(ret) + } + me.filled -= ret.Size + delete(me.paths, path) + return +} + +// Triggers the item for path to be updated. If access is non-zero, set the +// item's access time to that value, otherwise deduce it appropriately. +func (me *Cache) statItem(path string, access time.Time) { + info, ok := me.removeInfo(path) + fi, err := os.Stat(me.realpath(path)) + if os.IsNotExist(err) { + return + } + if err != nil { + panic(err) + } + if !ok { + info.Path = path + } + if !access.IsZero() { + info.Accessed = access + } + if info.Accessed.IsZero() { + info.Accessed = missinggo.FileInfoAccessTime(fi) + } + info.Size = fi.Size() + me.filled += info.Size + me.insertItem(info) + me.paths[path] = info +} + +func (me *Cache) realpath(path string) string { + return filepath.Join(me.root, filepath.FromSlash(path)) +} + +func (me *Cache) TrimToCapacity() { + me.mu.Lock() + defer me.mu.Unlock() + me.trimToCapacity() +} + +func (me *Cache) pruneEmptyDirs(path string) { + pruneEmptyDirs(me.root, me.realpath(path)) +} + +func (me *Cache) remove(path string) (err error) { + err = os.Remove(me.realpath(path)) + if os.IsNotExist(err) { + err = nil + } + me.pruneEmptyDirs(path) + me.removeInfo(path) + return +} + +func (me *Cache) trimToCapacity() { + if me.capacity < 0 { + return + } + for me.filled > me.capacity { + item := me.items.LRU() + me.remove(item.Path) + } +} + +func (me *Cache) pathInfo(p string) ItemInfo { + return me.paths[p] +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache_test.go new file mode 100644 index 0000000..ddaed5d --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache_test.go @@ -0,0 +1,84 @@ +package filecache + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" +) + +func TestCache(t *testing.T) { + td, err := ioutil.TempDir("", "gotest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(td) + c, err := NewCache(filepath.Join(td, "cache")) + if err != nil { + t.Fatal(err) + } + assert.EqualValues(t, 0, c.Info().Filled) + c.WalkItems(func(i ItemInfo) {}) + _, err = c.OpenFile("/", os.O_CREATE) + assert.NotNil(t, err) + _, err = c.OpenFile("", os.O_CREATE) + assert.NotNil(t, err) + c.WalkItems(func(i ItemInfo) {}) + require.Equal(t, 0, c.Info().NumItems) + _, err = c.OpenFile("notexist", 0) + assert.True(t, os.IsNotExist(err), err) + _, err = c.OpenFile("/notexist", 0) + assert.True(t, os.IsNotExist(err), err) + _, err = c.OpenFile("/dir/notexist", 0) + assert.True(t, os.IsNotExist(err), err) + f, err := c.OpenFile("dir/blah", os.O_CREATE) + require.NoError(t, err) + defer f.Close() + c.WalkItems(func(i ItemInfo) {}) + assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah")))) + assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/")))) + assert.Equal(t, 1, c.Info().NumItems) + f.Remove() + assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("dir/blah")))) + assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("dir/")))) + _, err = f.Read(nil) + assert.NotEqual(t, io.EOF, err) + a, err := c.OpenFile("/a", os.O_CREATE|os.O_WRONLY) + defer a.Close() + require.Nil(t, err) + b, err := c.OpenFile("b", os.O_CREATE|os.O_WRONLY) + defer b.Close() + require.Nil(t, err) + c.mu.Lock() + assert.True(t, c.pathInfo("a").Accessed.Before(c.pathInfo("b").Accessed)) + c.mu.Unlock() + n, err := a.Write([]byte("hello")) + assert.Nil(t, err) + assert.EqualValues(t, 5, n) + assert.EqualValues(t, 5, c.Info().Filled) + assert.True(t, c.pathInfo("b").Accessed.Before(c.pathInfo("a").Accessed)) + c.SetCapacity(5) + n, err = a.Write([]byte(" world")) + assert.NotNil(t, err) + _, err = b.Write([]byte("boom!")) + // "a" and "b" have been evicted. + assert.NotNil(t, err) + assert.EqualValues(t, 0, c.Info().Filled) + assert.EqualValues(t, 0, c.Info().NumItems) + _, err = a.Seek(0, os.SEEK_SET) + assert.NotNil(t, err) +} + +func TestSanitizePath(t *testing.T) { + assert.Equal(t, "", sanitizePath("////")) + assert.Equal(t, "", sanitizePath("/../..")) + assert.Equal(t, "a", sanitizePath("/a//b/..")) + assert.Equal(t, "a", sanitizePath("../a")) + assert.Equal(t, "a", sanitizePath("./a")) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/file.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/file.go new file mode 100644 index 0000000..70e4743 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/file.go @@ -0,0 +1,117 @@ +package filecache + +import ( + "errors" + "math" + "os" + "sync" + "time" +) + +type File struct { + mu sync.Mutex + c *Cache + path string + f *os.File + gone bool +} + +func (me *File) Remove() (err error) { + return me.c.Remove(me.path) +} + +func (me *File) Seek(offset int64, whence int) (ret int64, err error) { + ret, err = me.f.Seek(offset, whence) + return +} + +func (me *File) maxWrite() (max int64, err error) { + if me.c.capacity < 0 { + max = math.MaxInt64 + return + } + pos, err := me.Seek(0, os.SEEK_CUR) + if err != nil { + return + } + max = me.c.capacity - pos + if max < 0 { + max = 0 + } + return +} + +var ( + ErrFileTooLarge = errors.New("file too large for cache") + ErrFileDisappeared = errors.New("file disappeared") +) + +func (me *File) checkGone() { + if me.gone { + return + } + ffi, _ := me.Stat() + fsfi, _ := os.Stat(me.c.realpath(me.path)) + me.gone = !os.SameFile(ffi, fsfi) +} + +func (me *File) goneErr() error { + me.mu.Lock() + defer me.mu.Unlock() + me.checkGone() + if me.gone { + me.f.Close() + return ErrFileDisappeared + } + return nil +} + +func (me *File) Write(b []byte) (n int, err error) { + err = me.goneErr() + if err != nil { + return + } + n, err = me.f.Write(b) + me.c.mu.Lock() + me.c.statItem(me.path, time.Now()) + me.c.trimToCapacity() + me.c.mu.Unlock() + if err == nil { + err = me.goneErr() + } + return +} + +func (me *File) Close() error { + return me.f.Close() +} + +func (me *File) Stat() (os.FileInfo, error) { + return me.f.Stat() +} + +func (me *File) Read(b []byte) (n int, err error) { + err = me.goneErr() + if err != nil { + return + } + defer func() { + me.c.mu.Lock() + defer me.c.mu.Unlock() + me.c.statItem(me.path, time.Now()) + }() + return me.f.Read(b) +} + +func (me *File) ReadAt(b []byte, off int64) (n int, err error) { + err = me.goneErr() + if err != nil { + return + } + defer func() { + me.c.mu.Lock() + defer me.c.mu.Unlock() + me.c.statItem(me.path, time.Now()) + }() + return me.f.ReadAt(b, off) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems.go new file mode 100644 index 0000000..75f0f7b --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems.go @@ -0,0 +1,94 @@ +package filecache + +import ( + "container/list" + "io" + + "github.com/cznic/b" +) + +type Iterator interface { + Next() Iterator + Value() interface{} +} + +type listElementIterator struct { + le *list.Element +} + +func (me listElementIterator) Next() Iterator { + e := me.le.Next() + if e == nil { + return nil + } + return listElementIterator{e} +} + +func (me listElementIterator) Value() interface{} { + return me.le.Value +} + +func newLRUItems() *lruItems { + return &lruItems{b.TreeNew(func(_a, _b interface{}) int { + a := _a.(ItemInfo) + b := _b.(ItemInfo) + if a.Accessed != b.Accessed { + if a.Accessed.Before(b.Accessed) { + return -1 + } else { + return 1 + } + } + if a.Path == b.Path { + return 0 + } + if a.Path < b.Path { + return -1 + } + return 1 + })} +} + +type lruItems struct { + tree *b.Tree +} + +type bEnumeratorIterator struct { + e *b.Enumerator + v ItemInfo +} + +func (me bEnumeratorIterator) Next() Iterator { + _, v, err := me.e.Next() + if err == io.EOF { + return nil + } + return bEnumeratorIterator{me.e, v.(ItemInfo)} +} + +func (me bEnumeratorIterator) Value() interface{} { + return me.v +} + +func (me *lruItems) Front() Iterator { + e, _ := me.tree.SeekFirst() + if e == nil { + return nil + } + return bEnumeratorIterator{ + e: e, + }.Next() +} + +func (me *lruItems) LRU() ItemInfo { + _, v := me.tree.First() + return v.(ItemInfo) +} + +func (me *lruItems) Insert(ii ItemInfo) { + me.tree.Set(ii, ii) +} + +func (me *lruItems) Remove(ii ItemInfo) bool { + return me.tree.Delete(ii) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems_test.go new file mode 100644 index 0000000..557739a --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems_test.go @@ -0,0 +1,22 @@ +package filecache + +import ( + "math/rand" + "testing" + "time" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/bradfitz/iter" +) + +func BenchmarkInsert(b *testing.B) { + for _ = range iter.N(b.N) { + li := newLRUItems() + for _ = range iter.N(10000) { + r := rand.Int63() + t := time.Unix(r/1e9, r%1e9) + li.Insert(ItemInfo{ + Accessed: t, + }) + } + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange.go new file mode 100644 index 0000000..acf1002 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange.go @@ -0,0 +1,38 @@ +package missinggo + +import ( + "regexp" + "strconv" +) + +type HTTPBytesContentRange struct { + First, Last, Length int64 +} + +var bytesContentRangeRegexp = regexp.MustCompile(`bytes[ =](\d+)-(\d+)/(\d+|\*)`) + +func ParseHTTPBytesContentRange(s string) (ret HTTPBytesContentRange, ok bool) { + ss := bytesContentRangeRegexp.FindStringSubmatch(s) + if ss == nil { + return + } + var err error + ret.First, err = strconv.ParseInt(ss[1], 10, 64) + if err != nil { + return + } + ret.Last, err = strconv.ParseInt(ss[2], 10, 64) + if err != nil { + return + } + if ss[3] == "*" { + ret.Length = -1 + } else { + ret.Length, err = strconv.ParseInt(ss[3], 10, 64) + if err != nil { + return + } + } + ok = true + return +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange_test.go new file mode 100644 index 0000000..e76ce60 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange_test.go @@ -0,0 +1,27 @@ +package missinggo + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseHTTPContentRange(t *testing.T) { + for _, _case := range []struct { + h string + cr *HTTPBytesContentRange + }{ + {"", nil}, + {"1-2/*", nil}, + {"bytes=1-2/3", &HTTPBytesContentRange{1, 2, 3}}, + {"bytes=12-34/*", &HTTPBytesContentRange{12, 34, -1}}, + {" bytes=12-34/*", &HTTPBytesContentRange{12, 34, -1}}, + {" bytes 12-34/56", &HTTPBytesContentRange{12, 34, 56}}, + } { + ret, ok := ParseHTTPBytesContentRange(_case.h) + assert.Equal(t, _case.cr != nil, ok) + if _case.cr != nil { + assert.Equal(t, *_case.cr, ret) + } + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpfile/httpfile.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpfile/httpfile.go new file mode 100644 index 0000000..8208f85 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpfile/httpfile.go @@ -0,0 +1,222 @@ +package httpfile + +import ( + "bytes" + "errors" + "fmt" + "io" + "net/http" + "os" + "strconv" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" +) + +type File struct { + off int64 + r io.ReadCloser + rOff int64 + length int64 + url string +} + +func OpenSectionReader(url string, off, n int64) (ret io.ReadCloser, err error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return + } + req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, off+n-1)) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return + } + if resp.StatusCode == http.StatusNotFound { + err = ErrNotFound + resp.Body.Close() + return + } + if resp.StatusCode != http.StatusPartialContent { + err = fmt.Errorf("bad response status: %s", resp.Status) + resp.Body.Close() + return + } + ret = resp.Body + return +} + +func Open(url string) *File { + return &File{ + url: url, + } +} + +func (me *File) prepareReader() (err error) { + if me.r != nil && me.off != me.rOff { + me.r.Close() + me.r = nil + } + if me.r != nil { + return nil + } + req, err := http.NewRequest("GET", me.url, nil) + if err != nil { + return + } + if me.off != 0 { + req.Header.Set("Range", fmt.Sprintf("bytes=%d-", me.off)) + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return + } + switch resp.StatusCode { + case http.StatusPartialContent: + cr, ok := missinggo.ParseHTTPBytesContentRange(resp.Header.Get("Content-Range")) + if !ok || cr.First != me.off { + err = errors.New("bad response") + resp.Body.Close() + return + } + me.length = cr.Length + case http.StatusOK: + if me.off != 0 { + err = errors.New("bad response") + resp.Body.Close() + return + } + if h := resp.Header.Get("Content-Length"); h != "" { + var cl uint64 + cl, err = strconv.ParseUint(h, 10, 64) + if err != nil { + resp.Body.Close() + return + } + me.length = int64(cl) + } + default: + err = errors.New(resp.Status) + resp.Body.Close() + return + } + me.r = resp.Body + me.rOff = me.off + return +} + +func (me *File) Read(b []byte) (n int, err error) { + err = me.prepareReader() + if err != nil { + return + } + n, err = me.r.Read(b) + me.off += int64(n) + me.rOff += int64(n) + return +} + +func instanceLength(r *http.Response) (int64, error) { + switch r.StatusCode { + case http.StatusOK: + if h := r.Header.Get("Content-Length"); h != "" { + return strconv.ParseInt(h, 10, 64) + } else { + return -1, nil + } + case http.StatusPartialContent: + cr, ok := missinggo.ParseHTTPBytesContentRange(r.Header.Get("Content-Range")) + if !ok { + return -1, errors.New("bad 206 response") + } + return cr.Length, nil + default: + return -1, errors.New(r.Status) + } +} + +func (me *File) Seek(offset int64, whence int) (ret int64, err error) { + switch whence { + case os.SEEK_SET: + ret = offset + case os.SEEK_CUR: + ret = me.off + offset + case os.SEEK_END: + if me.length < 0 { + err = errors.New("length unknown") + return + } + ret = me.length + offset + default: + err = fmt.Errorf("unhandled whence: %d", whence) + return + } + me.off = ret + return +} + +func (me *File) Write(b []byte) (n int, err error) { + req, err := http.NewRequest("PATCH", me.url, bytes.NewReader(b)) + if err != nil { + return + } + req.Header.Set("Content-Range", fmt.Sprintf("bytes=%d-", me.off)) + req.ContentLength = int64(len(b)) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return + } + resp.Body.Close() + if resp.StatusCode != http.StatusPartialContent { + err = errors.New(resp.Status) + return + } + n = len(b) + me.off += int64(n) + return +} + +var ( + ErrNotFound = errors.New("not found") +) + +// Returns the length of the resource in bytes. +func GetLength(url string) (ret int64, err error) { + resp, err := http.Head(url) + if err != nil { + return + } + resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + err = ErrNotFound + return + } + return instanceLength(resp) +} + +func (me *File) Close() error { + me.url = "" + if me.r != nil { + me.r.Close() + me.r = nil + } + return nil +} + +func Delete(urlStr string) (err error) { + req, err := http.NewRequest("DELETE", urlStr, nil) + if err != nil { + return + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return + } + resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + err = ErrNotFound + return + } + if resp.StatusCode != 200 { + err = fmt.Errorf("response: %s", resp.Status) + } + return +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpgzip.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpgzip.go new file mode 100644 index 0000000..503eb0f --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpgzip.go @@ -0,0 +1,47 @@ +package missinggo + +import ( + "compress/gzip" + "io" + "net/http" + "strings" +) + +type gzipResponseWriter struct { + io.Writer + http.ResponseWriter + haveWritten bool +} + +func (w *gzipResponseWriter) Write(b []byte) (int, error) { + if w.haveWritten { + goto write + } + w.haveWritten = true + if w.Header().Get("Content-Type") != "" { + goto write + } + if type_ := http.DetectContentType(b); type_ != "application/octet-stream" { + w.Header().Set("Content-Type", type_) + } +write: + return w.Writer.Write(b) +} + +// Gzips response body if the request says it'll allow it. +func GzipHTTPHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") || w.Header().Get("Content-Encoding") != "" || w.Header().Get("Vary") != "" { + h.ServeHTTP(w, r) + return + } + w.Header().Set("Content-Encoding", "gzip") + w.Header().Set("Vary", "Accept-Encoding") + gz := gzip.NewWriter(w) + defer gz.Close() + h.ServeHTTP(&gzipResponseWriter{ + Writer: gz, + ResponseWriter: w, + }, r) + }) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpresponsestatus.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpresponsestatus.go new file mode 100644 index 0000000..a42e098 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/httpresponsestatus.go @@ -0,0 +1,60 @@ +package missinggo + +import ( + "bufio" + "io" + "net" + "net/http" +) + +// A http.ResponseWriter that tracks the status of the response. The status +// code, and number of bytes written for example. +type StatusResponseWriter struct { + RW http.ResponseWriter + Code int + BytesWritten int64 +} + +var _ http.ResponseWriter = &StatusResponseWriter{} + +func (me *StatusResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + return me.RW.(http.Hijacker).Hijack() +} + +func (me *StatusResponseWriter) CloseNotify() <-chan bool { + return me.RW.(http.CloseNotifier).CloseNotify() +} + +func (me *StatusResponseWriter) Flush() { + me.RW.(http.Flusher).Flush() +} + +func (me *StatusResponseWriter) Header() http.Header { + return me.RW.Header() +} + +func (me *StatusResponseWriter) Write(b []byte) (n int, err error) { + if me.Code == 0 { + me.Code = 200 + } + n, err = me.RW.Write(b) + me.BytesWritten += int64(n) + return +} + +func (me *StatusResponseWriter) WriteHeader(code int) { + me.RW.WriteHeader(code) + me.Code = code +} + +type ReaderFromStatusResponseWriter struct { + StatusResponseWriter + io.ReaderFrom +} + +func NewReaderFromStatusResponseWriter(w http.ResponseWriter) *ReaderFromStatusResponseWriter { + return &ReaderFromStatusResponseWriter{ + StatusResponseWriter{RW: w}, + w.(io.ReaderFrom), + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby.go new file mode 100644 index 0000000..17761fa --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby.go @@ -0,0 +1,81 @@ +package itertools + +type groupBy struct { + curKey interface{} + curKeyOk bool + curValue interface{} + keyFunc func(interface{}) interface{} + input Iterator + groupKey interface{} + groupKeyOk bool +} + +type Group interface { + Iterator + Key() interface{} +} + +type group struct { + gb *groupBy + key interface{} + first bool +} + +func (me *group) Next() (ok bool) { + if me.first { + me.first = false + return true + } + me.gb.advance() + if !me.gb.curKeyOk || me.gb.curKey != me.key { + return + } + ok = true + return +} + +func (me group) Value() (ret interface{}) { + ret = me.gb.curValue + return +} + +func (me group) Key() interface{} { + return me.key +} + +func (me *groupBy) advance() { + me.curKeyOk = me.input.Next() + if me.curKeyOk { + me.curValue = me.input.Value() + me.curKey = me.keyFunc(me.curValue) + } +} + +func (me *groupBy) Next() (ok bool) { + for me.curKey == me.groupKey { + ok = me.input.Next() + if !ok { + return + } + me.curValue = me.input.Value() + me.curKey = me.keyFunc(me.curValue) + me.curKeyOk = true + } + me.groupKey = me.curKey + me.groupKeyOk = true + return true +} + +func (me *groupBy) Value() (ret interface{}) { + return &group{me, me.groupKey, true} +} + +func GroupBy(input Iterator, keyFunc func(interface{}) interface{}) Iterator { + if keyFunc == nil { + keyFunc = func(a interface{}) interface{} { return a } + } + return &groupBy{ + input: input, + keyFunc: keyFunc, + } +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby_test.go new file mode 100644 index 0000000..c194e73 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby_test.go @@ -0,0 +1,31 @@ +package itertools + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGroupByKey(t *testing.T) { + var ks []byte + gb := GroupBy(StringIterator("AAAABBBCCDAABBB"), nil) + for gb.Next() { + ks = append(ks, gb.Value().(Group).Key().(byte)) + } + t.Log(ks) + require.EqualValues(t, "ABCDAB", ks) +} + +func TestGroupByList(t *testing.T) { + var gs []string + gb := GroupBy(StringIterator("AAAABBBCCD"), nil) + for gb.Next() { + i := gb.Value().(Iterator) + var g string + for i.Next() { + g += string(i.Value().(byte)) + } + gs = append(gs, g) + } + t.Log(gs) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator.go new file mode 100644 index 0000000..2cec8c1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator.go @@ -0,0 +1,41 @@ +package itertools + +import "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" + +type Iterator interface { + Next() bool + Value() interface{} +} + +type sliceIterator struct { + slice []interface{} + value interface{} + ok bool +} + +func (me *sliceIterator) Next() bool { + if len(me.slice) == 0 { + return false + } + me.value = me.slice[0] + me.slice = me.slice[1:] + me.ok = true + return true +} + +func (me *sliceIterator) Value() interface{} { + if !me.ok { + panic("no value; call Next") + } + return me.value +} + +func SliceIterator(a []interface{}) Iterator { + return &sliceIterator{ + slice: a, + } +} + +func StringIterator(a string) Iterator { + return SliceIterator(missinggo.ConvertToSliceOfEmptyInterface(a)) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator_test.go new file mode 100644 index 0000000..61f0be8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator_test.go @@ -0,0 +1,17 @@ +package itertools + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIterator(t *testing.T) { + const s = "AAAABBBCCDAABBB" + si := StringIterator(s) + for i := range s { + require.True(t, si.Next()) + require.Equal(t, s[i], si.Value().(byte)) + } + require.False(t, si.Next()) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/net.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/net.go new file mode 100644 index 0000000..ce406d3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/net.go @@ -0,0 +1,26 @@ +package missinggo + +import ( + "net" +) + +type HostPort struct { + Host string // Just the host, with no port. + Port string // May be empty if no port was given. + Err error // The error returned from net.SplitHostPort. +} + +// Parse a "hostport" string, a concept that floats around the stdlib a lot +// and is painful to work with. If no port is present, what's usually present +// is just the host. +func ParseHostPort(hostPort string) (ret HostPort) { + ret.Host, ret.Port, ret.Err = net.SplitHostPort(hostPort) + if ret.Err != nil { + ret.Host = hostPort + } + return +} + +func (me *HostPort) Join() string { + return net.JoinHostPort(me.Host, me.Port) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/path.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/path.go new file mode 100644 index 0000000..6e3428c --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/path.go @@ -0,0 +1,20 @@ +package missinggo + +import ( + "os" + "path" +) + +// Splits the pathname p into Root and Ext, such that Root+Ext==p. +func PathSplitExt(p string) (ret struct { + Root, Ext string +}) { + ret.Ext = path.Ext(p) + ret.Root = p[:len(p)-len(ret.Ext)] + return +} + +func FilePathExists(p string) bool { + _, err := os.Stat(p) + return err == nil +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/path_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/path_test.go new file mode 100644 index 0000000..a38aa2e --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/path_test.go @@ -0,0 +1,17 @@ +package missinggo + +import ( + "fmt" +) + +func ExamplePathSplitExt() { + fmt.Printf("%q\n", PathSplitExt(".cshrc")) + fmt.Printf("%q\n", PathSplitExt("dir/a.ext")) + fmt.Printf("%q\n", PathSplitExt("dir/.rc")) + fmt.Printf("%q\n", PathSplitExt("home/.secret/file")) + // Output: + // {"" ".cshrc"} + // {"dir/a" ".ext"} + // {"dir/" ".rc"} + // {"home/.secret/file" ""} +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/mutex.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/mutex.go new file mode 100644 index 0000000..ff9f1a7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/mutex.go @@ -0,0 +1,48 @@ +package perf + +import ( + "sync" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" +) + +type TimedLocker struct { + L sync.Locker + Desc string +} + +func (me *TimedLocker) Lock() { + tr := NewTimer() + me.L.Lock() + tr.Stop(me.Desc) +} + +func (me *TimedLocker) Unlock() { + me.L.Unlock() +} + +type TimedRWLocker struct { + RWL missinggo.RWLocker + WriteDesc string + ReadDesc string +} + +func (me *TimedRWLocker) Lock() { + tr := NewTimer() + me.RWL.Lock() + tr.Stop(me.WriteDesc) +} + +func (me *TimedRWLocker) Unlock() { + me.RWL.Unlock() +} + +func (me *TimedRWLocker) RLock() { + tr := NewTimer() + me.RWL.RLock() + tr.Stop(me.ReadDesc) +} + +func (me *TimedRWLocker) RUnlock() { + me.RWL.RUnlock() +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf.go new file mode 100644 index 0000000..3266e02 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf.go @@ -0,0 +1,92 @@ +package perf + +import ( + "bytes" + "expvar" + "fmt" + "strconv" + "sync" + "time" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" +) + +var ( + em = missinggo.NewExpvarIndentMap("perfBuckets") + mu sync.RWMutex +) + +type Timer struct { + started time.Time +} + +func NewTimer() Timer { + return Timer{time.Now()} +} + +func bucketExponent(d time.Duration) int { + e := -9 + for d != 0 { + d /= 10 + e++ + } + return e +} + +type buckets struct { + mu sync.Mutex + buckets []int64 +} + +func (me *buckets) Add(t time.Duration) { + e := bucketExponent(t) + me.mu.Lock() + for e+9 >= len(me.buckets) { + me.buckets = append(me.buckets, 0) + } + me.buckets[e+9]++ + me.mu.Unlock() +} + +func (me *buckets) String() string { + var b bytes.Buffer + fmt.Fprintf(&b, "{") + first := true + me.mu.Lock() + for i, count := range me.buckets { + if first { + if count == 0 { + continue + } + first = false + } else { + fmt.Fprintf(&b, ", ") + } + key := strconv.Itoa(i - 9) + fmt.Fprintf(&b, "%q: %d", key, count) + } + me.mu.Unlock() + fmt.Fprintf(&b, "}") + return b.String() +} + +var _ expvar.Var = &buckets{} + +func (t *Timer) Stop(desc string) time.Duration { + d := time.Since(t.started) + mu.RLock() + _m := em.Get(desc) + mu.RUnlock() + if _m == nil { + mu.Lock() + _m = em.Get(desc) + if _m == nil { + _m = new(buckets) + em.Set(desc, _m) + } + mu.Unlock() + } + m := _m.(*buckets) + m.Add(d) + return d +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf_test.go new file mode 100644 index 0000000..dc7397e --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf_test.go @@ -0,0 +1,52 @@ +package perf + +import ( + "fmt" + "strconv" + "testing" + "time" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/bradfitz/iter" + "github.com/stretchr/testify/assert" +) + +func TestTimer(t *testing.T) { + tr := NewTimer() + tr.Stop("hiyo") + tr.Stop("hiyo") + t.Log(em.Get("hiyo").(*buckets)) +} + +func BenchmarkStopWarm(b *testing.B) { + tr := NewTimer() + for _ = range iter.N(b.N) { + tr.Stop("a") + } +} + +func BenchmarkStopCold(b *testing.B) { + tr := NewTimer() + for i := range iter.N(b.N) { + tr.Stop(strconv.FormatInt(int64(i), 10)) + } +} + +func TestExponent(t *testing.T) { + for _, c := range []struct { + e int + d time.Duration + }{ + {-1, 10 * time.Millisecond}, + {-2, 5 * time.Millisecond}, + {-2, time.Millisecond}, + {-3, 500 * time.Microsecond}, + {-3, 100 * time.Microsecond}, + } { + tr := NewTimer() + time.Sleep(c.d) + assert.Equal(t, c.e, bucketExponent(tr.Stop(fmt.Sprintf("%d", c.e))), "%s", c.d) + } + assert.Equal(t, `{"-1": 1}`, em.Get("-1").String()) + assert.Equal(t, `{"-2": 2}`, em.Get("-2").String()) + assert.Equal(t, `{"-3": 2}`, em.Get("-3").String()) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub.go new file mode 100644 index 0000000..f923385 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub.go @@ -0,0 +1,92 @@ +package pubsub + +import ( + "sync" +) + +type PubSub struct { + mu sync.Mutex + next chan item + closed bool +} + +type item struct { + value interface{} + next chan item +} + +type Subscription struct { + next chan item + Values chan interface{} + mu sync.Mutex + closed chan struct{} +} + +func NewPubSub() (ret *PubSub) { + return &PubSub{ + next: make(chan item, 1), + } +} + +func (me *PubSub) Publish(v interface{}) { + next := make(chan item, 1) + i := item{v, next} + me.mu.Lock() + me.next <- i + me.next = next + me.mu.Unlock() +} + +func (me *Subscription) Close() { + me.mu.Lock() + defer me.mu.Unlock() + select { + case <-me.closed: + default: + close(me.closed) + } +} + +func (me *Subscription) runner() { + defer close(me.Values) + for { + select { + case i, ok := <-me.next: + if !ok { + me.Close() + return + } + me.next <- i + me.next = i.next + select { + case me.Values <- i.value: + case <-me.closed: + return + } + case <-me.closed: + return + } + } +} + +func (me *PubSub) Subscribe() (ret *Subscription) { + ret = &Subscription{ + closed: make(chan struct{}), + Values: make(chan interface{}), + } + me.mu.Lock() + ret.next = me.next + me.mu.Unlock() + go ret.runner() + return +} + +func (me *PubSub) Close() { + me.mu.Lock() + defer me.mu.Unlock() + if me.closed { + return + } + close(me.next) + me.closed = true +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub_test.go new file mode 100644 index 0000000..c761669 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub_test.go @@ -0,0 +1,74 @@ +package pubsub + +import ( + "sync" + "testing" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/bradfitz/iter" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDoubleClose(t *testing.T) { + ps := NewPubSub() + ps.Close() + ps.Close() +} + +func testBroadcast(t testing.TB, subs, vals int) { + ps := NewPubSub() + var wg sync.WaitGroup + for _ = range iter.N(subs) { + wg.Add(1) + s := ps.Subscribe() + go func() { + defer wg.Done() + var e int + for i := range s.Values { + assert.Equal(t, e, i.(int)) + e++ + } + assert.Equal(t, vals, e) + }() + } + for i := range iter.N(vals) { + ps.Publish(i) + } + ps.Close() + wg.Wait() +} + +func TestBroadcast(t *testing.T) { + testBroadcast(t, 100, 10) +} + +func BenchmarkBroadcast(b *testing.B) { + for _ = range iter.N(b.N) { + testBroadcast(b, 10, 1000) + } +} + +func TestCloseSubscription(t *testing.T) { + ps := NewPubSub() + ps.Publish(1) + s := ps.Subscribe() + select { + case <-s.Values: + t.FailNow() + default: + } + ps.Publish(2) + s2 := ps.Subscribe() + ps.Publish(3) + require.Equal(t, 2, <-s.Values) + require.EqualValues(t, 3, <-s.Values) + s.Close() + _, ok := <-s.Values + require.False(t, ok) + ps.Publish(4) + ps.Close() + require.Equal(t, 3, <-s2.Values) + require.Equal(t, 4, <-s2.Values) + require.Nil(t, <-s2.Values) + s2.Close() +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/rle.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/rle.go new file mode 100644 index 0000000..63d4997 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/rle.go @@ -0,0 +1,46 @@ +package missinggo + +// A RunLengthEncoder counts successive duplicate elements and emits the +// element and the run length when the element changes or the encoder is +// flushed. +type RunLengthEncoder interface { + // Add a series of identical elements to the stream. + Append(element interface{}, count uint64) + // Emit the current element and its count if non-zero without waiting for + // the element to change. + Flush() +} + +type runLengthEncoder struct { + eachRun func(element interface{}, count uint64) + element interface{} + count uint64 +} + +// Creates a new RunLengthEncoder. eachRun is called when an element and its +// count is emitted, per the RunLengthEncoder interface. +func NewRunLengthEncoder(eachRun func(element interface{}, count uint64)) RunLengthEncoder { + return &runLengthEncoder{ + eachRun: eachRun, + } +} + +func (me *runLengthEncoder) Append(element interface{}, count uint64) { + if element == me.element { + me.count += count + return + } + if me.count != 0 { + me.eachRun(me.element, me.count) + } + me.count = count + me.element = element +} + +func (me *runLengthEncoder) Flush() { + if me.count == 0 { + return + } + me.eachRun(me.element, me.count) + me.count = 0 +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/rle_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/rle_test.go new file mode 100644 index 0000000..d6f2051 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/rle_test.go @@ -0,0 +1,20 @@ +package missinggo_test + +import ( + "fmt" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" +) + +func ExampleNewRunLengthEncoder() { + var s string + rle := missinggo.NewRunLengthEncoder(func(e interface{}, count uint64) { + s += fmt.Sprintf("%d%c", count, e) + }) + for _, e := range "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW" { + rle.Append(e, 1) + } + rle.Flush() + fmt.Println(s) + // Output: 12W1B12W3B24W1B14W +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/singleflight.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/singleflight.go new file mode 100644 index 0000000..7a6e299 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/singleflight.go @@ -0,0 +1,39 @@ +package missinggo + +import "sync" + +type ongoing struct { + do sync.Mutex + users int +} + +type SingleFlight struct { + mu sync.Mutex + ongoing map[string]*ongoing +} + +func (me *SingleFlight) Lock(id string) { + me.mu.Lock() + on, ok := me.ongoing[id] + if !ok { + on = new(ongoing) + if me.ongoing == nil { + me.ongoing = make(map[string]*ongoing) + } + me.ongoing[id] = on + } + on.users++ + me.mu.Unlock() + on.do.Lock() +} + +func (me *SingleFlight) Unlock(id string) { + me.mu.Lock() + on := me.ongoing[id] + on.do.Unlock() + on.users-- + if on.users == 0 { + delete(me.ongoing, id) + } + me.mu.Unlock() +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/sync.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/sync.go new file mode 100644 index 0000000..fc099bd --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/sync.go @@ -0,0 +1,11 @@ +package missinggo + +import ( + "sync" +) + +type RWLocker interface { + sync.Locker + RLock() + RUnlock() +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/url.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/url.go new file mode 100644 index 0000000..5f01eee --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/url.go @@ -0,0 +1,30 @@ +package missinggo + +import ( + "net/http" + "net/url" +) + +// Deep copies a URL. +func CopyURL(u *url.URL) (ret *url.URL) { + ret = new(url.URL) + *ret = *u + if u.User != nil { + ret.User = new(url.Userinfo) + *ret.User = *u.User + } + return +} + +// Reconstructs the URL that would have produced the given Request. +// Request.URLs are not fully populated in http.Server handlers. +func RequestedURL(r *http.Request) (ret *url.URL) { + ret = CopyURL(r.URL) + ret.Host = r.Host + if r.TLS != nil { + ret.Scheme = "https" + } else { + ret.Scheme = "http" + } + return +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf.go new file mode 100644 index 0000000..5552c61 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf.go @@ -0,0 +1,51 @@ +package missinggo + +import ( + "log" + "runtime" + "sync" + "sync/atomic" +) + +const debug = false + +// A Wolf represents some event that becomes less and less interesting as it +// occurs. Call CryHeard to see if we should pay attention this time. +type Wolf struct { + cries uint64 +} + +// Returns true less and less often. Convenient for exponentially decreasing +// the amount of noise due to errors. +func (me *Wolf) CryHeard() bool { + n := atomic.AddUint64(&me.cries, 1) + return n&(n-1) == 0 +} + +var ( + mu sync.Mutex + wolves map[uintptr]*Wolf +) + +// Calls CryHeard() on a Wolf that is unique to the callers program counter. +// i.e. every CryHeard() expression has its own Wolf. +func CryHeard() bool { + pc, file, line, ok := runtime.Caller(1) + if debug { + log.Println(pc, file, line, ok) + } + if !ok { + return true + } + mu.Lock() + if wolves == nil { + wolves = make(map[uintptr]*Wolf) + } + w, ok := wolves[pc] + if !ok { + w = new(Wolf) + wolves[pc] = w + } + mu.Unlock() + return w.CryHeard() +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf_test.go b/Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf_test.go new file mode 100644 index 0000000..3ee59ca --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf_test.go @@ -0,0 +1,30 @@ +package missinggo + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func cryHeard() bool { + return CryHeard() +} + +func TestCrySameLocation(t *testing.T) { + require.True(t, cryHeard()) + require.True(t, cryHeard()) + require.False(t, cryHeard()) + require.True(t, cryHeard()) + require.False(t, cryHeard()) + require.False(t, cryHeard()) + require.False(t, cryHeard()) + require.True(t, cryHeard()) +} + +func TestCryDifferentLocations(t *testing.T) { + require.True(t, CryHeard()) + require.True(t, CryHeard()) + require.True(t, CryHeard()) + require.True(t, CryHeard()) + require.True(t, CryHeard()) +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/utp/.utp.go.swp b/Godeps/_workspace/src/github.com/anacrolix/utp/.utp.go.swp new file mode 100644 index 0000000000000000000000000000000000000000..612b7cc303f3d52754d75ea8f2a1257aa809d7e1 GIT binary patch literal 86016 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 z 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' +# Send from this uTP implementation to another client. +other_ucat -l | rate | md5 & +# sleep 1 +godo ${GODOFLAGS-} ./cmd/ucat localhost 4000 < "$1" +wait + +echo 'other_ucat->utp' +# Send from the other implementation, to this one. +GO_UTP_LOGGING=0 GOPPROF= godo ${GODOFLAGS-} ./cmd/ucat -l -p 4000 | rate | md5 & +# Never receive from h2so5's ucat without a small sleep first. Don't know why. +# sleep 1 +other_ucat < "$1" +wait + +echo 'libutp->libutp' +libutp-ucat -l -p 4000 | rate | md5 & +libutp-ucat localhost 4000 < "$1" +wait + +echo 'utp->utp' +godo ./cmd/ucat -l -p 4000 | rate | md5 & +godo ./cmd/ucat localhost 4000 < "$1" +wait + +# Now check the hashes match (yes you). diff --git a/Godeps/_workspace/src/github.com/anacrolix/utp/utp.go b/Godeps/_workspace/src/github.com/anacrolix/utp/utp.go new file mode 100644 index 0000000..e0c9972 --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/utp/utp.go @@ -0,0 +1,1461 @@ +// Package utp implements uTP, the micro transport protocol as used with +// Bittorrent. It opts for simplicity and reliability over strict adherence to +// the (poor) spec. It allows using the underlying OS-level transport despite +// dispatching uTP on top to allow for example, shared socket use with DHT. +// Additionally, multiple uTP connections can share the same OS socket, to +// truly realize uTP's claim to be light on system and network switching +// resources. +// +// Socket is a wrapper of net.UDPConn, and performs dispatching of uTP packets +// to attached uTP Conns. Dial and Accept is done via Socket. Conn implements +// net.Conn over uTP, via aforementioned Socket. +package utp + +import ( + "encoding/binary" + "errors" + "expvar" + "fmt" + "io" + "log" + "math/rand" + "net" + "os" + "strconv" + "sync" + "time" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/jitter" + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" +) + +const ( + // Maximum received SYNs that haven't been accepted. If more SYNs are + // received, a pseudo randomly selected SYN is replied to with a reset to + // make room. + backlog = 50 + + // IPv6 min MTU is 1280, -40 for IPv6 header, and ~8 for fragment header? + minMTU = 1232 + recvWindow = 0x8000 // 32KiB + // uTP header of 20, +2 for the next extension, and 8 bytes of selective + // ACK. + maxHeaderSize = 30 + maxPayloadSize = minMTU - maxHeaderSize + maxRecvSize = 0x2000 + + // Maximum out-of-order packets to buffer. + maxUnackedInbound = 64 + + // If an send isn't acknowledged after this period, its connection is + // destroyed. There are resends during this period. + sendTimeout = 15 * time.Second +) + +var ( + ackSkippedResends = expvar.NewInt("utpAckSkippedResends") + // Inbound packets processed by a Conn. + deliveriesProcessed = expvar.NewInt("utpDeliveriesProcessed") + sentStatePackets = expvar.NewInt("utpSentStatePackets") + unusedReads = expvar.NewInt("utpUnusedReads") + sendBufferPool = sync.Pool{ + New: func() interface{} { return make([]byte, minMTU) }, + } +) + +type deadlineCallback struct { + deadline time.Time + timer *time.Timer + callback func() + inited bool +} + +func (me *deadlineCallback) deadlineExceeded() bool { + return !me.deadline.IsZero() && !time.Now().Before(me.deadline) +} + +func (me *deadlineCallback) updateTimer() { + if me.timer != nil { + me.timer.Stop() + } + if me.deadline.IsZero() { + return + } + if me.callback == nil { + panic("deadline callback is nil") + } + me.timer = time.AfterFunc(me.deadline.Sub(time.Now()), me.callback) +} + +func (me *deadlineCallback) setDeadline(t time.Time) { + me.deadline = t + me.updateTimer() +} + +func (me *deadlineCallback) setCallback(f func()) { + me.callback = f + me.updateTimer() +} + +type connDeadlines struct { + // mu sync.Mutex + read, write deadlineCallback +} + +func (c *connDeadlines) SetDeadline(t time.Time) error { + c.read.setDeadline(t) + c.write.setDeadline(t) + return nil +} + +func (c *connDeadlines) SetReadDeadline(t time.Time) error { + c.read.setDeadline(t) + return nil +} + +func (c *connDeadlines) SetWriteDeadline(t time.Time) error { + c.write.setDeadline(t) + return nil +} + +// Strongly-type guarantee of resolved network address. +type resolvedAddrStr string + +// Uniquely identifies any uTP connection on top of the underlying packet +// stream. +type connKey struct { + remoteAddr resolvedAddrStr + connID uint16 +} + +// A Socket wraps a net.PacketConn, diverting uTP packets to its child uTP +// Conns. +type Socket struct { + mu sync.RWMutex + event sync.Cond + pc net.PacketConn + conns map[connKey]*Conn + backlog map[syn]struct{} + reads chan read + closing chan struct{} + + unusedReads chan read + connDeadlines + // If a read error occurs on the underlying net.PacketConn, it is put + // here. This is because reading is done in its own goroutine to dispatch + // to uTP Conns. + ReadErr error +} + +type read struct { + data []byte + from net.Addr +} + +type syn struct { + seq_nr, conn_id uint16 + addr string +} + +const ( + extensionTypeSelectiveAck = 1 +) + +type extensionField struct { + Type byte + Bytes []byte +} + +type header struct { + Type st + Version int + ConnID uint16 + Timestamp uint32 + TimestampDiff uint32 + WndSize uint32 + SeqNr uint16 + AckNr uint16 + Extensions []extensionField +} + +var ( + mu sync.RWMutex + logLevel = 0 + artificialPacketDropChance = 0.0 +) + +func init() { + logLevel, _ = strconv.Atoi(os.Getenv("GO_UTP_LOGGING")) + fmt.Sscanf(os.Getenv("GO_UTP_PACKET_DROP"), "%f", &artificialPacketDropChance) +} + +var ( + errClosed = errors.New("closed") + errNotImplemented = errors.New("not implemented") + errTimeout net.Error = timeoutError{"i/o timeout"} + errAckTimeout = timeoutError{"timed out waiting for ack"} +) + +type timeoutError struct { + msg string +} + +func (me timeoutError) Timeout() bool { return true } +func (me timeoutError) Error() string { return me.msg } +func (me timeoutError) Temporary() bool { return false } + +func unmarshalExtensions(_type byte, b []byte) (n int, ef []extensionField, err error) { + for _type != 0 { + if _type != extensionTypeSelectiveAck { + // An extension type that is not known to us. Generally we're + // unmarshalling an packet that isn't actually uTP but we don't + // yet know for sure until we try to deliver it. + + // logonce.Stderr.Printf("utp extension %d", _type) + } + if len(b) < 2 || len(b) < int(b[1])+2 { + err = fmt.Errorf("buffer ends prematurely: %x", b) + return + } + ef = append(ef, extensionField{ + Type: _type, + Bytes: append([]byte{}, b[2:int(b[1])+2]...), + }) + _type = b[0] + n += 2 + int(b[1]) + b = b[2+int(b[1]):] + } + return +} + +var errInvalidHeader = errors.New("invalid header") + +func (h *header) Unmarshal(b []byte) (n int, err error) { + h.Type = st(b[0] >> 4) + h.Version = int(b[0] & 0xf) + if h.Type > stMax || h.Version != 1 { + err = errInvalidHeader + return + } + n, h.Extensions, err = unmarshalExtensions(b[1], b[20:]) + if err != nil { + return + } + h.ConnID = binary.BigEndian.Uint16(b[2:4]) + h.Timestamp = binary.BigEndian.Uint32(b[4:8]) + h.TimestampDiff = binary.BigEndian.Uint32(b[8:12]) + h.WndSize = binary.BigEndian.Uint32(b[12:16]) + h.SeqNr = binary.BigEndian.Uint16(b[16:18]) + h.AckNr = binary.BigEndian.Uint16(b[18:20]) + n += 20 + return +} + +func (h *header) Marshal() (ret []byte) { + hLen := 20 + func() (ret int) { + for _, ext := range h.Extensions { + ret += 2 + len(ext.Bytes) + } + return + }() + ret = sendBufferPool.Get().([]byte)[:hLen:minMTU] + // ret = make([]byte, hLen, minMTU) + p := ret // Used for manipulating ret. + p[0] = byte(h.Type<<4 | 1) + binary.BigEndian.PutUint16(p[2:4], h.ConnID) + binary.BigEndian.PutUint32(p[4:8], h.Timestamp) + binary.BigEndian.PutUint32(p[8:12], h.TimestampDiff) + binary.BigEndian.PutUint32(p[12:16], h.WndSize) + binary.BigEndian.PutUint16(p[16:18], h.SeqNr) + binary.BigEndian.PutUint16(p[18:20], h.AckNr) + // Pointer to the last type field so the next extension can set it. + _type := &p[1] + // We're done with the basic header. + p = p[20:] + for _, ext := range h.Extensions { + *_type = ext.Type + // The next extension's type will go here. + _type = &p[0] + p[1] = uint8(len(ext.Bytes)) + if int(p[1]) != copy(p[2:], ext.Bytes) { + panic("unexpected extension length") + } + p = p[2+len(ext.Bytes):] + } + if len(p) != 0 { + panic("header length changed") + } + return +} + +var ( + _ net.Listener = &Socket{} + _ net.PacketConn = &Socket{} +) + +const ( + csInvalid = iota + csSynSent + csConnected + csDestroy +) + +type st int + +func (me st) String() string { + switch me { + case stData: + return "stData" + case stFin: + return "stFin" + case stState: + return "stState" + case stReset: + return "stReset" + case stSyn: + return "stSyn" + default: + panic(fmt.Sprintf("%d", me)) + } +} + +const ( + stData st = 0 + stFin = 1 + stState = 2 + stReset = 3 + stSyn = 4 + + // Used for validating packet headers. + stMax = stSyn +) + +// Conn is a uTP stream and implements net.Conn. It owned by a Socket, which +// handles dispatching packets to and from Conns. +type Conn struct { + mu sync.Mutex + event sync.Cond + + recv_id, send_id uint16 + seq_nr, ack_nr uint16 + lastAck uint16 + lastTimeDiff uint32 + peerWndSize uint32 + + readBuf []byte + + socket *Socket + remoteAddr net.Addr + // The uTP timestamp. + startTimestamp uint32 + // When the conn was allocated. + created time.Time + // Callback to unregister Conn from a parent Socket. Should be called when + // no more packets will be handled. + detach func() + + cs int + gotFin bool + sentFin bool + err error + + unackedSends []*send + // Inbound payloads, the first is ack_nr+1. + inbound []recv + packetsIn chan packet + connDeadlines + latencies []time.Duration + pendingSendState bool + destroyed chan struct{} +} + +type send struct { + acked chan struct{} // Closed with Conn lock. + payloadSize uint32 + started time.Time + // This send was skipped in a selective ack. + resend func() + timedOut func() + conn *Conn + + mu sync.Mutex + acksSkipped int + resendTimer *time.Timer + numResends int +} + +func (s *send) Ack() (latency time.Duration) { + s.mu.Lock() + defer s.mu.Unlock() + s.resendTimer.Stop() + select { + case <-s.acked: + return + default: + close(s.acked) + } + latency = time.Since(s.started) + return +} + +type recv struct { + seen bool + data []byte + Type st +} + +var ( + _ net.Conn = &Conn{} +) + +func (c *Conn) age() time.Duration { + return time.Since(c.created) +} + +func (c *Conn) timestamp() uint32 { + return nowTimestamp() - c.startTimestamp +} + +func (c *Conn) connected() bool { + return c.cs == csConnected +} + +// addr is used to create a listening UDP conn which becomes the underlying +// net.PacketConn for the Socket. +func NewSocket(network, addr string) (s *Socket, err error) { + s = &Socket{ + backlog: make(map[syn]struct{}, backlog), + reads: make(chan read, 100), + closing: make(chan struct{}), + + unusedReads: make(chan read, 100), + } + s.event.L = &s.mu + s.pc, err = net.ListenPacket(network, addr) + if err != nil { + return + } + go s.reader() + go s.dispatcher() + return +} + +func packetDebugString(h *header, payload []byte) string { + return fmt.Sprintf("%s->%d: %q", h.Type, h.ConnID, payload) +} + +func (s *Socket) reader() { + defer close(s.reads) + var b [maxRecvSize]byte + for { + if s.pc == nil { + break + } + n, addr, err := s.pc.ReadFrom(b[:]) + if err != nil { + select { + case <-s.closing: + default: + s.ReadErr = err + } + return + } + var nilB []byte + s.reads <- read{append(nilB, b[:n:n]...), addr} + } +} + +func (s *Socket) unusedRead(read read) { + unusedReads.Add(1) + select { + case s.unusedReads <- read: + default: + // Drop the packet. + } +} + +func stringAddr(s string) net.Addr { + addr, err := net.ResolveUDPAddr("udp", s) + if err != nil { + panic(err) + } + return addr +} + +func (s *Socket) pushBacklog(syn syn) { + if _, ok := s.backlog[syn]; ok { + return + } + for k := range s.backlog { + if len(s.backlog) < backlog { + break + } + delete(s.backlog, k) + // A syn is sent on the remote's recv_id, so this is where we can send + // the reset. + s.reset(stringAddr(k.addr), k.seq_nr, k.conn_id) + } + s.backlog[syn] = struct{}{} + s.event.Broadcast() +} + +func (s *Socket) dispatcher() { + for { + select { + case read, ok := <-s.reads: + if !ok { + return + } + if len(read.data) < 20 { + s.unusedRead(read) + continue + } + s.dispatch(read) + } + } +} + +func (s *Socket) dispatch(read read) { + b := read.data + addr := read.from + var h header + hEnd, err := h.Unmarshal(b) + if logLevel >= 1 { + log.Printf("recvd utp msg: %s", packetDebugString(&h, b[hEnd:])) + } + if err != nil || h.Type > stMax || h.Version != 1 { + s.unusedRead(read) + return + } + s.mu.RLock() + c, ok := s.conns[connKey{resolvedAddrStr(addr.String()), func() (recvID uint16) { + recvID = h.ConnID + // If a SYN is resent, its connection ID field will be one lower + // than we expect. + if h.Type == stSyn { + recvID++ + } + return + }()}] + s.mu.RUnlock() + if ok { + if h.Type == stSyn { + if h.ConnID == c.send_id-2 { + // This is a SYN for connection that cannot exist locally. The + // connection the remote wants to establish here with the proposed + // recv_id, already has an existing connection that was dialled + // *out* from this socket, which is why the send_id is 1 higher, + // rather than 1 lower than the recv_id. + log.Print("resetting conflicting syn") + s.reset(addr, h.SeqNr, h.ConnID) + return + } else if h.ConnID != c.send_id { + panic("bad assumption") + } + } + c.deliver(h, b[hEnd:]) + return + } + if h.Type == stSyn { + if logLevel >= 1 { + log.Printf("adding SYN to backlog") + } + syn := syn{ + seq_nr: h.SeqNr, + conn_id: h.ConnID, + addr: addr.String(), + } + s.mu.Lock() + s.pushBacklog(syn) + s.mu.Unlock() + return + } else if h.Type != stReset { + // This is an unexpected packet. We'll send a reset, but also pass + // it on. + // log.Print("resetting unexpected packet") + // I don't think you can reset on the received packets ConnID if it isn't a SYN, as the send_id will differ in this case. + s.reset(addr, h.SeqNr, h.ConnID) + s.reset(addr, h.SeqNr, h.ConnID-1) + s.reset(addr, h.SeqNr, h.ConnID+1) + } + s.unusedRead(read) +} + +// Send a reset in response to a packet with the given header. +func (s *Socket) reset(addr net.Addr, ackNr, connId uint16) { + go s.writeTo((&header{ + Type: stReset, + Version: 1, + ConnID: connId, + AckNr: ackNr, + }).Marshal(), addr) +} + +// Attempt to connect to a remote uTP listener, creating a Socket just for +// this connection. +func Dial(addr string) (net.Conn, error) { + return DialTimeout(addr, 0) +} + +// Same as Dial with a timeout parameter. +func DialTimeout(addr string, timeout time.Duration) (nc net.Conn, err error) { + s, err := NewSocket("udp", ":0") + if err != nil { + return + } + return s.DialTimeout(addr, timeout) + +} + +// Return a recv_id that should be free. Handling the case where it isn't is +// deferred to a more appropriate function. +func (s *Socket) newConnID(remoteAddr resolvedAddrStr) (id uint16) { + // Rather than use math.Rand, which requires generating all the IDs up + // front and allocating a slice, we do it on the stack, generating the IDs + // only as required. To do this, we use the fact that the array is + // default-initialized. IDs that are 0, are actually their index in the + // array. IDs that are non-zero, are +1 from their intended ID. + var idsBack [0x10000]int + ids := idsBack[:] + for len(ids) != 0 { + // Pick the next ID from the untried ids. + i := rand.Intn(len(ids)) + id = uint16(ids[i]) + // If it's zero, then treat it as though the index i was the ID. + // Otherwise the value we get is the ID+1. + if id == 0 { + id = uint16(i) + } else { + id-- + } + // Check there's no connection using this ID for its recv_id... + _, ok1 := s.conns[connKey{remoteAddr, id}] + // and if we're connecting to our own Socket, that there isn't a Conn + // already receiving on what will correspond to our send_id. Note that + // we just assume that we could be connecting to our own Socket. This + // will halve the available connection IDs to each distinct remote + // address. Presumably that's ~0x8000, down from ~0x10000. + _, ok2 := s.conns[connKey{remoteAddr, id + 1}] + _, ok4 := s.conns[connKey{remoteAddr, id - 1}] + if !ok1 && !ok2 && !ok4 { + return + } + // The set of possible IDs is shrinking. The highest one will be lost, so + // it's moved to the location of the one we just tried. + ids[i] = len(ids) // Conveniently already +1. + // And shrink. + ids = ids[:len(ids)-1] + } + return +} + +func (c *Conn) sendPendingState() { + if !c.pendingSendState { + return + } + c.sendState() +} + +func (s *Socket) newConn(addr net.Addr) (c *Conn) { + c = &Conn{ + socket: s, + remoteAddr: addr, + startTimestamp: nowTimestamp(), + created: time.Now(), + packetsIn: make(chan packet, 100), + destroyed: make(chan struct{}), + } + c.event.L = &c.mu + c.mu.Lock() + c.connDeadlines.read.setCallback(func() { + c.mu.Lock() + c.event.Broadcast() + c.mu.Unlock() + }) + c.connDeadlines.write.setCallback(func() { + c.mu.Lock() + c.event.Broadcast() + c.mu.Unlock() + }) + c.mu.Unlock() + go c.deliveryProcessor() + return +} + +func (s *Socket) Dial(addr string) (net.Conn, error) { + return s.DialTimeout(addr, 0) +} + +func (s *Socket) DialTimeout(addr string, timeout time.Duration) (nc net.Conn, err error) { + netAddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return + } + + s.mu.Lock() + c := s.newConn(netAddr) + c.recv_id = s.newConnID(resolvedAddrStr(netAddr.String())) + c.send_id = c.recv_id + 1 + if logLevel >= 1 { + log.Printf("dial registering addr: %s", netAddr.String()) + } + if !s.registerConn(c.recv_id, resolvedAddrStr(netAddr.String()), c) { + err = errors.New("couldn't register new connection") + log.Println(c.recv_id, netAddr.String()) + for k, c := range s.conns { + log.Println(k, c, c.age()) + } + log.Printf("that's %d connections", len(s.conns)) + } + s.mu.Unlock() + if err != nil { + return + } + + connErr := make(chan error, 1) + go func() { + connErr <- c.connect() + }() + var timeoutCh <-chan time.Time + if timeout != 0 { + timeoutCh = time.After(timeout) + } + select { + case err = <-connErr: + case <-timeoutCh: + c.Close() + err = errTimeout + } + if err == nil { + nc = c + } + return +} + +func (c *Conn) wndSize() uint32 { + if len(c.inbound) > maxUnackedInbound/2 { + return 0 + } + var buffered int + for _, r := range c.inbound { + buffered += len(r.data) + } + buffered += len(c.readBuf) + if buffered >= recvWindow { + return 0 + } + return recvWindow - uint32(buffered) +} + +func nowTimestamp() uint32 { + return uint32(time.Now().UnixNano() / int64(time.Microsecond)) +} + +// Send the given payload with an up to date header. +func (c *Conn) send(_type st, connID uint16, payload []byte, seqNr uint16) (err error) { + // Always selectively ack the first 64 packets. Don't bother with rest for + // now. + selAck := selectiveAckBitmask(make([]byte, 8)) + for i := 1; i < 65; i++ { + if len(c.inbound) <= i { + break + } + if c.inbound[i].seen { + selAck.SetBit(i - 1) + } + } + h := header{ + Type: _type, + Version: 1, + ConnID: connID, + SeqNr: seqNr, + AckNr: c.ack_nr, + WndSize: c.wndSize(), + Timestamp: c.timestamp(), + TimestampDiff: c.lastTimeDiff, + // Currently always send an 8 byte selective ack. + Extensions: []extensionField{{ + Type: extensionTypeSelectiveAck, + Bytes: selAck, + }}, + } + p := h.Marshal() + // Extension headers are currently fixed in size. + if len(p) != maxHeaderSize { + panic("header has unexpected size") + } + p = append(p, payload...) + if logLevel >= 1 { + log.Printf("writing utp msg to %s: %s", c.remoteAddr, packetDebugString(&h, payload)) + } + n1, err := c.socket.writeTo(p, c.remoteAddr) + if err != nil { + return + } + if n1 != len(p) { + panic(n1) + } + c.unpendSendState() + return +} + +func (me *Conn) unpendSendState() { + me.pendingSendState = false +} + +func (c *Conn) pendSendState() { + c.pendingSendState = true +} + +func (me *Socket) writeTo(b []byte, addr net.Addr) (n int, err error) { + mu.RLock() + apdc := artificialPacketDropChance + mu.RUnlock() + if apdc != 0 { + if rand.Float64() < apdc { + n = len(b) + return + } + } + n, err = me.pc.WriteTo(b, addr) + return +} + +func (s *send) timeoutResend() { + select { + case <-s.acked: + return + default: + } + if time.Since(s.started) >= sendTimeout { + s.timedOut() + return + } + s.conn.mu.Lock() + rt := s.conn.resendTimeout() + s.conn.mu.Unlock() + go s.resend() + s.mu.Lock() + s.numResends++ + s.resendTimer.Reset(rt) + s.mu.Unlock() +} + +func (me *Conn) writeSyn() (err error) { + if me.cs != csInvalid { + panic(me.cs) + } + _, err = me.write(stSyn, me.recv_id, nil, me.seq_nr) + return +} + +func (c *Conn) write(_type st, connID uint16, payload []byte, seqNr uint16) (n int, err error) { + switch _type { + case stSyn, stFin, stData: + default: + panic(_type) + } + switch c.cs { + case csConnected, csSynSent, csInvalid: + default: + panic(c.cs) + } + if c.sentFin { + panic(c) + } + if len(payload) > maxPayloadSize { + payload = payload[:maxPayloadSize] + } + err = c.send(_type, connID, payload, seqNr) + if err != nil { + return + } + n = len(payload) + // Copy payload so caller to write can continue to use the buffer. + if payload != nil { + payload = append(sendBufferPool.Get().([]byte)[:0:minMTU], payload...) + } + send := &send{ + acked: make(chan struct{}), + payloadSize: uint32(len(payload)), + started: time.Now(), + resend: func() { + c.mu.Lock() + err := c.send(_type, connID, payload, seqNr) + if err != nil { + log.Printf("error resending packet: %s", err) + } + c.mu.Unlock() + }, + timedOut: func() { + c.mu.Lock() + c.destroy(errAckTimeout) + c.mu.Unlock() + }, + conn: c, + } + send.mu.Lock() + send.resendTimer = time.AfterFunc(c.resendTimeout(), send.timeoutResend) + send.mu.Unlock() + c.unackedSends = append(c.unackedSends, send) + c.seq_nr++ + return +} + +func (c *Conn) latency() (ret time.Duration) { + if len(c.latencies) == 0 { + // Sort of the p95 of latencies? + return 200 * time.Millisecond + } + for _, l := range c.latencies { + ret += l + } + ret = (ret + time.Duration(len(c.latencies)) - 1) / time.Duration(len(c.latencies)) + return +} + +func (c *Conn) numUnackedSends() (num int) { + for _, s := range c.unackedSends { + select { + case <-s.acked: + default: + num++ + } + } + return +} + +func (c *Conn) cur_window() (window uint32) { + for _, s := range c.unackedSends { + select { + case <-s.acked: + default: + window += s.payloadSize + } + } + return +} + +func (c *Conn) sendState() { + c.send(stState, c.send_id, nil, c.seq_nr) + sentStatePackets.Add(1) +} + +func seqLess(a, b uint16) bool { + if b < 0x8000 { + return a < b || a >= b-0x8000 + } else { + return a < b && a >= b-0x8000 + } +} + +// Ack our send with the given sequence number. +func (c *Conn) ack(nr uint16) { + if !seqLess(c.lastAck, nr) { + // Already acked. + return + } + i := nr - c.lastAck - 1 + if int(i) >= len(c.unackedSends) { + log.Printf("got ack ahead of syn (%x > %x)", nr, c.seq_nr-1) + return + } + latency := c.unackedSends[i].Ack() + if latency != 0 { + c.latencies = append(c.latencies, latency) + if len(c.latencies) > 10 { + c.latencies = c.latencies[len(c.latencies)-10:] + } + } + for { + if len(c.unackedSends) == 0 { + break + } + select { + case <-c.unackedSends[0].acked: + default: + // Can't trim unacked sends any further. + return + } + // Trim the front of the unacked sends. + c.unackedSends = c.unackedSends[1:] + c.lastAck++ + } + c.event.Broadcast() +} + +func (c *Conn) ackTo(nr uint16) { + if !seqLess(nr, c.seq_nr) { + return + } + for seqLess(c.lastAck, nr) { + c.ack(c.lastAck + 1) + } +} + +type selectiveAckBitmask []byte + +func (me selectiveAckBitmask) NumBits() int { + return len(me) * 8 +} + +func (me selectiveAckBitmask) SetBit(index int) { + me[index/8] |= 1 << uint(index%8) +} + +func (me selectiveAckBitmask) BitIsSet(index int) bool { + return me[index/8]>>uint(index%8)&1 == 1 +} + +// Return the send state for the sequence number. Returns nil if there's no +// outstanding send for that sequence number. +func (c *Conn) seqSend(seqNr uint16) *send { + if !seqLess(c.lastAck, seqNr) { + // Presumably already acked. + return nil + } + i := int(seqNr - c.lastAck - 1) + if i >= len(c.unackedSends) { + // No such send. + return nil + } + return c.unackedSends[i] +} + +func (c *Conn) resendTimeout() time.Duration { + l := c.latency() + if l < 10*time.Millisecond { + l = 10 * time.Millisecond + } + ret := jitter.Duration(3*l, l) + // log.Print(ret) + return ret +} + +func (c *Conn) ackSkipped(seqNr uint16) { + send := c.seqSend(seqNr) + if send == nil { + return + } + send.mu.Lock() + defer send.mu.Unlock() + send.acksSkipped++ + switch send.acksSkipped { + case 3, 60: + ackSkippedResends.Add(1) + go send.resend() + send.resendTimer.Reset(c.resendTimeout()) + default: + } +} + +type packet struct { + h header + payload []byte +} + +func (c *Conn) deliver(h header, payload []byte) { + c.packetsIn <- packet{h, payload} +} + +func (c *Conn) deliveryProcessor() { + for { + select { + case p := <-c.packetsIn: + c.processDelivery(p.h, p.payload) + timeout := time.After(500 * time.Microsecond) + batched: + for { + select { + case p := <-c.packetsIn: + c.processDelivery(p.h, p.payload) + case <-timeout: + break batched + } + } + c.mu.Lock() + c.sendPendingState() + c.mu.Unlock() + case <-c.destroyed: + return + } + } +} + +func (c *Conn) processDelivery(h header, payload []byte) { + deliveriesProcessed.Add(1) + c.mu.Lock() + defer c.mu.Unlock() + defer c.event.Broadcast() + c.assertHeader(h) + c.peerWndSize = h.WndSize + c.applyAcks(h) + if h.Timestamp == 0 { + c.lastTimeDiff = 0 + } else { + c.lastTimeDiff = c.timestamp() - h.Timestamp + } + + // We want this connection destroyed, and our peer has acked everything. + if c.sentFin && len(c.unackedSends) == 0 { + // log.Print("gracefully completed") + c.destroy(nil) + return + } + if h.Type == stReset { + c.destroy(errors.New("peer reset")) + return + } + if c.cs == csSynSent { + if h.Type != stState { + return + } + c.changeState(csConnected) + c.ack_nr = h.SeqNr - 1 + return + } + if h.Type == stState { + return + } + c.pendSendState() + if !seqLess(c.ack_nr, h.SeqNr) { + // Already received this packet. + return + } + inboundIndex := int(h.SeqNr - c.ack_nr - 1) + if inboundIndex < len(c.inbound) && c.inbound[inboundIndex].seen { + // Already received this packet. + return + } + // Derived from running in production: + // grep -oP '(?<=packet out of order, index=)\d+' log | sort -n | uniq -c + // 64 should correspond to 8 bytes of selective ack. + if inboundIndex >= maxUnackedInbound { + // Discard packet too far ahead. + if missinggo.CryHeard() { + // I can't tell if this occurs due to bad peers, or something + // missing in the implementation. + log.Printf("received packet from %s %d ahead of next seqnr (%x > %x)", c.remoteAddr, inboundIndex, h.SeqNr, c.ack_nr+1) + } + return + } + // Extend inbound so the new packet has a place. + for inboundIndex >= len(c.inbound) { + c.inbound = append(c.inbound, recv{}) + } + c.inbound[inboundIndex] = recv{true, payload, h.Type} + c.processInbound() +} + +func (c *Conn) applyAcks(h header) { + c.ackTo(h.AckNr) + for _, ext := range h.Extensions { + switch ext.Type { + case extensionTypeSelectiveAck: + c.ackSkipped(h.AckNr + 1) + bitmask := selectiveAckBitmask(ext.Bytes) + for i := 0; i < bitmask.NumBits(); i++ { + if bitmask.BitIsSet(i) { + nr := h.AckNr + 2 + uint16(i) + // log.Printf("selectively acked %d", nr) + c.ack(nr) + } else { + c.ackSkipped(h.AckNr + 2 + uint16(i)) + } + } + } + } +} + +func (c *Conn) assertHeader(h header) { + if h.Type == stSyn { + if h.ConnID != c.send_id { + panic(fmt.Sprintf("%d != %d", h.ConnID, c.send_id)) + } + } else { + if h.ConnID != c.recv_id { + panic("erroneous delivery") + } + } +} + +func (c *Conn) processInbound() { + // Consume consecutive next packets. + for !c.gotFin && len(c.inbound) > 0 && c.inbound[0].seen { + c.ack_nr++ + p := c.inbound[0] + c.inbound = c.inbound[1:] + c.readBuf = append(c.readBuf, p.data...) + if p.Type == stFin { + c.gotFin = true + } + } +} + +func (c *Conn) waitAck(seq uint16) { + send := c.seqSend(seq) + if send == nil { + return + } + c.mu.Unlock() + defer c.mu.Lock() + <-send.acked + return +} + +func (c *Conn) changeState(cs int) { + // log.Println(c, "goes", c.cs, "->", cs) + c.cs = cs +} + +func (c *Conn) connect() error { + c.mu.Lock() + defer c.mu.Unlock() + c.seq_nr = 1 + err := c.writeSyn() + if err != nil { + return err + } + c.changeState(csSynSent) + if logLevel >= 2 { + log.Printf("sent syn") + } + // c.seq_nr++ + c.waitAck(1) + if c.err != nil { + err = c.err + } + c.event.Broadcast() + return err +} + +// Returns true if the connection was newly registered, false otherwise. +func (s *Socket) registerConn(recvID uint16, remoteAddr resolvedAddrStr, c *Conn) bool { + if s.conns == nil { + s.conns = make(map[connKey]*Conn) + } + key := connKey{remoteAddr, recvID} + if _, ok := s.conns[key]; ok { + return false + } + s.conns[key] = c + c.detach = func() { + s.mu.Lock() + defer s.mu.Unlock() + defer s.event.Broadcast() + if s.conns[key] != c { + panic("conn changed") + } + // log.Println("detached", key) + delete(s.conns, key) + if len(s.conns) == 0 { + s.pc.Close() + } + } + return true +} + +func (s *Socket) nextSyn() (syn syn, ok bool) { + s.mu.Lock() + defer s.mu.Unlock() + for { + for k := range s.backlog { + syn = k + delete(s.backlog, k) + ok = true + return + } + select { + case <-s.closing: + return + default: + } + s.event.Wait() + } +} + +// Accept and return a new uTP connection. +func (s *Socket) Accept() (c net.Conn, err error) { + for { + syn, ok := s.nextSyn() + if !ok { + err = errClosed + return + } + s.mu.Lock() + _c := s.newConn(stringAddr(syn.addr)) + _c.send_id = syn.conn_id + _c.recv_id = _c.send_id + 1 + _c.seq_nr = uint16(rand.Int()) + _c.lastAck = _c.seq_nr - 1 + _c.ack_nr = syn.seq_nr + _c.cs = csConnected + if !s.registerConn(_c.recv_id, resolvedAddrStr(syn.addr), _c) { + // SYN that triggered this accept duplicates existing connection. + // Ack again in case the SYN was a resend. + _c = s.conns[connKey{resolvedAddrStr(syn.addr), _c.recv_id}] + if _c.send_id != syn.conn_id { + panic(":|") + } + _c.sendState() + s.mu.Unlock() + continue + } + _c.sendState() + // _c.seq_nr++ + c = _c + s.mu.Unlock() + return + } +} + +// The address we're listening on for new uTP connections. +func (s *Socket) Addr() net.Addr { + return s.pc.LocalAddr() +} + +// Marks the Socket for close. Currently this just axes the underlying OS +// socket. +func (s *Socket) Close() (err error) { + s.mu.Lock() + defer s.mu.Unlock() + select { + case <-s.closing: + return + default: + } + s.event.Broadcast() + close(s.closing) + if len(s.conns) == 0 { + err = s.pc.Close() + } + return +} + +func (s *Socket) LocalAddr() net.Addr { + return s.pc.LocalAddr() +} + +func (s *Socket) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + read, ok := <-s.unusedReads + if !ok { + err = io.EOF + } + n = copy(p, read.data) + addr = read.from + return +} + +func (s *Socket) WriteTo(b []byte, addr net.Addr) (int, error) { + return s.pc.WriteTo(b, addr) +} + +func (c *Conn) writeFin() (err error) { + if c.sentFin { + return + } + _, err = c.write(stFin, c.send_id, nil, c.seq_nr) + if err != nil { + return + } + c.sentFin = true + c.event.Broadcast() + return +} + +func (c *Conn) destroy(reason error) { + if c.err != nil && reason != nil { + log.Printf("duplicate destroy call: %s", reason) + } + if c.cs == csDestroy { + return + } + close(c.destroyed) + c.writeFin() + c.changeState(csDestroy) + c.err = reason + c.event.Broadcast() + c.detach() + for _, s := range c.unackedSends { + s.Ack() + } +} + +func (c *Conn) Close() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.writeFin() +} + +func (c *Conn) LocalAddr() net.Addr { + return c.socket.Addr() +} + +func (c *Conn) Read(b []byte) (n int, err error) { + c.mu.Lock() + defer c.mu.Unlock() + for { + if len(c.readBuf) != 0 { + break + } + if c.cs == csDestroy || c.gotFin || c.sentFin { + err = c.err + if err == nil { + err = io.EOF + } + return + } + if c.connDeadlines.read.deadlineExceeded() { + err = errTimeout + return + } + if logLevel >= 2 { + log.Printf("nothing to read, state=%d", c.cs) + } + c.event.Wait() + } + n = copy(b, c.readBuf) + c.readBuf = c.readBuf[n:] + + return +} + +func (c *Conn) RemoteAddr() net.Addr { + return c.remoteAddr +} + +func (c *Conn) String() string { + return fmt.Sprintf("", c.LocalAddr(), c.RemoteAddr(), c.recv_id) +} + +func (c *Conn) Write(p []byte) (n int, err error) { + c.mu.Lock() + defer c.mu.Unlock() + for len(p) != 0 { + for { + if c.sentFin { + err = io.ErrClosedPipe + return + } + // If peerWndSize is 0, we still want to send something, so don't + // block until we exceed it. + if c.cur_window() <= c.peerWndSize && len(c.unackedSends) < 64 && c.cs == csConnected { + break + } + if c.connDeadlines.write.deadlineExceeded() { + err = errTimeout + return + } + c.event.Wait() + } + var n1 int + n1, err = c.write(stData, c.send_id, p, c.seq_nr) + if err != nil { + return + } + // c.seq_nr++ + n += n1 + p = p[n1:] + } + return +} diff --git a/Godeps/_workspace/src/github.com/anacrolix/utp/utp_test.go b/Godeps/_workspace/src/github.com/anacrolix/utp/utp_test.go new file mode 100644 index 0000000..b118d8e --- /dev/null +++ b/Godeps/_workspace/src/github.com/anacrolix/utp/utp_test.go @@ -0,0 +1,411 @@ +package utp + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "net" + "runtime" + "sync" + "testing" + "time" + + _ "github.com/anacrolix/envpprof" + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/missinggo" + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/bradfitz/iter" + "github.com/stretchr/testify/require" +) + +func init() { + log.SetFlags(log.Flags() | log.Lshortfile) +} + +func TestUTPPingPong(t *testing.T) { + defer goroutineLeakCheck(t)() + s, err := NewSocket("udp", "localhost:0") + require.NoError(t, err) + defer s.Close() + pingerClosed := make(chan struct{}) + go func() { + defer close(pingerClosed) + b, err := Dial(s.Addr().String()) + require.NoError(t, err) + defer b.Close() + n, err := b.Write([]byte("ping")) + require.NoError(t, err) + require.EqualValues(t, 4, n) + buf := make([]byte, 4) + b.Read(buf) + require.EqualValues(t, "pong", buf) + log.Printf("got pong") + }() + a, err := s.Accept() + require.NoError(t, err) + defer a.Close() + log.Printf("accepted %s", a) + buf := make([]byte, 42) + n, err := a.Read(buf) + require.NoError(t, err) + require.EqualValues(t, "ping", buf[:n]) + log.Print("got ping") + n, err = a.Write([]byte("pong")) + require.NoError(t, err) + require.Equal(t, 4, n) + log.Print("waiting for pinger to close") + <-pingerClosed +} + +func goroutineLeakCheck(t testing.TB) func() { + if !testing.Verbose() { + return func() {} + } + numStart := runtime.NumGoroutine() + return func() { + var numNow int + for _ = range iter.N(1) { + numNow = runtime.NumGoroutine() + if numNow == numStart { + return + } + time.Sleep(10 * time.Millisecond) + } + // I'd print stacks, or treat this as fatal, but I think + // runtime.NumGoroutine is including system routines for which we are + // not provided the stacks, and are spawned unpredictably. + t.Logf("have %d goroutines, started with %d", numNow, numStart) + } +} + +func TestDialTimeout(t *testing.T) { + defer goroutineLeakCheck(t)() + s, _ := NewSocket("udp", "localhost:0") + defer s.Close() + conn, err := DialTimeout(s.Addr().String(), 10*time.Millisecond) + if err == nil { + conn.Close() + t.Fatal("expected timeout") + } + t.Log(err) +} + +func TestMinMaxHeaderType(t *testing.T) { + require.Equal(t, stSyn, stMax) +} + +func TestUTPRawConn(t *testing.T) { + l, err := NewSocket("udp", "") + require.NoError(t, err) + defer l.Close() + go func() { + for { + _, err := l.Accept() + if err != nil { + break + } + } + }() + // Connect a UTP peer to see if the RawConn will still work. + log.Print("dialing") + utpPeer := func() net.Conn { + s, _ := NewSocket("udp", "") + defer s.Close() + ret, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr()))) + require.NoError(t, err) + return ret + }() + log.Print("dial returned") + if err != nil { + t.Fatalf("error dialing utp listener: %s", err) + } + defer utpPeer.Close() + peer, err := net.ListenPacket("udp", ":0") + if err != nil { + t.Fatal(err) + } + defer peer.Close() + + msgsReceived := 0 + const N = 5000 // How many messages to send. + readerStopped := make(chan struct{}) + // The reader goroutine. + go func() { + defer close(readerStopped) + b := make([]byte, 500) + for i := 0; i < N; i++ { + n, _, err := l.ReadFrom(b) + if err != nil { + t.Fatalf("error reading from raw conn: %s", err) + } + msgsReceived++ + var d int + fmt.Sscan(string(b[:n]), &d) + if d != i { + log.Printf("got wrong number: expected %d, got %d", i, d) + } + } + }() + udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr()))) + if err != nil { + t.Fatal(err) + } + for i := 0; i < N; i++ { + _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr) + if err != nil { + t.Fatal(err) + } + time.Sleep(time.Microsecond) + } + select { + case <-readerStopped: + case <-time.After(time.Second): + t.Fatal("reader timed out") + } + if msgsReceived != N { + t.Fatalf("messages received: %d", msgsReceived) + } +} + +func TestConnReadDeadline(t *testing.T) { + ls, _ := NewSocket("udp", "localhost:0") + ds, _ := NewSocket("udp", "localhost:0") + dcReadErr := make(chan error) + go func() { + c, _ := ds.Dial(ls.Addr().String()) + defer c.Close() + _, err := c.Read(nil) + dcReadErr <- err + }() + c, _ := ls.Accept() + dl := time.Now().Add(time.Millisecond) + c.SetReadDeadline(dl) + _, err := c.Read(nil) + require.Equal(t, errTimeout, err) + // The deadline has passed. + if !time.Now().After(dl) { + t.FailNow() + } + // Returns timeout on subsequent read. + _, err = c.Read(nil) + require.Equal(t, errTimeout, err) + // Disable the deadline. + c.SetReadDeadline(time.Time{}) + readReturned := make(chan struct{}) + go func() { + c.Read(nil) + close(readReturned) + }() + select { + case <-readReturned: + // Read returned but shouldn't have. + t.FailNow() + case <-time.After(time.Millisecond): + } + c.Close() + select { + case <-readReturned: + case <-time.After(time.Millisecond): + t.Fatal("read should return after Conn is closed") + } + if err := <-dcReadErr; err != io.EOF { + t.Fatalf("dial conn read returned %s", err) + } +} + +func connectSelfLots(n int, t testing.TB) { + defer goroutineLeakCheck(t)() + s, err := NewSocket("udp", "localhost:0") + if err != nil { + t.Fatal(err) + } + go func() { + for _ = range iter.N(n) { + c, err := s.Accept() + if err != nil { + log.Fatal(err) + } + defer c.Close() + } + }() + dialErr := make(chan error) + connCh := make(chan net.Conn) + dialSema := make(chan struct{}, backlog) + for _ = range iter.N(n) { + go func() { + dialSema <- struct{}{} + c, err := s.Dial(s.Addr().String()) + <-dialSema + if err != nil { + dialErr <- err + return + } + connCh <- c + }() + } + conns := make([]net.Conn, 0, n) + for _ = range iter.N(n) { + select { + case c := <-connCh: + conns = append(conns, c) + case err := <-dialErr: + t.Fatal(err) + } + } + for _, c := range conns { + if c != nil { + c.Close() + } + } + s.mu.Lock() + for len(s.conns) != 0 { + // log.Print(len(s.conns)) + s.event.Wait() + } + s.mu.Unlock() + s.Close() +} + +// Connect to ourself heaps. +func TestConnectSelf(t *testing.T) { + // A rough guess says that at worst, I can only have 0x10000/3 connections + // to the same socket, due to fragmentation in the assigned connection + // IDs. + connectSelfLots(0x1000, t) +} + +func BenchmarkConnectSelf(b *testing.B) { + for _ = range iter.N(b.N) { + connectSelfLots(2, b) + } +} + +func BenchmarkNewCloseSocket(b *testing.B) { + for _ = range iter.N(b.N) { + s, err := NewSocket("udp", "localhost:0") + if err != nil { + b.Fatal(err) + } + err = s.Close() + if err != nil { + b.Fatal(err) + } + } +} + +func TestRejectDialBacklogFilled(t *testing.T) { + s, err := NewSocket("udp", "localhost:0") + if err != nil { + t.Fatal(err) + } + errChan := make(chan error, 1) + dial := func() { + _, err := s.Dial(s.Addr().String()) + if err != nil { + errChan <- err + } + } + // Fill the backlog. + for _ = range iter.N(backlog + 1) { + go dial() + } + s.mu.Lock() + for len(s.backlog) < backlog { + s.event.Wait() + } + s.mu.Unlock() + select { + case <-errChan: + t.FailNow() + default: + } + // One more connection should cause a dial attempt to get reset. + go dial() + err = <-errChan + if err.Error() != "peer reset" { + t.FailNow() + } + s.Close() +} + +// Make sure that we can reset AfterFunc timers, so we don't have to create +// brand new ones everytime they fire. Specifically for the Conn resend timer. +func TestResetAfterFuncTimer(t *testing.T) { + fired := make(chan struct{}) + timer := time.AfterFunc(time.Millisecond, func() { + fired <- struct{}{} + }) + <-fired + if timer.Reset(time.Millisecond) { + // The timer should have expired + t.FailNow() + } + <-fired +} + +func connPair() (initer, accepted net.Conn) { + s, err := NewSocket("udp", "localhost:0") + if err != nil { + panic(err) + } + defer s.Close() + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + var err error + initer, err = Dial(s.Addr().String()) + if err != nil { + panic(err) + } + }() + accepted, err = s.Accept() + if err != nil { + panic(err) + } + wg.Wait() + return +} + +// Check that peer sending FIN doesn't cause unread data to be dropped in a +// receiver. +func TestReadFinishedConn(t *testing.T) { + a, b := connPair() + defer a.Close() + defer b.Close() + mu.Lock() + originalAPDC := artificialPacketDropChance + artificialPacketDropChance = 1 + mu.Unlock() + n, err := a.Write([]byte("hello")) + require.Equal(t, 5, n) + require.NoError(t, err) + n, err = a.Write([]byte("world")) + require.Equal(t, 5, n) + require.NoError(t, err) + mu.Lock() + artificialPacketDropChance = originalAPDC + mu.Unlock() + a.Close() + all, err := ioutil.ReadAll(b) + require.NoError(t, err) + require.EqualValues(t, "helloworld", all) +} + +func TestCloseDetachesQuickly(t *testing.T) { + s, _ := NewSocket("udp", "localhost:0") + defer s.Close() + go func() { + a, _ := s.Dial(s.Addr().String()) + log.Print("close a") + a.Close() + log.Print("closed a") + }() + b, _ := s.Accept() + b.Close() + s.mu.Lock() + for len(s.conns) != 0 { + log.Print(len(s.conns)) + s.event.Wait() + } + s.mu.Unlock() +} diff --git a/Godeps/_workspace/src/github.com/bradfitz/iter/.gitignore b/Godeps/_workspace/src/github.com/bradfitz/iter/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/Godeps/_workspace/src/github.com/bradfitz/iter/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/Godeps/_workspace/src/github.com/bradfitz/iter/README.txt b/Godeps/_workspace/src/github.com/bradfitz/iter/README.txt new file mode 100644 index 0000000..763a71c --- /dev/null +++ b/Godeps/_workspace/src/github.com/bradfitz/iter/README.txt @@ -0,0 +1 @@ +See http://godoc.org/github.com/bradfitz/iter diff --git a/Godeps/_workspace/src/github.com/bradfitz/iter/iter.go b/Godeps/_workspace/src/github.com/bradfitz/iter/iter.go new file mode 100644 index 0000000..547b3d9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bradfitz/iter/iter.go @@ -0,0 +1,17 @@ +// Package iter provides a syntantically different way to iterate over integers. That's it. +package iter + +// N returns a slice of n 0-sized elements, suitable for ranging over. +// +// For example: +// +// for i := range iter.N(10) { +// fmt.Println(i) +// } +// +// ... will print 0 to 9, inclusive. +// +// It does not cause any allocations. +func N(n int) []struct{} { + return make([]struct{}, n) +} diff --git a/Godeps/_workspace/src/github.com/bradfitz/iter/iter_test.go b/Godeps/_workspace/src/github.com/bradfitz/iter/iter_test.go new file mode 100644 index 0000000..b294261 --- /dev/null +++ b/Godeps/_workspace/src/github.com/bradfitz/iter/iter_test.go @@ -0,0 +1,29 @@ +package iter_test + +import ( + "fmt" + "testing" + + "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/bradfitz/iter" +) + +func ExampleN() { + for i := range iter.N(4) { + fmt.Println(i) + } + // Output: + // 0 + // 1 + // 2 + // 3 +} + +func TestAllocs(t *testing.T) { + var x []struct{} + allocs := testing.AllocsPerRun(500, func() { + x = iter.N(1e9) + }) + if allocs > 0.1 { + t.Errorf("allocs = %v", allocs) + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/.gitignore b/Godeps/_workspace/src/github.com/h2so5/utp/.gitignore deleted file mode 100644 index 485e6d2..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -_ucat_test/libutp diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml b/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml deleted file mode 100644 index a924a18..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go - -script: - - GO_UTP_LOGGING=2 go test -v - - GOMAXPROCS=4 GO_UTP_LOGGING=2 go test -v - - go test -v -race - - GO_UTP_LOGGING=2 go run benchmark/main.go -h - - GOMAXPROCS=4 GO_UTP_LOGGING=2 go run benchmark/main.go -h - - GO_UTP_LOGGING=2 cd _ucat_test; make test diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/LICENSE b/Godeps/_workspace/src/github.com/h2so5/utp/LICENSE deleted file mode 100644 index 8ef628a..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Ron Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/README.md b/Godeps/_workspace/src/github.com/h2so5/utp/README.md deleted file mode 100644 index c061f0c..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/README.md +++ /dev/null @@ -1,28 +0,0 @@ -utp -=== - -μTP (Micro Transport Protocol) implementation - -[![Build status](https://ci.appveyor.com/api/projects/status/j1be8y7p6nd2wqqw?svg=true&branch=master)](https://ci.appveyor.com/project/h2so5/utp) -[![Build Status](https://travis-ci.org/h2so5/utp.svg?branch=master)](https://travis-ci.org/h2so5/utp) -[![GoDoc](https://godoc.org/github.com/h2so5/utp?status.svg)](http://godoc.org/github.com/h2so5/utp) - -http://www.bittorrent.org/beps/bep_0029.html - -## Installation - -``` -go get github.com/h2so5/utp -``` - -## Debug Log - -Use GO_UTP_LOGGING to show debug logs. - -``` -GO_UTP_LOGGING=0 go test <- default, no logging -GO_UTP_LOGGING=1 go test -GO_UTP_LOGGING=2 go test -GO_UTP_LOGGING=3 go test -GO_UTP_LOGGING=4 go test <- most verbose -``` diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/addr.go b/Godeps/_workspace/src/github.com/h2so5/utp/addr.go deleted file mode 100644 index c8ddfb0..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/addr.go +++ /dev/null @@ -1,42 +0,0 @@ -package utp - -import "net" - -// Addr represents the address of a UTP end point. -type Addr struct { - net.Addr -} - -// Network returns the address's network name, "utp". -func (a Addr) Network() string { return "utp" } - -// ResolveAddr parses addr as a UTP address of the form "host:port" -// or "[ipv6-host%zone]:port" and resolves a pair of domain name and -// port name on the network net, which must be "utp", "utp4" or -// "utp6". A literal address or host name for IPv6 must be enclosed -// in square brackets, as in "[::1]:80", "[ipv6-host]:http" or -// "[ipv6-host%zone]:80". -func ResolveAddr(n, addr string) (*Addr, error) { - udpnet, err := utp2udp(n) - if err != nil { - return nil, err - } - udp, err := net.ResolveUDPAddr(udpnet, addr) - if err != nil { - return nil, err - } - return &Addr{Addr: udp}, nil -} - -func utp2udp(n string) (string, error) { - switch n { - case "utp": - return "udp", nil - case "utp4": - return "udp4", nil - case "utp6": - return "udp6", nil - default: - return "", net.UnknownNetworkError(n) - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/base.go b/Godeps/_workspace/src/github.com/h2so5/utp/base.go deleted file mode 100644 index 17114c4..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/base.go +++ /dev/null @@ -1,315 +0,0 @@ -package utp - -import ( - "errors" - "net" - "sync" - "sync/atomic" - "syscall" - "time" -) - -var baseConnMap = make(map[string]*baseConn) -var baseConnMutex sync.Mutex - -type packetHandler chan<- *packet - -type baseConn struct { - addr string - conn net.PacketConn - synPackets *packetRingBuffer - outOfBandPackets *packetRingBuffer - - handlers map[uint16]packetHandler - handlerMutex sync.RWMutex - ref int32 - refMutex sync.RWMutex - - rdeadline time.Time - wdeadline time.Time - - softClosed int32 - closed int32 -} - -func newBaseConn(n string, addr *Addr) (*baseConn, error) { - udpnet, err := utp2udp(n) - if err != nil { - return nil, err - } - var s string - if addr != nil { - s = addr.String() - } else { - s = ":0" - } - conn, err := net.ListenPacket(udpnet, s) - if err != nil { - return nil, err - } - c := &baseConn{ - conn: conn, - synPackets: newPacketRingBuffer(packetBufferSize), - outOfBandPackets: newPacketRingBuffer(packetBufferSize), - handlers: make(map[uint16]packetHandler), - } - c.Register(-1, nil) - go c.recvLoop() - return c, nil -} - -func getSharedBaseConn(n string, addr *Addr) (*baseConn, error) { - baseConnMutex.Lock() - defer baseConnMutex.Unlock() - var s string - if addr != nil { - s = addr.String() - } else { - s = ":0" - } - if c, ok := baseConnMap[s]; ok { - return c, nil - } - c, err := newBaseConn(n, addr) - if err != nil { - return nil, err - } - c.addr = s - baseConnMap[s] = c - go c.recvLoop() - return c, nil -} - -func (c *baseConn) ok() bool { return c != nil && c.conn != nil } - -func (c *baseConn) LocalAddr() net.Addr { - if !c.ok() { - return nil - } - return &Addr{Addr: c.conn.LocalAddr()} -} - -func (c *baseConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { - if !c.ok() { - return 0, nil, syscall.EINVAL - } - if !c.isOpen() { - return 0, nil, &net.OpError{ - Op: "read", - Net: c.LocalAddr().Network(), - Addr: c.LocalAddr(), - Err: errClosing, - } - } - var d time.Duration - if !c.rdeadline.IsZero() { - d = c.rdeadline.Sub(time.Now()) - if d < 0 { - d = 0 - } - } - p, err := c.outOfBandPackets.popOne(d) - if err != nil { - return 0, nil, &net.OpError{ - Op: "read", - Net: c.LocalAddr().Network(), - Addr: c.LocalAddr(), - Err: err, - } - } - return copy(b, p.payload), p.addr, nil -} - -func (c *baseConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - if !c.isOpen() { - return 0, &net.OpError{ - Op: "write", - Net: c.LocalAddr().Network(), - Addr: c.LocalAddr(), - Err: errClosing, - } - } - return c.conn.WriteTo(b, addr) -} - -func (c *baseConn) Close() error { - if !c.ok() { - return syscall.EINVAL - } - if c.isOpen() && atomic.CompareAndSwapInt32(&c.softClosed, 0, 1) { - c.Unregister(-1) - } else { - return &net.OpError{ - Op: "close", - Net: c.LocalAddr().Network(), - Addr: c.LocalAddr(), - Err: errClosing, - } - } - return nil -} - -func (c *baseConn) SetDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - err := c.SetReadDeadline(t) - if err != nil { - return err - } - return c.SetWriteDeadline(t) -} - -func (c *baseConn) SetReadDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - c.rdeadline = t - return nil -} - -func (c *baseConn) SetWriteDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - c.wdeadline = t - return nil -} - -func (c *baseConn) recvLoop() { - var buf [maxUdpPayload]byte - for { - l, addr, err := c.conn.ReadFrom(buf[:]) - if err != nil { - ulog.Printf(3, "baseConn(%v): %v", c.LocalAddr(), err) - return - } - p, err := c.decodePacket(buf[:l]) - if err != nil { - ulog.Printf(3, "baseConn(%v): RECV out-of-band packet (len: %d) from %v", c.LocalAddr(), l, addr) - c.outOfBandPackets.push(&packet{payload: append([]byte{}, buf[:l]...), addr: addr}) - } else { - p.addr = addr - ulog.Printf(3, "baseConn(%v): RECV: %v from %v", c.LocalAddr(), p, addr) - if p.header.typ == stSyn { - // ignore duplicated syns - if !c.exists(p.header.id + 1) { - c.synPackets.push(p) - } - } else { - c.processPacket(p) - } - } - } -} - -func (c *baseConn) decodePacket(b []byte) (*packet, error) { - var p packet - err := p.UnmarshalBinary(b) - if err != nil { - return nil, err - } - if p.header.ver != version { - return nil, errors.New("unsupported utp version") - } - return &p, nil -} - -func (c *baseConn) exists(id uint16) bool { - c.handlerMutex.RLock() - defer c.handlerMutex.RUnlock() - return c.handlers[id] != nil -} - -func (c *baseConn) processPacket(p *packet) { - c.handlerMutex.RLock() - h, ok := c.handlers[p.header.id] - c.handlerMutex.RUnlock() - if ok { - h <- p - } -} - -func (c *baseConn) Register(id int32, f packetHandler) { - if id < 0 { - c.refMutex.Lock() - c.ref++ - c.refMutex.Unlock() - } else { - if f == nil { - panic("nil handler not allowed") - } - c.handlerMutex.Lock() - _, ok := c.handlers[uint16(id)] - c.handlerMutex.Unlock() - if !ok { - c.refMutex.Lock() - c.ref++ - c.refMutex.Unlock() - c.handlerMutex.Lock() - c.handlers[uint16(id)] = f - c.handlerMutex.Unlock() - ulog.Printf(2, "baseConn(%v): register #%d (ref: %d)", c.LocalAddr(), id, c.ref) - } - } -} - -func (c *baseConn) Unregister(id int32) { - if id < 0 { - c.refMutex.Lock() - c.ref-- - c.refMutex.Unlock() - } else { - c.handlerMutex.Lock() - _, ok := c.handlers[uint16(id)] - c.handlerMutex.Unlock() - if ok { - c.handlerMutex.Lock() - delete(c.handlers, uint16(id)) - c.handlerMutex.Unlock() - c.refMutex.Lock() - c.ref-- - c.refMutex.Unlock() - } - } - c.refMutex.Lock() - r := c.ref - c.refMutex.Unlock() - if r <= 0 { - baseConnMutex.Lock() - defer baseConnMutex.Unlock() - c.close() - delete(baseConnMap, c.addr) - ulog.Printf(2, "baseConn(%v): unregister #%d (ref: %d)", c.LocalAddr(), id, c.ref) - } -} - -func (c *baseConn) close() { - if atomic.CompareAndSwapInt32(&c.closed, 0, 1) { - c.conn.Close() - } -} - -func (c *baseConn) isOpen() bool { - return atomic.LoadInt32(&c.closed) == 0 -} - -func (c *baseConn) Send(p *packet) { - b, err := p.MarshalBinary() - if err != nil { - panic(err) - } - ulog.Printf(3, "baseConn(%v): SEND: %v to %v", c.LocalAddr(), p, p.addr) - _, err = c.conn.WriteTo(b, p.addr) - if err != nil { - ulog.Printf(3, "%v", err) - panic(err) - } -} - -func (c *baseConn) RecvSyn(timeout time.Duration) (*packet, error) { - return c.synPackets.popOne(timeout) -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/base_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/base_test.go deleted file mode 100644 index 618dae1..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/base_test.go +++ /dev/null @@ -1,308 +0,0 @@ -package utp - -import ( - "bytes" - "net" - "sync" - "testing" - "time" -) - -func TestSharedConnRecvPacket(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - c, err := getSharedBaseConn("utp", addr) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } - - uc, err := net.DialUDP("udp", nil, uaddr) - if err != nil { - t.Fatal(err) - } - defer uc.Close() - - ch := make(chan *packet) - c.Register(5, ch) - - for i := 0; i < 100; i++ { - p := &packet{header: header{typ: stData, ver: version, id: 5}} - payload, err := p.MarshalBinary() - if err != nil { - t.Fatal(err) - } - go func() { - uc.Write(payload) - }() - <-ch - } - - c.Unregister(5) -} - -func TestSharedConnSendPacket(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - c, err := getSharedBaseConn("utp", addr) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } - - uc, err := net.DialUDP("udp", nil, uaddr) - if err != nil { - t.Fatal(err) - } - defer uc.Close() - - for i := 0; i < 100; i++ { - addr, err := net.ResolveUDPAddr("udp", uc.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } - p := &packet{header: header{typ: stData, ver: version, id: 5}, addr: addr} - payload, err := p.MarshalBinary() - if err != nil { - t.Fatal(err) - } - - c.Send(p) - - var b [256]byte - l, err := uc.Read(b[:]) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(b[:l], payload) { - t.Errorf("expected packet of %v; got %v", payload, b[:l]) - } - } -} - -func TestSharedConnRecvSyn(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - c, err := getSharedBaseConn("utp", addr) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } - - uc, err := net.DialUDP("udp", nil, uaddr) - if err != nil { - t.Fatal(err) - } - defer uc.Close() - - for i := 0; i < 100; i++ { - p := &packet{header: header{typ: stSyn, ver: version}} - payload, err := p.MarshalBinary() - if err != nil { - t.Fatal(err) - } - go func() { - uc.Write(payload) - }() - p, err = c.RecvSyn(time.Duration(0)) - if err != nil { - t.Fatal(err) - } - if p == nil { - t.Errorf("packet must not be nil") - } - } -} - -func TestSharedConnRecvOutOfBound(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - c, err := getSharedBaseConn("utp", addr) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } - - uc, err := net.DialUDP("udp", nil, uaddr) - if err != nil { - t.Fatal(err) - } - defer uc.Close() - - for i := 0; i < 100; i++ { - payload := []byte("Hello") - go func() { - uc.Write(payload) - }() - var b [256]byte - l, _, err := c.ReadFrom(b[:]) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(payload, b[:l]) { - t.Errorf("expected packet of %v; got %v", payload, b[:l]) - } - } -} - -func TestSharedConnSendOutOfBound(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - c, err := getSharedBaseConn("utp", addr) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } - - uc, err := net.DialUDP("udp", nil, uaddr) - if err != nil { - t.Fatal(err) - } - defer uc.Close() - - for i := 0; i < 100; i++ { - addr, err := net.ResolveUDPAddr("udp", uc.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } - payload := []byte("Hello") - _, err = c.WriteTo(payload, addr) - if err != nil { - t.Fatal(err) - } - - var b [256]byte - l, err := uc.Read(b[:]) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(payload, b[:l]) { - t.Errorf("expected packet of %v; got %v", payload, b[:l]) - } - } -} - -func TestSharedConnReferenceCount(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - c, err := getSharedBaseConn("utp", addr) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - var w sync.WaitGroup - - c.Register(-1, nil) - - for i := 0; i < 5; i++ { - w.Add(1) - go func(i int) { - defer w.Done() - c.Register(int32(i), make(chan *packet)) - }(i) - } - - w.Wait() - for i := 0; i < 5; i++ { - w.Add(1) - go func(i int) { - defer w.Done() - c.Unregister(int32(i)) - }(i) - } - - w.Wait() - c.Unregister(-1) - c.Close() - - c = baseConnMap[addr.String()] - if c != nil { - t.Errorf("baseConn should be released", c.ref) - } -} - -func TestSharedConnClose(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - c, err := getSharedBaseConn("utp", addr) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - for i := 0; i < 5; i++ { - c.Close() - } - - var b [256]byte - _, _, err = c.ReadFrom(b[:]) - if err == nil { - t.Fatal("ReadFrom should fail") - } - - uaddr, err := net.ResolveUDPAddr("udp", c.LocalAddr().String()) - if err != nil { - t.Fatal(err) - } - - uc, err := net.DialUDP("udp", nil, uaddr) - if err != nil { - t.Fatal(err) - } - defer uc.Close() - - payload := []byte("Hello") - _, err = c.WriteTo(payload, uc.LocalAddr()) - if err == nil { - t.Fatal("WriteTo should fail") - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go b/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go deleted file mode 100644 index e59877b..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go +++ /dev/null @@ -1,296 +0,0 @@ -package main - -import ( - "bytes" - "crypto/md5" - "flag" - "fmt" - "io" - "log" - "math/rand" - "sync" - "time" - - "github.com/davecheney/profile" - "github.com/dustin/go-humanize" - "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/h2so5/utp" -) - -type RandReader struct{} - -func (r RandReader) Read(p []byte) (n int, err error) { - for i := range p { - p[i] = byte(rand.Int()) - } - return len(p), nil -} - -type ByteCounter struct { - n int64 - mutex sync.RWMutex -} - -func (b *ByteCounter) Write(p []byte) (n int, err error) { - b.mutex.Lock() - defer b.mutex.Unlock() - b.n += int64(len(p)) - return len(p), nil -} - -func (b *ByteCounter) Length() int64 { - b.mutex.RLock() - defer b.mutex.RUnlock() - return b.n -} - -var h = flag.Bool("h", false, "Human readable") - -func main() { - var l = flag.Int("c", 10485760, "Payload length (bytes)") - var s = flag.Bool("s", false, "Stream mode(Low memory usage, but Slow)") - flag.Parse() - - defer profile.Start(profile.CPUProfile).Stop() - - if *h { - fmt.Printf("Payload: %s\n", humanize.IBytes(uint64(*l))) - } else { - fmt.Printf("Payload: %d\n", *l) - } - - c2s := c2s(int64(*l), *s) - n, p := humanize.ComputeSI(c2s) - if *h { - fmt.Printf("C2S: %f%sbps\n", n, p) - } else { - fmt.Printf("C2S: %f\n", c2s) - } - - s2c := s2c(int64(*l), *s) - n, p = humanize.ComputeSI(s2c) - if *h { - fmt.Printf("S2C: %f%sbps\n", n, p) - } else { - fmt.Printf("S2C: %f\n", s2c) - } - - avg := (c2s + s2c) / 2.0 - n, p = humanize.ComputeSI(avg) - - if *h { - fmt.Printf("AVG: %f%sbps\n", n, p) - } else { - fmt.Printf("AVG: %f\n", avg) - } -} - -func c2s(l int64, stream bool) float64 { - laddr, err := utp.ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - log.Fatal(err) - } - ln, err := utp.Listen("utp", laddr) - if err != nil { - log.Fatal(err) - } - - cch := make(chan *utp.Conn) - go func() { - c, err := utp.DialUTPTimeout("utp", nil, ln.Addr().(*utp.Addr), 1000*time.Millisecond) - if err != nil { - log.Fatal(err) - } - - if err != nil { - log.Fatal(err) - } - cch <- c - }() - - s, err := ln.Accept() - if err != nil { - log.Fatal(err) - } - defer s.Close() - ln.Close() - - c := <-cch - defer c.Close() - - rch := make(chan int) - wch := make(chan int) - - sendHash := md5.New() - readHash := md5.New() - counter := ByteCounter{} - - var bps float64 - if stream { - go func() { - defer c.Close() - defer close(wch) - io.Copy(io.MultiWriter(c, sendHash, &counter), io.LimitReader(RandReader{}, l)) - }() - - go func() { - defer close(rch) - io.Copy(readHash, s) - }() - - go func() { - for { - select { - case <-time.After(time.Second): - if *h { - fmt.Printf("\r <--> %s ", humanize.IBytes(uint64(counter.Length()))) - } else { - fmt.Printf("\r <--> %d ", counter.Length()) - } - case <-rch: - fmt.Printf("\r") - return - } - } - }() - - start := time.Now() - <-rch - <-wch - bps = float64(l*8) / (float64(time.Now().Sub(start)) / float64(time.Second)) - - } else { - var sendBuf, readBuf bytes.Buffer - io.Copy(io.MultiWriter(&sendBuf, sendHash), io.LimitReader(RandReader{}, l)) - - go func() { - defer c.Close() - defer close(wch) - io.Copy(c, &sendBuf) - }() - - go func() { - defer close(rch) - io.Copy(&readBuf, s) - }() - - start := time.Now() - <-rch - <-wch - bps = float64(l*8) / (float64(time.Now().Sub(start)) / float64(time.Second)) - - io.Copy(sendHash, &sendBuf) - io.Copy(readHash, &readBuf) - } - - if !bytes.Equal(sendHash.Sum(nil), readHash.Sum(nil)) { - log.Fatal("Broken payload") - } - - return bps -} - -func s2c(l int64, stream bool) float64 { - laddr, err := utp.ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - log.Fatal(err) - } - ln, err := utp.Listen("utp", laddr) - if err != nil { - log.Fatal(err) - } - - cch := make(chan *utp.Conn) - go func() { - c, err := utp.DialUTPTimeout("utp", nil, ln.Addr().(*utp.Addr), 1000*time.Millisecond) - if err != nil { - log.Fatal(err) - } - - if err != nil { - log.Fatal(err) - } - cch <- c - }() - - s, err := ln.Accept() - if err != nil { - log.Fatal(err) - } - defer s.Close() - ln.Close() - - c := <-cch - defer c.Close() - - rch := make(chan int) - wch := make(chan int) - - sendHash := md5.New() - readHash := md5.New() - counter := ByteCounter{} - - var bps float64 - - if stream { - go func() { - defer s.Close() - defer close(wch) - io.Copy(io.MultiWriter(s, sendHash, &counter), io.LimitReader(RandReader{}, l)) - }() - - go func() { - defer close(rch) - io.Copy(readHash, c) - }() - - go func() { - for { - select { - case <-time.After(time.Second): - if *h { - fmt.Printf("\r <--> %s ", humanize.IBytes(uint64(counter.Length()))) - } else { - fmt.Printf("\r <--> %d ", counter.Length()) - } - case <-rch: - fmt.Printf("\r") - return - } - } - }() - - start := time.Now() - <-rch - <-wch - bps = float64(l*8) / (float64(time.Now().Sub(start)) / float64(time.Second)) - - } else { - var sendBuf, readBuf bytes.Buffer - io.Copy(io.MultiWriter(&sendBuf, sendHash), io.LimitReader(RandReader{}, l)) - - go func() { - defer s.Close() - defer close(wch) - io.Copy(s, &sendBuf) - }() - - go func() { - defer close(rch) - io.Copy(&readBuf, c) - }() - - start := time.Now() - <-rch - <-wch - bps = float64(l*8) / (float64(time.Now().Sub(start)) / float64(time.Second)) - - io.Copy(sendHash, &sendBuf) - io.Copy(readHash, &readBuf) - } - - if !bytes.Equal(sendHash.Sum(nil), readHash.Sum(nil)) { - log.Fatal("Broken payload") - } - - return bps -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go b/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go deleted file mode 100644 index 830891f..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go +++ /dev/null @@ -1,469 +0,0 @@ -package utp - -import ( - "errors" - "io" - "math" - "sync" - "time" -) - -type packetBuffer struct { - root *packetBufferNode - size int - begin int -} - -type packetBufferNode struct { - p *packet - next *packetBufferNode - pushed time.Time -} - -func newPacketBuffer(size, begin int) *packetBuffer { - return &packetBuffer{ - size: size, - begin: begin, - } -} - -func (b *packetBuffer) push(p *packet) error { - if int(p.header.seq) > b.begin+b.size-1 { - return errors.New("out of bounds") - } else if int(p.header.seq) < b.begin { - if int(p.header.seq)+math.MaxUint16 > b.begin+b.size-1 { - return errors.New("out of bounds") - } - } - if b.root == nil { - b.root = &packetBufferNode{} - } - n := b.root - i := b.begin - for { - if i == int(p.header.seq) { - n.p = p - n.pushed = time.Now() - return nil - } else if n.next == nil { - n.next = &packetBufferNode{} - } - n = n.next - i = (i + 1) % (math.MaxUint16 + 1) - } - return nil -} - -func (b *packetBuffer) fetch(id uint16) *packet { - for p := b.root; p != nil; p = p.next { - if p.p != nil { - if p.p.header.seq < id { - p.p = nil - } else if p.p.header.seq == id { - r := p.p - p.p = nil - return r - } - } - } - return nil -} - -func (b *packetBuffer) compact() { - for b.root != nil && b.root.p == nil { - b.root = b.root.next - b.begin = (b.begin + 1) % (math.MaxUint16 + 1) - } -} - -func (b *packetBuffer) front() *packet { - if b.root == nil || b.root.p == nil { - return nil - } - return b.root.p -} - -func (b *packetBuffer) frontPushedTime() (time.Time, error) { - if b.root == nil || b.root.p == nil { - return time.Time{}, errors.New("no first packet") - } - return b.root.pushed, nil -} - -func (b *packetBuffer) fetchSequence() []*packet { - var a []*packet - for ; b.root != nil && b.root.p != nil; b.root = b.root.next { - a = append(a, b.root.p) - b.begin = (b.begin + 1) % (math.MaxUint16 + 1) - } - return a -} - -func (b *packetBuffer) sequence() []*packet { - var a []*packet - n := b.root - for ; n != nil && n.p != nil; n = n.next { - a = append(a, n.p) - } - return a -} - -func (b *packetBuffer) space() int { - s := b.size - for p := b.root; p != nil; p = p.next { - s-- - } - return s -} - -func (b *packetBuffer) empty() bool { - return b.root == nil -} - -// test use only -func (b *packetBuffer) all() []*packet { - var a []*packet - for p := b.root; p != nil; p = p.next { - if p.p != nil { - a = append(a, p.p) - } - } - return a -} - -func (b *packetBuffer) generateSelectiveACK() []byte { - if b.empty() { - return nil - } - - var ack []byte - var bit uint - var octet byte - for p := b.root.next; p != nil; p = p.next { - if p.p != nil { - octet |= (1 << bit) - } - bit++ - if bit == 8 { - ack = append(ack, octet) - bit = 0 - octet = 0 - } - } - - if bit != 0 { - ack = append(ack, octet) - } - - for len(ack) > 0 && ack[len(ack)-1] == 0 { - ack = ack[:len(ack)-1] - } - - if len(ack) == 0 { - return nil - } - return ack -} - -func (b *packetBuffer) processSelectiveACK(ack []byte) { - if b.empty() { - return - } - - p := b.root.next - if p == nil { - return - } - - for _, a := range ack { - for i := 0; i < 8; i++ { - acked := (a & 1) != 0 - a >>= 1 - if acked { - p.p = nil - } - p = p.next - if p == nil { - return - } - } - } -} - -type packetRingBuffer struct { - b []*packet - begin int - s int - mutex sync.RWMutex - rch chan int -} - -func newPacketRingBuffer(s int) *packetRingBuffer { - return &packetRingBuffer{ - b: make([]*packet, s), - rch: make(chan int), - } -} - -func (b *packetRingBuffer) size() int { - b.mutex.RLock() - defer b.mutex.RUnlock() - return b.s -} - -func (b *packetRingBuffer) empty() bool { - return b.size() == 0 -} - -func (b *packetRingBuffer) push(p *packet) { - b.mutex.Lock() - defer b.mutex.Unlock() - b.b[(b.begin+b.s)%len(b.b)] = p - if b.s < len(b.b) { - b.s++ - } else { - b.begin = (b.begin + 1) % len(b.b) - } - select { - case b.rch <- 0: - default: - } -} - -func (b *packetRingBuffer) pop() *packet { - if b.empty() { - return nil - } - b.mutex.Lock() - defer b.mutex.Unlock() - p := b.b[b.begin] - b.begin = (b.begin + 1) % len(b.b) - b.s-- - return p -} - -func (b *packetRingBuffer) popOne(timeout time.Duration) (*packet, error) { - var t <-chan time.Time - if timeout != 0 { - t = time.After(timeout) - } - if b.empty() { - select { - case <-b.rch: - case <-t: - return nil, errTimeout - } - } - return b.pop(), nil -} - -type byteRingBuffer struct { - b []byte - begin int - s int - mutex sync.RWMutex - rch chan int - closech chan int - closechMutex sync.Mutex -} - -func newByteRingBuffer(s int) *byteRingBuffer { - return &byteRingBuffer{ - b: make([]byte, s), - rch: make(chan int), - closech: make(chan int), - } -} - -func (r *byteRingBuffer) size() int { - r.mutex.RLock() - defer r.mutex.RUnlock() - return r.s -} - -func (r *byteRingBuffer) space() int { - r.mutex.RLock() - defer r.mutex.RUnlock() - return len(r.b) - r.s -} - -func (r *byteRingBuffer) empty() bool { - return r.size() == 0 -} - -func (r *byteRingBuffer) Write(b []byte) (int, error) { - r.mutex.Lock() - defer r.mutex.Unlock() - - for len(b) > 0 { - end := (r.begin + r.s) % len(r.b) - n := copy(r.b[end:], b) - b = b[n:] - - s := r.s + n - if s > len(r.b) { - r.begin = (r.begin + s - len(r.b)) % len(r.b) - r.s = len(r.b) - } else { - r.s += n - } - } - select { - case r.rch <- 0: - case <-r.closech: - return 0, io.EOF - default: - } - return len(b), nil -} - -func (r *byteRingBuffer) ReadTimeout(b []byte, timeout time.Duration) (int, error) { - var t <-chan time.Time - if timeout != 0 { - t = time.After(timeout) - } - if r.empty() { - select { - case <-r.rch: - case <-t: - return 0, errTimeout - case <-r.closech: - return 0, io.EOF - } - } - l := r.size() - if l > len(b) { - l = len(b) - } - r.mutex.Lock() - defer r.mutex.Unlock() - if r.begin+l > len(r.b) { - n := copy(b, r.b[r.begin:]) - n = copy(b[n:], r.b[:]) - r.begin = n - } else { - copy(b, r.b[r.begin:r.begin+l]) - r.begin = (r.begin + l) % len(r.b) - } - r.s -= l - return l, nil -} - -func (r *byteRingBuffer) Close() error { - r.closechMutex.Lock() - defer r.closechMutex.Unlock() - select { - case <-r.closech: - return errClosing - default: - close(r.closech) - } - return nil -} - -type rateLimitedBuffer struct { - wch chan<- []byte - closech chan int - closechMutex sync.Mutex - size uint32 - sizech chan uint32 - sizeMutex sync.Mutex -} - -func newRateLimitedBuffer(ch chan<- []byte, size uint32) *rateLimitedBuffer { - return &rateLimitedBuffer{ - wch: ch, - closech: make(chan int), - size: size, - sizech: make(chan uint32), - } -} - -func (r *rateLimitedBuffer) WriteTimeout(b []byte, timeout time.Duration) (int, error) { - var t <-chan time.Time - if timeout != 0 { - t = time.After(timeout) - } - - for wrote := uint32(0); wrote < uint32(len(b)); { - r.sizeMutex.Lock() - s := r.size - r.sizeMutex.Unlock() - if s == 0 { - select { - case ns := <-r.sizech: - s = ns - case <-r.closech: - return 0, errClosing - } - } - if s > uint32(len(b))-wrote { - s = uint32(len(b)) - wrote - } - select { - case r.wch <- append([]byte{}, b[wrote:wrote+s]...): - wrote += s - r.sizeMutex.Lock() - r.size -= uint32(s) - r.sizeMutex.Unlock() - case <-r.closech: - return 0, errClosing - case <-t: - return 0, errTimeout - } - } - - return len(b), nil -} - -func (r *rateLimitedBuffer) Reset(size uint32) { - r.sizeMutex.Lock() - defer r.sizeMutex.Unlock() - r.size = size - select { - case r.sizech <- size: - default: - } -} - -func (r *rateLimitedBuffer) Close() error { - r.closechMutex.Lock() - defer r.closechMutex.Unlock() - select { - case <-r.closech: - return errClosing - default: - close(r.closech) - } - return nil -} - -type baseDelayBuffer struct { - b [6]uint32 - last int - min uint32 -} - -func (b *baseDelayBuffer) Push(val uint32) { - t := time.Now() - i := t.Second()/20 + (t.Minute()%2)*3 - if b.last == i { - if b.b[i] > val { - b.b[i] = val - } - } else { - b.b[i] = val - b.last = i - } - min := val - for _, v := range b.b { - if v > 0 && min > v { - min = v - } - } - b.min = min -} - -func (b *baseDelayBuffer) Min() uint32 { - return b.min -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go deleted file mode 100644 index 8249473..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package utp - -import ( - "bytes" - "math" - "testing" - "time" -) - -func TestPacketBuffer(t *testing.T) { - size := 12 - b := newPacketBuffer(12, 1) - - if b.space() != size { - t.Errorf("expected space == %d; got %d", size, b.space()) - } - - for i := 1; i <= size; i++ { - b.push(&packet{header: header{seq: uint16(i)}}) - } - - if b.space() != 0 { - t.Errorf("expected space == 0; got %d", b.space()) - } - - a := []byte{255, 7} - ack := b.generateSelectiveACK() - if !bytes.Equal(a, ack) { - t.Errorf("expected ack == %v; got %v", a, ack) - } - - err := b.push(&packet{header: header{seq: 15}}) - if err == nil { - t.Fatal("push should fail") - } - - all := b.all() - if len(all) != size { - t.Errorf("expected %d packets sequence; got %d", size, len(all)) - } - - f := b.fetch(6) - if f == nil { - t.Fatal("fetch should not fail") - } - - b.compact() - - err = b.push(&packet{header: header{seq: 15}}) - if err != nil { - t.Fatal(err) - } - - err = b.push(&packet{header: header{seq: 17}}) - if err != nil { - t.Fatal(err) - } - - for i := 7; i <= size; i++ { - f := b.fetch(uint16(i)) - if f == nil { - t.Fatal("fetch should not fail") - } - } - - a = []byte{128, 2} - ack = b.generateSelectiveACK() - if !bytes.Equal(a, ack) { - t.Errorf("expected ack == %v; got %v", a, ack) - } - - all = b.all() - if len(all) != 2 { - t.Errorf("expected 2 packets sequence; got %d", len(all)) - } - - b.compact() - if b.space() != 9 { - t.Errorf("expected space == 9; got %d", b.space()) - } - - ack = b.generateSelectiveACK() - b.processSelectiveACK(ack) - - all = b.all() - if len(all) != 1 { - t.Errorf("expected size == 1; got %d", len(all)) - } -} - -func TestPacketBufferBoundary(t *testing.T) { - begin := math.MaxUint16 - 3 - b := newPacketBuffer(12, begin) - for i := begin; i != 5; i = (i + 1) % (math.MaxUint16 + 1) { - err := b.push(&packet{header: header{seq: uint16(i)}}) - if err != nil { - t.Fatal(err) - } - } -} - -func TestPacketRingBuffer(t *testing.T) { - b := newPacketRingBuffer(5) - for i := 0; i < 7; i++ { - b.push(&packet{header: header{seq: uint16(i)}}) - } - - if b.size() != 5 { - t.Errorf("expected size == 5; got %d", b.size()) - } - - p := b.pop() - if p.header.seq != 2 { - t.Errorf("expected header.seq == 2; got %d", p.header.seq) - } - - if b.size() != 4 { - t.Errorf("expected size == 4; got %d", b.size()) - } - - for b.pop() != nil {} - - if !b.empty() { - t.Errorf("buffer must be empty") - } - - go func() { - for i := 0; i < 5; i++ { - b.push(&packet{header: header{seq: uint16(i)}}) - } - }() - - p, err := b.popOne(time.Second) - if err != nil { - t.Fatal(err) - } - - if p.header.seq != 0 { - t.Errorf("expected header.seq == 0; got %d", p.header.seq) - } -} - -func TestByteRingBuffer(t *testing.T) { - - b := newByteRingBuffer(5) - for i := 0; i < 100; i++ { - b.Write([]byte{byte(i)}) - } - - var buf [10]byte - l, err := b.ReadTimeout(buf[:], 0) - if err != nil { - t.Fatal(err) - } - - e := []byte{95, 96, 97, 98, 99} - if !bytes.Equal(buf[:l], e) { - t.Errorf("expected payload of %v; got %v", e, buf[:l]) - } - - e2 := []byte("abcdefghijklmnopqrstuvwxyz") - go func() { - _, err := b.Write(e2) - if err != nil { - t.Fatal(err) - } - }() - - l, err = b.ReadTimeout(buf[:], 0) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(buf[:l], e2[len(e2)-5:]) { - t.Errorf("expected payload of %v; got %v", e2[len(e2)-5:], buf[:l]) - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/conn.go b/Godeps/_workspace/src/github.com/h2so5/utp/conn.go deleted file mode 100644 index b1544e9..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/conn.go +++ /dev/null @@ -1,555 +0,0 @@ -package utp - -import ( - "math" - "net" - "sync" - "sync/atomic" - "syscall" - "time" -) - -// Conn is an implementation of the Conn interface for UTP network -// connections. -type Conn struct { - conn *baseConn - raddr net.Addr - rid, sid, seq, ack, lastAck uint16 - rtt, rttVar, minRtt, rto int64 - dupAck int - diff, maxWindow uint32 - - state int - closed int32 - - recvbuf *packetBuffer - sendbuf *packetBuffer - - readbuf *byteRingBuffer - writebuf *rateLimitedBuffer - - baseDelay baseDelayBuffer - - writech chan []byte - ackch chan int - synch chan int - - rdeadline time.Time - wdeadline time.Time - deadlineMutex sync.RWMutex - - recv chan *packet - - closing bool - closingch chan int - - keepalivech chan time.Duration - - connch chan int - - closech chan int - closechMutex sync.Mutex - - stat statistics -} - -type statistics struct { - sentPackets int - resentPackets int - receivedPackets int - receivedDuplicatedACKs int - packetTimedOuts int - sentSelectiveACKs int - receivedSelectiveACKs int - rtoSum int64 - rtoCount int -} - -func newConn() *Conn { - wch := make(chan []byte) - c := &Conn{ - minRtt: math.MaxInt64, - maxWindow: mss, - rto: int64(60), - - recv: make(chan *packet), - connch: make(chan int), - - recvbuf: newPacketBuffer(0, 0), - - readbuf: newByteRingBuffer(readBufferSize), - writebuf: newRateLimitedBuffer(wch, mss), - - writech: wch, - ackch: make(chan int), - synch: make(chan int), - - closingch: make(chan int), - keepalivech: make(chan time.Duration), - closech: make(chan int), - } - return c -} - -func (c *Conn) ok() bool { return c != nil && c.conn != nil } - -// Close closes the connection. -func (c *Conn) Close() error { - if !c.ok() { - return syscall.EINVAL - } - if !c.isOpen() { - return nil - } - select { - case <-c.closingch: - default: - close(c.closingch) - } - select { - case <-c.connch: - default: - return nil - } - <-c.closech - return nil -} - -// LocalAddr returns the local network address. -func (c *Conn) LocalAddr() net.Addr { - if !c.ok() { - return nil - } - return c.conn.LocalAddr() -} - -// RemoteAddr returns the remote network address. -func (c *Conn) RemoteAddr() net.Addr { - if !c.ok() { - return nil - } - return c.raddr -} - -// Read implements the Conn Read method. -func (c *Conn) Read(b []byte) (int, error) { - if !c.ok() { - return 0, syscall.EINVAL - } - if !c.isOpen() { - return 0, &net.OpError{ - Op: "read", - Net: c.LocalAddr().Network(), - Addr: c.LocalAddr(), - Err: errClosing, - } - } - s := c.readbuf.space() - c.deadlineMutex.RLock() - d := timeToDeadline(c.rdeadline) - c.deadlineMutex.RUnlock() - l, err := c.readbuf.ReadTimeout(b, d) - if s < mss && c.readbuf.space() > 0 { - select { - case c.ackch <- 0: - default: - } - } - return l, err -} - -func timeToDeadline(deadline time.Time) (d time.Duration) { - if deadline.IsZero() { - return - } - d = deadline.Sub(time.Now()) - if d < 0 { - d = 0 - } - return -} - -// Write implements the Conn Write method. -func (c *Conn) Write(b []byte) (int, error) { - if !c.ok() { - return 0, syscall.EINVAL - } - if !c.isOpen() { - return 0, &net.OpError{ - Op: "write", - Net: c.LocalAddr().Network(), - Addr: c.LocalAddr(), - Err: errClosing, - } - } - c.deadlineMutex.RLock() - d := timeToDeadline(c.wdeadline) - c.deadlineMutex.RUnlock() - return c.writebuf.WriteTimeout(b, d) -} - -// SetDeadline implements the Conn SetDeadline method. -func (c *Conn) SetDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - err := c.SetReadDeadline(t) - if err != nil { - return err - } - return c.SetWriteDeadline(t) -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *Conn) SetReadDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - c.deadlineMutex.Lock() - defer c.deadlineMutex.Unlock() - c.rdeadline = t - return nil -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *Conn) SetWriteDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - c.deadlineMutex.Lock() - defer c.deadlineMutex.Unlock() - c.wdeadline = t - return nil -} - -// SetKeepAlive sets the keepalive interval associated with the connection. -func (c *Conn) SetKeepAlive(d time.Duration) error { - if !c.ok() { - return syscall.EINVAL - } - if !c.isOpen() { - return errClosing - } - c.keepalivech <- d - return nil -} - -func (c *Conn) loop() { - defer c.conn.Unregister(int32(c.rid)) - - var resendSeq uint16 - var resendCont int - var keepalive <-chan time.Time - - for { - f := c.sendbuf.front() - var resend <-chan time.Time - if f != nil { - resend = time.After(time.Duration(c.rto) * time.Millisecond) - } - select { - case <-c.ackch: - c.sendACK() - case <-c.synch: - c.sendSYN() - case p := <-c.recv: - c.stat.receivedPackets++ - c.processPacket(p) - case b := <-c.writech: - c.sendDATA(b) - case <-c.closingch: - c.enterClosing() - case <-resend: - if resendSeq == f.header.seq { - resendCont++ - } else { - resendCont = 0 - resendSeq = f.header.seq - } - c.stat.packetTimedOuts++ - if resendCont > maxRetry { - c.sendRST() - c.close() - } else { - c.maxWindow /= 2 - if c.maxWindow < mtu { - c.maxWindow = mtu - } - for _, p := range c.sendbuf.sequence() { - c.resend(p) - } - } - case <-c.closech: - c.readbuf.Close() - c.state = stateClosed - atomic.StoreInt32(&c.closed, 1) - return - case d := <-c.keepalivech: - if d <= 0 { - keepalive = nil - } else { - keepalive = time.Tick(d) - } - case <-keepalive: - ulog.Printf(2, "Conn(%v): Send keepalive", c.LocalAddr()) - c.sendACK() - } - if c.closing { - if c.state == stateSynSent || (c.recvbuf.empty() && c.sendbuf.empty()) { - c.close() - } - } - } -} - -func (c *Conn) enterClosing() { - if !c.closing { - c.sendFIN() - c.writebuf.Close() - c.state = stateFinSent - c.closing = true - } -} - -func (c *Conn) close() { - c.closechMutex.Lock() - defer c.closechMutex.Unlock() - select { - case <-c.closech: - default: - close(c.closech) - } - ulog.Printf(1, "Conn(%v): closed", c.LocalAddr()) - ulog.Printf(1, "Conn(%v): * SentPackets: %d", c.LocalAddr(), c.stat.sentPackets) - ulog.Printf(1, "Conn(%v): * ResentPackets: %d", c.LocalAddr(), c.stat.resentPackets) - ulog.Printf(1, "Conn(%v): * ReceivedPackets: %d", c.LocalAddr(), c.stat.receivedPackets) - ulog.Printf(1, "Conn(%v): * ReceivedDuplicatedACKs: %d", c.LocalAddr(), c.stat.receivedDuplicatedACKs) - ulog.Printf(1, "Conn(%v): * PacketTimedOuts: %d", c.LocalAddr(), c.stat.packetTimedOuts) - ulog.Printf(1, "Conn(%v): * SentSelectiveACKs: %d", c.LocalAddr(), c.stat.sentSelectiveACKs) - ulog.Printf(1, "Conn(%v): * ReceivedSelectiveACKs: %d", c.LocalAddr(), c.stat.receivedSelectiveACKs) - if c.stat.rtoCount > 0 { - ulog.Printf(1, "Conn(%v): * AverageRTO: %d", c.LocalAddr(), c.stat.rtoSum/int64(c.stat.rtoCount)) - } -} - -func (c *Conn) isOpen() bool { - return atomic.LoadInt32(&c.closed) == 0 -} - -func currentMicrosecond() uint32 { - return uint32(time.Now().Nanosecond() / 1000) -} - -func (c *Conn) processPacket(p *packet) { - if p.header.t == 0 { - c.diff = 0 - } else { - t := currentMicrosecond() - if t > p.header.t { - c.diff = t - p.header.t - if c.minRtt > int64(c.diff) { - c.minRtt = int64(c.diff) - } - } - } - - c.baseDelay.Push(c.diff) - - switch p.header.typ { - case stState: - f := c.sendbuf.front() - if f != nil && p.header.ack == f.header.seq { - for _, e := range p.ext { - if e.typ == extSelectiveAck { - ulog.Printf(3, "Conn(%v): Receive Selective ACK", c.LocalAddr()) - c.stat.receivedSelectiveACKs++ - c.sendbuf.processSelectiveACK(e.payload) - } - } - } - - s := c.sendbuf.fetch(p.header.ack) - if s != nil { - current := currentMicrosecond() - if current > s.header.t { - e := int64(current-s.header.t) / 1000 - if c.rtt == 0 { - c.rtt = e - c.rttVar = e / 2 - } else { - d := c.rtt - e - if d < 0 { - d = -d - } - c.rttVar += (d - c.rttVar) / 4 - c.rtt = c.rtt - c.rtt/8 + e/8 - } - c.rto = c.rtt + c.rttVar*4 - if c.rto < 60 { - c.rto = 60 - } else if c.rto > 1000 { - c.rto = 1000 - } - c.stat.rtoSum += c.rto - c.stat.rtoCount++ - } - - ourDelay := float64(c.diff - c.baseDelay.Min()) - if ourDelay != 0.0 { - offTarget := 100000.0 - ourDelay - windowFactor := float64(mtu) / float64(c.maxWindow) - delayFactor := offTarget / 100000.0 - gain := 3000.0 * delayFactor * windowFactor - c.maxWindow = uint32(int(c.maxWindow) + int(gain)) - if c.maxWindow < mtu { - c.maxWindow = mtu - } - ulog.Printf(4, "Conn(%v): Update maxWindow: %d", c.LocalAddr(), c.maxWindow) - } - } - - c.sendbuf.compact() - - if c.lastAck == p.header.ack { - c.dupAck++ - if c.dupAck >= 2 { - c.stat.receivedDuplicatedACKs++ - ulog.Printf(3, "Conn(%v): Receive 3 duplicated acks: %d", c.LocalAddr(), p.header.ack) - p := c.sendbuf.front() - if p != nil { - c.maxWindow /= 2 - if c.maxWindow < mtu { - c.maxWindow = mtu - } - ulog.Printf(4, "Conn(%v): Update maxWindow: %d", c.LocalAddr(), c.maxWindow) - c.resend(p) - } - c.dupAck = 0 - } - } else { - c.dupAck = 0 - } - - c.lastAck = p.header.ack - if p.header.ack == c.seq-1 { - wnd := p.header.wnd - if wnd > c.maxWindow { - wnd = c.maxWindow - } - c.writebuf.Reset(wnd) - } - - if c.state == stateSynSent { - c.recvbuf = newPacketBuffer(windowSize, int(p.header.seq)) - c.state = stateConnected - close(c.connch) - } - - case stReset: - c.sendRST() - c.close() - - default: - c.recvbuf.push(p) - for _, s := range c.recvbuf.fetchSequence() { - c.ack = s.header.seq - if s.header.typ == stData { - c.readbuf.Write(s.payload) - } else if s.header.typ == stFin { - c.enterClosing() - } - } - c.sendACK() - } -} - -func (c *Conn) sendACK() { - ack := c.makePacket(stState, nil, c.raddr) - selack := c.sendbuf.generateSelectiveACK() - if selack != nil { - c.stat.sentSelectiveACKs++ - ack.ext = []extension{ - extension{ - typ: extSelectiveAck, - payload: selack, - }, - } - } - c.stat.sentPackets++ - c.conn.Send(ack) -} - -func (c *Conn) sendSYN() { - syn := c.makePacket(stSyn, nil, c.raddr) - err := c.sendbuf.push(syn) - if err != nil { - panic(err) - } - c.stat.sentPackets++ - c.conn.Send(syn) -} - -func (c *Conn) sendFIN() { - fin := c.makePacket(stFin, nil, c.raddr) - err := c.sendbuf.push(fin) - if err != nil { - panic(err) - } - c.stat.sentPackets++ - c.conn.Send(fin) -} - -func (c *Conn) sendRST() { - rst := c.makePacket(stReset, nil, c.raddr) - c.stat.sentPackets++ - c.conn.Send(rst) -} - -func (c *Conn) sendDATA(b []byte) { - for i := 0; i <= len(b)/mss; i++ { - l := len(b) - i*mss - if l > mss { - l = mss - } - data := c.makePacket(stData, b[i*mss:i*mss+l], c.raddr) - c.sendbuf.push(data) - c.stat.sentPackets++ - c.conn.Send(data) - } -} - -func (c *Conn) resend(p *packet) { - c.stat.resentPackets++ - c.conn.Send(p) - ulog.Printf(3, "Conn(%v): RESEND: %s", c.LocalAddr(), p.String()) -} - -func (c *Conn) makePacket(typ int, payload []byte, dst net.Addr) *packet { - wnd := windowSize * mtu - if c.recvbuf != nil { - wnd = c.recvbuf.space() * mtu - } - s := c.readbuf.space() - if wnd > s { - wnd = s - } - id := c.sid - if typ == stSyn { - id = c.rid - } - p := &packet{} - p.header.typ = typ - p.header.ver = version - p.header.id = id - p.header.t = currentMicrosecond() - p.header.diff = c.diff - p.header.wnd = uint32(wnd) - p.header.seq = c.seq - p.header.ack = c.ack - p.addr = dst - if typ != stState { - c.seq++ - } - p.payload = payload - return p -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go deleted file mode 100644 index 8645d32..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package utp - -import ( - "bytes" - "testing" -) - -func TestReadWrite(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - l, err := Listen("utp", addr) - if err != nil { - t.Fatal(err) - } - defer l.Close() - - payload := []byte("abcdefgh") - - ch := make(chan int) - go func() { - c, err := l.Accept() - if err != nil { - t.Fatal(err) - } - defer c.Close() - - var buf [256]byte - length, err := c.Read(buf[:]) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(payload, buf[:length]) { - t.Errorf("expected payload of %v; got %v", payload, buf[:length]) - } - - ch <- 0 - }() - - c, err := DialUTP("utp", nil, l.Addr().(*Addr)) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - _, err = c.Write(payload) - if err != nil { - t.Fatal(err) - } - - <-ch -} - -func TestClose(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - l, err := Listen("utp", addr) - if err != nil { - t.Fatal(err) - } - defer l.Close() - - go func() { - c, err := l.Accept() - if err != nil { - t.Fatal(err) - } - c.Close() - }() - - c, err := DialUTP("utp", nil, l.Addr().(*Addr)) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - var b [128]byte - _, err = c.Read(b[:]) - if err == nil { - t.Fatal("Read should fail") - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/dial.go b/Godeps/_workspace/src/github.com/h2so5/utp/dial.go deleted file mode 100644 index ae1bf4f..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/dial.go +++ /dev/null @@ -1,101 +0,0 @@ -package utp - -import ( - "errors" - "math" - "math/rand" - "net" - "time" -) - -// DialUTP connects to the remote address raddr on the network net, -// which must be "utp", "utp4", or "utp6". If laddr is not nil, it is -// used as the local address for the connection. -func DialUTP(n string, laddr, raddr *Addr) (*Conn, error) { - return DialUTPTimeout(n, laddr, raddr, 0) -} - -// DialUTPTimeout acts like Dial but takes a timeout. -// The timeout includes name resolution, if required. -func DialUTPTimeout(n string, laddr, raddr *Addr, timeout time.Duration) (*Conn, error) { - conn, err := getSharedBaseConn(n, laddr) - if err != nil { - return nil, err - } - - id := uint16(rand.Intn(math.MaxUint16)) - c := newConn() - c.conn = conn - c.raddr = raddr.Addr - c.rid = id - c.sid = id + 1 - c.seq = 1 - c.state = stateSynSent - c.sendbuf = newPacketBuffer(windowSize*2, 1) - c.conn.Register(int32(c.rid), c.recv) - go c.loop() - c.synch <- 0 - - var t <-chan time.Time - if timeout != 0 { - t = time.After(timeout) - } - - select { - case <-c.connch: - case <-t: - c.Close() - return nil, &net.OpError{ - Op: "dial", - Net: c.LocalAddr().Network(), - Addr: c.LocalAddr(), - Err: errTimeout, - } - } - return c, nil -} - -// A Dialer contains options for connecting to an address. -// -// The zero value for each field is equivalent to dialing without -// that option. Dialing with the zero value of Dialer is therefore -// equivalent to just calling the Dial function. -type Dialer struct { - // Timeout is the maximum amount of time a dial will wait for - // a connect to complete. If Deadline is also set, it may fail - // earlier. - // - // The default is no timeout. - // - // With or without a timeout, the operating system may impose - // its own earlier timeout. For instance, TCP timeouts are - // often around 3 minutes. - Timeout time.Duration - - // LocalAddr is the local address to use when dialing an - // address. The address must be of a compatible type for the - // network being dialed. - // If nil, a local address is automatically chosen. - LocalAddr net.Addr -} - -// Dial connects to the address on the named network. -// -// See func Dial for a description of the network and address parameters. -func (d *Dialer) Dial(n, addr string) (*Conn, error) { - raddr, err := ResolveAddr(n, addr) - if err != nil { - return nil, err - } - - var laddr *Addr - if d.LocalAddr != nil { - var ok bool - laddr, ok = d.LocalAddr.(*Addr) - if !ok { - return nil, errors.New("Dialer.LocalAddr is not a Addr") - } - } - - return DialUTPTimeout(n, laddr, raddr, d.Timeout) -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go deleted file mode 100644 index 7846076..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package utp - -import ( - "testing" - "time" -) - -func TestDial(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - l, err := Listen("utp", addr) - if err != nil { - t.Fatal(err) - } - defer l.Close() - - ch := make(chan struct{}) - go func() { - l.Accept() - close(ch) - }() - - c, err := DialUTP("utp", nil, l.Addr().(*Addr)) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - <-ch -} - -func TestDialFastTimeout(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - l, err := Listen("utp", addr) - if err != nil { - t.Fatal(err) - } - defer l.Close() - _, err = (&Dialer{ - Timeout: time.Nanosecond, - }).Dial("utp", l.Addr().String()) - if err == nil { - t.Fatal("expected an error") - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/listener.go b/Godeps/_workspace/src/github.com/h2so5/utp/listener.go deleted file mode 100644 index cdf9a7c..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/listener.go +++ /dev/null @@ -1,146 +0,0 @@ -package utp - -import ( - "math" - "math/rand" - "net" - "sync" - "sync/atomic" - "syscall" - "time" -) - -// Listener is a UTP network listener. Clients should typically -// use variables of type Listener instead of assuming UTP. -type Listener struct { - // RawConn represents an out-of-band connection. - // This allows a single socket to handle multiple protocols. - RawConn net.PacketConn - - conn *baseConn - deadline time.Time - deadlineMutex sync.RWMutex - closed int32 -} - -func (l *Listener) ok() bool { return l != nil && l.conn != nil } - -// Listen announces on the UTP address laddr and returns a UTP -// listener. Net must be "utp", "utp4", or "utp6". If laddr has a -// port of 0, ListenUTP will choose an available port. The caller can -// use the Addr method of Listener to retrieve the chosen address. -func Listen(n string, laddr *Addr) (*Listener, error) { - conn, err := newBaseConn(n, laddr) - if err != nil { - return nil, err - } - l := &Listener{ - RawConn: conn, - conn: conn, - } - conn.Register(-1, nil) - return l, nil -} - -// Accept implements the Accept method in the Listener interface; it -// waits for the next call and returns a generic Conn. -func (l *Listener) Accept() (net.Conn, error) { - return l.AcceptUTP() -} - -// AcceptUTP accepts the next incoming call and returns the new -// connection. -func (l *Listener) AcceptUTP() (*Conn, error) { - if !l.ok() { - return nil, syscall.EINVAL - } - if !l.isOpen() { - return nil, &net.OpError{ - Op: "accept", - Net: l.conn.LocalAddr().Network(), - Addr: l.conn.LocalAddr(), - Err: errClosing, - } - } - l.deadlineMutex.RLock() - d := timeToDeadline(l.deadline) - l.deadlineMutex.RUnlock() - p, err := l.conn.RecvSyn(d) - if err != nil { - return nil, &net.OpError{ - Op: "accept", - Net: l.conn.LocalAddr().Network(), - Addr: l.conn.LocalAddr(), - Err: errClosing, - } - } - - seq := rand.Intn(math.MaxUint16) - rid := p.header.id + 1 - - c := newConn() - c.state = stateConnected - c.conn = l.conn - c.raddr = p.addr - c.rid = p.header.id + 1 - c.sid = p.header.id - c.seq = uint16(seq) - c.ack = p.header.seq - c.recvbuf = newPacketBuffer(windowSize, int(p.header.seq)) - c.sendbuf = newPacketBuffer(windowSize*2, seq) - l.conn.Register(int32(rid), c.recv) - go c.loop() - c.recv <- p - - ulog.Printf(2, "baseConn(%v): accept #%d from %v", c.LocalAddr(), c.rid, c.raddr) - return c, nil -} - -// Addr returns the listener's network address, a *Addr. -func (l *Listener) Addr() net.Addr { - if !l.ok() { - return nil - } - return l.conn.LocalAddr() -} - -// Close stops listening on the UTP address. -// Already Accepted connections are not closed. -func (l *Listener) Close() error { - if !l.ok() { - return syscall.EINVAL - } - if !l.close() { - return &net.OpError{ - Op: "close", - Net: l.conn.LocalAddr().Network(), - Addr: l.conn.LocalAddr(), - Err: errClosing, - } - } - return nil -} - -// SetDeadline sets the deadline associated with the listener. -// A zero time value disables the deadline. -func (l *Listener) SetDeadline(t time.Time) error { - if !l.ok() { - return syscall.EINVAL - } - l.deadlineMutex.Lock() - defer l.deadlineMutex.Unlock() - l.deadline = t - return nil -} - -func (l *Listener) close() bool { - if atomic.CompareAndSwapInt32(&l.closed, 0, 1) { - l.conn.Unregister(-1) - return true - } - return false -} - -func (l *Listener) isOpen() bool { - return atomic.LoadInt32(&l.closed) == 0 -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go deleted file mode 100644 index e9018a3..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package utp - -import ( - "net" - "testing" -) - -func TestListenerAccept(t *testing.T) { - addr, err := ResolveAddr("utp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - l, err := Listen("utp", addr) - if err != nil { - t.Fatal(err) - } - defer l.Close() - - uaddr, err := net.ResolveUDPAddr("udp", l.Addr().String()) - if err != nil { - t.Fatal(err) - } - - uc, err := net.DialUDP("udp", nil, uaddr) - if err != nil { - t.Fatal(err) - } - defer uc.Close() - - for i := 0; i < 1; i++ { - p := &packet{header: header{typ: stSyn, ver: version, id: uint16(i)}} - payload, err := p.MarshalBinary() - if err != nil { - t.Fatal(err) - } - go func() { - uc.Write(payload) - }() - - a, err := l.Accept() - if err != nil { - t.Fatal(err) - } - a.Close() - } -} - -func TestListenerClose(t *testing.T) { - addr, err := ResolveAddr("utp", ":0") - if err != nil { - t.Fatal(err) - } - - l, err := Listen("utp", addr) - if err != nil { - t.Fatal(err) - } - - for i := 0; i < 5; i++ { - l.Close() - } - - _, err = l.Accept() - if err == nil { - t.Fatal("Accept should fail") - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/log.go b/Godeps/_workspace/src/github.com/h2so5/utp/log.go deleted file mode 100644 index dbf4ddc..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/log.go +++ /dev/null @@ -1,50 +0,0 @@ -package utp - -import ( - "log" - "os" - "strconv" -) - -type logger struct { - level int -} - -var ulog *logger - -func init() { - logenv := os.Getenv("GO_UTP_LOGGING") - - var level int - if len(logenv) > 0 { - l, err := strconv.Atoi(logenv) - if err != nil { - log.Print("warning: GO_UTP_LOGGING must be numeric") - } else { - level = l - } - } - - ulog = &logger{level} -} - -func (l *logger) Print(level int, v ...interface{}) { - if l.level < level { - return - } - log.Print(v...) -} - -func (l *logger) Printf(level int, format string, v ...interface{}) { - if l.level < level { - return - } - log.Printf(format, v...) -} - -func (l *logger) Println(level int, v ...interface{}) { - if l.level < level { - return - } - log.Println(v...) -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/packet.go b/Godeps/_workspace/src/github.com/h2so5/utp/packet.go deleted file mode 100644 index da3c302..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/packet.go +++ /dev/null @@ -1,198 +0,0 @@ -package utp - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "net" -) - -type header struct { - typ, ver int - id uint16 - t, diff, wnd uint32 - seq, ack uint16 -} - -type extension struct { - typ int - payload []byte -} - -type packet struct { - header header - ext []extension - payload []byte - addr net.Addr -} - -func (p *packet) MarshalBinary() ([]byte, error) { - firstExt := extNone - if len(p.ext) > 0 { - firstExt = p.ext[0].typ - } - buf := new(bytes.Buffer) - var beforeExt = []interface{}{ - // | type | ver | - uint8(((byte(p.header.typ) << 4) & 0xF0) | (byte(p.header.ver) & 0xF)), - // | extension | - uint8(firstExt), - } - var afterExt = []interface{}{ - // | connection_id | - uint16(p.header.id), - // | timestamp_microseconds | - uint32(p.header.t), - // | timestamp_difference_microseconds | - uint32(p.header.diff), - // | wnd_size | - uint32(p.header.wnd), - // | seq_nr | - uint16(p.header.seq), - // | ack_nr | - uint16(p.header.ack), - } - - for _, v := range beforeExt { - err := binary.Write(buf, binary.BigEndian, v) - if err != nil { - return nil, err - } - } - - if len(p.ext) > 0 { - for i, e := range p.ext { - next := extNone - if i < len(p.ext)-1 { - next = p.ext[i+1].typ - } - var ext = []interface{}{ - // | extension | - uint8(next), - // | len | - uint8(len(e.payload)), - } - for _, v := range ext { - err := binary.Write(buf, binary.BigEndian, v) - if err != nil { - return nil, err - } - } - _, err := buf.Write(e.payload) - if err != nil { - return nil, err - } - } - } - - for _, v := range afterExt { - err := binary.Write(buf, binary.BigEndian, v) - if err != nil { - return nil, err - } - } - - _, err := buf.Write(p.payload) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func (p *packet) UnmarshalBinary(data []byte) error { - p.ext = nil - buf := bytes.NewReader(data) - var tv, e uint8 - - var beforeExt = []interface{}{ - // | type | ver | - (*uint8)(&tv), - // | extension | - (*uint8)(&e), - } - for _, v := range beforeExt { - err := binary.Read(buf, binary.BigEndian, v) - if err != nil { - return err - } - } - - for e != extNone { - currentExt := int(e) - var l uint8 - var ext = []interface{}{ - // | extension | - (*uint8)(&e), - // | len | - (*uint8)(&l), - } - for _, v := range ext { - err := binary.Read(buf, binary.BigEndian, v) - if err != nil { - return err - } - } - payload := make([]byte, l) - size, err := buf.Read(payload[:]) - if err != nil { - return err - } - if size != len(payload) { - return io.EOF - } - p.ext = append(p.ext, extension{typ: currentExt, payload: payload}) - } - - var afterExt = []interface{}{ - // | connection_id | - (*uint16)(&p.header.id), - // | timestamp_microseconds | - (*uint32)(&p.header.t), - // | timestamp_difference_microseconds | - (*uint32)(&p.header.diff), - // | wnd_size | - (*uint32)(&p.header.wnd), - // | seq_nr | - (*uint16)(&p.header.seq), - // | ack_nr | - (*uint16)(&p.header.ack), - } - for _, v := range afterExt { - err := binary.Read(buf, binary.BigEndian, v) - if err != nil { - return err - } - } - - p.header.typ = int((tv >> 4) & 0xF) - p.header.ver = int(tv & 0xF) - - data, err := ioutil.ReadAll(buf) - if err != nil { - return err - } - p.payload = data - - return nil -} - -func (p packet) String() string { - s := fmt.Sprintf("[%d ", p.header.id) - switch p.header.typ { - case stData: - s += "ST_DATA" - case stFin: - s += "ST_FIN" - case stState: - s += "ST_STATE" - case stReset: - s += "ST_RESET" - case stSyn: - s += "ST_SYN" - } - s += fmt.Sprintf(" seq:%d ack:%d len:%d", p.header.seq, p.header.ack, len(p.payload)) - s += "]" - return s -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go deleted file mode 100644 index 994fa2c..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package utp - -import ( - "io" - "reflect" - "testing" -) - -func TestPacketBinary(t *testing.T) { - h := header{ - typ: stFin, - ver: version, - id: 100, - t: 50000, - diff: 10000, - wnd: 65535, - seq: 100, - ack: 200, - } - - e := []extension{ - extension{ - typ: extSelectiveAck, - payload: []byte{0, 1, 0, 1}, - }, - extension{ - typ: extSelectiveAck, - payload: []byte{100, 0, 200, 0}, - }, - } - - p := packet{ - header: h, - ext: e, - payload: []byte("abcdefg"), - } - - b, err := p.MarshalBinary() - if err != nil { - t.Fatal(err) - } - - p2 := packet{payload: make([]byte, 0, mss)} - err = p2.UnmarshalBinary(b) - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(p, p2) { - t.Errorf("expected packet of %v; got %v", p, p2) - } -} - -func TestUnmarshalShortPacket(t *testing.T) { - b := make([]byte, 18) - p := packet{} - err := p.UnmarshalBinary(b) - - if err == nil { - t.Fatal("UnmarshalBinary should fail") - } else if err != io.EOF { - t.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore deleted file mode 100644 index aab84c1..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -ucat -random -.trash/ diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile deleted file mode 100644 index 6009f77..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# Run tests - -testnames=simple -tests=$(addprefix test_, $(testnames)) -trash=.trash/ - -all: ucat - -test: clean ucat ${tests} - @echo ${tests} - @echo "*** tests passed ***" - -# not sue why this doesn't work: -# test_%: test_%.sh -test_simple: test_simple.sh - mkdir -p ${trash} - @echo "*** running $@ ***" - ./$@.sh - -clean: - @echo "*** $@ ***" - -rm -r ${trash} - -deps: random ucat - -ucat: - go build - -random: - @echo "*** installing $@ ***" - go get github.com/jbenet/go-random/random - go build -o random github.com/jbenet/go-random/random - -.PHONY: clean ucat ${tests} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh deleted file mode 100644 index 5e7842c..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh - -set -e # exit on error -# set -v # verbose - -log() { - echo "--> $1" -} - -test_send() { - file=$1_ - count=$2 - addr=localhost:8765 - - # generate random data - log "generating $count bytes of random data" - ./random $count $RANDOM > ${file}expected - - # dialer sends - log "sending from dialer" - ./ucat -v $addr 2>&1 <${file}expected | sed "s/^/ dialer1: /" & - ./ucat -v -l $addr 2>&1 >${file}actual1 | sed "s/^/listener1: /" - diff ${file}expected ${file}actual1 - if test $? != 0; then - log "sending from dialer failed. compare with:\n" - log "diff ${file}expected ${file}actual1" - exit 1 - fi - - # listener sends - log "sending from listener" - ./ucat -v -l $addr 2>&1 <${file}expected | sed "s/^/listener2: /" & - ./ucat -v $addr 2>&1 >${file}actual2 | sed "s/^/ dialer2: /" - diff ${file}expected ${file}actual2 - if test $? != 0; then - log "sending from listener failed. compare with:\n" - log "diff ${file}expected ${file}actual2" - exit 1 - fi - - echo rm ${file}{expected,actual1,actual2} - rm ${file}{expected,actual1,actual2} - return 0 -} - - -test_send ".trash/1KB" 1024 -test_send ".trash/1MB" 1048576 -test_send ".trash/1GB" 1073741824 diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go deleted file mode 100644 index a377dad..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go +++ /dev/null @@ -1,192 +0,0 @@ -// package ucat provides an implementation of netcat using the go utp package. -// It is meant to exercise the utp implementation. -// Usage: -// ucat [] -// ucat -l -// -// Address format is: [host]:port -// -// Note that uTP's congestion control gives priority to tcp flows (web traffic), -// so you could use this ucat tool to transfer massive files without hogging -// all the bandwidth. -package main - -import ( - "flag" - "fmt" - "io" - "net" - "os" - "os/signal" - "syscall" - - utp "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/h2so5/utp" -) - -var verbose = false - -// Usage prints out the usage of this module. -// Assumes flags use go stdlib flag pacakage. -var Usage = func() { - text := `ucat - uTP netcat in Go - -Usage: - - listen: %s [] - dial: %s -l - -Address format is Go's: [host]:port -` - - fmt.Fprintf(os.Stderr, text, os.Args[0], os.Args[0]) - flag.PrintDefaults() -} - -type args struct { - listen bool - verbose bool - localAddr string - remoteAddr string -} - -func parseArgs() args { - var a args - - // setup + parse flags - flag.BoolVar(&a.listen, "listen", false, "listen for connections") - flag.BoolVar(&a.listen, "l", false, "listen for connections (short)") - flag.BoolVar(&a.verbose, "v", false, "verbose debugging") - flag.Usage = Usage - flag.Parse() - osArgs := flag.Args() - - if len(osArgs) < 1 { - exit("") - } - - if a.listen { - a.localAddr = osArgs[0] - } else { - if len(osArgs) > 1 { - a.localAddr = osArgs[0] - a.remoteAddr = osArgs[1] - } else { - a.remoteAddr = osArgs[0] - } - } - - return a -} - -func main() { - args := parseArgs() - verbose = args.verbose - - var err error - if args.listen { - err = Listen(args.localAddr) - } else { - err = Dial(args.localAddr, args.remoteAddr) - } - - if err != nil { - exit("%s", err) - } -} - -func exit(format string, vals ...interface{}) { - if format != "" { - fmt.Fprintf(os.Stderr, "ucat error: "+format+"\n", vals...) - } - Usage() - os.Exit(1) -} - -func log(format string, vals ...interface{}) { - if verbose { - fmt.Fprintf(os.Stderr, "ucat log: "+format+"\n", vals...) - } -} - -// Listen listens and accepts one incoming uTP connection on a given port, -// and pipes all incoming data to os.Stdout. -func Listen(localAddr string) error { - laddr, err := utp.ResolveAddr("utp", localAddr) - if err != nil { - return fmt.Errorf("failed to resolve address %s", localAddr) - } - l, err := utp.Listen("utp", laddr) - if err != nil { - return err - } - log("listening at %s", l.Addr()) - - c, err := l.Accept() - if err != nil { - return err - } - log("accepted connection from %s", c.RemoteAddr()) - - // should be able to close listener here, but utp.Listener.Close - // closes all open connections. - defer l.Close() - - netcat(c) - return c.Close() -} - -// Dial connects to a remote address and pipes all os.Stdin to the remote end. -// If localAddr is set, uses it to Dial from. -func Dial(localAddr, remoteAddr string) error { - - var laddr net.Addr - var err error - if localAddr != "" { - laddr, err = utp.ResolveAddr("utp", localAddr) - if err != nil { - return fmt.Errorf("failed to resolve address %s", localAddr) - } - } - - if laddr != nil { - log("dialing %s from %s", remoteAddr, laddr) - } else { - log("dialing %s", remoteAddr) - } - - d := utp.Dialer{LocalAddr: laddr} - c, err := d.Dial("utp", remoteAddr) - if err != nil { - return err - } - log("connected to %s", c.RemoteAddr()) - - netcat(c) - return c.Close() -} - -func netcat(c net.Conn) { - log("piping stdio to connection") - - done := make(chan struct{}) - - go func() { - n, _ := io.Copy(c, os.Stdin) - log("sent %d bytes", n) - done <- struct{}{} - }() - go func() { - n, _ := io.Copy(os.Stdout, c) - log("received %d bytes", n) - done <- struct{}{} - }() - - // wait until we exit. - sigc := make(chan os.Signal, 1) - signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT, - syscall.SIGTERM, syscall.SIGQUIT) - select { - case <-done: - case <-sigc: - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/utp.go b/Godeps/_workspace/src/github.com/h2so5/utp/utp.go deleted file mode 100644 index 1ac4b31..0000000 --- a/Godeps/_workspace/src/github.com/h2so5/utp/utp.go +++ /dev/null @@ -1,47 +0,0 @@ -package utp - -import ( - "errors" - "time" -) - -const ( - version = 1 - - stData = 0 - stFin = 1 - stState = 2 - stReset = 3 - stSyn = 4 - - stateClosed = iota - stateClosing - stateSynSent - stateConnected - stateFinSent - - extNone = 0 - extSelectiveAck = 1 - - headerSize = 20 - mtu = 3200 - mss = mtu - headerSize - windowSize = 100 - packetBufferSize = 256 - readBufferSize = 1048576 - maxRetry = 3 - - maxUdpPayload = 65507 - resetTimeout = time.Second -) - -type timeoutError struct{} - -func (e *timeoutError) Error() string { return "i/o timeout" } -func (e *timeoutError) Timeout() bool { return true } -func (e *timeoutError) Temporary() bool { return true } - -var ( - errTimeout error = &timeoutError{} - errClosing = errors.New("use of closed network connection") -) diff --git a/convert.go b/convert.go index 944c788..7d6500d 100644 --- a/convert.go +++ b/convert.go @@ -5,8 +5,8 @@ import ( "net" "strings" - utp "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/h2so5/utp" ma "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + utp "github.com/jbenet/go-multiaddr-net/utp" ) var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion") @@ -63,7 +63,7 @@ func FromNetAddr(a net.Addr) (ma.Multiaddr, error) { } // Get UDP Addr - ac, ok := acc.Addr.(*net.UDPAddr) + ac, ok := acc.Child().(*net.UDPAddr) if !ok { return nil, errIncorrectNetAddr } diff --git a/convert_test.go b/convert_test.go index ebdcb97..f99a8ec 100644 --- a/convert_test.go +++ b/convert_test.go @@ -4,8 +4,8 @@ import ( "net" "testing" - utp "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/h2so5/utp" ma "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + mautp "github.com/jbenet/go-multiaddr-net/utp" ) type GenFunc func() (ma.Multiaddr, error) @@ -90,13 +90,9 @@ func TestFromUDP(t *testing.T) { } func TestFromUTP(t *testing.T) { + a := &net.UDPAddr{IP: net.ParseIP("10.20.30.40"), Port: 1234} testConvert(t, "/ip4/10.20.30.40/udp/1234/utp", func() (ma.Multiaddr, error) { - return FromNetAddr(&utp.Addr{ - Addr: &net.UDPAddr{ - IP: net.ParseIP("10.20.30.40"), - Port: 1234, - }, - }) + return FromNetAddr(mautp.MakeAddr(a)) }) } diff --git a/multiaddr/multiaddr.go b/multiaddr/multiaddr.go index 71ecc73..3b216d7 100644 --- a/multiaddr/multiaddr.go +++ b/multiaddr/multiaddr.go @@ -6,8 +6,8 @@ import ( "fmt" "os" - ma "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" manet "github.com/jbenet/go-multiaddr-net" + ma "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) // flags diff --git a/net.go b/net.go index 828a318..5ea5a02 100644 --- a/net.go +++ b/net.go @@ -4,8 +4,8 @@ import ( "fmt" "net" - // utp "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/h2so5/utp" ma "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + mautp "github.com/jbenet/go-multiaddr-net/utp" ) // Conn is the equivalent of a net.Conn object. It is the @@ -109,24 +109,21 @@ func (d *Dialer) Dial(remote ma.Multiaddr) (Conn, error) { // ok, Dial! var nconn net.Conn switch rnet { - case "tcp", "tcp4", "tcp6": + case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": nconn, err = d.Dialer.Dial(rnet, rnaddr) if err != nil { return nil, err } - case "udp", "udp4", "udp6": - return nil, fmt.Errorf("utp is currently broken") - - // // construct utp dialer, with options on our net.Dialer - // utpd := utp.Dialer{ - // Timeout: d.Dialer.Timeout, - // LocalAddr: d.Dialer.LocalAddr, - // } - // - // nconn, err = utpd.Dial(rnet, rnaddr) - // if err != nil { - // return nil, err - // } + case "utp", "utp4", "utp6": + utpd := mautp.Dialer{ + Timeout: d.Timeout, + LocalAddr: d.Dialer.LocalAddr, + } + // construct utp dialer, with options on our net.Dialer + nconn, err = utpd.Dial(rnet, rnaddr) + if err != nil { + return nil, err + } } // get local address (pre-specified or assigned within net.Conn) @@ -229,9 +226,8 @@ func Listen(laddr ma.Multiaddr) (Listener, error) { var nl net.Listener switch lnet { - case "utp": - // nl, err = utp.Listen(lnet, lnaddr) - return nil, fmt.Errorf("utp is currently broken") + case "utp", "utp4", "utp6": + nl, err = mautp.Listen(lnet, lnaddr) default: nl, err = net.Listen(lnet, lnaddr) } diff --git a/net_test.go b/net_test.go index d0a68e0..0e7a37c 100644 --- a/net_test.go +++ b/net_test.go @@ -246,12 +246,10 @@ func TestListenAndDial(t *testing.T) { } func TestListenAndDialUTP(t *testing.T) { - t.Skip("utp is broken") - maddr := newMultiaddr(t, "/ip4/127.0.0.1/udp/4323/utp") listener, err := Listen(maddr) if err != nil { - t.Fatal("failed to listen") + t.Fatal("failed to listen: ", err) } var wg sync.WaitGroup @@ -267,6 +265,8 @@ func TestListenAndDialUTP(t *testing.T) { t.Fatal("local multiaddr not equal:", maddr, cB.LocalMultiaddr()) } + defer cB.Close() + // echo out buf := make([]byte, 1024) for { diff --git a/utp/utp_util.go b/utp/utp_util.go new file mode 100644 index 0000000..e1ce12f --- /dev/null +++ b/utp/utp_util.go @@ -0,0 +1,105 @@ +package utp + +import ( + "errors" + "net" + "time" + + utp "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/anacrolix/utp" +) + +type Listener struct { + *utp.Socket +} + +type Conn struct { + net.Conn +} + +type Addr struct { + net string + child net.Addr +} + +func (ca *Addr) Network() string { + return ca.net +} + +func (ca *Addr) String() string { + return ca.child.String() +} + +func (ca *Addr) Child() net.Addr { + return ca.child +} + +func MakeAddr(a net.Addr) net.Addr { + return &Addr{ + net: "utp", + child: a, + } +} + +func ResolveAddr(network string, host string) (net.Addr, error) { + a, err := net.ResolveUDPAddr("udp"+network[3:], host) + if err != nil { + return nil, err + } + + return MakeAddr(a), nil +} + +func (u *Conn) LocalAddr() net.Addr { + return MakeAddr(u.Conn.LocalAddr()) +} + +func (u *Conn) RemoteAddr() net.Addr { + return MakeAddr(u.Conn.RemoteAddr()) +} + +func Listen(network string, laddr string) (net.Listener, error) { + switch network { + case "utp", "utp4", "utp6": + s, err := utp.NewSocket("udp"+network[3:], laddr) + if err != nil { + return nil, err + } + + return &Listener{s}, nil + + default: + return nil, errors.New("unrecognized network: " + network) + } +} + +func (u *Listener) Accept() (net.Conn, error) { + c, err := u.Socket.Accept() + if err != nil { + return nil, err + } + + return &Conn{c}, nil +} + +func (u *Listener) Addr() net.Addr { + return MakeAddr(u.Socket.Addr()) +} + +type Dialer struct { + Timeout time.Duration + LocalAddr net.Addr +} + +func (d *Dialer) Dial(rnet string, raddr string) (net.Conn, error) { + if d.LocalAddr != nil { + s, err := utp.NewSocket(d.LocalAddr.Network(), d.LocalAddr.String()) + if err != nil { + return nil, err + } + + // zero timeout is the same as calling s.Dial() + return s.DialTimeout(raddr, d.Timeout) + } + + return utp.DialTimeout(raddr, d.Timeout) +}