mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-26 23:53:19 +08:00
update go-multiaddr-net dependency
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
This commit is contained in:
22
Godeps/Godeps.json
generated
22
Godeps/Godeps.json
generated
@ -32,10 +32,26 @@
|
||||
"ImportPath": "github.com/alecthomas/units",
|
||||
"Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/anacrolix/jitter",
|
||||
"Rev": "2ea5c18645100745b24e9f5cfc9b3f6f7eac51ef"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/anacrolix/missinggo",
|
||||
"Rev": "4e1ca5963308863b56c31863f60c394a7365ec29"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/anacrolix/utp",
|
||||
"Rev": "0bb24de92c268452fb9106ca4fb9302442ca0dee"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||
"Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bradfitz/iter",
|
||||
"Rev": "454541ec3da2a73fc34fd049b19ee5777bf19345"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bren2010/proquint",
|
||||
"Rev": "5958552242606512f714d2e93513b380f43f9991"
|
||||
@ -104,10 +120,6 @@
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "aece6fb931241ad332956db4f62798dfbea944b3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/h2so5/utp",
|
||||
"Rev": "6ca83358f5c331028feb9b97c445e9c7354967b0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/golang-lru",
|
||||
"Rev": "253b2dc1ca8bae42c3b5b6e53dd2eab1a7551116"
|
||||
@ -171,7 +183,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-multiaddr-net",
|
||||
"Rev": "6b29a00b65526d23f534813eb5bfa64dfa281e4a"
|
||||
"Rev": "4a8bd8f8baf45afcf2bb385bbc17e5208d5d4c71"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-multihash",
|
||||
|
12
Godeps/_workspace/src/github.com/anacrolix/jitter/jitter.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/anacrolix/jitter/jitter.go
generated
vendored
Normal file
@ -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
|
||||
}
|
72
Godeps/_workspace/src/github.com/anacrolix/missinggo/addr.go
generated
vendored
Normal file
72
Godeps/_workspace/src/github.com/anacrolix/missinggo/addr.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/addr_test.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/addr_test.go
generated
vendored
Normal file
@ -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())
|
||||
}
|
15
Godeps/_workspace/src/github.com/anacrolix/missinggo/args/args.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/anacrolix/missinggo/args/args.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_darwin.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_darwin.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_freebsd.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_freebsd.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_linux.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_linux.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_windows.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/atime_windows.go
generated
vendored
Normal file
@ -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()))
|
||||
}
|
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/castslice.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/castslice.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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
|
||||
}
|
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/go-env/main.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/go-env/main.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for _, v := range os.Environ() {
|
||||
fmt.Printf("%s\n", v)
|
||||
}
|
||||
}
|
3
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/nop/main.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/nop/main.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
package main
|
||||
|
||||
func main() {}
|
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-escape/main.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-escape/main.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(url.QueryEscape(os.Args[1]))
|
||||
}
|
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-unescape/main.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/cmd/query-unescape/main.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(url.QueryUnescape(os.Args[1]))
|
||||
}
|
32
Godeps/_workspace/src/github.com/anacrolix/missinggo/copy.go
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/anacrolix/missinggo/copy.go
generated
vendored
Normal file
@ -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")
|
||||
}
|
||||
}
|
89
Godeps/_workspace/src/github.com/anacrolix/missinggo/copy_test.go
generated
vendored
Normal file
89
Godeps/_workspace/src/github.com/anacrolix/missinggo/copy_test.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
||||
}
|
18
Godeps/_workspace/src/github.com/anacrolix/missinggo/croak.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/anacrolix/missinggo/croak.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
3
Godeps/_workspace/src/github.com/anacrolix/missinggo/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/anacrolix/missinggo/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
// Package missinggo contains miscellaneous helpers used in many of anacrolix'
|
||||
// projects.
|
||||
package missinggo
|
35
Godeps/_workspace/src/github.com/anacrolix/missinggo/expvarIndentMap.go
generated
vendored
Normal file
35
Godeps/_workspace/src/github.com/anacrolix/missinggo/expvarIndentMap.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
269
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache.go
generated
vendored
Normal file
269
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache.go
generated
vendored
Normal file
@ -0,0 +1,269 @@
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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]
|
||||
}
|
84
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache_test.go
generated
vendored
Normal file
84
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/cache_test.go
generated
vendored
Normal file
@ -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/ipfs/go-ipfs/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"))
|
||||
}
|
117
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/file.go
generated
vendored
Normal file
117
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/file.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
94
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems.go
generated
vendored
Normal file
94
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
22
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems_test.go
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/anacrolix/missinggo/filecache/lruitems_test.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
38
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange.go
generated
vendored
Normal file
38
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange.go
generated
vendored
Normal file
@ -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
|
||||
}
|
27
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange_test.go
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpcontentrange_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
222
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpfile/httpfile.go
generated
vendored
Normal file
222
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpfile/httpfile.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
package httpfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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
|
||||
}
|
47
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpgzip.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpgzip.go
generated
vendored
Normal file
@ -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)
|
||||
})
|
||||
}
|
60
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpresponsestatus.go
generated
vendored
Normal file
60
Godeps/_workspace/src/github.com/anacrolix/missinggo/httpresponsestatus.go
generated
vendored
Normal file
@ -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),
|
||||
}
|
||||
}
|
81
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby.go
generated
vendored
Normal file
81
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby.go
generated
vendored
Normal file
@ -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,
|
||||
}
|
||||
}
|
31
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby_test.go
generated
vendored
Normal file
31
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/groupby_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
41
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator.go
generated
vendored
Normal file
41
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package itertools
|
||||
|
||||
import "github.com/ipfs/go-ipfs/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))
|
||||
}
|
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator_test.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/itertools/iterator_test.go
generated
vendored
Normal file
@ -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())
|
||||
}
|
26
Godeps/_workspace/src/github.com/anacrolix/missinggo/net.go
generated
vendored
Normal file
26
Godeps/_workspace/src/github.com/anacrolix/missinggo/net.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
20
Godeps/_workspace/src/github.com/anacrolix/missinggo/path.go
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/anacrolix/missinggo/path.go
generated
vendored
Normal file
@ -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
|
||||
}
|
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/path_test.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/anacrolix/missinggo/path_test.go
generated
vendored
Normal file
@ -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" ""}
|
||||
}
|
48
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/mutex.go
generated
vendored
Normal file
48
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/mutex.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
package perf
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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()
|
||||
}
|
92
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf.go
generated
vendored
Normal file
92
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
package perf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"expvar"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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
|
||||
}
|
52
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf_test.go
generated
vendored
Normal file
52
Godeps/_workspace/src/github.com/anacrolix/missinggo/perf/perf_test.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
package perf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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())
|
||||
}
|
92
Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub.go
generated
vendored
Normal file
92
Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub.go
generated
vendored
Normal file
@ -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
|
||||
}
|
74
Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub_test.go
generated
vendored
Normal file
74
Godeps/_workspace/src/github.com/anacrolix/missinggo/pubsub/pubsub_test.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
package pubsub
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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()
|
||||
}
|
46
Godeps/_workspace/src/github.com/anacrolix/missinggo/rle.go
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/anacrolix/missinggo/rle.go
generated
vendored
Normal file
@ -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
|
||||
}
|
20
Godeps/_workspace/src/github.com/anacrolix/missinggo/rle_test.go
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/anacrolix/missinggo/rle_test.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package missinggo_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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
|
||||
}
|
39
Godeps/_workspace/src/github.com/anacrolix/missinggo/singleflight.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/anacrolix/missinggo/singleflight.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/sync.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/anacrolix/missinggo/sync.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package missinggo
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type RWLocker interface {
|
||||
sync.Locker
|
||||
RLock()
|
||||
RUnlock()
|
||||
}
|
30
Godeps/_workspace/src/github.com/anacrolix/missinggo/url.go
generated
vendored
Normal file
30
Godeps/_workspace/src/github.com/anacrolix/missinggo/url.go
generated
vendored
Normal file
@ -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
|
||||
}
|
51
Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf.go
generated
vendored
Normal file
51
Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
30
Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf_test.go
generated
vendored
Normal file
30
Godeps/_workspace/src/github.com/anacrolix/missinggo/wolf_test.go
generated
vendored
Normal file
@ -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())
|
||||
}
|
362
Godeps/_workspace/src/github.com/anacrolix/utp/LICENSE
generated
vendored
Normal file
362
Godeps/_workspace/src/github.com/anacrolix/utp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,362 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
19
Godeps/_workspace/src/github.com/anacrolix/utp/README.md
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/anacrolix/utp/README.md
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
# utp
|
||||
[](https://godoc.org/github.com/anacrolix/utp)
|
||||
[](https://drone.io/github.com/anacrolix/utp/latest)
|
||||
|
||||
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.
|
||||
|
||||
## Supported
|
||||
|
||||
* Multiple uTP connections switched on a single PacketConn, including those initiated locally.
|
||||
* Raw access to the PacketConn for non-uTP purposes, like sharing the PacketConn with a DHT implementation.
|
||||
|
||||
## Implementation characteristics
|
||||
|
||||
* Receive window size is used to limit out of order packets received.
|
||||
* There is no MTU path discovery. The minimum size is always used.
|
||||
* A fixed 64 slot selective ack window is used in both sending and receiving.
|
||||
* All received non-ACK packets are ACKed in response.
|
||||
|
||||
Patches welcomed.
|
66
Godeps/_workspace/src/github.com/anacrolix/utp/cmd/ucat/ucat.go
generated
vendored
Normal file
66
Godeps/_workspace/src/github.com/anacrolix/utp/cmd/ucat/ucat.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/anacrolix/envpprof"
|
||||
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/utp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer envpprof.Stop()
|
||||
listen := flag.Bool("l", false, "listen")
|
||||
port := flag.Int("p", 0, "port to listen on")
|
||||
flag.Parse()
|
||||
var (
|
||||
conn net.Conn
|
||||
err error
|
||||
)
|
||||
if *listen {
|
||||
s, err := utp.NewSocket("udp", fmt.Sprintf(":%d", *port))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
conn, err = s.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
conn, err = utp.Dial(net.JoinHostPort(flag.Arg(0), flag.Arg(1)))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
defer conn.Close()
|
||||
go func() {
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
<-sig
|
||||
conn.Close()
|
||||
}()
|
||||
writerDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(writerDone)
|
||||
written, err := io.Copy(conn, os.Stdin)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
log.Fatalf("error after writing %d bytes: %s", written, err)
|
||||
}
|
||||
log.Printf("wrote %d bytes", written)
|
||||
conn.Close()
|
||||
}()
|
||||
n, err := io.Copy(os.Stdout, conn)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("received %d bytes", n)
|
||||
// <-writerDone
|
||||
}
|
61
Godeps/_workspace/src/github.com/anacrolix/utp/pingpong
generated
vendored
Normal file
61
Godeps/_workspace/src/github.com/anacrolix/utp/pingpong
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
# This shell script uses nc-like executables to send and receive the file at
|
||||
# $1, and prints the checksums. 3 such executables are
|
||||
# github.com/h2so5/utp/ucat, invoked as h2so5-ucat, libutp-ucat, which is the
|
||||
# ucat or ucat-static generated by the C++ libutp, and lastly, ./cmd/ucat from
|
||||
# this repository. A good file in my experiments is no more than a few 100MB,
|
||||
# or you'll be waiting a while.
|
||||
|
||||
set -eu
|
||||
# set -x
|
||||
|
||||
# Passed to invocations of godo for package ./cmd/ucat.
|
||||
#GODOFLAGS=-race
|
||||
|
||||
#export GO_UTP_PACKET_DROP=0.1
|
||||
export GOPPROF=
|
||||
|
||||
# Invokes the implementation to test against. If there's an arg, then it's
|
||||
# expected to listen.
|
||||
function other_ucat() {
|
||||
if [[ $# != 0 ]]; then
|
||||
libutp-ucat -l -p 4000
|
||||
# h2so5-ucat -l :4000
|
||||
else
|
||||
libutp-ucat localhost 4000
|
||||
# h2so5-ucat localhost:4000
|
||||
fi
|
||||
}
|
||||
|
||||
# Check what the correct result is.
|
||||
md5 "$1"
|
||||
|
||||
rate() {
|
||||
pv -a -W -b
|
||||
}
|
||||
|
||||
echo 'utp->other_ucat'
|
||||
# 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).
|
1461
Godeps/_workspace/src/github.com/anacrolix/utp/utp.go
generated
vendored
Normal file
1461
Godeps/_workspace/src/github.com/anacrolix/utp/utp.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
411
Godeps/_workspace/src/github.com/anacrolix/utp/utp_test.go
generated
vendored
Normal file
411
Godeps/_workspace/src/github.com/anacrolix/utp/utp_test.go
generated
vendored
Normal file
@ -0,0 +1,411 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "github.com/anacrolix/envpprof"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/anacrolix/missinggo"
|
||||
"github.com/ipfs/go-ipfs/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()
|
||||
}
|
1
Godeps/_workspace/src/github.com/bradfitz/iter/.gitignore
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/bradfitz/iter/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
*~
|
1
Godeps/_workspace/src/github.com/bradfitz/iter/README.txt
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/bradfitz/iter/README.txt
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
See http://godoc.org/github.com/bradfitz/iter
|
17
Godeps/_workspace/src/github.com/bradfitz/iter/iter.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/bradfitz/iter/iter.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
29
Godeps/_workspace/src/github.com/bradfitz/iter/iter_test.go
generated
vendored
Normal file
29
Godeps/_workspace/src/github.com/bradfitz/iter/iter_test.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package iter_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-ipfs/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)
|
||||
}
|
||||
}
|
26
Godeps/_workspace/src/github.com/h2so5/utp/.gitignore
generated
vendored
26
Godeps/_workspace/src/github.com/h2so5/utp/.gitignore
generated
vendored
@ -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
|
9
Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml
generated
vendored
9
Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml
generated
vendored
@ -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
|
21
Godeps/_workspace/src/github.com/h2so5/utp/LICENSE
generated
vendored
21
Godeps/_workspace/src/github.com/h2so5/utp/LICENSE
generated
vendored
@ -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.
|
28
Godeps/_workspace/src/github.com/h2so5/utp/README.md
generated
vendored
28
Godeps/_workspace/src/github.com/h2so5/utp/README.md
generated
vendored
@ -1,28 +0,0 @@
|
||||
utp
|
||||
===
|
||||
|
||||
μTP (Micro Transport Protocol) implementation
|
||||
|
||||
[](https://ci.appveyor.com/project/h2so5/utp)
|
||||
[](https://travis-ci.org/h2so5/utp)
|
||||
[](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
|
||||
```
|
42
Godeps/_workspace/src/github.com/h2so5/utp/addr.go
generated
vendored
42
Godeps/_workspace/src/github.com/h2so5/utp/addr.go
generated
vendored
@ -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)
|
||||
}
|
||||
}
|
325
Godeps/_workspace/src/github.com/h2so5/utp/base.go
generated
vendored
325
Godeps/_workspace/src/github.com/h2so5/utp/base.go
generated
vendored
@ -1,325 +0,0 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var baseConnMap = make(map[string]*baseConn)
|
||||
var baseConnMutex sync.Mutex
|
||||
|
||||
type packetHandler struct {
|
||||
send chan<- *packet
|
||||
closed chan int
|
||||
}
|
||||
|
||||
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 {
|
||||
select {
|
||||
case <-h.closed:
|
||||
case h.send <- p:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *baseConn) Register(id int32, f chan<- *packet) {
|
||||
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)] = &packetHandler{
|
||||
send: f,
|
||||
closed: make(chan int),
|
||||
}
|
||||
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()
|
||||
f, ok := c.handlers[uint16(id)]
|
||||
c.handlerMutex.Unlock()
|
||||
if ok {
|
||||
c.handlerMutex.Lock()
|
||||
close(f.closed)
|
||||
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)
|
||||
}
|
308
Godeps/_workspace/src/github.com/h2so5/utp/base_test.go
generated
vendored
308
Godeps/_workspace/src/github.com/h2so5/utp/base_test.go
generated
vendored
@ -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")
|
||||
}
|
||||
}
|
296
Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go
generated
vendored
296
Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go
generated
vendored
@ -1,296 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/davecheney/profile"
|
||||
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/go-humanize"
|
||||
"github.com/ipfs/go-ipfs/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
|
||||
}
|
472
Godeps/_workspace/src/github.com/h2so5/utp/buffer.go
generated
vendored
472
Godeps/_workspace/src/github.com/h2so5/utp/buffer.go
generated
vendored
@ -1,472 +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) {
|
||||
t := time.NewTimer(timeout)
|
||||
defer t.Stop()
|
||||
if timeout == 0 {
|
||||
t.Stop()
|
||||
}
|
||||
if b.empty() {
|
||||
select {
|
||||
case <-b.rch:
|
||||
case <-t.C:
|
||||
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) {
|
||||
t := time.NewTimer(timeout)
|
||||
defer t.Stop()
|
||||
if timeout == 0 {
|
||||
t.Stop()
|
||||
}
|
||||
if r.empty() {
|
||||
select {
|
||||
case <-r.rch:
|
||||
case <-t.C:
|
||||
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) {
|
||||
t := time.NewTimer(timeout)
|
||||
defer t.Stop()
|
||||
if timeout == 0 {
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
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.C:
|
||||
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
|
||||
}
|
178
Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go
generated
vendored
178
Godeps/_workspace/src/github.com/h2so5/utp/buffer_test.go
generated
vendored
@ -1,178 +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])
|
||||
}
|
||||
}
|
571
Godeps/_workspace/src/github.com/h2so5/utp/conn.go
generated
vendored
571
Godeps/_workspace/src/github.com/h2so5/utp/conn.go
generated
vendored
@ -1,571 +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
|
||||
|
||||
resend := time.NewTimer(0)
|
||||
resend.Stop()
|
||||
defer resend.Stop()
|
||||
|
||||
for {
|
||||
resend.Stop()
|
||||
f := c.sendbuf.front()
|
||||
if f != nil {
|
||||
resend.Reset(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.C:
|
||||
if f != nil {
|
||||
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 {
|
||||
c.tryFIN()
|
||||
if c.state == stateSynSent || c.state == stateFinSent || (c.recvbuf.empty() && c.sendbuf.empty()) {
|
||||
c.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) tryFIN() {
|
||||
if c.state != stateFinSent {
|
||||
if c.sendFIN() == nil {
|
||||
c.writebuf.Close()
|
||||
c.state = stateFinSent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) enterClosing() {
|
||||
if !c.closing {
|
||||
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 {
|
||||
ulog.Printf(2, "Conn(%v): buffer error: %v", c.LocalAddr(), err)
|
||||
return
|
||||
}
|
||||
c.stat.sentPackets++
|
||||
c.conn.Send(syn)
|
||||
}
|
||||
|
||||
func (c *Conn) sendFIN() error {
|
||||
fin := c.makePacket(stFin, nil, c.raddr)
|
||||
err := c.sendbuf.push(fin)
|
||||
if err != nil {
|
||||
ulog.Printf(2, "Conn(%v): buffer error: %v", c.LocalAddr(), err)
|
||||
return err
|
||||
}
|
||||
c.stat.sentPackets++
|
||||
c.conn.Send(fin)
|
||||
return nil
|
||||
}
|
||||
|
||||
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 && typ != stFin {
|
||||
c.seq++
|
||||
}
|
||||
p.payload = payload
|
||||
return p
|
||||
}
|
87
Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go
generated
vendored
87
Godeps/_workspace/src/github.com/h2so5/utp/conn_test.go
generated
vendored
@ -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")
|
||||
}
|
||||
}
|
102
Godeps/_workspace/src/github.com/h2so5/utp/dial.go
generated
vendored
102
Godeps/_workspace/src/github.com/h2so5/utp/dial.go
generated
vendored
@ -1,102 +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
|
||||
|
||||
t := time.NewTimer(timeout)
|
||||
defer t.Stop()
|
||||
if timeout == 0 {
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-c.connch:
|
||||
case <-t.C:
|
||||
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)
|
||||
}
|
52
Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go
generated
vendored
52
Godeps/_workspace/src/github.com/h2so5/utp/dial_test.go
generated
vendored
@ -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")
|
||||
}
|
||||
}
|
146
Godeps/_workspace/src/github.com/h2so5/utp/listener.go
generated
vendored
146
Godeps/_workspace/src/github.com/h2so5/utp/listener.go
generated
vendored
@ -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
|
||||
}
|
68
Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go
generated
vendored
68
Godeps/_workspace/src/github.com/h2so5/utp/listener_test.go
generated
vendored
@ -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")
|
||||
}
|
||||
}
|
50
Godeps/_workspace/src/github.com/h2so5/utp/log.go
generated
vendored
50
Godeps/_workspace/src/github.com/h2so5/utp/log.go
generated
vendored
@ -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...)
|
||||
}
|
198
Godeps/_workspace/src/github.com/h2so5/utp/packet.go
generated
vendored
198
Godeps/_workspace/src/github.com/h2so5/utp/packet.go
generated
vendored
@ -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
|
||||
}
|
64
Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go
generated
vendored
64
Godeps/_workspace/src/github.com/h2so5/utp/packet_test.go
generated
vendored
@ -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)
|
||||
}
|
||||
}
|
3
Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore
generated
vendored
3
Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore
generated
vendored
@ -1,3 +0,0 @@
|
||||
ucat
|
||||
random
|
||||
.trash/
|
34
Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile
generated
vendored
34
Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile
generated
vendored
@ -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}
|
49
Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh
generated
vendored
49
Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh
generated
vendored
@ -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
|
192
Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go
generated
vendored
192
Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go
generated
vendored
@ -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 [<local address>] <remote address>
|
||||
// ucat -l <local address>
|
||||
//
|
||||
// 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/ipfs/go-ipfs/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 [<local address>] <remote address>
|
||||
dial: %s -l <local address>
|
||||
|
||||
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:
|
||||
}
|
||||
}
|
47
Godeps/_workspace/src/github.com/h2so5/utp/utp.go
generated
vendored
47
Godeps/_workspace/src/github.com/h2so5/utp/utp.go
generated
vendored
@ -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")
|
||||
)
|
16
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Godeps/Godeps.json
generated
vendored
16
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Godeps/Godeps.json
generated
vendored
@ -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",
|
||||
|
8
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert.go
generated
vendored
8
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert.go
generated
vendored
@ -5,14 +5,18 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
utp "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/h2so5/utp"
|
||||
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
|
||||
utp "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/utp"
|
||||
)
|
||||
|
||||
var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion")
|
||||
|
||||
// FromNetAddr converts a net.Addr type to a Multiaddr.
|
||||
func FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
if a == nil {
|
||||
return nil, fmt.Errorf("nil multiaddr")
|
||||
}
|
||||
|
||||
switch a.Network() {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
ac, ok := a.(*net.TCPAddr)
|
||||
@ -63,7 +67,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
|
||||
}
|
||||
|
10
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert_test.go
generated
vendored
10
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert_test.go
generated
vendored
@ -4,8 +4,8 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
utp "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/h2so5/utp"
|
||||
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
|
||||
mautp "github.com/ipfs/go-ipfs/Godeps/_workspace/src/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))
|
||||
})
|
||||
}
|
||||
|
||||
|
4
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/multiaddr/multiaddr.go
generated
vendored
4
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/multiaddr/multiaddr.go
generated
vendored
@ -10,8 +10,8 @@ import (
|
||||
manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
|
||||
)
|
||||
|
||||
var formats = []string{"string", "bytes", // flags
|
||||
"hex", "slice"}
|
||||
// flags
|
||||
var formats = []string{"string", "bytes", "hex", "slice"}
|
||||
var format string
|
||||
var hideLoopback bool
|
||||
|
||||
|
32
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net.go
generated
vendored
32
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net.go
generated
vendored
@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
// utp "github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/h2so5/utp"
|
||||
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
|
||||
mautp "github.com/ipfs/go-ipfs/Godeps/_workspace/src/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)
|
||||
}
|
||||
|
6
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net_test.go
generated
vendored
6
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net_test.go
generated
vendored
@ -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 {
|
||||
|
105
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/utp/utp_util.go
generated
vendored
Normal file
105
Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/utp/utp_util.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
utp "github.com/ipfs/go-ipfs/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)
|
||||
}
|
Reference in New Issue
Block a user