Commit d08b27ac authored by Bruce Flynn's avatar Bruce Flynn
Browse files

support text protocol

parent 7b477e8b
......@@ -15,16 +15,6 @@ against an SFTP server via a simple JSON protocol over file-like objects, typica
stdin, stdout, and stderr.
## Protocol
The process reads commands from the input as JSON objects prefixed by the size of
the command payload as a 4 byte big-endian unsigned int:
```
*******************************
* uint32 Length of payload *
* --------------------------- *
* JSON encoded command *
*******************************
```
The command format is:
```
{"command": <name (str)>,
......@@ -40,6 +30,26 @@ The result will be of the format:
}
```
### Text Protocol
The process reads commands from the input, one per line, as JSON objects, handles
the command an writes results, one-per-line to the output.
> NOTE: While text is simple, it will have issues for any commands or strings that
may mistakenly have a new-line in it. So, if you use it make sure you are stripping
whitespace from your strings.
### Binary Protocol
The process reads commands from the input as JSON objects prefixed by the size of
the command payload as a 4 byte big-endian unsigned int:
```
*******************************
* uint32 Length of payload *
* --------------------------- *
* JSON encoded command *
*******************************
```
## Protocol in Python
Running the process:
```
......
package main
import (
"bufio"
"encoding/binary"
"encoding/json"
"fmt"
......@@ -61,7 +62,14 @@ func decodeCommand(buf []byte) (command, error) {
return command, err
}
func (s sftpAPI) commands() <-chan command {
type protocol interface {
readCommands() <-chan command
writeResult(result) error
}
type commandReader func() <-chan command
type resultWriter func(result) error
func (s sftpAPI) binaryCommands() <-chan command {
ch := make(chan command)
go func() {
......@@ -97,6 +105,26 @@ func (s sftpAPI) commands() <-chan command {
return ch
}
func (s sftpAPI) textCommands() <-chan command {
ch := make(chan command)
go func() {
scanner := bufio.NewScanner(s.req)
for scanner.Scan() {
line := scanner.Text()
cmd, err := decodeCommand([]byte(line))
if err != nil {
info("ERROR bad command, bailing!!!: %s", err)
break
}
ch <- cmd
}
close(ch)
}()
return ch
}
// args: source, dest as abs paths
func (s sftpAPI) doPut(source, dest string) error {
fout, err := s.client.Create(dest)
......@@ -217,7 +245,7 @@ func (s sftpAPI) doCommand(cmd command) result {
return zult
}
func (s sftpAPI) writeResult(zult result) error {
func (s sftpAPI) writeBinaryResult(zult result) error {
dat, err := json.Marshal(zult)
if err != nil {
return err
......@@ -230,6 +258,16 @@ func (s sftpAPI) writeResult(zult result) error {
return err
}
func (s sftpAPI) writeTextResult(zult result) error {
dat, err := json.Marshal(zult)
if err != nil {
return err
}
dat = append(dat, '\n')
_, err = s.resp.Write(dat)
return err
}
func (s *sftpAPI) connect() error {
debug("connecting to %s", s.url.Host)
client, err := ssh.Dial("tcp", s.url.Host, &s.cfg)
......
......@@ -50,8 +50,11 @@ https://gitlab.ssec.wisc.edu/brucef/sftper
}
var (
err error
input io.Reader
err error
input io.Reader
text = pflag.Bool("text", false, "Use text protocol where each command must be on its "+
"own line and each result will be written one per line. This is generally not recommended unless "+
"you're super-duper sure you will have no strings with new lines. But, it sure is handy for testing.")
source = pflag.StringP("commands", "c", "-", "File where commands are read from.")
help = pflag.Bool("help", false, "Print help and exit")
urlArg = pflag.String("url", "", "SFTP base url sftp://[<user>@]<hostname>[:port]. Any provided path will be ignored")
......@@ -87,7 +90,14 @@ https://gitlab.ssec.wisc.edu/brucef/sftper
}
defer sftp.close()
for cmd := range sftp.commands() {
readCommands := sftp.binaryCommands
writeResult := sftp.writeBinaryResult
if *text {
readCommands = sftp.textCommands
writeResult = sftp.writeTextResult
}
for cmd := range readCommands() {
debug("command: %+v", cmd)
zult := sftp.doCommand(cmd)
debug("result %+v", zult)
......@@ -95,6 +105,7 @@ https://gitlab.ssec.wisc.edu/brucef/sftper
if zult.Status == "fail" {
info("FAIL: %+v", zult)
}
sftp.writeResult(zult)
writeResult(zult)
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment