mirror of
https://github.com/logos-messaging/go-multiaddr.git
synced 2026-01-03 05:23:08 +00:00
vendor things
This commit is contained in:
parent
1089720859
commit
4d83cc616a
18
Godeps/Godeps.json
generated
Normal file
18
Godeps/Godeps.json
generated
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-multiaddr-net",
|
||||
"GoVersion": "go1.3",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/h2so5/utp",
|
||||
"Rev": "654d875bb65e96729678180215cf080fe2810371"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jbenet/go-multiaddr",
|
||||
"Comment": "0.1.2-30-g99cf3ed",
|
||||
"Rev": "99cf3edc711751cf7b43505fac0e3913f6b9a75c"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
Godeps/Readme
generated
Normal file
5
Godeps/Readme
generated
Normal file
@ -0,0 +1,5 @@
|
||||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
||||
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/pkg
|
||||
/bin
|
||||
26
Godeps/_workspace/src/github.com/h2so5/utp/.gitignore
generated
vendored
Normal file
26
Godeps/_workspace/src/github.com/h2so5/utp/.gitignore
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# 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
|
||||
7
Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
language: go
|
||||
|
||||
script:
|
||||
- GO_UTP_LOGGING=2 go test -v -bench .
|
||||
- go test -v -race
|
||||
- GO_UTP_LOGGING=2 go run benchmark/main.go -h
|
||||
- GO_UTP_LOGGING=2 cd _ucat_test; make test
|
||||
21
Godeps/_workspace/src/github.com/h2so5/utp/LICENSE
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/h2so5/utp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
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.
|
||||
57
Godeps/_workspace/src/github.com/h2so5/utp/README.md
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/h2so5/utp/README.md
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
utp
|
||||
===
|
||||
|
||||
μTP (Micro Transport Protocol) implementation
|
||||
|
||||
[](https://ci.appveyor.com/project/h2so5/utp)
|
||||
[](https://travis-ci.org/h2so5/utp)
|
||||
[](http://godoc.org/github.com/h2so5/utp)
|
||||
|
||||
http://www.bittorrent.org/beps/bep_0029.html
|
||||
|
||||
**warning: This is a buggy alpha version.**
|
||||
|
||||
## Benchmark History
|
||||
|
||||
[]()
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
go get github.com/h2so5/utp
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Echo server
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/h2so5/utp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ln, _ := utp.Listen("utp", ":11000")
|
||||
defer ln.Close()
|
||||
|
||||
conn, _ := ln.AcceptUTP()
|
||||
conn.SetKeepAlive(time.Minute)
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
var buf [1024]byte
|
||||
l, err := conn.Read(buf[:])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
_, err = conn.Write(buf[:l])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
34
Godeps/_workspace/src/github.com/h2so5/utp/addr.go
generated
vendored
Normal file
34
Godeps/_workspace/src/github.com/h2so5/utp/addr.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package utp
|
||||
|
||||
import "net"
|
||||
|
||||
type UTPAddr struct {
|
||||
net.Addr
|
||||
}
|
||||
|
||||
func (a UTPAddr) Network() string { return "utp" }
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func ResolveUTPAddr(n, addr string) (*UTPAddr, error) {
|
||||
udpnet, err := utp2udp(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
udp, err := net.ResolveUDPAddr(udpnet, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UTPAddr{Addr: udp}, nil
|
||||
}
|
||||
276
Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go
generated
vendored
Normal file
276
Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go
generated
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/davecheney/profile"
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/h2so5/utp"
|
||||
)
|
||||
|
||||
type RandReader struct{}
|
||||
|
||||
func (r RandReader) Read(p []byte) (n int, err error) {
|
||||
for i := range p {
|
||||
p[i] = byte(rand.Int())
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type ByteCounter struct {
|
||||
n int64
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (b *ByteCounter) Write(p []byte) (n int, err error) {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
b.n += int64(len(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (b *ByteCounter) Length() int64 {
|
||||
b.mutex.RLock()
|
||||
defer b.mutex.RUnlock()
|
||||
return b.n
|
||||
}
|
||||
|
||||
var h = flag.Bool("h", false, "Human readable")
|
||||
|
||||
func main() {
|
||||
var l = flag.Int("c", 10485760, "Payload length (bytes)")
|
||||
var s = flag.Bool("s", false, "Stream mode(Low memory usage, but Slow)")
|
||||
flag.Parse()
|
||||
|
||||
defer profile.Start(profile.CPUProfile).Stop()
|
||||
|
||||
if *h {
|
||||
fmt.Printf("Payload: %s\n", humanize.IBytes(uint64(*l)))
|
||||
} else {
|
||||
fmt.Printf("Payload: %d\n", *l)
|
||||
}
|
||||
|
||||
c2s := c2s(int64(*l), *s)
|
||||
n, p := humanize.ComputeSI(c2s)
|
||||
if *h {
|
||||
fmt.Printf("C2S: %f%sbps\n", n, p)
|
||||
} else {
|
||||
fmt.Printf("C2S: %f\n", c2s)
|
||||
}
|
||||
|
||||
s2c := s2c(int64(*l), *s)
|
||||
n, p = humanize.ComputeSI(s2c)
|
||||
if *h {
|
||||
fmt.Printf("S2C: %f%sbps\n", n, p)
|
||||
} else {
|
||||
fmt.Printf("S2C: %f\n", s2c)
|
||||
}
|
||||
|
||||
avg := (c2s + s2c) / 2.0
|
||||
n, p = humanize.ComputeSI(avg)
|
||||
|
||||
if *h {
|
||||
fmt.Printf("AVG: %f%sbps\n", n, p)
|
||||
} else {
|
||||
fmt.Printf("AVG: %f\n", avg)
|
||||
}
|
||||
}
|
||||
|
||||
func c2s(l int64, stream bool) float64 {
|
||||
ln, err := utp.Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
raddr, err := utp.ResolveUTPAddr("utp", ln.Addr().String())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := utp.DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s, err := ln.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
ln.Close()
|
||||
|
||||
rch := make(chan int)
|
||||
|
||||
sendHash := md5.New()
|
||||
readHash := md5.New()
|
||||
counter := ByteCounter{}
|
||||
|
||||
var bps float64
|
||||
if stream {
|
||||
go func() {
|
||||
defer c.Close()
|
||||
io.Copy(io.MultiWriter(c, sendHash, &counter), io.LimitReader(RandReader{}, l))
|
||||
}()
|
||||
|
||||
go func() {
|
||||
io.Copy(readHash, s)
|
||||
close(rch)
|
||||
}()
|
||||
|
||||
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
|
||||
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()
|
||||
io.Copy(c, &sendBuf)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
io.Copy(&readBuf, s)
|
||||
rch <- 0
|
||||
}()
|
||||
|
||||
start := time.Now()
|
||||
<-rch
|
||||
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 {
|
||||
ln, err := utp.Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
raddr, err := utp.ResolveUTPAddr("utp", ln.Addr().String())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := utp.DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
s, err := ln.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
ln.Close()
|
||||
|
||||
rch := make(chan int)
|
||||
|
||||
sendHash := md5.New()
|
||||
readHash := md5.New()
|
||||
counter := ByteCounter{}
|
||||
|
||||
var bps float64
|
||||
|
||||
if stream {
|
||||
go func() {
|
||||
defer s.Close()
|
||||
io.Copy(io.MultiWriter(s, sendHash, &counter), io.LimitReader(RandReader{}, l))
|
||||
}()
|
||||
|
||||
go func() {
|
||||
io.Copy(readHash, c)
|
||||
close(rch)
|
||||
}()
|
||||
|
||||
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
|
||||
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()
|
||||
io.Copy(s, &sendBuf)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
io.Copy(&readBuf, c)
|
||||
rch <- 0
|
||||
}()
|
||||
|
||||
start := time.Now()
|
||||
<-rch
|
||||
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
|
||||
}
|
||||
230
Godeps/_workspace/src/github.com/h2so5/utp/buffer.go
generated
vendored
Normal file
230
Godeps/_workspace/src/github.com/h2so5/utp/buffer.go
generated
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"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) first() *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]
|
||||
}
|
||||
|
||||
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 timedBuffer struct {
|
||||
d time.Duration
|
||||
root *timedBufferNode
|
||||
}
|
||||
|
||||
type timedBufferNode struct {
|
||||
val float64
|
||||
next *timedBufferNode
|
||||
pushed time.Time
|
||||
}
|
||||
|
||||
func (b *timedBuffer) push(val float64) {
|
||||
var before *timedBufferNode
|
||||
for n := b.root; n != nil; n = n.next {
|
||||
if time.Now().Sub(n.pushed) >= b.d {
|
||||
if before != nil {
|
||||
before.next = nil
|
||||
} else {
|
||||
b.root = nil
|
||||
}
|
||||
break
|
||||
}
|
||||
before = n
|
||||
}
|
||||
b.root = &timedBufferNode{
|
||||
val: val,
|
||||
next: b.root,
|
||||
pushed: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *timedBuffer) min() float64 {
|
||||
if b.root == nil {
|
||||
return 0
|
||||
}
|
||||
min := b.root.val
|
||||
for n := b.root; n != nil; n = n.next {
|
||||
if min > n.val {
|
||||
min = n.val
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
761
Godeps/_workspace/src/github.com/h2so5/utp/conn.go
generated
vendored
Normal file
761
Godeps/_workspace/src/github.com/h2so5/utp/conn.go
generated
vendored
Normal file
@ -0,0 +1,761 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UTPConn struct {
|
||||
conn net.PacketConn
|
||||
raddr net.Addr
|
||||
rid, sid, seq, ack, lastAck uint16
|
||||
rtt, rttVar, minRtt, rto, dupAck int64
|
||||
diff, maxWindow uint32
|
||||
rdeadline, wdeadline time.Time
|
||||
|
||||
state state
|
||||
lastTimedOut time.Time
|
||||
|
||||
outch chan *outgoingPacket
|
||||
outchch chan int
|
||||
sendch chan *outgoingPacket
|
||||
sendchch chan int
|
||||
recvch chan *packet
|
||||
recvchch chan int
|
||||
readch chan []byte
|
||||
readchch chan int
|
||||
winch chan uint32
|
||||
quitch chan int
|
||||
activech chan int
|
||||
connch chan error
|
||||
finch chan int
|
||||
closech chan<- uint16
|
||||
eofid uint16
|
||||
keepalivech chan time.Duration
|
||||
|
||||
readbuf bytes.Buffer
|
||||
recvbuf *packetBuffer
|
||||
sendbuf *packetBuffer
|
||||
|
||||
stat statistics
|
||||
}
|
||||
|
||||
type statistics struct {
|
||||
sentPackets int
|
||||
resentPackets int
|
||||
receivedPackets int
|
||||
receivedDuplicatedACKs int
|
||||
packetTimedOuts int
|
||||
sentSelectiveACKs int
|
||||
receivedSelectiveACKs int
|
||||
|
||||
rtoSum int
|
||||
rtoCount int
|
||||
}
|
||||
|
||||
func dial(n string, laddr, raddr *UTPAddr, timeout time.Duration) (*UTPConn, error) {
|
||||
udpnet, err := utp2udp(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO extract
|
||||
if laddr == nil {
|
||||
addr, err := net.ResolveUDPAddr(udpnet, ":0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
laddr = &UTPAddr{Addr: addr}
|
||||
}
|
||||
|
||||
conn, err := net.ListenPacket(udpnet, laddr.Addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id := uint16(rand.Intn(math.MaxUint16))
|
||||
|
||||
c := newUTPConn()
|
||||
c.conn = conn
|
||||
c.raddr = raddr.Addr
|
||||
c.rid = id
|
||||
c.sid = id + 1
|
||||
c.seq = 1
|
||||
c.state = state_syn_sent
|
||||
c.sendbuf = newPacketBuffer(window_size, 1)
|
||||
|
||||
go c.recv()
|
||||
go c.loop()
|
||||
|
||||
select {
|
||||
case c.sendch <- &outgoingPacket{st_syn, nil, nil}:
|
||||
case <-c.sendchch:
|
||||
return nil, errors.New("use of closed network connection")
|
||||
}
|
||||
|
||||
var t <-chan time.Time
|
||||
if timeout != 0 {
|
||||
t = time.After(timeout)
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-c.connch:
|
||||
if err != nil {
|
||||
c.closed()
|
||||
return nil, err
|
||||
}
|
||||
ulog.Printf(1, "Conn(%v): Connected", c.LocalAddr())
|
||||
return c, nil
|
||||
case <-t:
|
||||
c.quitch <- 0
|
||||
return nil, &timeoutError{}
|
||||
}
|
||||
}
|
||||
|
||||
func newUTPConn() *UTPConn {
|
||||
rto := 60
|
||||
|
||||
return &UTPConn{
|
||||
minRtt: math.MaxInt64,
|
||||
maxWindow: mtu,
|
||||
rto: int64(rto),
|
||||
|
||||
outch: make(chan *outgoingPacket, 1),
|
||||
outchch: make(chan int),
|
||||
sendch: make(chan *outgoingPacket, 1),
|
||||
sendchch: make(chan int),
|
||||
recvch: make(chan *packet, 2),
|
||||
recvchch: make(chan int),
|
||||
winch: make(chan uint32, 1),
|
||||
quitch: make(chan int),
|
||||
activech: make(chan int),
|
||||
readch: make(chan []byte, 1),
|
||||
readchch: make(chan int),
|
||||
connch: make(chan error, 1),
|
||||
finch: make(chan int, 1),
|
||||
|
||||
keepalivech: make(chan time.Duration),
|
||||
|
||||
stat: statistics{
|
||||
rtoSum: rto,
|
||||
rtoCount: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UTPConn) ok() bool { return c != nil && c.conn != nil }
|
||||
|
||||
func (c *UTPConn) Close() error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
|
||||
select {
|
||||
case <-c.activech:
|
||||
default:
|
||||
c.quitch <- 0
|
||||
ulog.Printf(2, "Conn(%v): Wait for close", c.LocalAddr())
|
||||
<-c.finch
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *UTPConn) LocalAddr() net.Addr {
|
||||
return &UTPAddr{Addr: c.conn.LocalAddr()}
|
||||
}
|
||||
|
||||
func (c *UTPConn) RemoteAddr() net.Addr {
|
||||
return &UTPAddr{Addr: c.raddr}
|
||||
}
|
||||
|
||||
func (c *UTPConn) Read(b []byte) (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
|
||||
if c.readbuf.Len() == 0 {
|
||||
var timeout <-chan time.Time
|
||||
if !c.rdeadline.IsZero() {
|
||||
timeout = time.After(c.rdeadline.Sub(time.Now()))
|
||||
}
|
||||
|
||||
select {
|
||||
case b := <-c.readch:
|
||||
if b == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
_, err := c.readbuf.Write(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
case <-c.readchch:
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case b := <-c.readch:
|
||||
_, err := c.readbuf.Write(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if c.readbuf.Len() == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
case <-timeout:
|
||||
return 0, &timeoutError{}
|
||||
}
|
||||
}
|
||||
return c.readbuf.Read(b)
|
||||
}
|
||||
|
||||
func (c *UTPConn) Write(b []byte) (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
|
||||
var wrote uint64
|
||||
for {
|
||||
l := uint64(len(b)) - wrote
|
||||
if l > mss {
|
||||
l = mss
|
||||
}
|
||||
select {
|
||||
case c.outch <- &outgoingPacket{st_data, nil, b[wrote : wrote+l]}:
|
||||
case <-c.outchch:
|
||||
return 0, errors.New("use of closed network connection")
|
||||
}
|
||||
|
||||
wrote += l
|
||||
ulog.Printf(4, "Conn(%v): Write %d/%d bytes", c.LocalAddr(), wrote, len(b))
|
||||
if l < mss {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *UTPConn) SetDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
if err := c.SetReadDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.SetWriteDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *UTPConn) SetReadDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
c.rdeadline = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *UTPConn) SetWriteDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
c.wdeadline = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *UTPConn) SetKeepAlive(d time.Duration) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
select {
|
||||
case <-c.activech:
|
||||
default:
|
||||
c.keepalivech <- d
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readPacket(data []byte) (*packet, error) {
|
||||
p := globalPool.get()
|
||||
err := p.UnmarshalBinary(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.header.ver != version {
|
||||
return nil, errors.New("unsupported header version")
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (c *UTPConn) recv() {
|
||||
for {
|
||||
var buf [mtu]byte
|
||||
len, addr, err := c.conn.ReadFrom(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if addr.String() != c.raddr.String() {
|
||||
continue
|
||||
}
|
||||
p, err := readPacket(buf[:len])
|
||||
if err == nil {
|
||||
select {
|
||||
case c.recvch <- p:
|
||||
case <-c.recvchch:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UTPConn) loop() {
|
||||
var recvExit, sendExit bool
|
||||
var lastReceived time.Time
|
||||
var keepalive <-chan time.Time
|
||||
|
||||
go func() {
|
||||
var window uint32 = window_size * mtu
|
||||
for {
|
||||
if window >= mtu {
|
||||
select {
|
||||
case b := <-c.outch:
|
||||
select {
|
||||
case c.sendch <- b:
|
||||
window -= mtu
|
||||
case <-c.sendchch:
|
||||
return
|
||||
}
|
||||
case <-c.outchch:
|
||||
return
|
||||
case w := <-c.winch:
|
||||
window = w
|
||||
}
|
||||
} else {
|
||||
window = <-c.winch
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.sendchch:
|
||||
sendExit = true
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case <-c.recvchch:
|
||||
recvExit = true
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case p := <-c.recvch:
|
||||
ack := c.processPacket(p)
|
||||
lastReceived = time.Now()
|
||||
if ack {
|
||||
out := &outgoingPacket{st_state, nil, nil}
|
||||
selack := c.sendbuf.generateSelectiveACK()
|
||||
if len(selack) > 0 {
|
||||
out.ext = []extension{
|
||||
extension{
|
||||
typ: ext_selective_ack,
|
||||
payload: selack,
|
||||
},
|
||||
}
|
||||
c.stat.sentSelectiveACKs++
|
||||
}
|
||||
c.sendPacket(out)
|
||||
}
|
||||
|
||||
case b := <-c.sendch:
|
||||
c.sendPacket(b)
|
||||
|
||||
case <-time.After(time.Duration(c.rto) * time.Millisecond):
|
||||
if !c.state.active && time.Now().Sub(lastReceived) > reset_timeout {
|
||||
ulog.Printf(2, "Conn(%v): Connection timed out", c.LocalAddr())
|
||||
c.sendPacket(&outgoingPacket{st_reset, nil, nil})
|
||||
c.close()
|
||||
} else {
|
||||
t, err := c.sendbuf.frontPushedTime()
|
||||
if err == nil && c.lastTimedOut != t && time.Now().Sub(t) > time.Duration(c.rto)*time.Millisecond {
|
||||
c.lastTimedOut = t
|
||||
c.stat.packetTimedOuts++
|
||||
c.maxWindow /= 2
|
||||
if c.maxWindow < mtu {
|
||||
c.maxWindow = mtu
|
||||
}
|
||||
for _, p := range c.sendbuf.sequence() {
|
||||
c.resendPacket(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
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.sendPacket(&outgoingPacket{st_state, nil, nil})
|
||||
|
||||
case <-c.quitch:
|
||||
if c.state.exit != nil {
|
||||
c.state.exit(c)
|
||||
}
|
||||
}
|
||||
if recvExit && sendExit {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UTPConn) sendPacket(b *outgoingPacket) {
|
||||
p := c.makePacket(b)
|
||||
bin, err := p.MarshalBinary()
|
||||
if err == nil {
|
||||
ulog.Printf(3, "SEND %v -> %v: %v", c.conn.LocalAddr(), c.raddr, p.String())
|
||||
c.stat.sentPackets++
|
||||
_, err = c.conn.WriteTo(bin, c.raddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if b.typ != st_state {
|
||||
c.sendbuf.push(p)
|
||||
} else {
|
||||
globalPool.put(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UTPConn) resendPacket(p *packet) {
|
||||
bin, err := p.MarshalBinary()
|
||||
if err == nil {
|
||||
ulog.Printf(3, "RESEND %v -> %v: %v", c.conn.LocalAddr(), c.raddr, p.String())
|
||||
c.stat.resentPackets++
|
||||
_, err = c.conn.WriteTo(bin, c.raddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func currentMicrosecond() uint32 {
|
||||
return uint32(time.Now().Nanosecond() / 1000)
|
||||
}
|
||||
|
||||
func (c *UTPConn) processPacket(p *packet) bool {
|
||||
var ack bool
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ulog.Printf(3, "RECV %v -> %v: %v", c.raddr, c.conn.LocalAddr(), p.String())
|
||||
c.stat.receivedPackets++
|
||||
|
||||
if p.header.typ == st_state {
|
||||
|
||||
f := c.sendbuf.first()
|
||||
if f != nil && p.header.ack == f.header.seq {
|
||||
for _, e := range p.ext {
|
||||
if e.typ == ext_selective_ack {
|
||||
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 += int(c.rto)
|
||||
c.stat.rtoCount++
|
||||
}
|
||||
|
||||
if c.diff != 0 {
|
||||
ourDelay := float64(c.diff)
|
||||
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)
|
||||
}
|
||||
globalPool.put(s)
|
||||
}
|
||||
c.sendbuf.compact()
|
||||
if c.lastAck == p.header.ack {
|
||||
c.dupAck++
|
||||
if c.dupAck >= 2 {
|
||||
ulog.Printf(3, "Conn(%v): Receive 3 duplicated acks: %d", c.LocalAddr(), p.header.ack)
|
||||
c.stat.receivedDuplicatedACKs++
|
||||
p := c.sendbuf.first()
|
||||
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.resendPacket(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
|
||||
}
|
||||
ulog.Printf(4, "Conn(%v): Reset window: %d", c.LocalAddr(), wnd)
|
||||
go func() {
|
||||
c.winch <- wnd
|
||||
}()
|
||||
}
|
||||
if c.state.state != nil {
|
||||
c.state.state(c, p)
|
||||
}
|
||||
globalPool.put(p)
|
||||
} else if p.header.typ == st_reset {
|
||||
globalPool.put(p)
|
||||
c.close()
|
||||
} else {
|
||||
if c.recvbuf == nil {
|
||||
return false
|
||||
}
|
||||
ack = true
|
||||
c.recvbuf.push(p)
|
||||
for _, s := range c.recvbuf.fetchSequence() {
|
||||
c.ack = s.header.seq
|
||||
switch s.header.typ {
|
||||
case st_data:
|
||||
if c.state.data != nil {
|
||||
c.state.data(c, s)
|
||||
}
|
||||
case st_fin:
|
||||
if c.state.fin != nil {
|
||||
c.state.fin(c, s)
|
||||
}
|
||||
case st_state:
|
||||
if c.state.state != nil {
|
||||
c.state.state(c, s)
|
||||
}
|
||||
}
|
||||
globalPool.put(s)
|
||||
}
|
||||
}
|
||||
return ack
|
||||
}
|
||||
|
||||
func (c *UTPConn) makePacket(b *outgoingPacket) *packet {
|
||||
wnd := window_size * mtu
|
||||
if c.recvbuf != nil {
|
||||
wnd = c.recvbuf.space() * mtu
|
||||
}
|
||||
id := c.sid
|
||||
if b.typ == st_syn {
|
||||
id = c.rid
|
||||
}
|
||||
p := globalPool.get()
|
||||
p.header.typ = b.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
|
||||
if b.typ == st_fin {
|
||||
c.eofid = c.seq
|
||||
}
|
||||
if !(b.typ == st_state && len(b.payload) == 0) {
|
||||
c.seq++
|
||||
}
|
||||
p.payload = p.payload[:len(b.payload)]
|
||||
copy(p.payload, b.payload)
|
||||
return p
|
||||
}
|
||||
|
||||
func (c *UTPConn) close() {
|
||||
if !c.state.closed {
|
||||
close(c.outchch)
|
||||
close(c.readchch)
|
||||
close(c.sendchch)
|
||||
close(c.recvchch)
|
||||
close(c.activech)
|
||||
close(c.finch)
|
||||
c.closed()
|
||||
|
||||
// Accepted connection
|
||||
if c.closech != nil {
|
||||
c.closech <- c.sid
|
||||
} else {
|
||||
c.conn.Close()
|
||||
}
|
||||
|
||||
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)
|
||||
ulog.Printf(1, "Conn(%v): * AverageRTO: %d", c.LocalAddr(), c.stat.rtoSum/c.stat.rtoCount)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *UTPConn) closed() {
|
||||
ulog.Printf(2, "Conn(%v): Change state: CLOSED", c.LocalAddr())
|
||||
c.state = state_closed
|
||||
}
|
||||
|
||||
func (c *UTPConn) closing() {
|
||||
ulog.Printf(2, "Conn(%v): Change state: CLOSING", c.LocalAddr())
|
||||
c.state = state_closing
|
||||
}
|
||||
|
||||
func (c *UTPConn) syn_sent() {
|
||||
ulog.Printf(2, "Conn(%v): Change state: SYN_SENT", c.LocalAddr())
|
||||
c.state = state_syn_sent
|
||||
}
|
||||
|
||||
func (c *UTPConn) connected() {
|
||||
ulog.Printf(2, "Conn(%v): Change state: CONNECTED", c.LocalAddr())
|
||||
c.state = state_connected
|
||||
}
|
||||
|
||||
func (c *UTPConn) fin_sent() {
|
||||
ulog.Printf(2, "Conn(%v): Change state: FIN_SENT", c.LocalAddr())
|
||||
c.state = state_fin_sent
|
||||
}
|
||||
|
||||
type state struct {
|
||||
data func(c *UTPConn, p *packet)
|
||||
fin func(c *UTPConn, p *packet)
|
||||
state func(c *UTPConn, p *packet)
|
||||
exit func(c *UTPConn)
|
||||
active bool
|
||||
closed bool
|
||||
}
|
||||
|
||||
var state_closed state = state{
|
||||
closed: true,
|
||||
}
|
||||
|
||||
var state_closing state = state{
|
||||
data: func(c *UTPConn, p *packet) {
|
||||
select {
|
||||
case c.readch <- append([]byte(nil), p.payload...):
|
||||
case <-c.readchch:
|
||||
}
|
||||
if c.recvbuf.empty() && c.sendbuf.empty() {
|
||||
c.close()
|
||||
}
|
||||
},
|
||||
state: func(c *UTPConn, p *packet) {
|
||||
if c.recvbuf.empty() && c.sendbuf.empty() {
|
||||
c.close()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var state_syn_sent state = state{
|
||||
state: func(c *UTPConn, p *packet) {
|
||||
c.recvbuf = newPacketBuffer(window_size, int(p.header.seq))
|
||||
c.connected()
|
||||
c.connch <- nil
|
||||
},
|
||||
exit: func(c *UTPConn) {
|
||||
go func() {
|
||||
select {
|
||||
case c.outch <- &outgoingPacket{st_fin, nil, nil}:
|
||||
case <-c.outchch:
|
||||
}
|
||||
}()
|
||||
c.fin_sent()
|
||||
},
|
||||
active: true,
|
||||
}
|
||||
|
||||
var state_connected state = state{
|
||||
data: func(c *UTPConn, p *packet) {
|
||||
select {
|
||||
case c.readch <- append([]byte(nil), p.payload...):
|
||||
case <-c.readchch:
|
||||
}
|
||||
},
|
||||
fin: func(c *UTPConn, p *packet) {
|
||||
if c.recvbuf.empty() && c.sendbuf.empty() {
|
||||
c.close()
|
||||
} else {
|
||||
c.closing()
|
||||
}
|
||||
},
|
||||
exit: func(c *UTPConn) {
|
||||
go func() {
|
||||
select {
|
||||
case c.outch <- &outgoingPacket{st_fin, nil, nil}:
|
||||
case <-c.outchch:
|
||||
}
|
||||
}()
|
||||
c.fin_sent()
|
||||
},
|
||||
active: true,
|
||||
}
|
||||
|
||||
var state_fin_sent state = state{
|
||||
state: func(c *UTPConn, p *packet) {
|
||||
if p.header.ack == c.eofid {
|
||||
if c.recvbuf.empty() && c.sendbuf.empty() {
|
||||
c.close()
|
||||
} else {
|
||||
c.closing()
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
68
Godeps/_workspace/src/github.com/h2so5/utp/dial.go
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/h2so5/utp/dial.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Dial(n, addr string) (*UTPConn, error) {
|
||||
raddr, err := ResolveUTPAddr(n, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return DialUTP(n, nil, raddr)
|
||||
}
|
||||
|
||||
func DialUTP(n string, laddr, raddr *UTPAddr) (*UTPConn, error) {
|
||||
return dial(n, laddr, raddr, 0)
|
||||
}
|
||||
|
||||
func DialUTPTimeout(n string, laddr, raddr *UTPAddr, timeout time.Duration) (*UTPConn, error) {
|
||||
return dial(n, laddr, raddr, timeout)
|
||||
}
|
||||
|
||||
// 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) (*UTPConn, error) {
|
||||
raddr, err := ResolveUTPAddr(n, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var laddr *UTPAddr
|
||||
if d.LocalAddr != nil {
|
||||
var ok bool
|
||||
laddr, ok = d.LocalAddr.(*UTPAddr)
|
||||
if !ok {
|
||||
return nil, errors.New("Dialer.LocalAddr is not a UTPAddr")
|
||||
}
|
||||
}
|
||||
|
||||
return DialUTPTimeout(n, laddr, raddr, d.Timeout)
|
||||
}
|
||||
329
Godeps/_workspace/src/github.com/h2so5/utp/listener.go
generated
vendored
Normal file
329
Godeps/_workspace/src/github.com/h2so5/utp/listener.go
generated
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UTPListener struct {
|
||||
// RawConn represents an out-of-band connection.
|
||||
// This allows a single socket to handle multiple protocols.
|
||||
RawConn net.PacketConn
|
||||
|
||||
conn net.PacketConn
|
||||
conns map[uint16]*UTPConn
|
||||
accept chan (*UTPConn)
|
||||
err chan (error)
|
||||
lasterr error
|
||||
deadline time.Time
|
||||
closech chan int
|
||||
connch chan uint16
|
||||
closed bool
|
||||
}
|
||||
|
||||
func Listen(n, laddr string) (*UTPListener, error) {
|
||||
addr, err := ResolveUTPAddr(n, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ListenUTP(n, addr)
|
||||
}
|
||||
|
||||
func ListenUTP(n string, laddr *UTPAddr) (*UTPListener, error) {
|
||||
udpnet, err := utp2udp(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := listenPacket(udpnet, laddr.Addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := UTPListener{
|
||||
RawConn: newRawConn(conn),
|
||||
conn: conn,
|
||||
conns: make(map[uint16]*UTPConn),
|
||||
accept: make(chan (*UTPConn), 10),
|
||||
err: make(chan (error), 1),
|
||||
closech: make(chan int),
|
||||
connch: make(chan uint16),
|
||||
lasterr: nil,
|
||||
}
|
||||
|
||||
l.listen()
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
type incoming struct {
|
||||
p *packet
|
||||
addr net.Addr
|
||||
}
|
||||
|
||||
func (l *UTPListener) listen() {
|
||||
inch := make(chan incoming)
|
||||
raw := l.RawConn.(*rawConn)
|
||||
|
||||
// reads udp packets
|
||||
go func() {
|
||||
for {
|
||||
var buf [mtu]byte
|
||||
len, addr, err := l.conn.ReadFrom(buf[:])
|
||||
if err != nil {
|
||||
l.err <- err
|
||||
return
|
||||
}
|
||||
p, err := readPacket(buf[:len])
|
||||
if err == nil {
|
||||
inch <- incoming{p, addr}
|
||||
} else {
|
||||
select {
|
||||
case <-raw.closed:
|
||||
default:
|
||||
i := rawIncoming{b: buf[:len], addr: addr}
|
||||
select {
|
||||
case raw.in <- i:
|
||||
default:
|
||||
// discard the oldest packet
|
||||
<-raw.in
|
||||
raw.in <- i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case i := <-inch:
|
||||
l.processPacket(i.p, i.addr)
|
||||
case <-l.closech:
|
||||
ulog.Printf(2, "Listener(%v): Stop listening", l.conn.LocalAddr())
|
||||
close(l.accept)
|
||||
l.closed = true
|
||||
case id := <-l.connch:
|
||||
if _, ok := l.conns[id]; !ok {
|
||||
delete(l.conns, id+1)
|
||||
ulog.Printf(2, "Listener(%v): Connection closed #%d (alive: %d)", l.conn.LocalAddr(), id, len(l.conns))
|
||||
if l.closed && len(l.conns) == 0 {
|
||||
ulog.Printf(2, "Listener(%v): All accepted connections are closed", l.conn.LocalAddr())
|
||||
l.conn.Close()
|
||||
ulog.Printf(1, "Listener(%v): Closed", l.conn.LocalAddr())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
ulog.Printf(1, "Listener(%v): Start listening", l.conn.LocalAddr())
|
||||
}
|
||||
|
||||
func listenPacket(n, addr string) (net.PacketConn, error) {
|
||||
if n == "mem" {
|
||||
return nil, errors.New("TODO implement in-memory packet connection")
|
||||
}
|
||||
return net.ListenPacket(n, addr)
|
||||
}
|
||||
|
||||
func (l *UTPListener) processPacket(p *packet, addr net.Addr) {
|
||||
switch p.header.typ {
|
||||
case st_data, st_fin, st_state, st_reset:
|
||||
if c, ok := l.conns[p.header.id]; ok {
|
||||
select {
|
||||
case c.recvch <- p:
|
||||
case <-c.recvchch:
|
||||
}
|
||||
}
|
||||
case st_syn:
|
||||
if l.closed {
|
||||
return
|
||||
}
|
||||
sid := p.header.id + 1
|
||||
if _, ok := l.conns[p.header.id]; !ok {
|
||||
seq := rand.Intn(math.MaxUint16)
|
||||
|
||||
c := newUTPConn()
|
||||
c.conn = l.conn
|
||||
c.raddr = addr
|
||||
c.rid = p.header.id + 1
|
||||
c.sid = p.header.id
|
||||
c.seq = uint16(seq)
|
||||
c.ack = p.header.seq
|
||||
c.diff = currentMicrosecond() - p.header.t
|
||||
c.state = state_connected
|
||||
c.closech = l.connch
|
||||
c.recvbuf = newPacketBuffer(window_size, int(p.header.seq))
|
||||
c.sendbuf = newPacketBuffer(window_size, seq)
|
||||
|
||||
go c.loop()
|
||||
select {
|
||||
case c.recvch <- p:
|
||||
case <-c.recvchch:
|
||||
}
|
||||
|
||||
l.conns[sid] = c
|
||||
ulog.Printf(2, "Listener(%v): New incoming connection #%d from %v (alive: %d)", l.conn.LocalAddr(), sid, addr, len(l.conns))
|
||||
|
||||
l.accept <- c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UTPListener) Accept() (net.Conn, error) {
|
||||
return l.AcceptUTP()
|
||||
}
|
||||
|
||||
func (l *UTPListener) AcceptUTP() (*UTPConn, error) {
|
||||
if l == nil || l.conn == nil {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
if l.lasterr != nil {
|
||||
return nil, l.lasterr
|
||||
}
|
||||
var timeout <-chan time.Time
|
||||
if !l.deadline.IsZero() {
|
||||
timeout = time.After(l.deadline.Sub(time.Now()))
|
||||
}
|
||||
select {
|
||||
case conn := <-l.accept:
|
||||
if conn == nil {
|
||||
return nil, errors.New("use of closed network connection")
|
||||
}
|
||||
return conn, nil
|
||||
case err := <-l.err:
|
||||
l.lasterr = err
|
||||
return nil, err
|
||||
case <-timeout:
|
||||
return nil, &timeoutError{}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UTPListener) Addr() net.Addr {
|
||||
return &UTPAddr{Addr: l.conn.LocalAddr()}
|
||||
}
|
||||
|
||||
func (l *UTPListener) Close() error {
|
||||
if l == nil || l.conn == nil {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
l.closech <- 0
|
||||
l.RawConn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *UTPListener) SetDeadline(t time.Time) error {
|
||||
if l == nil || l.conn == nil {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
l.deadline = t
|
||||
return nil
|
||||
}
|
||||
|
||||
type rawIncoming struct {
|
||||
b []byte
|
||||
addr net.Addr
|
||||
}
|
||||
|
||||
type rawConn struct {
|
||||
conn net.PacketConn
|
||||
rdeadline, wdeadline time.Time
|
||||
in chan rawIncoming
|
||||
closed chan int
|
||||
}
|
||||
|
||||
func newRawConn(conn net.PacketConn) *rawConn {
|
||||
return &rawConn{
|
||||
conn: conn,
|
||||
in: make(chan rawIncoming, 100),
|
||||
closed: make(chan int),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *rawConn) ok() bool { return c != nil && c.conn != nil }
|
||||
|
||||
func (c *rawConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
|
||||
if !c.ok() {
|
||||
return 0, nil, syscall.EINVAL
|
||||
}
|
||||
select {
|
||||
case <-c.closed:
|
||||
return 0, nil, errors.New("use of closed network connection")
|
||||
default:
|
||||
}
|
||||
var timeout <-chan time.Time
|
||||
if !c.rdeadline.IsZero() {
|
||||
timeout = time.After(c.rdeadline.Sub(time.Now()))
|
||||
}
|
||||
select {
|
||||
case r := <-c.in:
|
||||
return copy(b, r.b), r.addr, nil
|
||||
case <-timeout:
|
||||
return 0, nil, &timeoutError{}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *rawConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
||||
if !c.ok() {
|
||||
return 0, syscall.EINVAL
|
||||
}
|
||||
select {
|
||||
case <-c.closed:
|
||||
return 0, errors.New("use of closed network connection")
|
||||
default:
|
||||
}
|
||||
return c.conn.WriteTo(b, addr)
|
||||
}
|
||||
|
||||
func (c *rawConn) Close() error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
select {
|
||||
case <-c.closed:
|
||||
return errors.New("use of closed network connection")
|
||||
default:
|
||||
close(c.closed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *rawConn) LocalAddr() net.Addr {
|
||||
if !c.ok() {
|
||||
return nil
|
||||
}
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *rawConn) SetDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
if err := c.SetReadDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.SetWriteDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *rawConn) SetReadDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
c.rdeadline = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *rawConn) SetWriteDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
c.wdeadline = t
|
||||
return nil
|
||||
}
|
||||
50
Godeps/_workspace/src/github.com/h2so5/utp/log.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/h2so5/utp/log.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
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...)
|
||||
}
|
||||
240
Godeps/_workspace/src/github.com/h2so5/utp/packet.go
generated
vendored
Normal file
240
Godeps/_workspace/src/github.com/h2so5/utp/packet.go
generated
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type outgoingPacket struct {
|
||||
typ int
|
||||
ext []extension
|
||||
payload []byte
|
||||
}
|
||||
|
||||
func (p *packet) MarshalBinary() ([]byte, error) {
|
||||
firstExt := ext_none
|
||||
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 := ext_none
|
||||
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 != ext_none {
|
||||
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)
|
||||
|
||||
l := buf.Len()
|
||||
if l > 0 {
|
||||
p.payload = p.payload[:l]
|
||||
_, err := buf.Read(p.payload[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p packet) String() string {
|
||||
var s string = fmt.Sprintf("[%d ", p.header.id)
|
||||
switch p.header.typ {
|
||||
case st_data:
|
||||
s += "ST_DATA"
|
||||
case st_fin:
|
||||
s += "ST_FIN"
|
||||
case st_state:
|
||||
s += "ST_STATE"
|
||||
case st_reset:
|
||||
s += "ST_RESET"
|
||||
case st_syn:
|
||||
s += "ST_SYN"
|
||||
}
|
||||
s += fmt.Sprintf(" seq:%d ack:%d len:%d", p.header.seq, p.header.ack, len(p.payload))
|
||||
s += "]"
|
||||
return s
|
||||
}
|
||||
|
||||
var globalPool packetPool
|
||||
|
||||
type packetPool struct {
|
||||
root *packetPoolNode
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type packetPoolNode struct {
|
||||
p *packet
|
||||
next *packetPoolNode
|
||||
}
|
||||
|
||||
func (o *packetPool) get() *packet {
|
||||
o.mutex.Lock()
|
||||
defer o.mutex.Unlock()
|
||||
r := o.root
|
||||
if r != nil {
|
||||
o.root = o.root.next
|
||||
return r.p
|
||||
} else {
|
||||
return &packet{
|
||||
payload: make([]byte, 0, mss),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *packetPool) put(p *packet) {
|
||||
o.mutex.Lock()
|
||||
defer o.mutex.Unlock()
|
||||
o.root = &packetPoolNode{
|
||||
p: p,
|
||||
next: o.root,
|
||||
}
|
||||
}
|
||||
3
Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
ucat
|
||||
random
|
||||
.trash/
|
||||
34
Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile
generated
vendored
Normal file
34
Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# Run tests
|
||||
|
||||
testnames=simple
|
||||
tests=$(addprefix test_, $(testnames))
|
||||
trash=.trash/
|
||||
|
||||
all: ucat
|
||||
|
||||
test: clean ucat ${tests}
|
||||
@echo ${tests}
|
||||
@echo "*** tests passed ***"
|
||||
|
||||
# not sue why this doesn't work:
|
||||
# test_%: test_%.sh
|
||||
test_simple: test_simple.sh
|
||||
mkdir -p ${trash}
|
||||
@echo "*** running $@ ***"
|
||||
./$@.sh
|
||||
|
||||
clean:
|
||||
@echo "*** $@ ***"
|
||||
-rm -r ${trash}
|
||||
|
||||
deps: random ucat
|
||||
|
||||
ucat:
|
||||
go build
|
||||
|
||||
random:
|
||||
@echo "*** installing $@ ***"
|
||||
go get github.com/jbenet/go-random/random
|
||||
go build -o random github.com/jbenet/go-random/random
|
||||
|
||||
.PHONY: clean ucat ${tests}
|
||||
49
Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh
generated
vendored
Normal file
49
Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
#!/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
|
||||
188
Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go
generated
vendored
Normal file
188
Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go
generated
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
// 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/jbenet/go-multiaddr-net/Godeps/_workspace/src/github.com/h2so5/utp"
|
||||
)
|
||||
|
||||
var verbose = false
|
||||
|
||||
// Usage prints out the usage of this module.
|
||||
// Assumes flags use go stdlib flag pacakage.
|
||||
var Usage = func() {
|
||||
text := `ucat - uTP netcat in Go
|
||||
|
||||
Usage:
|
||||
|
||||
listen: %s [<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 {
|
||||
l, err := utp.Listen("utp", localAddr)
|
||||
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.ResolveUTPAddr("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:
|
||||
}
|
||||
}
|
||||
29
Godeps/_workspace/src/github.com/h2so5/utp/utp.go
generated
vendored
Normal file
29
Godeps/_workspace/src/github.com/h2so5/utp/utp.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package utp
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
version = 1
|
||||
|
||||
st_data = 0
|
||||
st_fin = 1
|
||||
st_state = 2
|
||||
st_reset = 3
|
||||
st_syn = 4
|
||||
|
||||
ext_none = 0
|
||||
ext_selective_ack = 1
|
||||
|
||||
header_size = 20
|
||||
mtu = 3200
|
||||
mss = mtu - header_size
|
||||
window_size = 100
|
||||
|
||||
reset_timeout = 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 }
|
||||
601
Godeps/_workspace/src/github.com/h2so5/utp/utp_test.go
generated
vendored
Normal file
601
Godeps/_workspace/src/github.com/h2so5/utp/utp_test.go
generated
vendored
Normal file
@ -0,0 +1,601 @@
|
||||
package utp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
}
|
||||
|
||||
func TestReadWrite(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
raddr, err := ResolveUTPAddr("utp", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
err = ln.SetDeadline(time.Now().Add(1000 * time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ln.Close()
|
||||
|
||||
payload := []byte("Hello!")
|
||||
_, err = c.Write(payload)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = s.SetDeadline(time.Now().Add(1000 * time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buf [256]byte
|
||||
l, err := s.Read(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(payload, buf[:l]) {
|
||||
t.Errorf("expected payload of %v; got %v", payload, buf[:l])
|
||||
}
|
||||
|
||||
payload2 := []byte("World!")
|
||||
_, err = s.Write(payload2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = c.SetDeadline(time.Now().Add(1000 * time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
l, err = c.Read(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(payload2, buf[:l]) {
|
||||
t.Errorf("expected payload of %v; got %v", payload2, buf[:l])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawReadWrite(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
raddr, err := net.ResolveUDPAddr("udp", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := net.DialUDP("udp", nil, raddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
payload := []byte("Hello!")
|
||||
_, err = c.Write(payload)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buf [256]byte
|
||||
n, addr, err := ln.RawConn.ReadFrom(buf[:])
|
||||
if !bytes.Equal(payload, buf[:n]) {
|
||||
t.Errorf("expected payload of %v; got %v", payload, buf[:n])
|
||||
}
|
||||
if addr.String() != c.LocalAddr().String() {
|
||||
t.Errorf("expected addr of %v; got %v", c.LocalAddr(), addr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLongReadWriteC2S(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
raddr, err := ResolveUTPAddr("utp", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
err = ln.SetDeadline(time.Now().Add(1000 * time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
ln.Close()
|
||||
|
||||
var payload [10485760]byte
|
||||
for i := range payload {
|
||||
payload[i] = byte(rand.Int())
|
||||
}
|
||||
|
||||
rch := make(chan []byte)
|
||||
ech := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
defer c.Close()
|
||||
_, err := c.Write(payload[:])
|
||||
if err != nil {
|
||||
ech <- err
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
b, err := ioutil.ReadAll(s)
|
||||
if err != nil {
|
||||
ech <- err
|
||||
rch <- nil
|
||||
} else {
|
||||
ech <- nil
|
||||
rch <- b
|
||||
}
|
||||
}()
|
||||
|
||||
err = <-ech
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := <-rch
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(r, payload[:]) {
|
||||
t.Errorf("expected payload of %d; got %d", len(payload[:]), len(r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLongReadWriteS2C(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
raddr, err := ResolveUTPAddr("utp", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := DialUTPTimeout("utp", nil, raddr, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
err = ln.SetDeadline(time.Now().Add(1000 * time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
ln.Close()
|
||||
|
||||
var payload [10485760]byte
|
||||
for i := range payload {
|
||||
payload[i] = byte(rand.Int())
|
||||
}
|
||||
|
||||
rch := make(chan []byte)
|
||||
ech := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
defer s.Close()
|
||||
_, err := s.Write(payload[:])
|
||||
if err != nil {
|
||||
ech <- err
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
b, err := ioutil.ReadAll(c)
|
||||
if err != nil {
|
||||
ech <- err
|
||||
rch <- nil
|
||||
} else {
|
||||
ech <- nil
|
||||
rch <- b
|
||||
}
|
||||
}()
|
||||
|
||||
err = <-ech
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := <-rch
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(r, payload[:]) {
|
||||
t.Errorf("expected payload of %d; got %d", len(payload[:]), len(r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccept(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
c, err := DialUTPTimeout("utp", nil, ln.Addr().(*UTPAddr), 200*time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
err = ln.SetDeadline(time.Now().Add(100 * time.Millisecond))
|
||||
_, err = ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcceptDeadline(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
err = ln.SetDeadline(time.Now().Add(time.Millisecond))
|
||||
_, err = ln.Accept()
|
||||
if err == nil {
|
||||
t.Fatal("Accept should failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcceptClosedListener(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ln.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = ln.Accept()
|
||||
if err == nil {
|
||||
t.Fatal("Accept should failed")
|
||||
}
|
||||
_, err = ln.Accept()
|
||||
if err == nil {
|
||||
t.Fatal("Accept should failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialer(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
d := Dialer{}
|
||||
c, err := d.Dial("utp", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
}
|
||||
|
||||
func TestDialerAddrs(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
laddr, err := ResolveUTPAddr("utp", "127.0.0.1:45678")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := Dialer{LocalAddr: laddr}
|
||||
c1, err := d.Dial("utp", ln.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c1.Close()
|
||||
|
||||
c2, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c2.Close()
|
||||
|
||||
eq := func(a, b net.Addr) bool {
|
||||
return a.String() == b.String()
|
||||
}
|
||||
|
||||
if !eq(d.LocalAddr, c2.RemoteAddr()) {
|
||||
t.Fatal("dialer.LocalAddr not equal to c2.RemoteAddr ")
|
||||
}
|
||||
if !eq(c1.LocalAddr(), c2.RemoteAddr()) {
|
||||
t.Fatal("c1.LocalAddr not equal to c2.RemoteAddr ")
|
||||
}
|
||||
if !eq(c2.LocalAddr(), c1.RemoteAddr()) {
|
||||
t.Fatal("c2.LocalAddr not equal to c1.RemoteAddr ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialerTimeout(t *testing.T) {
|
||||
timeout := time.Millisecond * 200
|
||||
d := Dialer{Timeout: timeout}
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
_, err := d.Dial("utp", "127.0.0.1:34567")
|
||||
if err == nil {
|
||||
t.Fatal("should not connect")
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(timeout * 2):
|
||||
t.Fatal("should have ended already")
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketBinary(t *testing.T) {
|
||||
h := header{
|
||||
typ: st_fin,
|
||||
ver: version,
|
||||
id: 100,
|
||||
t: 50000,
|
||||
diff: 10000,
|
||||
wnd: 65535,
|
||||
seq: 100,
|
||||
ack: 200,
|
||||
}
|
||||
|
||||
e := []extension{
|
||||
extension{
|
||||
typ: ext_selective_ack,
|
||||
payload: []byte{0, 1, 0, 1},
|
||||
},
|
||||
extension{
|
||||
typ: ext_selective_ack,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteOnClosedChannel(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
c, err := DialUTPTimeout("utp", nil, ln.Addr().(*UTPAddr), 200*time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
_, err := c.Write([]byte{100})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
c.Close()
|
||||
}
|
||||
|
||||
func TestReadOnClosedChannel(t *testing.T) {
|
||||
ln, err := Listen("utp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
c, err := DialUTPTimeout("utp", nil, ln.Addr().(*UTPAddr), 200*time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
var buf [16]byte
|
||||
_, err := c.Read(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
c.Close()
|
||||
}
|
||||
|
||||
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 TestTimedBufferNode(t *testing.T) {
|
||||
b := timedBuffer{d: time.Millisecond * 100}
|
||||
b.push(100)
|
||||
b.push(200)
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
b.push(300)
|
||||
b.push(400)
|
||||
m := b.min()
|
||||
if m != 300 {
|
||||
t.Errorf("expected min == 300; got %d", m)
|
||||
}
|
||||
}
|
||||
10
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- release
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
21
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/LICENSE
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Juan Batiz-Benet
|
||||
|
||||
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.
|
||||
58
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/README.md
generated
vendored
Normal file
58
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/README.md
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
# go-multiaddr
|
||||
|
||||
[multiaddr](https://github.com/jbenet/multiaddr) implementation in Go.
|
||||
|
||||
## Example
|
||||
|
||||
### Simple
|
||||
|
||||
```go
|
||||
import ma "github.com/jbenet/go-multiaddr"
|
||||
|
||||
// construct from a string (err signals parse failure)
|
||||
m1, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
|
||||
// construct from bytes (err signals parse failure)
|
||||
m2, err := ma.NewMultiaddrBytes(m1.Bytes())
|
||||
|
||||
// true
|
||||
strings.Equal(m1.String(), "/ip4/127.0.0.1/udp/1234")
|
||||
strings.Equal(m1.String(), m2.String())
|
||||
bytes.Equal(m1.Bytes(), m2.Bytes())
|
||||
m1.Equal(m2)
|
||||
m2.Equal(m1)
|
||||
```
|
||||
|
||||
### Protocols
|
||||
|
||||
```go
|
||||
// get the multiaddr protocol description objects
|
||||
addr.Protocols()
|
||||
// []*Protocol{
|
||||
// &Protocol{ Code: 4, Name: 'ip4', Size: 32},
|
||||
// &Protocol{ Code: 17, Name: 'udp', Size: 16},
|
||||
// }
|
||||
```
|
||||
|
||||
### En/decapsulate
|
||||
|
||||
```go
|
||||
m.Encapsulate(ma.NewMultiaddr("/sctp/5678"))
|
||||
// <Multiaddr /ip4/127.0.0.1/udp/1234/sctp/5678>
|
||||
m.Decapsulate(ma.NewMultiaddr("/udp")) // up to + inc last occurrence of subaddr
|
||||
// <Multiaddr /ip4/127.0.0.1>
|
||||
```
|
||||
|
||||
### Tunneling
|
||||
|
||||
Multiaddr allows expressing tunnels very nicely.
|
||||
|
||||
```js
|
||||
printer, _ := ma.NewMultiaddr("/ip4/192.168.0.13/tcp/80")
|
||||
proxy, _ := ma.NewMultiaddr("/ip4/10.20.30.40/tcp/443")
|
||||
printerOverProxy := proxy.Encapsulate(printer)
|
||||
// /ip4/10.20.30.40/tcp/443/ip4/192.168.0.13/tcp/80
|
||||
|
||||
proxyAgain := printerOverProxy.Decapsulate(printer)
|
||||
// /ip4/10.20.30.40/tcp/443
|
||||
```
|
||||
137
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go
generated
vendored
Normal file
137
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func stringToBytes(s string) ([]byte, error) {
|
||||
|
||||
// consume trailing slashes
|
||||
s = strings.TrimRight(s, "/")
|
||||
|
||||
b := []byte{}
|
||||
sp := strings.Split(s, "/")
|
||||
|
||||
if sp[0] != "" {
|
||||
return nil, fmt.Errorf("invalid multiaddr, must begin with /")
|
||||
}
|
||||
|
||||
// consume first empty elem
|
||||
sp = sp[1:]
|
||||
|
||||
for len(sp) > 0 {
|
||||
p := ProtocolWithName(sp[0])
|
||||
if p == nil {
|
||||
return nil, fmt.Errorf("no protocol with name %s", sp[0])
|
||||
}
|
||||
b = append(b, CodeToVarint(p.Code)...)
|
||||
sp = sp[1:]
|
||||
|
||||
if p.Size > 0 {
|
||||
a := addressStringToBytes(p, sp[0])
|
||||
b = append(b, a...)
|
||||
sp = sp[1:]
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func bytesToString(b []byte) (ret string, err error) {
|
||||
// panic handler, in case we try accessing bytes incorrectly.
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ret = ""
|
||||
err = e.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
s := ""
|
||||
|
||||
for len(b) > 0 {
|
||||
|
||||
code, n := ReadVarintCode(b)
|
||||
b = b[n:]
|
||||
p := ProtocolWithCode(code)
|
||||
if p == nil {
|
||||
return "", fmt.Errorf("no protocol with code %d", code)
|
||||
}
|
||||
s = strings.Join([]string{s, "/", p.Name}, "")
|
||||
|
||||
if p.Size > 0 {
|
||||
a := addressBytesToString(p, b[:(p.Size/8)])
|
||||
if len(a) > 0 {
|
||||
s = strings.Join([]string{s, "/", a}, "")
|
||||
}
|
||||
b = b[(p.Size / 8):]
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func bytesSplit(b []byte) (ret [][]byte, err error) {
|
||||
// panic handler, in case we try accessing bytes incorrectly.
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ret = [][]byte{}
|
||||
err = e.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
ret = [][]byte{}
|
||||
for len(b) > 0 {
|
||||
code, n := ReadVarintCode(b)
|
||||
p := ProtocolWithCode(code)
|
||||
if p == nil {
|
||||
return [][]byte{}, fmt.Errorf("no protocol with code %d", b[0])
|
||||
}
|
||||
|
||||
length := n + (p.Size / 8)
|
||||
ret = append(ret, b[:length])
|
||||
b = b[length:]
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func addressStringToBytes(p *Protocol, s string) []byte {
|
||||
switch p.Code {
|
||||
|
||||
case P_IP4: // ipv4
|
||||
return net.ParseIP(s).To4()
|
||||
|
||||
case P_IP6: // ipv6
|
||||
return net.ParseIP(s).To16()
|
||||
|
||||
// tcp udp dccp sctp
|
||||
case P_TCP, P_UDP, P_DCCP, P_SCTP:
|
||||
b := make([]byte, 2)
|
||||
i, err := strconv.Atoi(s)
|
||||
if err == nil {
|
||||
binary.BigEndian.PutUint16(b, uint16(i))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
func addressBytesToString(p *Protocol, b []byte) string {
|
||||
switch p.Code {
|
||||
|
||||
// ipv4,6
|
||||
case P_IP4, P_IP6:
|
||||
return net.IP(b).String()
|
||||
|
||||
// tcp udp dccp sctp
|
||||
case P_TCP, P_UDP, P_DCCP, P_SCTP:
|
||||
i := binary.BigEndian.Uint16(b)
|
||||
return strconv.Itoa(int(i))
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
36
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/doc.go
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/doc.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Package multiaddr provides an implementation of the Multiaddr network
|
||||
address format. Multiaddr emphasizes explicitness, self-description, and
|
||||
portability. It allows applications to treat addresses as opaque tokens,
|
||||
and to avoid making assumptions about the address representation (e.g. length).
|
||||
Learn more at https://github.com/jbenet/multiaddr
|
||||
|
||||
Basic Use:
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
ma "github.com/jbenet/go-multiaddr"
|
||||
)
|
||||
|
||||
// construct from a string (err signals parse failure)
|
||||
m1, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
|
||||
// construct from bytes (err signals parse failure)
|
||||
m2, err := ma.NewMultiaddrBytes(m1.Bytes())
|
||||
|
||||
// true
|
||||
strings.Equal(m1.String(), "/ip4/127.0.0.1/udp/1234")
|
||||
strings.Equal(m1.String(), m2.String())
|
||||
bytes.Equal(m1.Bytes(), m2.Bytes())
|
||||
m1.Equal(m2)
|
||||
m2.Equal(m1)
|
||||
|
||||
// tunneling (en/decap)
|
||||
printer, _ := ma.NewMultiaddr("/ip4/192.168.0.13/tcp/80")
|
||||
proxy, _ := ma.NewMultiaddr("/ip4/10.20.30.40/tcp/443")
|
||||
printerOverProxy := proxy.Encapsulate(printer)
|
||||
proxyAgain := printerOverProxy.Decapsulate(printer)
|
||||
|
||||
*/
|
||||
package multiaddr
|
||||
42
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/interface.go
generated
vendored
Normal file
42
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/interface.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package multiaddr
|
||||
|
||||
/*
|
||||
Multiaddr is a cross-protocol, cross-platform format for representing
|
||||
internet addresses. It emphasizes explicitness and self-description.
|
||||
Learn more here: https://github.com/jbenet/multiaddr
|
||||
|
||||
Multiaddrs have both a binary and string representation.
|
||||
|
||||
import ma "github.com/jbenet/go-multiaddr"
|
||||
|
||||
addr, err := ma.NewMultiaddr("/ip4/1.2.3.4/tcp/80")
|
||||
// err non-nil when parsing failed.
|
||||
|
||||
*/
|
||||
type Multiaddr interface {
|
||||
// Equal returns whether two Multiaddrs are exactly equal
|
||||
Equal(Multiaddr) bool
|
||||
|
||||
// Bytes returns the []byte representation of this Multiaddr
|
||||
Bytes() []byte
|
||||
|
||||
// String returns the string representation of this Multiaddr
|
||||
// (may panic if internal state is corrupted)
|
||||
String() string
|
||||
|
||||
// Protocols returns the list of Protocols this Multiaddr includes
|
||||
// will panic if protocol code incorrect (and bytes accessed incorrectly)
|
||||
Protocols() []*Protocol
|
||||
|
||||
// Encapsulate wraps this Multiaddr around another. For example:
|
||||
//
|
||||
// /ip4/1.2.3.4 encapsulate /tcp/80 = /ip4/1.2.3.4/tcp/80
|
||||
//
|
||||
Encapsulate(Multiaddr) Multiaddr
|
||||
|
||||
// Decapsultate removes a Multiaddr wrapping. For example:
|
||||
//
|
||||
// /ip4/1.2.3.4/tcp/80 decapsulate /ip4/1.2.3.4 = /tcp/80
|
||||
//
|
||||
Decapsulate(Multiaddr) Multiaddr
|
||||
}
|
||||
111
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go
generated
vendored
Normal file
111
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go
generated
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// multiaddr is the data structure representing a Multiaddr
|
||||
type multiaddr struct {
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
// NewMultiaddr parses and validates an input string, returning a *Multiaddr
|
||||
func NewMultiaddr(s string) (Multiaddr, error) {
|
||||
b, err := stringToBytes(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &multiaddr{bytes: b}, nil
|
||||
}
|
||||
|
||||
// NewMultiaddrBytes initializes a Multiaddr from a byte representation.
|
||||
// It validates it as an input string.
|
||||
func NewMultiaddrBytes(b []byte) (Multiaddr, error) {
|
||||
s, err := bytesToString(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewMultiaddr(s)
|
||||
}
|
||||
|
||||
// Equal tests whether two multiaddrs are equal
|
||||
func (m *multiaddr) Equal(m2 Multiaddr) bool {
|
||||
return bytes.Equal(m.bytes, m2.Bytes())
|
||||
}
|
||||
|
||||
// Bytes returns the []byte representation of this Multiaddr
|
||||
func (m *multiaddr) Bytes() []byte {
|
||||
// consider returning copy to prevent changing underneath us?
|
||||
cpy := make([]byte, len(m.bytes))
|
||||
copy(cpy, m.bytes)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// String returns the string representation of a Multiaddr
|
||||
func (m *multiaddr) String() string {
|
||||
s, err := bytesToString(m.bytes)
|
||||
if err != nil {
|
||||
panic("multiaddr failed to convert back to string. corrupted?")
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Protocols returns the list of protocols this Multiaddr has.
|
||||
// will panic in case we access bytes incorrectly.
|
||||
func (m *multiaddr) Protocols() []*Protocol {
|
||||
|
||||
// panic handler, in case we try accessing bytes incorrectly.
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err := e.(error)
|
||||
panic("Multiaddr.Protocols error: " + err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
ps := []*Protocol{}
|
||||
b := m.bytes[:]
|
||||
for len(b) > 0 {
|
||||
code, n := ReadVarintCode(b)
|
||||
p := ProtocolWithCode(code)
|
||||
if p == nil {
|
||||
// this is a panic (and not returning err) because this should've been
|
||||
// caught on constructing the Multiaddr
|
||||
panic(fmt.Errorf("no protocol with code %d", b[0]))
|
||||
}
|
||||
ps = append(ps, p)
|
||||
b = b[n+(p.Size/8):]
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr
|
||||
func (m *multiaddr) Encapsulate(o Multiaddr) Multiaddr {
|
||||
mb := m.bytes
|
||||
ob := o.Bytes()
|
||||
|
||||
b := make([]byte, len(mb)+len(ob))
|
||||
copy(b, mb)
|
||||
copy(b[len(mb):], ob)
|
||||
return &multiaddr{bytes: b}
|
||||
}
|
||||
|
||||
// Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
|
||||
func (m *multiaddr) Decapsulate(o Multiaddr) Multiaddr {
|
||||
s1 := m.String()
|
||||
s2 := o.String()
|
||||
i := strings.LastIndex(s1, s2)
|
||||
if i < 0 {
|
||||
// if multiaddr not contained, returns a copy.
|
||||
cpy := make([]byte, len(m.bytes))
|
||||
copy(cpy, m.bytes)
|
||||
return &multiaddr{bytes: cpy}
|
||||
}
|
||||
|
||||
ma, err := NewMultiaddr(s1[:i])
|
||||
if err != nil {
|
||||
panic("Multiaddr.Decapsulate incorrect byte boundaries.")
|
||||
}
|
||||
return ma
|
||||
}
|
||||
189
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go
generated
vendored
Normal file
189
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newMultiaddr(t *testing.T, a string) Multiaddr {
|
||||
m, err := NewMultiaddr(a)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
m1 := newMultiaddr(t, "/ip4/127.0.0.1/udp/1234")
|
||||
m2 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234")
|
||||
m3 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234")
|
||||
m4 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234/")
|
||||
|
||||
if m1.Equal(m2) {
|
||||
t.Error("should not be equal")
|
||||
}
|
||||
|
||||
if m2.Equal(m1) {
|
||||
t.Error("should not be equal")
|
||||
}
|
||||
|
||||
if !m2.Equal(m3) {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
|
||||
if !m3.Equal(m2) {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
|
||||
if !m1.Equal(m1) {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
|
||||
if !m2.Equal(m4) {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
|
||||
if !m4.Equal(m3) {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringToBytes(t *testing.T) {
|
||||
|
||||
testString := func(s string, h string) {
|
||||
b1, err := hex.DecodeString(h)
|
||||
if err != nil {
|
||||
t.Error("failed to decode hex", h)
|
||||
}
|
||||
|
||||
b2, err := stringToBytes(s)
|
||||
if err != nil {
|
||||
t.Error("failed to convert", s)
|
||||
}
|
||||
|
||||
if !bytes.Equal(b1, b2) {
|
||||
t.Error("failed to convert", s, "to", b1, "got", b2)
|
||||
}
|
||||
}
|
||||
|
||||
testString("/ip4/127.0.0.1/udp/1234", "047f0000011104d2")
|
||||
testString("/ip4/127.0.0.1/tcp/4321", "047f0000010610e1")
|
||||
testString("/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321", "047f0000011104d2047f0000010610e1")
|
||||
}
|
||||
|
||||
func TestBytesToString(t *testing.T) {
|
||||
|
||||
testString := func(s1 string, h string) {
|
||||
b, err := hex.DecodeString(h)
|
||||
if err != nil {
|
||||
t.Error("failed to decode hex", h)
|
||||
}
|
||||
|
||||
s2, err := bytesToString(b)
|
||||
if err != nil {
|
||||
t.Error("failed to convert", b)
|
||||
}
|
||||
|
||||
if s1 != s2 {
|
||||
t.Error("failed to convert", b, "to", s1, "got", s2)
|
||||
}
|
||||
}
|
||||
|
||||
testString("/ip4/127.0.0.1/udp/1234", "047f0000011104d2")
|
||||
testString("/ip4/127.0.0.1/tcp/4321", "047f0000010610e1")
|
||||
testString("/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321", "047f0000011104d2047f0000010610e1")
|
||||
}
|
||||
|
||||
func TestBytesSplitAndJoin(t *testing.T) {
|
||||
|
||||
testString := func(s string, res []string) {
|
||||
m, err := NewMultiaddr(s)
|
||||
if err != nil {
|
||||
t.Fatal("failed to convert", s, err)
|
||||
}
|
||||
|
||||
split := Split(m)
|
||||
if len(split) != len(res) {
|
||||
t.Error("not enough split components", split)
|
||||
return
|
||||
}
|
||||
|
||||
for i, a := range split {
|
||||
if a.String() != res[i] {
|
||||
t.Errorf("split component failed: %s != %s", a, res[i])
|
||||
}
|
||||
}
|
||||
|
||||
joined := Join(split...)
|
||||
if !m.Equal(joined) {
|
||||
t.Errorf("joined components failed: %s != %s", m, joined)
|
||||
}
|
||||
|
||||
// modifying underlying bytes is fine.
|
||||
m2 := m.(*multiaddr)
|
||||
for i := range m2.bytes {
|
||||
m2.bytes[i] = 0
|
||||
}
|
||||
|
||||
for i, a := range split {
|
||||
if a.String() != res[i] {
|
||||
t.Errorf("split component failed: %s != %s", a, res[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testString("/ip4/1.2.3.4/udp/1234", []string{"/ip4/1.2.3.4", "/udp/1234"})
|
||||
testString("/ip4/1.2.3.4/tcp/1/ip4/2.3.4.5/udp/2",
|
||||
[]string{"/ip4/1.2.3.4", "/tcp/1", "/ip4/2.3.4.5", "/udp/2"})
|
||||
testString("/ip4/1.2.3.4/utp/ip4/2.3.4.5/udp/2/udt",
|
||||
[]string{"/ip4/1.2.3.4", "/utp", "/ip4/2.3.4.5", "/udp/2", "/udt"})
|
||||
}
|
||||
|
||||
func TestProtocols(t *testing.T) {
|
||||
m, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
if err != nil {
|
||||
t.Error("failed to construct", "/ip4/127.0.0.1/udp/1234")
|
||||
}
|
||||
|
||||
ps := m.Protocols()
|
||||
if ps[0] != ProtocolWithName("ip4") {
|
||||
t.Error(ps[0], ProtocolWithName("ip4"))
|
||||
t.Error("failed to get ip4 protocol")
|
||||
}
|
||||
|
||||
if ps[1] != ProtocolWithName("udp") {
|
||||
t.Error(ps[1], ProtocolWithName("udp"))
|
||||
t.Error("failed to get udp protocol")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEncapsulate(t *testing.T) {
|
||||
m, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
m2, err := NewMultiaddr("/udp/5678")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
b := m.Encapsulate(m2)
|
||||
if s := b.String(); s != "/ip4/127.0.0.1/udp/1234/udp/5678" {
|
||||
t.Error("encapsulate /ip4/127.0.0.1/udp/1234/udp/5678 failed.", s)
|
||||
}
|
||||
|
||||
m3, _ := NewMultiaddr("/udp/5678")
|
||||
c := b.Decapsulate(m3)
|
||||
if s := c.String(); s != "/ip4/127.0.0.1/udp/1234" {
|
||||
t.Error("decapsulate /udp failed.", "/ip4/127.0.0.1/udp/1234", s)
|
||||
}
|
||||
|
||||
m4, _ := NewMultiaddr("/ip4/127.0.0.1")
|
||||
d := c.Decapsulate(m4)
|
||||
if s := d.String(); s != "" {
|
||||
t.Error("decapsulate /ip4 failed.", "/", s)
|
||||
}
|
||||
}
|
||||
11
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
code size name
|
||||
4 32 ip4
|
||||
6 16 tcp
|
||||
17 16 udp
|
||||
33 16 dccp
|
||||
41 128 ip6
|
||||
132 16 sctp
|
||||
301 0 udt
|
||||
302 0 utp
|
||||
480 0 http
|
||||
443 0 https
|
||||
|
86
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go
generated
vendored
Normal file
86
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// Protocol is a Multiaddr protocol description structure.
|
||||
type Protocol struct {
|
||||
Code int
|
||||
Size int
|
||||
Name string
|
||||
VCode []byte
|
||||
}
|
||||
|
||||
// replicating table here to:
|
||||
// 1. avoid parsing the csv
|
||||
// 2. ensuring errors in the csv don't screw up code.
|
||||
// 3. changing a number has to happen in two places.
|
||||
const (
|
||||
P_IP4 = 4
|
||||
P_TCP = 6
|
||||
P_UDP = 17
|
||||
P_DCCP = 33
|
||||
P_IP6 = 41
|
||||
P_SCTP = 132
|
||||
P_UTP = 301
|
||||
P_UDT = 302
|
||||
)
|
||||
|
||||
// Protocols is the list of multiaddr protocols supported by this module.
|
||||
var Protocols = []*Protocol{
|
||||
&Protocol{P_IP4, 32, "ip4", CodeToVarint(P_IP4)},
|
||||
&Protocol{P_TCP, 16, "tcp", CodeToVarint(P_TCP)},
|
||||
&Protocol{P_UDP, 16, "udp", CodeToVarint(P_UDP)},
|
||||
&Protocol{P_DCCP, 16, "dccp", CodeToVarint(P_DCCP)},
|
||||
&Protocol{P_IP6, 128, "ip6", CodeToVarint(P_IP6)},
|
||||
// these require varint:
|
||||
&Protocol{P_SCTP, 16, "sctp", CodeToVarint(P_SCTP)},
|
||||
&Protocol{P_UTP, 0, "utp", CodeToVarint(P_UTP)},
|
||||
&Protocol{P_UDT, 0, "udt", CodeToVarint(P_UDT)},
|
||||
// {480, 0, "http"},
|
||||
// {443, 0, "https"},
|
||||
}
|
||||
|
||||
// ProtocolWithName returns the Protocol description with given string name.
|
||||
func ProtocolWithName(s string) *Protocol {
|
||||
for _, p := range Protocols {
|
||||
if p.Name == s {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProtocolWithCode returns the Protocol description with given protocol code.
|
||||
func ProtocolWithCode(c int) *Protocol {
|
||||
for _, p := range Protocols {
|
||||
if p.Code == c {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CodeToVarint converts an integer to a varint-encoded []byte
|
||||
func CodeToVarint(num int) []byte {
|
||||
buf := make([]byte, (num/7)+1) // varint package is uint64
|
||||
n := binary.PutUvarint(buf, uint64(num))
|
||||
return buf[:n]
|
||||
}
|
||||
|
||||
// VarintToCode converts a varint-encoded []byte to an integer protocol code
|
||||
func VarintToCode(buf []byte) int {
|
||||
num, _ := ReadVarintCode(buf)
|
||||
return num
|
||||
}
|
||||
|
||||
// ReadVarintCode reads a varint code from the beginning of buf.
|
||||
// returns the code, and the number of bytes read.
|
||||
func ReadVarintCode(buf []byte) (int, int) {
|
||||
num, n := binary.Uvarint(buf)
|
||||
if n < 0 {
|
||||
panic("varints larger than uint64 not yet supported")
|
||||
}
|
||||
return int(num), n
|
||||
}
|
||||
56
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/util.go
generated
vendored
Normal file
56
Godeps/_workspace/src/github.com/jbenet/go-multiaddr/util.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package multiaddr
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Split returns the sub-address portions of a multiaddr.
|
||||
func Split(m Multiaddr) []Multiaddr {
|
||||
split, err := bytesSplit(m.Bytes())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid multiaddr %s", m.String()))
|
||||
}
|
||||
|
||||
addrs := make([]Multiaddr, len(split))
|
||||
for i, addr := range split {
|
||||
addrs[i] = &multiaddr{bytes: addr}
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
// Join returns a combination of addresses.
|
||||
func Join(ms ...Multiaddr) Multiaddr {
|
||||
|
||||
length := 0
|
||||
bs := make([][]byte, len(ms))
|
||||
for i, m := range ms {
|
||||
bs[i] = m.Bytes()
|
||||
length += len(bs[i])
|
||||
}
|
||||
|
||||
bidx := 0
|
||||
b := make([]byte, length)
|
||||
for _, mb := range bs {
|
||||
for i := range mb {
|
||||
b[bidx] = mb[i]
|
||||
bidx++
|
||||
}
|
||||
}
|
||||
return &multiaddr{bytes: b}
|
||||
}
|
||||
|
||||
// Cast re-casts a byte slice as a multiaddr. will panic if it fails to parse.
|
||||
func Cast(b []byte) Multiaddr {
|
||||
_, err := bytesToString(b)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("multiaddr failed to parse: %s", err))
|
||||
}
|
||||
return &multiaddr{bytes: b}
|
||||
}
|
||||
|
||||
// StringCast like Cast, but parses a string. Will also panic if it fails to parse.
|
||||
func StringCast(s string) Multiaddr {
|
||||
m, err := NewMultiaddr(s)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("multiaddr failed to parse: %s", err))
|
||||
}
|
||||
return m
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user