1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-26 23:53:19 +08:00

Merge pull request #1829 from ipfs/update-maddr-net

update go-multiaddr-net dependency
This commit is contained in:
Juan Benet
2015-10-12 13:08:31 +08:00
90 changed files with 4796 additions and 3407 deletions

22
Godeps/Godeps.json generated
View File

@ -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",

View 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
}

View 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)
}
}

View 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())
}

View 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)
}
}

View 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)
}

View 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))
}

View 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))
}

View 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))
}

View 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()))
}

View 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
}

View File

@ -0,0 +1,12 @@
package main
import (
"fmt"
"os"
)
func main() {
for _, v := range os.Environ() {
fmt.Printf("%s\n", v)
}
}

View File

@ -0,0 +1,3 @@
package main
func main() {}

View File

@ -0,0 +1,11 @@
package main
import (
"fmt"
"net/url"
"os"
)
func main() {
fmt.Println(url.QueryEscape(os.Args[1]))
}

View File

@ -0,0 +1,11 @@
package main
import (
"fmt"
"net/url"
"os"
)
func main() {
fmt.Println(url.QueryUnescape(os.Args[1]))
}

View 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")
}
}

View 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()
}
}

View 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)
}

View File

@ -0,0 +1,3 @@
// Package missinggo contains miscellaneous helpers used in many of anacrolix'
// projects.
package missinggo

View 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()
}

View 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]
}

View 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"))
}

View 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)
}

View 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)
}

View 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,
})
}
}
}

View 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
}

View 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)
}
}
}

View 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
}

View 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)
})
}

View 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),
}
}

View 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,
}
}

View 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)
}

View 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))
}

View 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())
}

View 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)
}

View 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
}

View 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" ""}
}

View 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()
}

View 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
}

View 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())
}

View 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
}

View 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()
}

View 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
}

View 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
}

View 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()
}

View File

@ -0,0 +1,11 @@
package missinggo
import (
"sync"
)
type RWLocker interface {
sync.Locker
RLock()
RUnlock()
}

View 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
}

View 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()
}

View 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
View 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.

View File

@ -0,0 +1,19 @@
# utp
[![GoDoc](https://godoc.org/github.com/anacrolix/utp?status.svg)](https://godoc.org/github.com/anacrolix/utp)
[![Build Status](https://drone.io/github.com/anacrolix/utp/status.png)](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.

View 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
}

View 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

File diff suppressed because it is too large Load Diff

View 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()
}

View File

@ -0,0 +1 @@
*~

View 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
View 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)
}

View 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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -1,28 +0,0 @@
utp
===
μTP (Micro Transport Protocol) implementation
[![Build status](https://ci.appveyor.com/api/projects/status/j1be8y7p6nd2wqqw?svg=true&branch=master)](https://ci.appveyor.com/project/h2so5/utp)
[![Build Status](https://travis-ci.org/h2so5/utp.svg?branch=master)](https://travis-ci.org/h2so5/utp)
[![GoDoc](https://godoc.org/github.com/h2so5/utp?status.svg)](http://godoc.org/github.com/h2so5/utp)
http://www.bittorrent.org/beps/bep_0029.html
## Installation
```
go get github.com/h2so5/utp
```
## Debug Log
Use GO_UTP_LOGGING to show debug logs.
```
GO_UTP_LOGGING=0 go test <- default, no logging
GO_UTP_LOGGING=1 go test
GO_UTP_LOGGING=2 go test
GO_UTP_LOGGING=3 go test
GO_UTP_LOGGING=4 go test <- most verbose
```

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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])
}
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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)
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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...)
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -1,3 +0,0 @@
ucat
random
.trash/

View File

@ -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}

View File

@ -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

View File

@ -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:
}
}

View File

@ -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")
)

View File

@ -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",

View File

@ -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
}

View File

@ -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))
})
}

View File

@ -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

View File

@ -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)
}

View File

@ -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 {

View 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)
}