From 5114b11d6f5a11e32aef8775e2cb14fbb172c5bb Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Tue, 6 Mar 2018 23:22:29 +0100 Subject: [PATCH] serve restic: add http2 server on stdin/stdout --- cmd/serve/restic/restic.go | 27 ++++++++++++ cmd/serve/restic/stdio_conn.go | 54 +++++++++++++++++++++++ cmd/serve/restic/stdio_conn_go1.10.go | 27 ++++++++++++ cmd/serve/restic/stdio_conn_pre_go1.10.go | 22 +++++++++ 4 files changed, 130 insertions(+) create mode 100644 cmd/serve/restic/stdio_conn.go create mode 100644 cmd/serve/restic/stdio_conn_go1.10.go create mode 100644 cmd/serve/restic/stdio_conn_pre_go1.10.go diff --git a/cmd/serve/restic/restic.go b/cmd/serve/restic/restic.go index 8874c082f..c7ed3b38c 100644 --- a/cmd/serve/restic/restic.go +++ b/cmd/serve/restic/restic.go @@ -3,9 +3,11 @@ package restic import ( "encoding/json" + "errors" "fmt" "io" "net/http" + "os" "path" "regexp" "strconv" @@ -21,10 +23,17 @@ import ( "github.com/ncw/rclone/fs/operations" "github.com/ncw/rclone/fs/walk" "github.com/spf13/cobra" + "golang.org/x/crypto/ssh/terminal" + "golang.org/x/net/http2" +) + +var ( + stdio bool ) func init() { httpflags.AddFlags(Command.Flags()) + Command.Flags().BoolVar(&stdio, "stdio", false, "run an HTTP2 server on stdin/stdout") } // Command definition for cobra @@ -110,6 +119,24 @@ these **must** end with /. Eg f := cmd.NewFsSrc(args) cmd.Run(false, true, command, func() error { s := newServer(f, &httpflags.Opt) + if stdio { + if terminal.IsTerminal(int(os.Stdout.Fd())) { + return errors.New("Refusing to run HTTP2 server directly on a terminal, please let restic start rclone") + } + + conn := &StdioConn{ + stdin: os.Stdin, + stdout: os.Stdout, + } + + httpSrv := &http2.Server{} + opts := &http2.ServeConnOpts{ + Handler: http.HandlerFunc(s.handler), + } + httpSrv.ServeConn(conn, opts) + return nil + } + s.serve() return nil }) diff --git a/cmd/serve/restic/stdio_conn.go b/cmd/serve/restic/stdio_conn.go new file mode 100644 index 000000000..684344d4a --- /dev/null +++ b/cmd/serve/restic/stdio_conn.go @@ -0,0 +1,54 @@ +package restic + +import ( + "log" + "net" + "os" +) + +// Addr implements net.Addr for stdin/stdout. +type Addr struct{} + +// Network returns the network type as a string. +func (a Addr) Network() string { + return "stdio" +} + +func (a Addr) String() string { + return "stdio" +} + +// StdioConn implements a net.Conn via stdin/stdout. +type StdioConn struct { + stdin *os.File + stdout *os.File +} + +func (s *StdioConn) Read(p []byte) (int, error) { + return s.stdin.Read(p) +} + +func (s *StdioConn) Write(p []byte) (int, error) { + return s.stdout.Write(p) +} + +// Close closes both streams. +func (s *StdioConn) Close() error { + log.Printf("Server.Close()\n") + err1 := s.stdin.Close() + err2 := s.stdout.Close() + if err1 != nil { + return err1 + } + return err2 +} + +// LocalAddr returns nil. +func (s *StdioConn) LocalAddr() net.Addr { + return Addr{} +} + +// RemoteAddr returns nil. +func (s *StdioConn) RemoteAddr() net.Addr { + return Addr{} +} diff --git a/cmd/serve/restic/stdio_conn_go1.10.go b/cmd/serve/restic/stdio_conn_go1.10.go new file mode 100644 index 000000000..97c108991 --- /dev/null +++ b/cmd/serve/restic/stdio_conn_go1.10.go @@ -0,0 +1,27 @@ +//+build go1.10 + +// Deadline setting for go1.10+ + +package restic + +import "time" + +// SetDeadline sets the read/write deadline. +func (s *StdioConn) SetDeadline(t time.Time) error { + err1 := s.stdin.SetReadDeadline(t) + err2 := s.stdout.SetWriteDeadline(t) + if err1 != nil { + return err1 + } + return err2 +} + +// SetReadDeadline sets the read/write deadline. +func (s *StdioConn) SetReadDeadline(t time.Time) error { + return s.stdin.SetReadDeadline(t) +} + +// SetWriteDeadline sets the read/write deadline. +func (s *StdioConn) SetWriteDeadline(t time.Time) error { + return s.stdout.SetWriteDeadline(t) +} diff --git a/cmd/serve/restic/stdio_conn_pre_go1.10.go b/cmd/serve/restic/stdio_conn_pre_go1.10.go new file mode 100644 index 000000000..32500fa8a --- /dev/null +++ b/cmd/serve/restic/stdio_conn_pre_go1.10.go @@ -0,0 +1,22 @@ +//+build !go1.10 + +// Fallback deadline setting for pre go1.10 + +package restic + +import "time" + +// SetDeadline sets the read/write deadline. +func (s *StdioConn) SetDeadline(t time.Time) error { + return nil +} + +// SetReadDeadline sets the read/write deadline. +func (s *StdioConn) SetReadDeadline(t time.Time) error { + return nil +} + +// SetWriteDeadline sets the read/write deadline. +func (s *StdioConn) SetWriteDeadline(t time.Time) error { + return nil +}