diff --git a/Dockerfile b/Dockerfile index 4c57ab29e..b1326d19f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ MAINTAINER Brian Tiger Chow COPY . /go/src/github.com/jbenet/go-ipfs RUN cd /go/src/github.com/jbenet/go-ipfs/cmd/ipfs && go install -EXPOSE 4001 5001 +EXPOSE 4001 5001 4002/udp ENTRYPOINT ["ipfs"] @@ -12,3 +12,4 @@ CMD ["daemon", "--init"] # build: docker build -t go-ipfs . # run: docker run -p 4001:4001 -p 5001:5001 go-ipfs:latest daemon --init +# run: docker run -p 4002:4002/udp -p 4001:4001 -p 5001:5001 go-ipfs:latest daemon --init diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 5364a8e82..76c9cfea1 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/jbenet/go-ipfs", - "GoVersion": "go1.3.3", + "GoVersion": "go1.3", "Packages": [ "./..." ], @@ -73,10 +73,6 @@ "ImportPath": "github.com/facebookgo/stackerr", "Rev": "060fbf9364c89acd41bf710e9e92915a90e7a5b5" }, - { - "ImportPath": "github.com/gonuts/flag", - "Rev": "741a6cbd37a30dedc93f817e7de6aaf0ca38a493" - }, { "ImportPath": "github.com/gorilla/context", "Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a" @@ -86,12 +82,12 @@ "Rev": "4b8fbc56f3b2400a7c7ea3dba9b3539787c486b6" }, { - "ImportPath": "github.com/inconshreveable/go-update", - "Rev": "221d034a558b4c21b0624b2a450c076913854a57" + "ImportPath": "github.com/h2so5/utp", + "Rev": "654d875bb65e96729678180215cf080fe2810371" }, { - "ImportPath": "github.com/jbenet/commander", - "Rev": "e0cf317891f0ab6f1ac64dfcb754b4fb5e69f7df" + "ImportPath": "github.com/inconshreveable/go-update", + "Rev": "221d034a558b4c21b0624b2a450c076913854a57" }, { "ImportPath": "github.com/jbenet/go-base58", @@ -115,8 +111,12 @@ }, { "ImportPath": "github.com/jbenet/go-multiaddr", - "Comment": "0.1.2-27-g62a88e0", - "Rev": "62a88e015e1bf5d6aaca29aec1aba0722f21c8d3" + "Comment": "0.1.2-30-g99cf3ed", + "Rev": "99cf3edc711751cf7b43505fac0e3913f6b9a75c" + }, + { + "ImportPath": "github.com/jbenet/go-multiaddr-net", + "Rev": "b6265d8119558acf3912db44abb34d97c30c3220" }, { "ImportPath": "github.com/jbenet/go-multihash", diff --git a/Godeps/_workspace/src/github.com/gonuts/flag/LICENSE b/Godeps/_workspace/src/github.com/gonuts/flag/LICENSE deleted file mode 100644 index 744875676..000000000 --- a/Godeps/_workspace/src/github.com/gonuts/flag/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/gonuts/flag/README.md b/Godeps/_workspace/src/github.com/gonuts/flag/README.md deleted file mode 100644 index 06b7a02be..000000000 --- a/Godeps/_workspace/src/github.com/gonuts/flag/README.md +++ /dev/null @@ -1,6 +0,0 @@ -flag -======= - -[![Build Status](https://drone.io/github.com/gonuts/flag/status.png)](https://drone.io/github.com/gonuts/flag/latest) - -A fork of the official "flag" package but with the flag.Value interface extended to provide a ``Get() interface{}`` method. diff --git a/Godeps/_workspace/src/github.com/gonuts/flag/example_test.go b/Godeps/_workspace/src/github.com/gonuts/flag/example_test.go deleted file mode 100644 index 04a0d20ee..000000000 --- a/Godeps/_workspace/src/github.com/gonuts/flag/example_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// These examples demonstrate more intricate uses of the flag package. -package flag_test - -import ( - "errors" - "flag" - "fmt" - "strings" - "time" -) - -// Example 1: A single string flag called "species" with default value "gopher". -var species = flag.String("species", "gopher", "the species we are studying") - -// Example 2: Two flags sharing a variable, so we can have a shorthand. -// The order of initialization is undefined, so make sure both use the -// same default value. They must be set up with an init function. -var gopherType string - -func init() { - const ( - defaultGopher = "pocket" - usage = "the variety of gopher" - ) - flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage) - flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)") -} - -// Example 3: A user-defined flag type, a slice of durations. -type interval []time.Duration - -// String is the method to format the flag's value, part of the flag.Value interface. -// The String method's output will be used in diagnostics. -func (i *interval) String() string { - return fmt.Sprint(*i) -} - -// Set is the method to set the flag value, part of the flag.Value interface. -// Set's argument is a string to be parsed to set the flag. -// It's a comma-separated list, so we split it. -func (i *interval) Set(value string) error { - // If we wanted to allow the flag to be set multiple times, - // accumulating values, we would delete this if statement. - // That would permit usages such as - // -deltaT 10s -deltaT 15s - // and other combinations. - if len(*i) > 0 { - return errors.New("interval flag already set") - } - for _, dt := range strings.Split(value, ",") { - duration, err := time.ParseDuration(dt) - if err != nil { - return err - } - *i = append(*i, duration) - } - return nil -} - -// Define a flag to accumulate durations. Because it has a special type, -// we need to use the Var function and therefore create the flag during -// init. - -var intervalFlag interval - -func init() { - // Tie the command-line flag to the intervalFlag variable and - // set a usage message. - flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events") -} - -func Example() { - // All the interesting pieces are with the variables declared above, but - // to enable the flag package to see the flags defined there, one must - // execute, typically at the start of main (not init!): - // flag.Parse() - // We don't run it here because this is not a main function and - // the testing suite has already parsed the flags. -} diff --git a/Godeps/_workspace/src/github.com/gonuts/flag/export_test.go b/Godeps/_workspace/src/github.com/gonuts/flag/export_test.go deleted file mode 100644 index 7b190807a..000000000 --- a/Godeps/_workspace/src/github.com/gonuts/flag/export_test.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package flag - -import "os" - -// Additional routines compiled into the package only during testing. - -// ResetForTesting clears all flag state and sets the usage function as directed. -// After calling ResetForTesting, parse errors in flag handling will not -// exit the program. -func ResetForTesting(usage func()) { - commandLine = NewFlagSet(os.Args[0], ContinueOnError) - Usage = usage -} - -// CommandLine returns the default FlagSet. -func CommandLine() *FlagSet { - return commandLine -} diff --git a/Godeps/_workspace/src/github.com/gonuts/flag/flag.go b/Godeps/_workspace/src/github.com/gonuts/flag/flag.go deleted file mode 100644 index 1e939e9ef..000000000 --- a/Godeps/_workspace/src/github.com/gonuts/flag/flag.go +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - Package flag implements command-line flag parsing. - - Usage: - - Define flags using flag.String(), Bool(), Int(), etc. - - This declares an integer flag, -flagname, stored in the pointer ip, with type *int. - import "flag" - var ip = flag.Int("flagname", 1234, "help message for flagname") - If you like, you can bind the flag to a variable using the Var() functions. - var flagvar int - func init() { - flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") - } - Or you can create custom flags that satisfy the Value interface (with - pointer receivers) and couple them to flag parsing by - flag.Var(&flagVal, "name", "help message for flagname") - For such flags, the default value is just the initial value of the variable. - - After all flags are defined, call - flag.Parse() - to parse the command line into the defined flags. - - Flags may then be used directly. If you're using the flags themselves, - they are all pointers; if you bind to variables, they're values. - fmt.Println("ip has value ", *ip) - fmt.Println("flagvar has value ", flagvar) - - After parsing, the arguments after the flag are available as the - slice flag.Args() or individually as flag.Arg(i). - The arguments are indexed from 0 up to flag.NArg(). - - Command line flag syntax: - -flag - -flag=x - -flag x // non-boolean flags only - One or two minus signs may be used; they are equivalent. - The last form is not permitted for boolean flags because the - meaning of the command - cmd -x * - will change if there is a file called 0, false, etc. You must - use the -flag=false form to turn off a boolean flag. - - Flag parsing stops just before the first non-flag argument - ("-" is a non-flag argument) or after the terminator "--". - - Integer flags accept 1234, 0664, 0x1234 and may be negative. - Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. - Duration flags accept any input valid for time.ParseDuration. - - The default set of command-line flags is controlled by - top-level functions. The FlagSet type allows one to define - independent sets of flags, such as to implement subcommands - in a command-line interface. The methods of FlagSet are - analogous to the top-level functions for the command-line - flag set. -*/ -package flag - -import ( - "errors" - "fmt" - "io" - "os" - "sort" - "strconv" - "time" -) - -// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. -var ErrHelp = errors.New("flag: help requested") - -// -- bool Value -type boolValue bool - -func newBoolValue(val bool, p *bool) *boolValue { - *p = val - return (*boolValue)(p) -} - -func (b *boolValue) Set(s string) error { - v, err := strconv.ParseBool(s) - *b = boolValue(v) - return err -} - -func (b *boolValue) Get() interface{} { return bool(*b) } - -func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } - -// -- int Value -type intValue int - -func newIntValue(val int, p *int) *intValue { - *p = val - return (*intValue)(p) -} - -func (i *intValue) Set(s string) error { - v, err := strconv.ParseInt(s, 0, 64) - *i = intValue(v) - return err -} - -func (i *intValue) Get() interface{} { return int(*i) } - -func (i *intValue) String() string { return fmt.Sprintf("%v", *i) } - -// -- int64 Value -type int64Value int64 - -func newInt64Value(val int64, p *int64) *int64Value { - *p = val - return (*int64Value)(p) -} - -func (i *int64Value) Set(s string) error { - v, err := strconv.ParseInt(s, 0, 64) - *i = int64Value(v) - return err -} - -func (i *int64Value) Get() interface{} { return int64(*i) } - -func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) } - -// -- uint Value -type uintValue uint - -func newUintValue(val uint, p *uint) *uintValue { - *p = val - return (*uintValue)(p) -} - -func (i *uintValue) Set(s string) error { - v, err := strconv.ParseUint(s, 0, 64) - *i = uintValue(v) - return err -} - -func (i *uintValue) Get() interface{} { return uint(*i) } - -func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) } - -// -- uint64 Value -type uint64Value uint64 - -func newUint64Value(val uint64, p *uint64) *uint64Value { - *p = val - return (*uint64Value)(p) -} - -func (i *uint64Value) Set(s string) error { - v, err := strconv.ParseUint(s, 0, 64) - *i = uint64Value(v) - return err -} - -func (i *uint64Value) Get() interface{} { return uint64(*i) } - -func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) } - -// -- string Value -type stringValue string - -func newStringValue(val string, p *string) *stringValue { - *p = val - return (*stringValue)(p) -} - -func (s *stringValue) Set(val string) error { - *s = stringValue(val) - return nil -} - -func (s *stringValue) Get() interface{} { return s.String() } - -func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } - -// -- float64 Value -type float64Value float64 - -func newFloat64Value(val float64, p *float64) *float64Value { - *p = val - return (*float64Value)(p) -} - -func (f *float64Value) Set(s string) error { - v, err := strconv.ParseFloat(s, 64) - *f = float64Value(v) - return err -} - -func (f *float64Value) Get() interface{} { return float64(*f) } - -func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) } - -// -- time.Duration Value -type durationValue time.Duration - -func newDurationValue(val time.Duration, p *time.Duration) *durationValue { - *p = val - return (*durationValue)(p) -} - -func (d *durationValue) Set(s string) error { - v, err := time.ParseDuration(s) - *d = durationValue(v) - return err -} - -func (d *durationValue) Get() interface{} { return *(*time.Duration)(d) } - -func (d *durationValue) String() string { return (*time.Duration)(d).String() } - -// Value is the interface to the dynamic value stored in a flag. -// (The default value is represented as a string.) -type Value interface { - String() string - Set(string) error - Get() interface{} -} - -// ErrorHandling defines how to handle flag parsing errors. -type ErrorHandling int - -const ( - ContinueOnError ErrorHandling = iota - ExitOnError - PanicOnError -) - -// A FlagSet represents a set of defined flags. -type FlagSet struct { - // Usage is the function called when an error occurs while parsing flags. - // The field is a function (not a method) that may be changed to point to - // a custom error handler. - Usage func() - - name string - parsed bool - actual map[string]*Flag - formal map[string]*Flag - args []string // arguments after flags - exitOnError bool // does the program exit if there's an error? - errorHandling ErrorHandling - output io.Writer // nil means stderr; use out() accessor -} - -// A Flag represents the state of a flag. -type Flag struct { - Name string // name as it appears on command line - Usage string // help message - Value Value // value as set - DefValue string // default value (as text); for usage message -} - -// sortFlags returns the flags as a slice in lexicographical sorted order. -func sortFlags(flags map[string]*Flag) []*Flag { - list := make(sort.StringSlice, len(flags)) - i := 0 - for _, f := range flags { - list[i] = f.Name - i++ - } - list.Sort() - result := make([]*Flag, len(list)) - for i, name := range list { - result[i] = flags[name] - } - return result -} - -func (f *FlagSet) out() io.Writer { - if f.output == nil { - return os.Stderr - } - return f.output -} - -// SetOutput sets the destination for usage and error messages. -// If output is nil, os.Stderr is used. -func (f *FlagSet) SetOutput(output io.Writer) { - f.output = output -} - -// VisitAll visits the flags in lexicographical order, calling fn for each. -// It visits all flags, even those not set. -func (f *FlagSet) VisitAll(fn func(*Flag)) { - for _, flag := range sortFlags(f.formal) { - fn(flag) - } -} - -// VisitAll visits the command-line flags in lexicographical order, calling -// fn for each. It visits all flags, even those not set. -func VisitAll(fn func(*Flag)) { - commandLine.VisitAll(fn) -} - -// Visit visits the flags in lexicographical order, calling fn for each. -// It visits only those flags that have been set. -func (f *FlagSet) Visit(fn func(*Flag)) { - for _, flag := range sortFlags(f.actual) { - fn(flag) - } -} - -// Visit visits the command-line flags in lexicographical order, calling fn -// for each. It visits only those flags that have been set. -func Visit(fn func(*Flag)) { - commandLine.Visit(fn) -} - -// Lookup returns the Flag structure of the named flag, returning nil if none exists. -func (f *FlagSet) Lookup(name string) *Flag { - return f.formal[name] -} - -// Lookup returns the Flag structure of the named command-line flag, -// returning nil if none exists. -func Lookup(name string) *Flag { - return commandLine.formal[name] -} - -// Set sets the value of the named flag. -func (f *FlagSet) Set(name, value string) error { - flag, ok := f.formal[name] - if !ok { - return fmt.Errorf("no such flag -%v", name) - } - err := flag.Value.Set(value) - if err != nil { - return err - } - if f.actual == nil { - f.actual = make(map[string]*Flag) - } - f.actual[name] = flag - return nil -} - -// Set sets the value of the named command-line flag. -func Set(name, value string) error { - return commandLine.Set(name, value) -} - -// PrintDefaults prints, to standard error unless configured -// otherwise, the default values of all defined flags in the set. -func (f *FlagSet) PrintDefaults() { - f.VisitAll(func(flag *Flag) { - format := " -%s=%s: %s\n" - if _, ok := flag.Value.(*stringValue); ok { - // put quotes on the value - format = " -%s=%q: %s\n" - } - fmt.Fprintf(f.out(), format, flag.Name, flag.DefValue, flag.Usage) - }) -} - -// PrintDefaults prints to standard error the default values of all defined command-line flags. -func PrintDefaults() { - commandLine.PrintDefaults() -} - -// defaultUsage is the default function to print a usage message. -func defaultUsage(f *FlagSet) { - fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) - f.PrintDefaults() -} - -// NOTE: Usage is not just defaultUsage(commandLine) -// because it serves (via godoc flag Usage) as the example -// for how to write your own usage function. - -// Usage prints to standard error a usage message documenting all defined command-line flags. -// The function is a variable that may be changed to point to a custom function. -var Usage = func() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - PrintDefaults() -} - -// NFlag returns the number of flags that have been set. -func (f *FlagSet) NFlag() int { return len(f.actual) } - -// NFlag returns the number of command-line flags that have been set. -func NFlag() int { return len(commandLine.actual) } - -// Arg returns the i'th argument. Arg(0) is the first remaining argument -// after flags have been processed. -func (f *FlagSet) Arg(i int) string { - if i < 0 || i >= len(f.args) { - return "" - } - return f.args[i] -} - -// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument -// after flags have been processed. -func Arg(i int) string { - return commandLine.Arg(i) -} - -// NArg is the number of arguments remaining after flags have been processed. -func (f *FlagSet) NArg() int { return len(f.args) } - -// NArg is the number of arguments remaining after flags have been processed. -func NArg() int { return len(commandLine.args) } - -// Args returns the non-flag arguments. -func (f *FlagSet) Args() []string { return f.args } - -// Args returns the non-flag command-line arguments. -func Args() []string { return commandLine.args } - -// BoolVar defines a bool flag with specified name, default value, and usage string. -// The argument p points to a bool variable in which to store the value of the flag. -func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) { - f.Var(newBoolValue(value, p), name, usage) -} - -// BoolVar defines a bool flag with specified name, default value, and usage string. -// The argument p points to a bool variable in which to store the value of the flag. -func BoolVar(p *bool, name string, value bool, usage string) { - commandLine.Var(newBoolValue(value, p), name, usage) -} - -// Bool defines a bool flag with specified name, default value, and usage string. -// The return value is the address of a bool variable that stores the value of the flag. -func (f *FlagSet) Bool(name string, value bool, usage string) *bool { - p := new(bool) - f.BoolVar(p, name, value, usage) - return p -} - -// Bool defines a bool flag with specified name, default value, and usage string. -// The return value is the address of a bool variable that stores the value of the flag. -func Bool(name string, value bool, usage string) *bool { - return commandLine.Bool(name, value, usage) -} - -// IntVar defines an int flag with specified name, default value, and usage string. -// The argument p points to an int variable in which to store the value of the flag. -func (f *FlagSet) IntVar(p *int, name string, value int, usage string) { - f.Var(newIntValue(value, p), name, usage) -} - -// IntVar defines an int flag with specified name, default value, and usage string. -// The argument p points to an int variable in which to store the value of the flag. -func IntVar(p *int, name string, value int, usage string) { - commandLine.Var(newIntValue(value, p), name, usage) -} - -// Int defines an int flag with specified name, default value, and usage string. -// The return value is the address of an int variable that stores the value of the flag. -func (f *FlagSet) Int(name string, value int, usage string) *int { - p := new(int) - f.IntVar(p, name, value, usage) - return p -} - -// Int defines an int flag with specified name, default value, and usage string. -// The return value is the address of an int variable that stores the value of the flag. -func Int(name string, value int, usage string) *int { - return commandLine.Int(name, value, usage) -} - -// Int64Var defines an int64 flag with specified name, default value, and usage string. -// The argument p points to an int64 variable in which to store the value of the flag. -func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) { - f.Var(newInt64Value(value, p), name, usage) -} - -// Int64Var defines an int64 flag with specified name, default value, and usage string. -// The argument p points to an int64 variable in which to store the value of the flag. -func Int64Var(p *int64, name string, value int64, usage string) { - commandLine.Var(newInt64Value(value, p), name, usage) -} - -// Int64 defines an int64 flag with specified name, default value, and usage string. -// The return value is the address of an int64 variable that stores the value of the flag. -func (f *FlagSet) Int64(name string, value int64, usage string) *int64 { - p := new(int64) - f.Int64Var(p, name, value, usage) - return p -} - -// Int64 defines an int64 flag with specified name, default value, and usage string. -// The return value is the address of an int64 variable that stores the value of the flag. -func Int64(name string, value int64, usage string) *int64 { - return commandLine.Int64(name, value, usage) -} - -// UintVar defines a uint flag with specified name, default value, and usage string. -// The argument p points to a uint variable in which to store the value of the flag. -func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) { - f.Var(newUintValue(value, p), name, usage) -} - -// UintVar defines a uint flag with specified name, default value, and usage string. -// The argument p points to a uint variable in which to store the value of the flag. -func UintVar(p *uint, name string, value uint, usage string) { - commandLine.Var(newUintValue(value, p), name, usage) -} - -// Uint defines a uint flag with specified name, default value, and usage string. -// The return value is the address of a uint variable that stores the value of the flag. -func (f *FlagSet) Uint(name string, value uint, usage string) *uint { - p := new(uint) - f.UintVar(p, name, value, usage) - return p -} - -// Uint defines a uint flag with specified name, default value, and usage string. -// The return value is the address of a uint variable that stores the value of the flag. -func Uint(name string, value uint, usage string) *uint { - return commandLine.Uint(name, value, usage) -} - -// Uint64Var defines a uint64 flag with specified name, default value, and usage string. -// The argument p points to a uint64 variable in which to store the value of the flag. -func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) { - f.Var(newUint64Value(value, p), name, usage) -} - -// Uint64Var defines a uint64 flag with specified name, default value, and usage string. -// The argument p points to a uint64 variable in which to store the value of the flag. -func Uint64Var(p *uint64, name string, value uint64, usage string) { - commandLine.Var(newUint64Value(value, p), name, usage) -} - -// Uint64 defines a uint64 flag with specified name, default value, and usage string. -// The return value is the address of a uint64 variable that stores the value of the flag. -func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 { - p := new(uint64) - f.Uint64Var(p, name, value, usage) - return p -} - -// Uint64 defines a uint64 flag with specified name, default value, and usage string. -// The return value is the address of a uint64 variable that stores the value of the flag. -func Uint64(name string, value uint64, usage string) *uint64 { - return commandLine.Uint64(name, value, usage) -} - -// StringVar defines a string flag with specified name, default value, and usage string. -// The argument p points to a string variable in which to store the value of the flag. -func (f *FlagSet) StringVar(p *string, name string, value string, usage string) { - f.Var(newStringValue(value, p), name, usage) -} - -// StringVar defines a string flag with specified name, default value, and usage string. -// The argument p points to a string variable in which to store the value of the flag. -func StringVar(p *string, name string, value string, usage string) { - commandLine.Var(newStringValue(value, p), name, usage) -} - -// String defines a string flag with specified name, default value, and usage string. -// The return value is the address of a string variable that stores the value of the flag. -func (f *FlagSet) String(name string, value string, usage string) *string { - p := new(string) - f.StringVar(p, name, value, usage) - return p -} - -// String defines a string flag with specified name, default value, and usage string. -// The return value is the address of a string variable that stores the value of the flag. -func String(name string, value string, usage string) *string { - return commandLine.String(name, value, usage) -} - -// Float64Var defines a float64 flag with specified name, default value, and usage string. -// The argument p points to a float64 variable in which to store the value of the flag. -func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) { - f.Var(newFloat64Value(value, p), name, usage) -} - -// Float64Var defines a float64 flag with specified name, default value, and usage string. -// The argument p points to a float64 variable in which to store the value of the flag. -func Float64Var(p *float64, name string, value float64, usage string) { - commandLine.Var(newFloat64Value(value, p), name, usage) -} - -// Float64 defines a float64 flag with specified name, default value, and usage string. -// The return value is the address of a float64 variable that stores the value of the flag. -func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { - p := new(float64) - f.Float64Var(p, name, value, usage) - return p -} - -// Float64 defines a float64 flag with specified name, default value, and usage string. -// The return value is the address of a float64 variable that stores the value of the flag. -func Float64(name string, value float64, usage string) *float64 { - return commandLine.Float64(name, value, usage) -} - -// DurationVar defines a time.Duration flag with specified name, default value, and usage string. -// The argument p points to a time.Duration variable in which to store the value of the flag. -func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) { - f.Var(newDurationValue(value, p), name, usage) -} - -// DurationVar defines a time.Duration flag with specified name, default value, and usage string. -// The argument p points to a time.Duration variable in which to store the value of the flag. -func DurationVar(p *time.Duration, name string, value time.Duration, usage string) { - commandLine.Var(newDurationValue(value, p), name, usage) -} - -// Duration defines a time.Duration flag with specified name, default value, and usage string. -// The return value is the address of a time.Duration variable that stores the value of the flag. -func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration { - p := new(time.Duration) - f.DurationVar(p, name, value, usage) - return p -} - -// Duration defines a time.Duration flag with specified name, default value, and usage string. -// The return value is the address of a time.Duration variable that stores the value of the flag. -func Duration(name string, value time.Duration, usage string) *time.Duration { - return commandLine.Duration(name, value, usage) -} - -// Var defines a flag with the specified name and usage string. The type and -// value of the flag are represented by the first argument, of type Value, which -// typically holds a user-defined implementation of Value. For instance, the -// caller could create a flag that turns a comma-separated string into a slice -// of strings by giving the slice the methods of Value; in particular, Set would -// decompose the comma-separated string into the slice. -func (f *FlagSet) Var(value Value, name string, usage string) { - // Remember the default value as a string; it won't change. - flag := &Flag{name, usage, value, value.String()} - _, alreadythere := f.formal[name] - if alreadythere { - msg := fmt.Sprintf("%s flag redefined: %s", f.name, name) - fmt.Fprintln(f.out(), msg) - panic(msg) // Happens only if flags are declared with identical names - } - if f.formal == nil { - f.formal = make(map[string]*Flag) - } - f.formal[name] = flag -} - -// Var defines a flag with the specified name and usage string. The type and -// value of the flag are represented by the first argument, of type Value, which -// typically holds a user-defined implementation of Value. For instance, the -// caller could create a flag that turns a comma-separated string into a slice -// of strings by giving the slice the methods of Value; in particular, Set would -// decompose the comma-separated string into the slice. -func Var(value Value, name string, usage string) { - commandLine.Var(value, name, usage) -} - -// failf prints to standard error a formatted error and usage message and -// returns the error. -func (f *FlagSet) failf(format string, a ...interface{}) error { - err := fmt.Errorf(format, a...) - fmt.Fprintln(f.out(), err) - f.usage() - return err -} - -// usage calls the Usage method for the flag set, or the usage function if -// the flag set is commandLine. -func (f *FlagSet) usage() { - if f == commandLine { - Usage() - } else if f.Usage == nil { - defaultUsage(f) - } else { - f.Usage() - } -} - -// parseOne parses one flag. It returns whether a flag was seen. -func (f *FlagSet) parseOne() (bool, error) { - if len(f.args) == 0 { - return false, nil - } - s := f.args[0] - if len(s) == 0 || s[0] != '-' || len(s) == 1 { - return false, nil - } - num_minuses := 1 - if s[1] == '-' { - num_minuses++ - if len(s) == 2 { // "--" terminates the flags - f.args = f.args[1:] - return false, nil - } - } - name := s[num_minuses:] - if len(name) == 0 || name[0] == '-' || name[0] == '=' { - return false, f.failf("bad flag syntax: %s", s) - } - - // it's a flag. does it have an argument? - f.args = f.args[1:] - has_value := false - value := "" - for i := 1; i < len(name); i++ { // equals cannot be first - if name[i] == '=' { - value = name[i+1:] - has_value = true - name = name[0:i] - break - } - } - m := f.formal - flag, alreadythere := m[name] // BUG - if !alreadythere { - if name == "help" || name == "h" { // special case for nice help message. - f.usage() - return false, ErrHelp - } - return false, f.failf("flag provided but not defined: -%s", name) - } - if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg - if has_value { - if err := fv.Set(value); err != nil { - f.failf("invalid boolean value %q for -%s: %v", value, name, err) - } - } else { - fv.Set("true") - } - } else { - // It must have a value, which might be the next argument. - if !has_value && len(f.args) > 0 { - // value is the next arg - has_value = true - value, f.args = f.args[0], f.args[1:] - } - if !has_value { - return false, f.failf("flag needs an argument: -%s", name) - } - if err := flag.Value.Set(value); err != nil { - return false, f.failf("invalid value %q for flag -%s: %v", value, name, err) - } - } - if f.actual == nil { - f.actual = make(map[string]*Flag) - } - f.actual[name] = flag - return true, nil -} - -// Parse parses flag definitions from the argument list, which should not -// include the command name. Must be called after all flags in the FlagSet -// are defined and before flags are accessed by the program. -// The return value will be ErrHelp if -help was set but not defined. -func (f *FlagSet) Parse(arguments []string) error { - f.parsed = true - f.args = arguments - for { - seen, err := f.parseOne() - if seen { - continue - } - if err == nil { - break - } - switch f.errorHandling { - case ContinueOnError: - return err - case ExitOnError: - os.Exit(2) - case PanicOnError: - panic(err) - } - } - return nil -} - -// Parsed reports whether f.Parse has been called. -func (f *FlagSet) Parsed() bool { - return f.parsed -} - -// Parse parses the command-line flags from os.Args[1:]. Must be called -// after all flags are defined and before flags are accessed by the program. -func Parse() { - // Ignore errors; commandLine is set for ExitOnError. - commandLine.Parse(os.Args[1:]) -} - -// Parsed returns true if the command-line flags have been parsed. -func Parsed() bool { - return commandLine.Parsed() -} - -// The default set of command-line flags, parsed from os.Args. -var commandLine = NewFlagSet(os.Args[0], ExitOnError) - -// NewFlagSet returns a new, empty flag set with the specified name and -// error handling property. -func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { - f := &FlagSet{ - name: name, - errorHandling: errorHandling, - } - return f -} - -// Init sets the name and error handling property for a flag set. -// By default, the zero FlagSet uses an empty name and the -// ContinueOnError error handling policy. -func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { - f.name = name - f.errorHandling = errorHandling -} diff --git a/Godeps/_workspace/src/github.com/gonuts/flag/flag_test.go b/Godeps/_workspace/src/github.com/gonuts/flag/flag_test.go deleted file mode 100644 index bf1987cd7..000000000 --- a/Godeps/_workspace/src/github.com/gonuts/flag/flag_test.go +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package flag_test - -import ( - "bytes" - "fmt" - "os" - "sort" - "strings" - "testing" - "time" - - . "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" -) - -var ( - test_bool = Bool("test_bool", false, "bool value") - test_int = Int("test_int", 0, "int value") - test_int64 = Int64("test_int64", 0, "int64 value") - test_uint = Uint("test_uint", 0, "uint value") - test_uint64 = Uint64("test_uint64", 0, "uint64 value") - test_string = String("test_string", "0", "string value") - test_float64 = Float64("test_float64", 0, "float64 value") - test_duration = Duration("test_duration", 0, "time.Duration value") -) - -func boolString(s string) string { - if s == "0" { - return "false" - } - return "true" -} - -func TestEverything(t *testing.T) { - m := make(map[string]*Flag) - desired := "0" - visitor := func(f *Flag) { - if len(f.Name) > 5 && f.Name[0:5] == "test_" { - m[f.Name] = f - ok := false - switch { - case f.Value.String() == desired: - ok = true - case f.Name == "test_bool" && f.Value.String() == boolString(desired): - ok = true - case f.Name == "test_duration" && f.Value.String() == desired+"s": - ok = true - } - if !ok { - t.Error("Visit: bad value", f.Value.String(), "for", f.Name) - } - } - } - VisitAll(visitor) - if len(m) != 8 { - t.Error("VisitAll misses some flags") - for k, v := range m { - t.Log(k, *v) - } - } - m = make(map[string]*Flag) - Visit(visitor) - if len(m) != 0 { - t.Errorf("Visit sees unset flags") - for k, v := range m { - t.Log(k, *v) - } - } - // Now set all flags - Set("test_bool", "true") - Set("test_int", "1") - Set("test_int64", "1") - Set("test_uint", "1") - Set("test_uint64", "1") - Set("test_string", "1") - Set("test_float64", "1") - Set("test_duration", "1s") - desired = "1" - Visit(visitor) - if len(m) != 8 { - t.Error("Visit fails after set") - for k, v := range m { - t.Log(k, *v) - } - } - // Now test they're visited in sort order. - var flagNames []string - Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) }) - if !sort.StringsAreSorted(flagNames) { - t.Errorf("flag names not sorted: %v", flagNames) - } -} - -func TestUsage(t *testing.T) { - called := false - ResetForTesting(func() { called = true }) - if CommandLine().Parse([]string{"-x"}) == nil { - t.Error("parse did not fail for unknown flag") - } - if !called { - t.Error("did not call Usage for unknown flag") - } -} - -func testParse(f *FlagSet, t *testing.T) { - if f.Parsed() { - t.Error("f.Parse() = true before Parse") - } - boolFlag := f.Bool("bool", false, "bool value") - bool2Flag := f.Bool("bool2", false, "bool2 value") - intFlag := f.Int("int", 0, "int value") - int64Flag := f.Int64("int64", 0, "int64 value") - uintFlag := f.Uint("uint", 0, "uint value") - uint64Flag := f.Uint64("uint64", 0, "uint64 value") - stringFlag := f.String("string", "0", "string value") - float64Flag := f.Float64("float64", 0, "float64 value") - durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") - extra := "one-extra-argument" - args := []string{ - "-bool", - "-bool2=true", - "--int", "22", - "--int64", "0x23", - "-uint", "24", - "--uint64", "25", - "-string", "hello", - "-float64", "2718e28", - "-duration", "2m", - extra, - } - if err := f.Parse(args); err != nil { - t.Fatal(err) - } - if !f.Parsed() { - t.Error("f.Parse() = false after Parse") - } - if *boolFlag != true { - t.Error("bool flag should be true, is ", *boolFlag) - } - if *bool2Flag != true { - t.Error("bool2 flag should be true, is ", *bool2Flag) - } - if *intFlag != 22 { - t.Error("int flag should be 22, is ", *intFlag) - } - if *int64Flag != 0x23 { - t.Error("int64 flag should be 0x23, is ", *int64Flag) - } - if *uintFlag != 24 { - t.Error("uint flag should be 24, is ", *uintFlag) - } - if *uint64Flag != 25 { - t.Error("uint64 flag should be 25, is ", *uint64Flag) - } - if *stringFlag != "hello" { - t.Error("string flag should be `hello`, is ", *stringFlag) - } - if *float64Flag != 2718e28 { - t.Error("float64 flag should be 2718e28, is ", *float64Flag) - } - if *durationFlag != 2*time.Minute { - t.Error("duration flag should be 2m, is ", *durationFlag) - } - if len(f.Args()) != 1 { - t.Error("expected one argument, got", len(f.Args())) - } else if f.Args()[0] != extra { - t.Errorf("expected argument %q got %q", extra, f.Args()[0]) - } -} - -func TestParse(t *testing.T) { - ResetForTesting(func() { t.Error("bad parse") }) - testParse(CommandLine(), t) -} - -func TestFlagSetParse(t *testing.T) { - testParse(NewFlagSet("test", ContinueOnError), t) -} - -// Declare a user-defined flag type. -type flagVar []string - -func (f *flagVar) String() string { - return fmt.Sprint([]string(*f)) -} - -func (f *flagVar) Set(value string) error { - *f = append(*f, value) - return nil -} - -func (f *flagVar) Get() interface{} { return []string(*f) } - -func TestUserDefined(t *testing.T) { - var flags FlagSet - flags.Init("test", ContinueOnError) - var v flagVar - flags.Var(&v, "v", "usage") - if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { - t.Error(err) - } - if len(v) != 3 { - t.Fatal("expected 3 args; got ", len(v)) - } - expect := "[1 2 3]" - if v.String() != expect { - t.Errorf("expected value %q got %q", expect, v.String()) - } -} - -func TestSetOutput(t *testing.T) { - var flags FlagSet - var buf bytes.Buffer - flags.SetOutput(&buf) - flags.Init("test", ContinueOnError) - flags.Parse([]string{"-unknown"}) - if out := buf.String(); !strings.Contains(out, "-unknown") { - t.Logf("expected output mentioning unknown; got %q", out) - } -} - -// This tests that one can reset the flags. This still works but not well, and is -// superseded by FlagSet. -func TestChangingArgs(t *testing.T) { - ResetForTesting(func() { t.Fatal("bad parse") }) - oldArgs := os.Args - defer func() { os.Args = oldArgs }() - os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} - before := Bool("before", false, "") - if err := CommandLine().Parse(os.Args[1:]); err != nil { - t.Fatal(err) - } - cmd := Arg(0) - os.Args = Args() - after := Bool("after", false, "") - Parse() - args := Args() - - if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { - t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) - } -} - -// Test that -help invokes the usage message and returns ErrHelp. -func TestHelp(t *testing.T) { - var helpCalled = false - fs := NewFlagSet("help test", ContinueOnError) - fs.Usage = func() { helpCalled = true } - var flag bool - fs.BoolVar(&flag, "flag", false, "regular flag") - // Regular flag invocation should work - err := fs.Parse([]string{"-flag=true"}) - if err != nil { - t.Fatal("expected no error; got ", err) - } - if !flag { - t.Error("flag was not set by -flag") - } - if helpCalled { - t.Error("help called for regular flag") - helpCalled = false // reset for next test - } - // Help flag should work as expected. - err = fs.Parse([]string{"-help"}) - if err == nil { - t.Fatal("error expected") - } - if err != ErrHelp { - t.Fatal("expected ErrHelp; got ", err) - } - if !helpCalled { - t.Fatal("help was not called") - } - // If we define a help flag, that should override. - var help bool - fs.BoolVar(&help, "help", false, "help flag") - helpCalled = false - err = fs.Parse([]string{"-help"}) - if err != nil { - t.Fatal("expected no error for defined -help; got ", err) - } - if helpCalled { - t.Fatal("help was called; should not have been for defined help flag") - } -} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/.gitignore b/Godeps/_workspace/src/github.com/h2so5/utp/.gitignore new file mode 100644 index 000000000..485e6d2e2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/.gitignore @@ -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 diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml b/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml new file mode 100644 index 000000000..935bde506 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/.travis.yml @@ -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 diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/LICENSE b/Godeps/_workspace/src/github.com/h2so5/utp/LICENSE new file mode 100644 index 000000000..8ef628adc --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/LICENSE @@ -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. diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/README.md b/Godeps/_workspace/src/github.com/h2so5/utp/README.md new file mode 100644 index 000000000..8fca2b3e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/README.md @@ -0,0 +1,57 @@ +utp +=== + +μTP (Micro Transport Protocol) implementation + +[![Build status](https://ci.appveyor.com/api/projects/status/j1be8y7p6nd2wqqw?svg=true)](https://ci.appveyor.com/project/h2so5/utp) +[![Build Status](https://travis-ci.org/h2so5/utp.svg)](https://travis-ci.org/h2so5/utp) +[![GoDoc](https://godoc.org/github.com/h2so5/utp?status.svg)](http://godoc.org/github.com/h2so5/utp) + +http://www.bittorrent.org/beps/bep_0029.html + +**warning: This is a buggy alpha version.** + +## Benchmark History + +[![Benchmark status](http://107.170.244.57:80/go-utp-bench.php)]() + +## 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 + } + } +} +``` diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/addr.go b/Godeps/_workspace/src/github.com/h2so5/utp/addr.go new file mode 100644 index 000000000..bb5af1d10 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/addr.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go b/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go new file mode 100644 index 000000000..fb29e6a72 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/benchmark/main.go @@ -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-ipfs/Godeps/_workspace/src/github.com/h2so5/utp" +) + +type RandReader struct{} + +func (r RandReader) Read(p []byte) (n int, err error) { + for i := range p { + p[i] = byte(rand.Int()) + } + return len(p), nil +} + +type ByteCounter struct { + n int64 + mutex sync.RWMutex +} + +func (b *ByteCounter) Write(p []byte) (n int, err error) { + b.mutex.Lock() + defer b.mutex.Unlock() + b.n += int64(len(p)) + return len(p), nil +} + +func (b *ByteCounter) Length() int64 { + b.mutex.RLock() + defer b.mutex.RUnlock() + return b.n +} + +var h = flag.Bool("h", false, "Human readable") + +func main() { + var l = flag.Int("c", 10485760, "Payload length (bytes)") + var s = flag.Bool("s", false, "Stream mode(Low memory usage, but Slow)") + flag.Parse() + + defer profile.Start(profile.CPUProfile).Stop() + + if *h { + fmt.Printf("Payload: %s\n", humanize.IBytes(uint64(*l))) + } else { + fmt.Printf("Payload: %d\n", *l) + } + + c2s := c2s(int64(*l), *s) + n, p := humanize.ComputeSI(c2s) + if *h { + fmt.Printf("C2S: %f%sbps\n", n, p) + } else { + fmt.Printf("C2S: %f\n", c2s) + } + + s2c := s2c(int64(*l), *s) + n, p = humanize.ComputeSI(s2c) + if *h { + fmt.Printf("S2C: %f%sbps\n", n, p) + } else { + fmt.Printf("S2C: %f\n", s2c) + } + + avg := (c2s + s2c) / 2.0 + n, p = humanize.ComputeSI(avg) + + if *h { + fmt.Printf("AVG: %f%sbps\n", n, p) + } else { + fmt.Printf("AVG: %f\n", avg) + } +} + +func c2s(l int64, stream bool) float64 { + 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 +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go b/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go new file mode 100644 index 000000000..d889b5b30 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/buffer.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/conn.go b/Godeps/_workspace/src/github.com/h2so5/utp/conn.go new file mode 100644 index 000000000..ee41e973d --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/conn.go @@ -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() + } + } + }, +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/dial.go b/Godeps/_workspace/src/github.com/h2so5/utp/dial.go new file mode 100644 index 000000000..10e76fab8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/dial.go @@ -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) +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/listener.go b/Godeps/_workspace/src/github.com/h2so5/utp/listener.go new file mode 100644 index 000000000..25eb0ef0f --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/listener.go @@ -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 +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/log.go b/Godeps/_workspace/src/github.com/h2so5/utp/log.go new file mode 100644 index 000000000..dbf4ddcd3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/log.go @@ -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...) +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/packet.go b/Godeps/_workspace/src/github.com/h2so5/utp/packet.go new file mode 100644 index 000000000..b23cac108 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/packet.go @@ -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, + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore new file mode 100644 index 000000000..aab84c12d --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/.gitignore @@ -0,0 +1,3 @@ +ucat +random +.trash/ diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile new file mode 100644 index 000000000..6009f77a2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/Makefile @@ -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} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh new file mode 100644 index 000000000..5e7842ca2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/test_simple.sh @@ -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 diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go new file mode 100644 index 000000000..4382f6e0c --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/ucat/ucat.go @@ -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 [] +// ucat -l +// +// Address format is: [host]:port +// +// Note that uTP's congestion control gives priority to tcp flows (web traffic), +// so you could use this ucat tool to transfer massive files without hogging +// all the bandwidth. +package main + +import ( + "flag" + "fmt" + "io" + "net" + "os" + "os/signal" + "syscall" + + utp "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/h2so5/utp" +) + +var verbose = false + +// Usage prints out the usage of this module. +// Assumes flags use go stdlib flag pacakage. +var Usage = func() { + text := `ucat - uTP netcat in Go + +Usage: + + listen: %s [] + dial: %s -l + +Address format is Go's: [host]:port +` + + fmt.Fprintf(os.Stderr, text, os.Args[0], os.Args[0]) + flag.PrintDefaults() +} + +type args struct { + listen bool + verbose bool + localAddr string + remoteAddr string +} + +func parseArgs() args { + var a args + + // setup + parse flags + flag.BoolVar(&a.listen, "listen", false, "listen for connections") + flag.BoolVar(&a.listen, "l", false, "listen for connections (short)") + flag.BoolVar(&a.verbose, "v", false, "verbose debugging") + flag.Usage = Usage + flag.Parse() + osArgs := flag.Args() + + if len(osArgs) < 1 { + exit("") + } + + if a.listen { + a.localAddr = osArgs[0] + } else { + if len(osArgs) > 1 { + a.localAddr = osArgs[0] + a.remoteAddr = osArgs[1] + } else { + a.remoteAddr = osArgs[0] + } + } + + return a +} + +func main() { + args := parseArgs() + verbose = args.verbose + + var err error + if args.listen { + err = Listen(args.localAddr) + } else { + err = Dial(args.localAddr, args.remoteAddr) + } + + if err != nil { + exit("%s", err) + } +} + +func exit(format string, vals ...interface{}) { + if format != "" { + fmt.Fprintf(os.Stderr, "ucat error: "+format+"\n", vals...) + } + Usage() + os.Exit(1) +} + +func log(format string, vals ...interface{}) { + if verbose { + fmt.Fprintf(os.Stderr, "ucat log: "+format+"\n", vals...) + } +} + +// Listen listens and accepts one incoming uTP connection on a given port, +// and pipes all incoming data to os.Stdout. +func Listen(localAddr string) error { + 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: + } +} diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/utp.go b/Godeps/_workspace/src/github.com/h2so5/utp/utp.go new file mode 100644 index 000000000..5a92bae64 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/utp.go @@ -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 } diff --git a/Godeps/_workspace/src/github.com/h2so5/utp/utp_test.go b/Godeps/_workspace/src/github.com/h2so5/utp/utp_test.go new file mode 100644 index 000000000..7e4f11a79 --- /dev/null +++ b/Godeps/_workspace/src/github.com/h2so5/utp/utp_test.go @@ -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) + } +} diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/AUTHORS b/Godeps/_workspace/src/github.com/jbenet/commander/AUTHORS deleted file mode 100644 index be7ce33fc..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/AUTHORS +++ /dev/null @@ -1,11 +0,0 @@ -# This is the official list of Go-Commander authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. - -# Names should be added to this file as -# Name or Organization -# The email address is not required for organizations. - -# Please keep the list sorted. - -Google Inc diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/CONTRIBUTORS b/Godeps/_workspace/src/github.com/jbenet/commander/CONTRIBUTORS deleted file mode 100644 index b38ea4974..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/CONTRIBUTORS +++ /dev/null @@ -1,31 +0,0 @@ -# This is the official list of people who can contribute -# (and typically have contributed) code to the Go-Commander repository. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# The submission process automatically checks to make sure -# that people submitting code are listed in this file (by email address). -# -# Names should be added to this file only after verifying that -# the individual or the individual's organization has agreed to -# the appropriate Contributor License Agreement, found here: -# -# http://code.google.com/legal/individual-cla-v1.0.html -# http://code.google.com/legal/corporate-cla-v1.0.html -# -# The agreement for individuals can be filled out on the web. -# -# When adding J Random Contributor's name to this file, -# either J's name or J's organization's name should be -# added to the AUTHORS file, depending on whether the -# individual or corporate CLA was used. - -# Names should be added to this file like so: -# Name - -# Please keep the list sorted. - -Juan Batiz-Benet -Sebastien Binet -Yves Junqueira diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/LICENSE b/Godeps/_workspace/src/github.com/jbenet/commander/LICENSE deleted file mode 100644 index 811abfed5..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go-Commander Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/README.md b/Godeps/_workspace/src/github.com/jbenet/commander/README.md deleted file mode 100644 index 5a773a843..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/README.md +++ /dev/null @@ -1,107 +0,0 @@ -commander -============ - -[![Build Status](https://drone.io/github.com/gonuts/commander/status.png)](https://drone.io/github.com/gonuts/commander/latest) - -``commander`` is a spin off of [golang](http://golang.org) ``go tool`` infrastructure to provide commands and sub-commands. - -A ``commander.Command`` has a ``Subcommands`` field holding ``[]*commander.Command`` subcommands, referenced by name from the command line. - -So a ``Command`` can have sub commands. - -So you can have, _e.g._: -```sh -$ mycmd action1 [options...] -$ mycmd subcmd1 action1 [options...] -``` - -Example provided by: -- [hwaf](https://github.com/hwaf/hwaf) -- [examples/my-cmd](examples/my-cmd) - -## Documentation -Is available on [godoc](http://godoc.org/github.com/gonuts/commander) - -## Installation -Is performed with the usual: -```sh -$ go get github.com/gonuts/commander -``` - -## Example - -See the simple ``my-cmd`` example command for how this all hangs -together [there](http://github.com/gonuts/commander/blob/master/examples/my-cmd/main.go): - -```sh -$ my-cmd cmd1 -my-cmd-cmd1: hello from cmd1 (quiet=true) - -$ my-cmd cmd1 -q -my-cmd-cmd1: hello from cmd1 (quiet=true) - -$ my-cmd cmd1 -q=0 -my-cmd-cmd1: hello from cmd1 (quiet=false) - -$ my-cmd cmd2 -my-cmd-cmd2: hello from cmd2 (quiet=true) - -$ my-cmd subcmd1 cmd1 -my-cmd-subcmd1-cmd1: hello from subcmd1-cmd1 (quiet=true) - -$ my-cmd subcmd1 cmd2 -my-cmd-subcmd1-cmd2: hello from subcmd1-cmd2 (quiet=true) - -$ my-cmd subcmd2 cmd1 -my-cmd-subcmd2-cmd1: hello from subcmd2-cmd1 (quiet=true) - -$ my-cmd subcmd2 cmd2 -my-cmd-subcmd2-cmd2: hello from subcmd2-cmd2 (quiet=true) - -$ my-cmd help -Usage: - - my-cmd command [arguments] - -The commands are: - - cmd1 runs cmd1 and exits - cmd2 runs cmd2 and exits - subcmd1 subcmd1 subcommand. does subcmd1 thingies - subcmd2 subcmd2 subcommand. does subcmd2 thingies - -Use "my-cmd help [command]" for more information about a command. - -Additional help topics: - - -Use "my-cmd help [topic]" for more information about that topic. - - -$ my-cmd help subcmd1 -Usage: - - subcmd1 command [arguments] - -The commands are: - - cmd1 runs cmd1 and exits - cmd2 runs cmd2 and exits - - -Use "subcmd1 help [command]" for more information about a command. - -Additional help topics: - - -Use "subcmd1 help [topic]" for more information about that topic. - -``` - - -## TODO - -- automatically generate the bash/zsh/csh autocompletion lists -- automatically generate Readme examples text -- test cases - diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/commands.go b/Godeps/_workspace/src/github.com/jbenet/commander/commands.go deleted file mode 100644 index 135db22f7..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/commands.go +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2012 The Go-Commander Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// Based on the original work by The Go Authors: -// Copyright 2011 The Go Authors. All rights reserved. - -// commander helps creating command line programs whose arguments are flags, -// commands and subcommands. -package commander - -import ( - "bytes" - "fmt" - "io" - "os" - "os/exec" - "sort" - "strings" - "text/template" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" -) - -// UsageSection differentiates between sections in the usage text. -type Listing int - -const ( - CommandsList = iota - HelpTopicsList - Unlisted -) - -// A Command is an implementation of a subcommand. -type Command struct { - - // UsageLine is the short usage message. - // The first word in the line is taken to be the command name. - UsageLine string - - // Short is the short description line shown in command lists. - Short string - - // Long is the long description shown in the 'help ' output. - Long string - - // List reports which list to show this command in Usage and Help. - // Choose between {CommandsList (default), HelpTopicsList, Unlisted} - List Listing - - // Run runs the command. - // The args are the arguments after the command name. - Run func(cmd *Command, args []string) error - - // Flag is a set of flags specific to this command. - Flag flag.FlagSet - - // CustomFlags indicates that the command will do its own - // flag parsing. - CustomFlags bool - - // Subcommands are dispatched from this command - Subcommands []*Command - - // Parent command, nil for root. - Parent *Command - - // UsageTemplate formats the usage (short) information displayed to the user - // (leave empty for default) - UsageTemplate string - - // HelpTemplate formats the help (long) information displayed to the user - // (leave empty for default) - HelpTemplate string - - // Stdout and Stderr by default are os.Stdout and os.Stderr, but you can - // point them at any io.Writer - Stdout io.Writer - Stderr io.Writer -} - -// Name returns the command's name: the first word in the usage line. -func (c *Command) Name() string { - name := c.UsageLine - i := strings.Index(name, " ") - if i >= 0 { - name = name[:i] - } - return name -} - -// Usage prints the usage details to the standard error output. -func (c *Command) Usage() { - c.usage() -} - -// FlagOptions returns the flag's options as a string -func (c *Command) FlagOptions() string { - var buf bytes.Buffer - c.Flag.SetOutput(&buf) - c.Flag.PrintDefaults() - - str := string(buf.Bytes()) - if len(str) > 0 { - return fmt.Sprintf("\nOptions:\n%s", str) - } - return "" -} - -// Runnable reports whether the command can be run; otherwise -// it is a documentation pseudo-command such as importpath. -func (c *Command) Runnable() bool { - return c.Run != nil -} - -// Type to allow us to use sort.Sort on a slice of Commands -type CommandSlice []*Command - -func (c CommandSlice) Len() int { - return len(c) -} - -func (c CommandSlice) Less(i, j int) bool { - return c[i].Name() < c[j].Name() -} - -func (c CommandSlice) Swap(i, j int) { - c[i], c[j] = c[j], c[i] -} - -// Sort the commands -func (c *Command) SortCommands() { - sort.Sort(CommandSlice(c.Subcommands)) -} - -// Init the command -func (c *Command) init() { - if c.Parent != nil { - return // already initialized. - } - - // setup strings - if len(c.UsageLine) < 1 { - c.UsageLine = Defaults.UsageLine - } - if len(c.UsageTemplate) < 1 { - c.UsageTemplate = Defaults.UsageTemplate - } - if len(c.HelpTemplate) < 1 { - c.HelpTemplate = Defaults.HelpTemplate - } - - if c.Stderr == nil { - c.Stderr = os.Stderr - } - if c.Stdout == nil { - c.Stdout = os.Stdout - } - - // init subcommands - for _, cmd := range c.Subcommands { - cmd.init() - } - - // init hierarchy... - for _, cmd := range c.Subcommands { - cmd.Parent = c - } -} - -// Dispatch executes the command using the provided arguments. -// If a subcommand exists matching the first argument, it is dispatched. -// Otherwise, the command's Run function is called. -func (c *Command) Dispatch(args []string) error { - if c == nil { - return fmt.Errorf("Called Run() on a nil Command") - } - - // Ensure command is initialized. - c.init() - - // First, try a sub-command - if len(args) > 0 { - for _, cmd := range c.Subcommands { - n := cmd.Name() - if n == args[0] { - return cmd.Dispatch(args[1:]) - } - } - - // help is builtin (but after, to allow overriding) - if args[0] == "help" { - return c.help(args[1:]) - } - - // then, try out an external binary (git-style) - bin, err := exec.LookPath(c.FullName() + "-" + args[0]) - if err == nil { - cmd := exec.Command(bin, args[1:]...) - cmd.Stdin = os.Stdin - cmd.Stdout = c.Stdout - cmd.Stderr = c.Stderr - return cmd.Run() - } - } - - // then, try running this command - if c.Runnable() { - if !c.CustomFlags { - var err = error(nil) - c.Flag.Usage = func() { - c.Usage() - err = fmt.Errorf("Failed to parse flags.") - } - c.Flag.Parse(args) - if err != nil { - return err - } - args = c.Flag.Args() - } - return c.Run(c, args) - } - - // TODO: try an alias - //... - - // Last, print usage - if err := c.usage(); err != nil { - return err - } - return nil -} - -func (c *Command) usage() error { - // c.SortCommands() - err := tmpl(c.Stderr, c.UsageTemplate, c) - if err != nil { - fmt.Println(err) - } - return err -} - -// help implements the 'help' command. -func (c *Command) help(args []string) error { - - // help exactly for this command? - if len(args) == 0 { - if len(c.Long) > 0 { - return tmpl(c.Stdout, c.HelpTemplate, c) - } else { - return c.usage() - } - } - - arg := args[0] - - // is this help for a subcommand? - for _, cmd := range c.Subcommands { - n := cmd.Name() - // strip out "-"" name - if strings.HasPrefix(n, c.Name()+"-") { - n = n[len(c.Name()+"-"):] - } - if n == arg { - return cmd.help(args[1:]) - } - } - - return fmt.Errorf("Unknown help topic %#q. Run '%v help'.\n", arg, c.Name()) -} - -func (c *Command) MaxLen() (res int) { - res = 0 - for _, cmd := range c.Subcommands { - i := len(cmd.Name()) - if i > res { - res = i - } - } - return -} - -// ColFormat returns the column header size format for printing in the template -func (c *Command) ColFormat() string { - sz := c.MaxLen() - if sz < 11 { - sz = 11 - } - return fmt.Sprintf("%%-%ds", sz) -} - -// FullName returns the full name of the command, prefixed with parent commands -func (c *Command) FullName() string { - n := c.Name() - if c.Parent != nil { - n = c.Parent.FullName() + "-" + n - } - return n -} - -// FullSpacedName returns the full name of the command, with ' ' instead of '-' -func (c *Command) FullSpacedName() string { - n := c.Name() - if c.Parent != nil { - n = c.Parent.FullSpacedName() + " " + n - } - return n -} - -func (c *Command) SubcommandList(list Listing) []*Command { - var cmds []*Command - for _, cmd := range c.Subcommands { - if cmd.List == list { - cmds = append(cmds, cmd) - } - } - return cmds -} - -var Defaults = Command{ - UsageTemplate: `{{if .Runnable}}Usage: {{if .Parent}}{{.Parent.FullSpacedName}}{{end}} {{.UsageLine}} - -{{else}}{{.FullSpacedName}} - {{end}}{{.Short}} - -{{if commandList}}Commands: -{{range commandList}} - {{.Name | printf (colfmt)}} {{.Short}}{{end}} - -Use "{{.Name}} help " for more information about a command. - -{{end}}{{.FlagOptions}}{{if helpList}} -Additional help topics: -{{range helpList}} - {{.Name | printf (colfmt)}} {{.Short}}{{end}} - -Use "{{.Name}} help " for more information about that topic. - -{{end}}`, - - HelpTemplate: `{{if .Runnable}}Usage: {{if .Parent}}{{.Parent.FullSpacedName}}{{end}} {{.UsageLine}} - -{{end}}{{.Long | trim}} -{{.FlagOptions}} -`, -} - -// tmpl executes the given template text on data, writing the result to w. -func tmpl(w io.Writer, text string, data interface{}) error { - t := template.New("top") - t.Funcs(template.FuncMap{ - "trim": strings.TrimSpace, - "colfmt": func() string { return data.(*Command).ColFormat() }, - "commandList": func() []*Command { return data.(*Command).SubcommandList(CommandsList) }, - "helpList": func() []*Command { return data.(*Command).SubcommandList(HelpTopicsList) }, - }) - template.Must(t.Parse(text)) - return t.Execute(w, data) -} diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_cmd1.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_cmd1.go deleted file mode 100644 index f89a1c26e..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_cmd1.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -var cmd_cmd1 = &commander.Command{ - Run: ex_run_cmd_cmd1, - UsageLine: "cmd1 [options]", - Short: "runs cmd1 and exits", - Long: ` -runs cmd1 and exits. - -ex: -$ my-cmd cmd1 -`, - Flag: *flag.NewFlagSet("my-cmd-cmd1", flag.ExitOnError), -} - -func init() { - cmd_cmd1.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed") -} - -func ex_run_cmd_cmd1(cmd *commander.Command, args []string) error { - name := "my-cmd-" + cmd.Name() - quiet := cmd.Flag.Lookup("q").Value.Get().(bool) - fmt.Printf("%s: hello from cmd1 (quiet=%v)\n", name, quiet) - return nil -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_cmd2.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_cmd2.go deleted file mode 100644 index e6a0b54e6..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_cmd2.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -func ex_make_cmd_cmd2() *commander.Command { - cmd := &commander.Command{ - Run: ex_run_cmd_cmd2, - UsageLine: "cmd2 [options]", - Short: "runs cmd2 and exits", - Long: ` -runs cmd2 and exits. - -ex: - $ my-cmd cmd2 -`, - Flag: *flag.NewFlagSet("my-cmd-cmd2", flag.ExitOnError), - } - cmd.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed") - return cmd -} - -func ex_run_cmd_cmd2(cmd *commander.Command, args []string) error { - name := "my-cmd-" + cmd.Name() - quiet := cmd.Flag.Lookup("q").Value.Get().(bool) - fmt.Printf("%s: hello from cmd2 (quiet=%v)\n", name, quiet) - return nil -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1.go deleted file mode 100644 index 375a0c5e0..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -var cmd_subcmd1 = &commander.Command{ - UsageLine: "subcmd1 ", - Short: "subcmd1 subcommand. does subcmd1 thingies", - Subcommands: []*commander.Command{ - cmd_subcmd1_cmd1, - cmd_subcmd1_cmd2, - }, - Flag: *flag.NewFlagSet("my-cmd-subcmd1", flag.ExitOnError), -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1_cmd1.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1_cmd1.go deleted file mode 100644 index 82433a63e..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1_cmd1.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -var cmd_subcmd1_cmd1 = &commander.Command{ - Run: ex_run_cmd_subcmd1_cmd1, - UsageLine: "cmd1 [options]", - Short: "runs cmd1 and exits", - Long: ` -runs cmd1 and exits. - -ex: -$ my-cmd subcmd1 cmd1 -`, - Flag: *flag.NewFlagSet("my-cmd-subcmd1-cmd1", flag.ExitOnError), -} - -func init() { - cmd_subcmd1_cmd1.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed") -} - -func ex_run_cmd_subcmd1_cmd1(cmd *commander.Command, args []string) error { - name := "my-cmd-subcmd1-" + cmd.Name() - quiet := cmd.Flag.Lookup("q").Value.Get().(bool) - fmt.Printf("%s: hello from subcmd1-cmd1 (quiet=%v)\n", name, quiet) - return nil -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1_cmd2.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1_cmd2.go deleted file mode 100644 index 1cc194f87..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd1_cmd2.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -var cmd_subcmd1_cmd2 = &commander.Command{ - Run: ex_run_cmd_subcmd1_cmd2, - UsageLine: "cmd2 [options]", - Short: "runs cmd2 and exits", - Long: ` -runs cmd2 and exits. - -ex: -$ my-cmd subcmd1 cmd2 -`, - Flag: *flag.NewFlagSet("my-cmd-subcmd1-cmd2", flag.ExitOnError), -} - -func init() { - cmd_subcmd1_cmd2.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed") -} - -func ex_run_cmd_subcmd1_cmd2(cmd *commander.Command, args []string) error { - name := "my-cmd-subcmd1-" + cmd.Name() - quiet := cmd.Flag.Lookup("q").Value.Get().(bool) - fmt.Printf("%s: hello from subcmd1-cmd2 (quiet=%v)\n", name, quiet) - return nil -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2.go deleted file mode 100644 index 83c0160c2..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -func ex_make_cmd_subcmd2() *commander.Command { - cmd := &commander.Command{ - UsageLine: "subcmd2", - Short: "subcmd2 subcommand. does subcmd2 thingies (help list)", - List: commander.HelpTopicsList, - Subcommands: []*commander.Command{ - ex_make_cmd_subcmd2_cmd1(), - ex_make_cmd_subcmd2_cmd2(), - }, - Flag: *flag.NewFlagSet("my-cmd-subcmd2", flag.ExitOnError), - } - return cmd -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2_cmd1.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2_cmd1.go deleted file mode 100644 index 11d659b08..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2_cmd1.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -func ex_make_cmd_subcmd2_cmd1() *commander.Command { - cmd := &commander.Command{ - Run: ex_run_cmd_subcmd2_cmd1, - UsageLine: "cmd1 [options]", - Short: "runs cmd1 and exits", - Long: ` -runs cmd1 and exits. - -ex: - $ my-cmd subcmd2 cmd1 -`, - Flag: *flag.NewFlagSet("my-cmd-subcmd2-cmd1", flag.ExitOnError), - } - cmd.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed") - return cmd -} - -func ex_run_cmd_subcmd2_cmd1(cmd *commander.Command, args []string) error { - name := "my-cmd-subcmd2-" + cmd.Name() - quiet := cmd.Flag.Lookup("q").Value.Get().(bool) - fmt.Printf("%s: hello from subcmd2-cmd1 (quiet=%v)\n", name, quiet) - return nil -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2_cmd2.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2_cmd2.go deleted file mode 100644 index dbdb5bbe6..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/cmd_subcmd2_cmd2.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -func ex_make_cmd_subcmd2_cmd2() *commander.Command { - cmd := &commander.Command{ - Run: ex_run_cmd_subcmd2_cmd2, - UsageLine: "cmd2 [options]", - Short: "runs cmd2 and exits", - Long: ` -runs cmd2 and exits. - -ex: - $ my-cmd subcmd2 cmd2 -`, - Flag: *flag.NewFlagSet("my-cmd-subcmd2-cmd2", flag.ExitOnError), - } - cmd.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed") - return cmd -} - -func ex_run_cmd_subcmd2_cmd2(cmd *commander.Command, args []string) error { - name := "my-cmd-subcmd2-" + cmd.Name() - quiet := cmd.Flag.Lookup("q").Value.Get().(bool) - fmt.Printf("%s: hello from subcmd2-cmd2 (quiet=%v)\n", name, quiet) - return nil -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/main.go b/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/main.go deleted file mode 100644 index 6fa34aa7c..000000000 --- a/Godeps/_workspace/src/github.com/jbenet/commander/examples/my-cmd/main.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -var g_cmd = &commander.Command{ - UsageLine: os.Args[0] + " does cool things", -} - -func init() { - g_cmd.Subcommands = []*commander.Command{ - cmd_cmd1, - ex_make_cmd_cmd2(), - cmd_subcmd1, - ex_make_cmd_subcmd2(), - } -} - -func main() { - err := g_cmd.Dispatch(os.Args[1:]) - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } - - return -} - -// EOF diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Godeps/Godeps.json b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Godeps/Godeps.json new file mode 100644 index 000000000..7ec629513 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Godeps/Godeps.json @@ -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" + } + ] +} diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Godeps/Readme b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Godeps/Readme new file mode 100644 index 000000000..4cdaa53d5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Godeps/Readme @@ -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. diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/LICENSE b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/LICENSE new file mode 100644 index 000000000..c7386b3c9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/LICENSE @@ -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. diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Makefile b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Makefile new file mode 100644 index 000000000..0ab6e6813 --- /dev/null +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/Makefile @@ -0,0 +1,16 @@ +all: install + +godep: + go get github.com/tools/godep + +# saves/vendors third-party dependencies to Godeps/_workspace +# -r flag rewrites import paths to use the vendored path +# ./... performs operation on all packages in tree +vendor: godep + godep save -r ./... + +install: dep + cd multiaddr && go install + +dep: + cd multiaddr && go get ./... diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/README.md b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/README.md similarity index 73% rename from Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/README.md rename to Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/README.md index 41f1cb337..1b500814b 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/README.md +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/README.md @@ -1,11 +1,11 @@ # multiaddr/net - Multiaddr friendly net -Package multiaddr/net provides Multiaddr specific versions of common +Package multiaddr/net provides [Multiaddr](http://github.com/jbenet/go-multiaddr) specific versions of common functions in stdlib's net package. This means wrappers of standard net symbols like net.Dial and net.Listen, as well as conversion to/from net.Addr. Docs: -- `multiaddr/net`: https://godoc.org/github.com/jbenet/go-multiaddr/net +- `multiaddr/net`: https://godoc.org/github.com/jbenet/go-multiaddr-net - `multiaddr`: https://godoc.org/github.com/jbenet/go-multiaddr diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert.go similarity index 79% rename from Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert.go rename to Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert.go index c28159f84..05f00a66c 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert.go @@ -5,6 +5,7 @@ import ( "net" "strings" + utp "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/h2so5/utp" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) @@ -55,6 +56,33 @@ func FromNetAddr(a net.Addr) (ma.Multiaddr, error) { // Encapsulate return ipm.Encapsulate(udpm), nil + case "utp", "utp4", "utp6": + acc, ok := a.(*utp.UTPAddr) + if !ok { + return nil, errIncorrectNetAddr + } + + // Get UDP Addr + ac, ok := acc.Addr.(*net.UDPAddr) + if !ok { + return nil, errIncorrectNetAddr + } + + // Get IP Addr + ipm, err := FromIP(ac.IP) + if err != nil { + return nil, errIncorrectNetAddr + } + + // Get UDP Addr + utpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d/utp", ac.Port)) + if err != nil { + return nil, errIncorrectNetAddr + } + + // Encapsulate + return ipm.Encapsulate(utpm), nil + case "ip", "ip4", "ip6": ac, ok := a.(*net.IPAddr) if !ok { @@ -88,6 +116,8 @@ func ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) { return net.ResolveTCPAddr(network, host) case "udp": return net.ResolveUDPAddr(network, host) + case "utp": + return utp.ResolveUTPAddr(network, host) case "ip": return net.ResolveIPAddr(network, host) } @@ -121,6 +151,10 @@ func DialArgs(m ma.Multiaddr) (string, string, error) { } network := parts[2] + if parts[2] == "udp" && len(parts) > 4 && parts[4] == "utp" { + network = parts[4] + } + var host string switch parts[0] { case "ip4": diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert_test.go similarity index 71% rename from Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert_test.go rename to Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert_test.go index 47b25e7b9..de4b338b1 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/convert_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/convert_test.go @@ -4,6 +4,7 @@ import ( "net" "testing" + utp "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/h2so5/utp" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) @@ -88,17 +89,31 @@ func TestFromUDP(t *testing.T) { }) } +func TestFromUTP(t *testing.T) { + testConvert(t, "/ip4/10.20.30.40/udp/1234/utp", func() (ma.Multiaddr, error) { + return FromNetAddr(&utp.UTPAddr{ + Addr: &net.UDPAddr{ + IP: net.ParseIP("10.20.30.40"), + Port: 1234, + }, + }) + }) +} + func TestThinWaist(t *testing.T) { addrs := map[string]bool{ "/ip4/127.0.0.1/udp/1234": true, "/ip4/127.0.0.1/tcp/1234": true, + "/ip4/127.0.0.1/udp/1234/utp": true, "/ip4/127.0.0.1/udp/1234/tcp/1234": true, "/ip4/127.0.0.1/tcp/12345/ip4/1.2.3.4": true, "/ip6/::1/tcp/80": true, "/ip6/::1/udp/80": true, "/ip6/::1": true, + "/ip6/::1/utp": false, "/tcp/1234/ip4/1.2.3.4": false, "/tcp/1234": false, + "/tcp/1234/utp": false, "/tcp/1234/udp/1234": false, "/ip4/1.2.3.4/ip4/2.3.4.5": true, "/ip6/::1/ip4/2.3.4.5": true, @@ -117,21 +132,27 @@ func TestThinWaist(t *testing.T) { } func TestDialArgs(t *testing.T) { - m, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/1234") - if err != nil { - t.Fatal("failed to construct", "/ip4/127.0.0.1/udp/1234") + test := func(e_maddr, e_nw, e_host string) { + m, err := ma.NewMultiaddr(e_maddr) + if err != nil { + t.Fatal("failed to construct", "/ip4/127.0.0.1/udp/1234", e_maddr) + } + + nw, host, err := DialArgs(m) + if err != nil { + t.Fatal("failed to get dial args", e_maddr, m, err) + } + + if nw != e_nw { + t.Error("failed to get udp network Dial Arg", e_nw, nw) + } + + if host != e_host { + t.Error("failed to get host:port Dial Arg", e_host, host) + } } - nw, host, err := DialArgs(m) - if err != nil { - t.Fatal("failed to get dial args", "/ip4/127.0.0.1/udp/1234", err) - } - - if nw != "udp" { - t.Error("failed to get udp network Dial Arg") - } - - if host != "127.0.0.1:1234" { - t.Error("failed to get host:port Dial Arg") - } + test("/ip4/127.0.0.1/udp/1234", "udp", "127.0.0.1:1234") + test("/ip4/127.0.0.1/tcp/4321", "tcp", "127.0.0.1:4321") + test("/ip4/127.0.0.1/udp/1234/utp", "utp", "127.0.0.1:1234") } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/doc.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/doc.go similarity index 100% rename from Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/doc.go rename to Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/doc.go diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/ip.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/ip.go similarity index 100% rename from Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/ip.go rename to Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/ip.go diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/multiaddr/multiaddr b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/multiaddr/multiaddr new file mode 100644 index 000000000..7020fc25e Binary files /dev/null and b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/multiaddr/multiaddr differ diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/multiaddr/multiaddr.go similarity index 98% rename from Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr.go rename to Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/multiaddr/multiaddr.go index 441f607a6..5133b3fee 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/multiaddr/multiaddr.go @@ -7,7 +7,7 @@ import ( "os" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" ) // flags diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net.go similarity index 89% rename from Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net.go rename to Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net.go index a4eb08938..3e19dba38 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net.go @@ -4,6 +4,7 @@ import ( "fmt" "net" + utp "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/h2so5/utp" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) @@ -68,7 +69,7 @@ func (c *maConn) RemoteMultiaddr() ma.Multiaddr { // and RemoteAddr options are Multiaddrs, instead of net.Addrs. type Dialer struct { - // Dialer is just an embed net.Dialer, with all its options. + // Dialer is just an embedded net.Dialer, with all its options. net.Dialer // LocalAddr is the local address to use when dialing an @@ -103,9 +104,24 @@ func (d *Dialer) Dial(remote ma.Multiaddr) (Conn, error) { } // ok, Dial! - nconn, err := d.Dialer.Dial(rnet, rnaddr) - if err != nil { - return nil, err + var nconn net.Conn + switch rnet { + case "tcp": + nconn, err = d.Dialer.Dial(rnet, rnaddr) + if err != nil { + return nil, err + } + case "utp": + // construct utp dialer, with options on our net.Dialer + utpd := utp.Dialer{ + Timeout: d.Dialer.Timeout, + LocalAddr: d.Dialer.LocalAddr, + } + + nconn, err = utpd.Dial(rnet, rnaddr) + if err != nil { + return nil, err + } } // get local address (pre-specified or assigned within net.Conn) @@ -206,7 +222,13 @@ func Listen(laddr ma.Multiaddr) (Listener, error) { return nil, err } - nl, err := net.Listen(lnet, lnaddr) + var nl net.Listener + switch lnet { + case "utp": + nl, err = utp.Listen(lnet, lnaddr) + default: + nl, err = net.Listen(lnet, lnaddr) + } if err != nil { return nil, err } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net_test.go similarity index 71% rename from Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net_test.go rename to Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net_test.go index 46fd7aed4..f87dd837e 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net/net_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net/net_test.go @@ -12,7 +12,7 @@ import ( func newMultiaddr(t *testing.T, m string) ma.Multiaddr { maddr, err := ma.NewMultiaddr(m) if err != nil { - t.Fatalf("failed to construct multiaddr: %s", m) + t.Fatal("failed to construct multiaddr:", m, err) } return maddr } @@ -138,6 +138,36 @@ func TestListen(t *testing.T) { wg.Wait() } +func TestListenAddrs(t *testing.T) { + + test := func(addr string, succeed bool) { + + maddr := newMultiaddr(t, addr) + l, err := Listen(maddr) + if !succeed { + if err == nil { + t.Fatal("succeeded in listening", addr) + } + return + } + if succeed && err != nil { + t.Fatal("failed to listen", addr, err) + } + if l == nil { + t.Fatal("failed to listen", addr, succeed, err) + } + + if err = l.Close(); err != nil { + t.Fatal("failed to close listener", addr, err) + } + } + + test("/ip4/127.0.0.1/tcp/4324", true) + test("/ip4/127.0.0.1/udp/4325", false) + test("/ip4/127.0.0.1/udp/4326/udt", false) + test("/ip4/127.0.0.1/udp/4326/utp", true) +} + func TestListenAndDial(t *testing.T) { maddr := newMultiaddr(t, "/ip4/127.0.0.1/tcp/4323") @@ -199,6 +229,67 @@ func TestListenAndDial(t *testing.T) { wg.Wait() } +func TestListenAndDialUTP(t *testing.T) { + + maddr := newMultiaddr(t, "/ip4/127.0.0.1/udp/4323/utp") + listener, err := Listen(maddr) + if err != nil { + t.Fatal("failed to listen") + } + + var wg sync.WaitGroup + wg.Add(1) + go func() { + + cB, err := listener.Accept() + if err != nil { + t.Fatal("failed to accept") + } + + if !cB.LocalMultiaddr().Equal(maddr) { + t.Fatal("local multiaddr not equal:", maddr, cB.LocalMultiaddr()) + } + + // echo out + buf := make([]byte, 1024) + for { + _, err := cB.Read(buf) + if err != nil { + break + } + cB.Write(buf) + } + + wg.Done() + }() + + cA, err := Dial(newMultiaddr(t, "/ip4/127.0.0.1/udp/4323/utp")) + if err != nil { + t.Fatal("failed to dial", err) + } + + buf := make([]byte, 1024) + if _, err := cA.Write([]byte("beep boop")); err != nil { + t.Fatal("failed to write:", err) + } + + if _, err := cA.Read(buf); err != nil { + t.Fatal("failed to read:", buf, err) + } + + if !bytes.Equal(buf[:9], []byte("beep boop")) { + t.Fatal("failed to echo:", buf) + } + + maddr2 := cA.RemoteMultiaddr() + if !maddr2.Equal(maddr) { + t.Fatal("remote multiaddr not equal:", maddr, maddr2) + } + + cA.Close() + wg.Wait() +} + func TestIPLoopback(t *testing.T) { if IP4Loopback.String() != "/ip4/127.0.0.1" { t.Error("IP4Loopback incorrect:", IP4Loopback) diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go index d08544b46..cc8a2ed99 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go @@ -28,12 +28,14 @@ func stringToBytes(s string) ([]byte, error) { if p == nil { return nil, fmt.Errorf("no protocol with name %s", sp[0]) } - b = append(b, byte(p.Code)) + b = append(b, CodeToVarint(p.Code)...) + sp = sp[1:] - a := addressStringToBytes(p, sp[1]) - b = append(b, a...) - - sp = sp[2:] + if p.Size > 0 { + a := addressStringToBytes(p, sp[0]) + b = append(b, a...) + sp = sp[1:] + } } return b, nil } @@ -50,18 +52,22 @@ func bytesToString(b []byte) (ret string, err error) { s := "" for len(b) > 0 { - p := ProtocolWithCode(int(b[0])) + + code, n := ReadVarintCode(b) + b = b[n:] + p := ProtocolWithCode(code) if p == nil { - return "", fmt.Errorf("no protocol with code %d", b[0]) + return "", fmt.Errorf("no protocol with code %d", code) } s = strings.Join([]string{s, "/", p.Name}, "") - b = b[1:] - a := addressBytesToString(p, b[:(p.Size/8)]) - if len(a) > 0 { - s = strings.Join([]string{s, "/", a}, "") + 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):] } - b = b[(p.Size / 8):] } return s, nil @@ -78,12 +84,13 @@ func bytesSplit(b []byte) (ret [][]byte, err error) { ret = [][]byte{} for len(b) > 0 { - p := ProtocolWithCode(int(b[0])) + code, n := ReadVarintCode(b) + p := ProtocolWithCode(code) if p == nil { return [][]byte{}, fmt.Errorf("no protocol with code %d", b[0]) } - length := 1 + (p.Size / 8) + length := n + (p.Size / 8) ret = append(ret, b[:length]) b = b[length:] } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go index a2bea09ad..3071fa9a7 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr.go @@ -67,14 +67,15 @@ func (m *multiaddr) Protocols() []*Protocol { ps := []*Protocol{} b := m.bytes[:] for len(b) > 0 { - p := ProtocolWithCode(int(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[1+(p.Size/8):] + b = b[n+(p.Size/8):] } return ps } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr deleted file mode 100644 index c54b3bf08..000000000 Binary files a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr/multiaddr and /dev/null differ diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go index a5cb666a0..15e2be0b6 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go @@ -68,6 +68,8 @@ func TestStringToBytes(t *testing.T) { } 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) { @@ -89,6 +91,8 @@ func TestBytesToString(t *testing.T) { } 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) { @@ -96,7 +100,7 @@ func TestBytesSplitAndJoin(t *testing.T) { testString := func(s string, res []string) { m, err := NewMultiaddr(s) if err != nil { - t.Error("failed to convert", s) + t.Fatal("failed to convert", s, err) } split := Split(m) @@ -132,6 +136,8 @@ func TestBytesSplitAndJoin(t *testing.T) { 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) { diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv index 62bc5c217..a8b1e3a47 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.csv @@ -5,5 +5,7 @@ code size name 33 16 dccp 41 128 ip6 132 16 sctp +301 0 udt +302 0 utp 480 0 http 443 0 https diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go index ed4d29f13..49051be25 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go @@ -1,10 +1,15 @@ package multiaddr +import ( + "encoding/binary" +) + // Protocol is a Multiaddr protocol description structure. type Protocol struct { - Code int - Size int - Name string + Code int + Size int + Name string + VCode []byte } // replicating table here to: @@ -18,17 +23,21 @@ const ( 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"}, - &Protocol{P_TCP, 16, "tcp"}, - &Protocol{P_UDP, 16, "udp"}, - &Protocol{P_DCCP, 16, "dccp"}, - &Protocol{P_IP6, 128, "ip6"}, + &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"}, + &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"}, } @@ -52,3 +61,26 @@ func ProtocolWithCode(c int) *Protocol { } 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 +} diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 29e22b9ed..6f731f52d 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -6,7 +6,7 @@ import ( manners "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/braintree/manners" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" cmds "github.com/jbenet/go-ipfs/commands" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" core "github.com/jbenet/go-ipfs/core" diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go index 2b41befb1..936342592 100644 --- a/cmd/ipfs/init.go +++ b/cmd/ipfs/init.go @@ -177,10 +177,14 @@ func initConfig(configFilename string, dspathOverride string, nBitsForKeypair in conf := &config.Config{ - // setup the node addresses. + // setup the node's default addresses. + // Note: two swarm listen addrs, one tcp, one utp. Addresses: config.Addresses{ - Swarm: "/ip4/0.0.0.0/tcp/4001", - API: "/ip4/127.0.0.1/tcp/5001", + Swarm: []string{ + "/ip4/0.0.0.0/tcp/4001", + "/ip4/0.0.0.0/udp/4002/utp", + }, + API: "/ip4/127.0.0.1/tcp/5001", }, Bootstrap: []*config.BootstrapPeer{ diff --git a/cmd/ipfs/main.go b/cmd/ipfs/main.go index 37283a2a3..991385435 100644 --- a/cmd/ipfs/main.go +++ b/cmd/ipfs/main.go @@ -12,7 +12,7 @@ import ( // TODO rm direct reference to go-logging logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" cmds "github.com/jbenet/go-ipfs/commands" cmdsCli "github.com/jbenet/go-ipfs/commands/cli" diff --git a/config/config.go b/config/config.go index 73b2bc707..46fc8badd 100644 --- a/config/config.go +++ b/config/config.go @@ -36,8 +36,8 @@ type Datastore struct { // Addresses stores the (string) multiaddr addresses for the node. type Addresses struct { - Swarm string // address for the swarm network - API string // address for the local API (RPC) + Swarm []string // addresses for the swarm network + API string // address for the local API (RPC) } // Mounts stores the (string) mount points diff --git a/config/serialize.go b/config/serialize.go index ba17686cb..b71d945b0 100644 --- a/config/serialize.go +++ b/config/serialize.go @@ -17,7 +17,10 @@ func ReadConfigFile(filename string, cfg interface{}) error { } defer f.Close() - return Decode(f, cfg) + if err := Decode(f, cfg); err != nil { + return fmt.Errorf("Failure to decode config: %s", err) + } + return nil } // WriteConfigFile writes the config from `cfg` into `filename`. diff --git a/core/core.go b/core/core.go index 09014cd11..1fe9c9992 100644 --- a/core/core.go +++ b/core/core.go @@ -268,15 +268,15 @@ func initConnections(ctx context.Context, cfg *config.Config, pstore peer.Peerst } func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) { - var listen []ma.Multiaddr - if len(cfg.Addresses.Swarm) > 0 { - maddr, err := ma.NewMultiaddr(cfg.Addresses.Swarm) + var err error + listen := make([]ma.Multiaddr, len(cfg.Addresses.Swarm)) + for i, addr := range cfg.Addresses.Swarm { + + listen[i], err = ma.NewMultiaddr(addr) if err != nil { - return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm) + return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm[%d]: %s", i, cfg.Addresses.Swarm) } - - listen = append(listen, maddr) } return listen, nil diff --git a/core/core_test.go b/core/core_test.go index 8c01b350a..91c311050 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -17,7 +17,7 @@ func TestInitialization(t *testing.T) { Type: "memory", }, Addresses: config.Addresses{ - Swarm: "/ip4/0.0.0.0/tcp/4001", + Swarm: []string{"/ip4/0.0.0.0/tcp/4001"}, API: "/ip4/127.0.0.1/tcp/8000", }, }, @@ -29,7 +29,7 @@ func TestInitialization(t *testing.T) { Path: ".testdb", }, Addresses: config.Addresses{ - Swarm: "/ip4/0.0.0.0/tcp/4001", + Swarm: []string{"/ip4/0.0.0.0/tcp/4001"}, API: "/ip4/127.0.0.1/tcp/8000", }, }, diff --git a/net/conn/conn.go b/net/conn/conn.go index 92bf2259b..61176a084 100644 --- a/net/conn/conn.go +++ b/net/conn/conn.go @@ -8,7 +8,7 @@ import ( context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" msgio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" diff --git a/net/conn/dial.go b/net/conn/dial.go index c19927713..9eab93b22 100644 --- a/net/conn/dial.go +++ b/net/conn/dial.go @@ -1,30 +1,58 @@ package conn import ( - "fmt" + "strings" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" peer "github.com/jbenet/go-ipfs/peer" + debugerror "github.com/jbenet/go-ipfs/util/debugerror" ) // Dial connects to a particular peer, over a given network // Example: d.Dial(ctx, "udp", peer) func (d *Dialer) Dial(ctx context.Context, network string, remote peer.Peer) (Conn, error) { - laddr := d.LocalPeer.NetAddress(network) - if laddr == nil { - return nil, fmt.Errorf("No local address for network %s", network) - } - raddr := remote.NetAddress(network) if raddr == nil { - return nil, fmt.Errorf("No remote address for network %s", network) + return nil, debugerror.Errorf("No remote address for network %s", network) + } + return d.DialAddr(ctx, raddr, remote) +} + +// DialAddr connects to a peer over a particular address +// Ensures raddr is part of peer.Addresses() +// Example: d.DialAddr(ctx, peer.Addresses()[0], peer) +func (d *Dialer) DialAddr(ctx context.Context, raddr ma.Multiaddr, remote peer.Peer) (Conn, error) { + + found := false + for _, addr := range remote.Addresses() { + if addr.Equal(raddr) { + found = true + } + } + if !found { + return nil, debugerror.Errorf("address %s is not in peer %s", raddr, remote) + } + + network, _, err := manet.DialArgs(raddr) + if err != nil { + return nil, err + } + + laddr := d.LocalPeer.NetAddress(network) + if laddr == nil { + return nil, debugerror.Errorf("No local address for network %s", network) + } + + if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") { + return nil, debugerror.Errorf("Attempted to connect to zero address: %s", raddr) } remote.SetType(peer.Remote) - remote, err := d.Peerstore.Add(remote) + remote, err = d.Peerstore.Add(remote) if err != nil { log.Errorf("Error putting peer into peerstore: %s", remote) } diff --git a/net/conn/dial_test.go b/net/conn/dial_test.go index 50ef51e77..f3d3808d0 100644 --- a/net/conn/dial_test.go +++ b/net/conn/dial_test.go @@ -157,3 +157,73 @@ func TestDialer(t *testing.T) { l.Close() cancel() } + +func TestDialAddr(t *testing.T) { + // t.Skip("Skipping in favor of another test") + + p1, err := setupPeer("/ip4/127.0.0.1/tcp/4334") + if err != nil { + t.Fatal("error setting up peer", err) + } + + p2, err := setupPeer("/ip4/127.0.0.1/tcp/4335") + if err != nil { + t.Fatal("error setting up peer", err) + } + + ctx, cancel := context.WithCancel(context.Background()) + + laddr := p1.NetAddress("tcp") + if laddr == nil { + t.Fatal("Listen address is nil.") + } + + ps1 := peer.NewPeerstore() + ps2 := peer.NewPeerstore() + ps1.Add(p1) + ps2.Add(p2) + + l, err := Listen(ctx, laddr, p1, ps1) + if err != nil { + t.Fatal(err) + } + + go echoListen(ctx, l) + + d := &Dialer{ + Peerstore: ps2, + LocalPeer: p2, + } + + raddr := p1.NetAddress("tcp") + if raddr == nil { + t.Fatal("Dial address is nil.") + } + + c, err := d.DialAddr(ctx, raddr, p1) + if err != nil { + t.Fatal("error dialing peer", err) + } + + // fmt.Println("sending") + c.Out() <- []byte("beep") + c.Out() <- []byte("boop") + + out := <-c.In() + // fmt.Println("recving", string(out)) + data := string(out) + if data != "beep" { + t.Error("unexpected conn output", data) + } + + out = <-c.In() + data = string(out) + if string(out) != "boop" { + t.Error("unexpected conn output", data) + } + + // fmt.Println("closing") + c.Close() + l.Close() + cancel() +} diff --git a/net/conn/listen.go b/net/conn/listen.go index 4cbe9a766..32e10ca94 100644 --- a/net/conn/listen.go +++ b/net/conn/listen.go @@ -1,9 +1,11 @@ package conn import ( + "fmt" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" peer "github.com/jbenet/go-ipfs/peer" ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" @@ -73,6 +75,7 @@ func (l *listener) listen() { } for { + log.Info("swarm listening on %s -- %v\n", l.Multiaddr(), l.Listener) maconn, err := l.Listener.Accept() if err != nil { @@ -120,7 +123,7 @@ func Listen(ctx context.Context, addr ma.Multiaddr, local peer.Peer, peers peer. ml, err := manet.Listen(addr) if err != nil { - return nil, err + return nil, fmt.Errorf("Failed to listen on %s: %s", addr, err) } // todo make this a variable diff --git a/net/swarm/addrs.go b/net/swarm/addrs.go index 8d3a287ce..a099d3e64 100644 --- a/net/swarm/addrs.go +++ b/net/swarm/addrs.go @@ -2,7 +2,7 @@ package swarm import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" "github.com/jbenet/go-ipfs/util/eventlog" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" diff --git a/net/swarm/conn.go b/net/swarm/conn.go index d215ea0e8..74c3b03d4 100644 --- a/net/swarm/conn.go +++ b/net/swarm/conn.go @@ -6,6 +6,7 @@ import ( conn "github.com/jbenet/go-ipfs/net/conn" msg "github.com/jbenet/go-ipfs/net/message" + peer "github.com/jbenet/go-ipfs/peer" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" @@ -95,8 +96,38 @@ func (s *Swarm) handleIncomingConn(nconn conn.Conn) { } } -// connSetup adds the passed in connection to its peerMap and starts -// the fanInSingle routine for that connection +// peerMultiConn returns the MultiConn responsible for handling this peer. +// if there is none, it creates one and returns it. Note that timeouts +// and connection teardowns will remove it. +func (s *Swarm) peerMultiConn(p peer.Peer) (*conn.MultiConn, error) { + + s.connsLock.Lock() + mc, found := s.conns[p.Key()] + if found { + s.connsLock.Unlock() + return mc, nil + } + + // multiconn doesn't exist, make a new one. + mc, err := conn.NewMultiConn(s.Context(), s.local, p, nil) + if err != nil { + s.connsLock.Unlock() + log.Errorf("error creating multiconn: %s", err) + return nil, err + } + s.conns[p.Key()] = mc + s.connsLock.Unlock() + + // kick off reader goroutine + s.Children().Add(1) + mc.Children().Add(1) // child of Conn as well. + go s.fanInSingle(mc) + log.Debugf("added new multiconn: %s", mc) + return mc, nil +} + +// connSetup takes a new connection, performs the IPFS handshake (handshake3) +// and then adds it to the appropriate MultiConn. func (s *Swarm) connSetup(c conn.Conn) (conn.Conn, error) { if c == nil { return nil, errors.New("Tried to start nil connection.") @@ -126,35 +157,14 @@ func (s *Swarm) connSetup(c conn.Conn) (conn.Conn, error) { } // add to conns - s.connsLock.Lock() - - mc, found := s.conns[c.RemotePeer().Key()] - if !found { - // multiconn doesn't exist, make a new one. - conns := []conn.Conn{c} - mc, err := conn.NewMultiConn(s.Context(), s.local, c.RemotePeer(), conns) - if err != nil { - log.Errorf("error creating multiconn: %s", err) - c.Close() - return nil, err - } - - s.conns[c.RemotePeer().Key()] = mc - s.connsLock.Unlock() - - // kick off reader goroutine - s.Children().Add(1) - mc.Children().Add(1) // child of Conn as well. - go s.fanInSingle(mc) - log.Debugf("added new multiconn: %s", mc) - } else { - s.connsLock.Unlock() // unlock before adding new conn - - mc.Add(c) - log.Debugf("multiconn found: %s", mc) + mc, err := s.peerMultiConn(c.RemotePeer()) + if err != nil { + c.Close() + return nil, err } - + mc.Add(c) log.Debugf("multiconn added new conn %s", c) + return c, nil } diff --git a/net/swarm/swarm.go b/net/swarm/swarm.go index 3fc65390e..9328ed7cd 100644 --- a/net/swarm/swarm.go +++ b/net/swarm/swarm.go @@ -5,7 +5,6 @@ package swarm import ( "errors" "fmt" - "strings" "sync" conn "github.com/jbenet/go-ipfs/net/conn" @@ -133,17 +132,15 @@ func (s *Swarm) Dial(peer peer.Peer) (conn.Conn, error) { Peerstore: s.peers, } - // If we are attempting to connect to the zero addr, fail out early - raddr := peer.NetAddress("tcp") - if raddr == nil { - return nil, fmt.Errorf("No remote address for network tcp") + // try to connect to one of the peer's known addresses. + // for simplicity, we do this sequentially. + // A future commit will do this asynchronously. + for _, addr := range peer.Addresses() { + c, err = d.DialAddr(s.Context(), addr, peer) + if err == nil { + break + } } - - if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") { - return nil, fmt.Errorf("Attempted to connect to loopback address: %s", raddr) - } - - c, err = d.Dial(s.Context(), "tcp", peer) if err != nil { return nil, err } diff --git a/server/http/http.go b/server/http/http.go index 9bab3ebfd..f9f3502cc 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -8,7 +8,7 @@ import ( mux "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/mux" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" core "github.com/jbenet/go-ipfs/core"