diff --git a/commands/command.go b/commands/command.go index 6baddc2b5..f46fbe324 100644 --- a/commands/command.go +++ b/commands/command.go @@ -3,6 +3,7 @@ package commands import ( "errors" "fmt" + "io" "reflect" "strings" @@ -15,9 +16,9 @@ var log = u.Logger("command") // It reads from the Request, and writes results to the Response. type Function func(Request) (interface{}, error) -// Marshaler is a function that takes in a Response, and returns a marshalled []byte +// Marshaler is a function that takes in a Response, and returns an io.Reader // (or an error on failure) -type Marshaler func(Response) ([]byte, error) +type Marshaler func(Response) (io.Reader, error) // MarshalerMap is a map of Marshaler functions, keyed by EncodingType // (or an error on failure) @@ -113,12 +114,16 @@ func (c *Command) Call(req Request) Response { return res } - // If the command specified an output type, ensure the actual value returned is of that type - if cmd.Type != nil { - definedType := reflect.ValueOf(cmd.Type).Type() - actualType := reflect.ValueOf(output).Type() + actualType := reflect.ValueOf(output).Type() - if definedType != actualType { + // test if output is a channel + isChan := actualType.Kind() == reflect.Chan + + // If the command specified an output type, ensure the actual value returned is of that type + if cmd.Type != nil && !isChan { + expectedType := reflect.ValueOf(cmd.Type).Type() + + if actualType != expectedType { res.SetError(ErrIncorrectType, ErrNormal) return res } diff --git a/commands/response.go b/commands/response.go index 7e12e5922..f776605b3 100644 --- a/commands/response.go +++ b/commands/response.go @@ -41,17 +41,33 @@ const ( ) var marshallers = map[EncodingType]Marshaler{ - JSON: func(res Response) ([]byte, error) { + JSON: func(res Response) (io.Reader, error) { + var value interface{} if res.Error() != nil { - return json.MarshalIndent(res.Error(), "", " ") + value = res.Error() + } else { + value = res.Output() } - return json.MarshalIndent(res.Output(), "", " ") + + b, err := json.MarshalIndent(value, "", " ") + if err != nil { + return nil, err + } + return bytes.NewReader(b), nil }, - XML: func(res Response) ([]byte, error) { + XML: func(res Response) (io.Reader, error) { + var value interface{} if res.Error() != nil { - return xml.Marshal(res.Error()) + value = res.Error() + } else { + value = res.Output() } - return xml.Marshal(res.Output()) + + b, err := xml.Marshal(value) + if err != nil { + return nil, err + } + return bytes.NewReader(b), nil }, } @@ -70,7 +86,7 @@ type Response interface { // Marshal marshals out the response into a buffer. It uses the EncodingType // on the Request to chose a Marshaler (Codec). - Marshal() ([]byte, error) + Marshal() (io.Reader, error) // Gets a io.Reader that reads the marshalled output Reader() (io.Reader, error) @@ -103,9 +119,9 @@ func (r *response) SetError(err error, code ErrorType) { r.err = &Error{Message: err.Error(), Code: code} } -func (r *response) Marshal() ([]byte, error) { +func (r *response) Marshal() (io.Reader, error) { if r.err == nil && r.value == nil { - return []byte{}, nil + return bytes.NewReader([]byte{}), nil } enc, found, err := r.req.Option(EncShort).String() @@ -119,7 +135,7 @@ func (r *response) Marshal() ([]byte, error) { // Special case: if text encoding and an error, just print it out. if encType == Text && r.Error() != nil { - return []byte(r.Error().Error()), nil + return strings.NewReader(r.Error().Error()), nil } var marshaller Marshaler @@ -140,22 +156,20 @@ func (r *response) Marshal() ([]byte, error) { // Reader returns an `io.Reader` representing marshalled output of this Response // Note that multiple calls to this will return a reference to the same io.Reader func (r *response) Reader() (io.Reader, error) { - // if command set value to a io.Reader, use that as our reader if r.out == nil { if out, ok := r.value.(io.Reader); ok { + // if command returned a io.Reader, use that as our reader r.out = out - } - } - if r.out == nil { - // no reader set, so marshal the error or value - marshalled, err := r.Marshal() - if err != nil { - return nil, err - } + } else { + // otherwise, use the response marshaler output + marshalled, err := r.Marshal() + if err != nil { + return nil, err + } - // create a Reader from the marshalled data - r.out = bytes.NewReader(marshalled) + r.out = marshalled + } } return r.out, nil diff --git a/core/commands/add.go b/core/commands/add.go index fbac578a9..e1be611e1 100644 --- a/core/commands/add.go +++ b/core/commands/add.go @@ -76,7 +76,7 @@ remains to be implemented. return added, nil }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { val, ok := res.Output().(*AddOutput) if !ok { return nil, u.ErrCast() @@ -93,7 +93,7 @@ remains to be implemented. buf.Write([]byte(fmt.Sprintf("added %s %s\n", obj.Hash, val.Names[i]))) } } - return buf.Bytes(), nil + return &buf, nil }, }, Type: &AddOutput{}, diff --git a/core/commands/block.go b/core/commands/block.go index 326dac2fa..37b08cafb 100644 --- a/core/commands/block.go +++ b/core/commands/block.go @@ -2,7 +2,9 @@ package commands import ( "bytes" + "io" "io/ioutil" + "strings" "time" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -123,9 +125,9 @@ It reads from stdin, and is a base58 encoded multihash. }, Type: &Block{}, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { block := res.Output().(*Block) - return []byte(block.Key + "\n"), nil + return strings.NewReader(block.Key + "\n"), nil }, }, } diff --git a/core/commands/bootstrap.go b/core/commands/bootstrap.go index 0be6a1295..4bfdc2dab 100644 --- a/core/commands/bootstrap.go +++ b/core/commands/bootstrap.go @@ -114,7 +114,7 @@ in the bootstrap list). }, Type: &BootstrapOutput{}, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { v, ok := res.Output().(*BootstrapOutput) if !ok { return nil, u.ErrCast() @@ -122,7 +122,7 @@ in the bootstrap list). var buf bytes.Buffer err := bootstrapWritePeers(&buf, "added ", v.Peers) - return buf.Bytes(), err + return &buf, err }, }, } @@ -175,7 +175,7 @@ var bootstrapRemoveCmd = &cmds.Command{ }, Type: &BootstrapOutput{}, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { v, ok := res.Output().(*BootstrapOutput) if !ok { return nil, u.ErrCast() @@ -183,7 +183,7 @@ var bootstrapRemoveCmd = &cmds.Command{ var buf bytes.Buffer err := bootstrapWritePeers(&buf, "removed ", v.Peers) - return buf.Bytes(), err + return &buf, err }, }, } @@ -209,7 +209,7 @@ var bootstrapListCmd = &cmds.Command{ }, } -func bootstrapMarshaler(res cmds.Response) ([]byte, error) { +func bootstrapMarshaler(res cmds.Response) (io.Reader, error) { v, ok := res.Output().(*BootstrapOutput) if !ok { return nil, u.ErrCast() @@ -217,7 +217,7 @@ func bootstrapMarshaler(res cmds.Response) ([]byte, error) { var buf bytes.Buffer err := bootstrapWritePeers(&buf, "", v.Peers) - return buf.Bytes(), err + return &buf, err } func bootstrapWritePeers(w io.Writer, prefix string, peers []config.BootstrapPeer) error { diff --git a/core/commands/commands.go b/core/commands/commands.go index 63124861d..4d850a468 100644 --- a/core/commands/commands.go +++ b/core/commands/commands.go @@ -2,6 +2,7 @@ package commands import ( "bytes" + "io" "sort" cmds "github.com/jbenet/go-ipfs/commands" @@ -26,13 +27,13 @@ func CommandsCmd(root *cmds.Command) *cmds.Command { return &root, nil }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { v := res.Output().(*Command) var buf bytes.Buffer for _, s := range cmdPathStrings(v) { buf.Write([]byte(s + "\n")) } - return buf.Bytes(), nil + return &buf, nil }, }, Type: &Command{}, diff --git a/core/commands/config.go b/core/commands/config.go index 8b15b7550..c51deb295 100644 --- a/core/commands/config.go +++ b/core/commands/config.go @@ -72,7 +72,7 @@ Set the value of the 'datastore.path' key: } }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { if len(res.Request().Arguments()) == 2 { return nil, nil // dont output anything } @@ -92,7 +92,7 @@ Set the value of the 'datastore.path' key: return nil, err } buf = append(buf, byte('\n')) - return buf, nil + return bytes.NewReader(buf), nil }, }, Type: &ConfigField{}, diff --git a/core/commands/diag.go b/core/commands/diag.go index 764ac3f8b..dabc74fdc 100644 --- a/core/commands/diag.go +++ b/core/commands/diag.go @@ -86,7 +86,7 @@ connected peers and latencies between them. }, Type: &DiagnosticOutput{}, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(r cmds.Response) ([]byte, error) { + cmds.Text: func(r cmds.Response) (io.Reader, error) { output, ok := r.Output().(*DiagnosticOutput) if !ok { return nil, util.ErrCast() @@ -96,7 +96,7 @@ connected peers and latencies between them. if err != nil { return nil, err } - return buf.Bytes(), nil + return &buf, nil }, }, } diff --git a/core/commands/id.go b/core/commands/id.go index c71b0cddb..3177e7b6b 100644 --- a/core/commands/id.go +++ b/core/commands/id.go @@ -1,9 +1,11 @@ package commands import ( + "bytes" "encoding/base64" "encoding/json" "errors" + "io" "time" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" @@ -76,13 +78,17 @@ if no peer is specified, prints out local peers info. return printPeer(node.Peerstore, p.ID) }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { val, ok := res.Output().(*IdOutput) if !ok { return nil, u.ErrCast() } - return json.MarshalIndent(val, "", "\t") + marshaled, err := json.MarshalIndent(val, "", "\t") + if err != nil { + return nil, err + } + return bytes.NewReader(marshaled), nil }, }, Type: &IdOutput{}, diff --git a/core/commands/ls.go b/core/commands/ls.go index 87f65b278..54c8cb345 100644 --- a/core/commands/ls.go +++ b/core/commands/ls.go @@ -2,6 +2,8 @@ package commands import ( "fmt" + "io" + "strings" cmds "github.com/jbenet/go-ipfs/commands" merkledag "github.com/jbenet/go-ipfs/merkledag" @@ -70,7 +72,7 @@ it contains, with the following format: return &LsOutput{output}, nil }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { s := "" output := res.Output().(*LsOutput).Objects @@ -84,7 +86,7 @@ it contains, with the following format: } } - return []byte(s), nil + return strings.NewReader(s), nil }, }, Type: &LsOutput{}, diff --git a/core/commands/mount_unix.go b/core/commands/mount_unix.go index b06451f9e..a46fe6dc7 100644 --- a/core/commands/mount_unix.go +++ b/core/commands/mount_unix.go @@ -4,6 +4,7 @@ package commands import ( "fmt" + "io" "strings" "time" @@ -134,11 +135,11 @@ baz }, Type: &config.Mounts{}, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { v := res.Output().(*config.Mounts) s := fmt.Sprintf("IPFS mounted at: %s\n", v.IPFS) s += fmt.Sprintf("IPNS mounted at: %s\n", v.IPNS) - return []byte(s), nil + return strings.NewReader(s), nil }, }, } diff --git a/core/commands/object.go b/core/commands/object.go index 5e85ddc18..87890d335 100644 --- a/core/commands/object.go +++ b/core/commands/object.go @@ -6,6 +6,7 @@ import ( "errors" "io" "io/ioutil" + "strings" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" @@ -101,10 +102,10 @@ multihash. return objectLinks(n, key) }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { object := res.Output().(*Object) marshalled := marshalLinks(object.Links) - return []byte(marshalled), nil + return strings.NewReader(marshalled), nil }, }, Type: &Object{}, @@ -163,13 +164,18 @@ This command outputs data in the following encodings: }, Type: &Node{}, Marshalers: cmds.MarshalerMap{ - cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) { + cmds.EncodingType("protobuf"): func(res cmds.Response) (io.Reader, error) { node := res.Output().(*Node) object, err := deserializeNode(node) if err != nil { return nil, err } - return object.Marshal() + + marshaled, err := object.Marshal() + if err != nil { + return nil, err + } + return bytes.NewReader(marshaled), nil }, }, } @@ -221,9 +227,9 @@ Data should be in the format specified by . return output, nil }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { object := res.Output().(*Object) - return []byte("added " + object.Hash), nil + return strings.NewReader("added " + object.Hash), nil }, }, Type: &Object{}, diff --git a/core/commands/publish.go b/core/commands/publish.go index 9b6448a7a..966337376 100644 --- a/core/commands/publish.go +++ b/core/commands/publish.go @@ -3,6 +3,8 @@ package commands import ( "errors" "fmt" + "io" + "strings" cmds "github.com/jbenet/go-ipfs/commands" core "github.com/jbenet/go-ipfs/core" @@ -78,10 +80,10 @@ Publish a to another public key: return publish(n, n.PrivateKey, ref) }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { v := res.Output().(*IpnsEntry) s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value) - return []byte(s), nil + return strings.NewReader(s), nil }, }, Type: &IpnsEntry{}, diff --git a/core/commands/refs.go b/core/commands/refs.go index e3cbfaab9..6133da24b 100644 --- a/core/commands/refs.go +++ b/core/commands/refs.go @@ -1,7 +1,9 @@ package commands import ( + "bytes" "fmt" + "io" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" cmds "github.com/jbenet/go-ipfs/commands" @@ -16,13 +18,13 @@ type KeyList struct { } // KeyListTextMarshaler outputs a KeyList as plaintext, one key per line -func KeyListTextMarshaler(res cmds.Response) ([]byte, error) { +func KeyListTextMarshaler(res cmds.Response) (io.Reader, error) { output := res.Output().(*KeyList) - s := "" + var buf bytes.Buffer for _, key := range output.Keys { - s += key.B58String() + "\n" + buf.WriteString(key.B58String() + "\n") } - return []byte(s), nil + return &buf, nil } var RefsCmd = &cmds.Command{ diff --git a/core/commands/resolve.go b/core/commands/resolve.go index 02de15177..8fb67e755 100644 --- a/core/commands/resolve.go +++ b/core/commands/resolve.go @@ -2,6 +2,8 @@ package commands import ( "errors" + "io" + "strings" cmds "github.com/jbenet/go-ipfs/commands" ) @@ -71,9 +73,9 @@ Resolve te value of another name: return output, nil }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { output := res.Output().(string) - return []byte(output), nil + return strings.NewReader(output), nil }, }, } diff --git a/core/commands/root.go b/core/commands/root.go index b2aa25110..9ab6185a1 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -1,6 +1,9 @@ package commands import ( + "io" + "strings" + cmds "github.com/jbenet/go-ipfs/commands" u "github.com/jbenet/go-ipfs/util" ) @@ -90,6 +93,6 @@ type MessageOutput struct { Message string } -func MessageTextMarshaler(res cmds.Response) ([]byte, error) { - return []byte(res.Output().(*MessageOutput).Message), nil +func MessageTextMarshaler(res cmds.Response) (io.Reader, error) { + return strings.NewReader(res.Output().(*MessageOutput).Message), nil } diff --git a/core/commands/swarm.go b/core/commands/swarm.go index b85373d2c..4a37ef170 100644 --- a/core/commands/swarm.go +++ b/core/commands/swarm.go @@ -3,6 +3,7 @@ package commands import ( "bytes" "fmt" + "io" "path" cmds "github.com/jbenet/go-ipfs/commands" @@ -124,7 +125,7 @@ ipfs swarm connect /ip4/104.131.131.82/tcp/4001/QmaCpDMGvV2BGHeYERUEnRQAwe3N8Szb Type: &stringList{}, } -func stringListMarshaler(res cmds.Response) ([]byte, error) { +func stringListMarshaler(res cmds.Response) (io.Reader, error) { list, ok := res.Output().(*stringList) if !ok { return nil, errors.New("failed to cast []string") @@ -132,10 +133,10 @@ func stringListMarshaler(res cmds.Response) ([]byte, error) { var buf bytes.Buffer for _, s := range list.Strings { - buf.Write([]byte(s)) - buf.Write([]byte("\n")) + buf.WriteString(s) + buf.WriteString("\n") } - return buf.Bytes(), nil + return &buf, nil } // splitAddresses is a function that takes in a slice of string peer addresses diff --git a/core/commands/update.go b/core/commands/update.go index 81aad84e8..17f258df7 100644 --- a/core/commands/update.go +++ b/core/commands/update.go @@ -1,8 +1,10 @@ package commands import ( + "bytes" "errors" "fmt" + "io" cmds "github.com/jbenet/go-ipfs/commands" "github.com/jbenet/go-ipfs/core" @@ -33,16 +35,16 @@ var UpdateCmd = &cmds.Command{ "log": UpdateLogCmd, }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { v := res.Output().(*UpdateOutput) - s := "" + var buf bytes.Buffer if v.NewVersion != v.OldVersion { - s = fmt.Sprintf("Successfully updated to IPFS version '%s' (from '%s')\n", - v.NewVersion, v.OldVersion) + buf.WriteString(fmt.Sprintf("Successfully updated to IPFS version '%s' (from '%s')\n", + v.NewVersion, v.OldVersion)) } else { - s = fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion) + buf.WriteString(fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion)) } - return []byte(s), nil + return &buf, nil }, }, } @@ -62,16 +64,16 @@ var UpdateCheckCmd = &cmds.Command{ }, Type: &UpdateOutput{}, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { v := res.Output().(*UpdateOutput) - s := "" + var buf bytes.Buffer if v.NewVersion != v.OldVersion { - s = fmt.Sprintf("A new version of IPFS is available ('%s', currently running '%s')\n", - v.NewVersion, v.OldVersion) + buf.WriteString(fmt.Sprintf("A new version of IPFS is available ('%s', currently running '%s')\n", + v.NewVersion, v.OldVersion)) } else { - s = fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion) + buf.WriteString(fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion)) } - return []byte(s), nil + return &buf, nil }, }, } diff --git a/core/commands/version.go b/core/commands/version.go index 047c36d56..cec310036 100644 --- a/core/commands/version.go +++ b/core/commands/version.go @@ -2,6 +2,8 @@ package commands import ( "fmt" + "io" + "strings" cmds "github.com/jbenet/go-ipfs/commands" config "github.com/jbenet/go-ipfs/config" @@ -26,7 +28,7 @@ var VersionCmd = &cmds.Command{ }, nil }, Marshalers: cmds.MarshalerMap{ - cmds.Text: func(res cmds.Response) ([]byte, error) { + cmds.Text: func(res cmds.Response) (io.Reader, error) { v := res.Output().(*VersionOutput) number, found, err := res.Request().Option("number").Bool() @@ -34,9 +36,9 @@ var VersionCmd = &cmds.Command{ return nil, err } if found && number { - return []byte(fmt.Sprintln(v.Version)), nil + return strings.NewReader(fmt.Sprintln(v.Version)), nil } - return []byte(fmt.Sprintf("ipfs version %s\n", v.Version)), nil + return strings.NewReader(fmt.Sprintf("ipfs version %s\n", v.Version)), nil }, }, Type: &VersionOutput{},