From 6240f2b7b05c36d841aac8be7c97f20341e5f265 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sat, 17 Mar 2018 20:16:47 -0700 Subject: [PATCH] forward the remaining of the stdin args to the server --- arguments.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ command.go | 11 ++++----- http/client.go | 13 ++++++---- request.go | 30 +++++++++++++++++------ 4 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 arguments.go diff --git a/arguments.go b/arguments.go new file mode 100644 index 00000000..8955df82 --- /dev/null +++ b/arguments.go @@ -0,0 +1,66 @@ +package cmds + +import ( + "bufio" + "io" +) + +// StdinArguments is used to iterate through arguments piped through stdin. +type StdinArguments interface { + io.ReadCloser + // Returns the next argument passed via stdin. + // + // This method will never return an error along with a value, it will + // return one or the other. + // + // Once all arguments have been read, it will return "", io.EOF + Next() (string, error) +} + +type arguments struct { + reader *bufio.Reader + closer io.Closer +} + +func newArguments(r io.ReadCloser) *arguments { + return &arguments{ + reader: bufio.NewReader(r), + closer: r, + } +} + +// Read implements the io.Reader interface +func (a *arguments) Read(b []byte) (int, error) { + return a.reader.Read(b) +} + +// Close implements the io.Closer interface +func (a *arguments) Close() error { + return a.closer.Close() +} + +// WriteTo implements the io.WriterTo interface +func (a *arguments) WriteTo(w io.Writer) (int64, error) { + return a.reader.WriteTo(w) +} + +// Next returns the next argument +func (a *arguments) Next() (string, error) { + s, err := a.reader.ReadString('\n') + switch err { + case io.EOF: + if s == "" { + return "", io.EOF + } + // drop the error. + return s, nil + case nil: + l := len(s) + if l >= 2 && s[l-2] == '\r' { + return s[:l-2], nil + } + return s[:l-1], nil + default: + return "", err + } +} diff --git a/command.go b/command.go index 0fd4a7dc..4be0e71f 100644 --- a/command.go +++ b/command.go @@ -9,7 +9,6 @@ output to the user, including text, JSON, and XML marshallers. package cmds import ( - "bufio" "errors" "fmt" "io" @@ -234,7 +233,7 @@ func (c *Command) CheckArguments(req *Request) error { switch err { case io.EOF: case nil: - req.bodyArgs = bufio.NewScanner(fi) + req.bodyArgs = newArguments(fi) // Can't pass files and stdin arguments. req.Files = nil default: @@ -267,13 +266,13 @@ func (c *Command) CheckArguments(req *Request) error { // Can we get it from stdin? if argDef.SupportsStdin && req.bodyArgs != nil { - if req.bodyArgs.Scan() { + next, err := req.bodyArgs.Next() + if err == nil { // Found it! - req.Arguments = append(req.Arguments, req.bodyArgs.Text()) + req.Arguments = append(req.Arguments, next) continue } - // Nope! Maybe we had a read error? - if err := req.bodyArgs.Err(); err != nil { + if err != io.EOF { return err } // No, just missing. diff --git a/http/client.go b/http/client.go index 690c1405..c02a463d 100644 --- a/http/client.go +++ b/http/client.go @@ -137,17 +137,20 @@ func (c *client) Send(req *cmds.Request) (cmds.Response, error) { } var fileReader *files.MultiFileReader - var reader io.Reader - - if req.Files != nil { + if bodyArgs := req.BodyArgs(); bodyArgs != nil { + // In the end, this wraps a file reader in a file reader. + // However, such is life. + fileReader = files.NewMultiFileReader(files.NewSliceFile("", "", []files.File{ + files.NewReaderFile("stdin", "", bodyArgs, nil), + }), true) + } else if req.Files != nil { fileReader = files.NewMultiFileReader(req.Files, true) - reader = fileReader } path := strings.Join(req.Path, "/") url := fmt.Sprintf(ApiUrlFormat, c.serverAddress, c.apiPrefix, path, query) - httpReq, err := http.NewRequest("POST", url, reader) + httpReq, err := http.NewRequest("POST", url, fileReader) if err != nil { return nil, err } diff --git a/request.go b/request.go index 96f1b48f..9177f501 100644 --- a/request.go +++ b/request.go @@ -1,9 +1,9 @@ package cmds import ( - "bufio" "context" "fmt" + "io" "reflect" "github.com/ipfs/go-ipfs-cmdkit" @@ -21,7 +21,7 @@ type Request struct { Files files.File - bodyArgs *bufio.Scanner + bodyArgs *arguments } // NewRequest returns a request initialized with given arguments @@ -50,8 +50,17 @@ func NewRequest(ctx context.Context, path []string, opts cmdkit.OptMap, args []s } // BodyArgs returns a scanner that returns arguments passed in the body as tokens. -func (req *Request) BodyArgs() *bufio.Scanner { - return req.bodyArgs +// +// Returns nil if there are no arguments to be consumed via stdin. +func (req *Request) BodyArgs() StdinArguments { + // dance to make sure we return an *untyped* nil. + // DO NOT just return `req.bodyArgs`. + // If you'd like to complain, go to + // https://github.com/golang/go/issues/. + if req.bodyArgs != nil { + return req.bodyArgs + } + return nil } func (req *Request) ParseBodyArgs() error { @@ -60,11 +69,16 @@ func (req *Request) ParseBodyArgs() error { return nil } - for s.Scan() { - req.Arguments = append(req.Arguments, s.Text()) + for { + next, err := s.Next() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + req.Arguments = append(req.Arguments, next) } - - return s.Err() } func (req *Request) SetOption(name string, value interface{}) {