diff --git a/cmd/serve/http/http.go b/cmd/serve/http/http.go index e29fb2c7b..b0454b867 100644 --- a/cmd/serve/http/http.go +++ b/cmd/serve/http/http.go @@ -3,7 +3,6 @@ package http import ( "fmt" "html/template" - "log" "net/http" "os" "path" @@ -11,6 +10,7 @@ import ( "strings" "github.com/ncw/rclone/cmd" + "github.com/ncw/rclone/cmd/serve/httplib" "github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs/accounting" "github.com/ncw/rclone/lib/rest" @@ -19,13 +19,8 @@ import ( "github.com/spf13/cobra" ) -// Globals -var ( - bindAddress = "localhost:8080" -) - func init() { - Command.Flags().StringVarP(&bindAddress, "addr", "", bindAddress, "IPaddress:Port to bind server to.") + httplib.AddFlags(Command.Flags()) vfsflags.AddFlags(Command.Flags()) } @@ -37,10 +32,6 @@ var Command = &cobra.Command{ over HTTP. This can be viewed in a web browser or you can make a remote of type http read from it. -Use --addr to specify which IP address and port the server should -listen on, eg --addr 1.2.3.4:8000 or --addr :8080 to listen to all -IPs. By default it only listens on localhost. - You can use the filter flags (eg --include, --exclude) to control what is served. @@ -48,12 +39,12 @@ The server will log errors. Use -v to see access logs. --bwlimit will be respected for file transfers. Use --stats to control the stats printing. -` + vfs.Help, +` + httplib.Help + vfs.Help, Run: func(command *cobra.Command, args []string) { cmd.CheckArgs(1, 1, command, args) f := cmd.NewFsSrc(args) cmd.Run(false, true, command, func() error { - s := newServer(f, bindAddress) + s := newServer(f) s.serve() return nil }) @@ -62,33 +53,26 @@ control the stats printing. // server contains everything to run the server type server struct { - f fs.Fs - bindAddress string - vfs *vfs.VFS + f fs.Fs + vfs *vfs.VFS + srv *httplib.Server } -func newServer(f fs.Fs, bindAddress string) *server { +func newServer(f fs.Fs) *server { + mux := http.NewServeMux() s := &server{ - f: f, - bindAddress: bindAddress, - vfs: vfs.New(f, &vfsflags.Opt), + f: f, + vfs: vfs.New(f, &vfsflags.Opt), + srv: httplib.NewServer(mux), } + mux.HandleFunc("/", s.handler) return s } -// serve creates the http server +// serve runs the http server - doesn't return func (s *server) serve() { - mux := http.NewServeMux() - mux.HandleFunc("/", s.handler) - // FIXME make a transport? - httpServer := &http.Server{ - Addr: s.bindAddress, - Handler: mux, - MaxHeaderBytes: 1 << 20, - } - initServer(httpServer) - fs.Logf(s.f, "Serving on http://%s/", bindAddress) - log.Fatal(httpServer.ListenAndServe()) + fs.Logf(s.f, "Serving on %s", s.srv.URL()) + s.srv.Serve() } // handler reads incoming requests and dispatches them diff --git a/cmd/serve/http/http_test.go b/cmd/serve/http/http_test.go index 5647347b7..05da842db 100644 --- a/cmd/serve/http/http_test.go +++ b/cmd/serve/http/http_test.go @@ -28,7 +28,8 @@ const ( ) func startServer(t *testing.T, f fs.Fs) { - s := newServer(f, testBindAddress) + s := newServer(f) + s.srv.SetBindAddress(testBindAddress) go s.serve() // try to connect to the test server diff --git a/cmd/serve/http/http_new.go b/cmd/serve/httplib/http_new.go similarity index 80% rename from cmd/serve/http/http_new.go rename to cmd/serve/httplib/http_new.go index 8b15933f1..21cce8832 100644 --- a/cmd/serve/http/http_new.go +++ b/cmd/serve/httplib/http_new.go @@ -2,14 +2,14 @@ //+build go1.8 -package http +package httplib import ( "net/http" "time" ) -// Initialise the http.Server for pre go1.8 +// Initialise the http.Server for post go1.8 func initServer(s *http.Server) { s.ReadHeaderTimeout = 10 * time.Second // time to send the headers s.IdleTimeout = 60 * time.Second // time to keep idle connections open diff --git a/cmd/serve/http/http_old.go b/cmd/serve/httplib/http_old.go similarity index 90% rename from cmd/serve/http/http_old.go rename to cmd/serve/httplib/http_old.go index 92f570392..ddc37a1fd 100644 --- a/cmd/serve/http/http_old.go +++ b/cmd/serve/httplib/http_old.go @@ -2,7 +2,7 @@ //+build !go1.8 -package http +package httplib import ( "net/http" diff --git a/cmd/serve/httplib/httplib.go b/cmd/serve/httplib/httplib.go new file mode 100644 index 000000000..ea84b8eb9 --- /dev/null +++ b/cmd/serve/httplib/httplib.go @@ -0,0 +1,68 @@ +// Package httplib provides common functionality for http servers +package httplib + +import ( + "fmt" + "log" + "net/http" + + "github.com/spf13/pflag" +) + +// Globals +var ( + bindAddress = "localhost:8080" +) + +// AddFlags adds the http server specific flags +func AddFlags(flagSet *pflag.FlagSet) { + flagSet.StringVarP(&bindAddress, "addr", "", bindAddress, "IPaddress:Port to bind server to.") +} + +// Help contains text describing the http server to add to the command +// help. +var Help = ` +### Server options + +Use --addr to specify which IP address and port the server should +listen on, eg --addr 1.2.3.4:8000 or --addr :8080 to listen to all +IPs. By default it only listens on localhost. +` + +// Server contains info about the running http server +type Server struct { + bindAddress string + httpServer *http.Server +} + +// NewServer creates an http server +func NewServer(handler http.Handler) *Server { + s := &Server{ + bindAddress: bindAddress, + } + // FIXME make a transport? + s.httpServer = &http.Server{ + Addr: s.bindAddress, + Handler: handler, + MaxHeaderBytes: 1 << 20, + } + // go version specific initialisation + initServer(s.httpServer) + return s +} + +// SetBindAddress overrides the config flag +func (s *Server) SetBindAddress(addr string) { + s.bindAddress = addr + s.httpServer.Addr = addr +} + +// Serve runs the server - doesn't return +func (s *Server) Serve() { + log.Fatal(s.httpServer.ListenAndServe()) +} + +// URL returns the serving address of this server +func (s *Server) URL() string { + return fmt.Sprintf("http://%s/", s.bindAddress) +} diff --git a/cmd/serve/webdav/webdav.go b/cmd/serve/webdav/webdav.go index 5f9945814..615322025 100644 --- a/cmd/serve/webdav/webdav.go +++ b/cmd/serve/webdav/webdav.go @@ -8,6 +8,8 @@ import ( "os" "github.com/ncw/rclone/cmd" + "github.com/ncw/rclone/cmd/serve/httplib" + "github.com/ncw/rclone/cmd/serve/httplib/httpflags" "github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs/log" "github.com/ncw/rclone/vfs" @@ -17,13 +19,8 @@ import ( "golang.org/x/net/webdav" ) -// Globals -var ( - bindAddress = "localhost:8081" -) - func init() { - Command.Flags().StringVarP(&bindAddress, "addr", "", bindAddress, "IPaddress:Port to bind server to.") + httpflags.AddFlags(Command.Flags()) vfsflags.AddFlags(Command.Flags()) } @@ -39,37 +36,18 @@ write it. NB at the moment each directory listing reads the start of each file which is undesirable: see https://github.com/golang/go/issues/22577 - -` + vfs.Help, +` + httplib.Help + vfs.Help, Run: func(command *cobra.Command, args []string) { cmd.CheckArgs(1, 1, command, args) - fsrc := cmd.NewFsSrc(args) + f := cmd.NewFsSrc(args) cmd.Run(false, false, command, func() error { - return serveWebDav(fsrc) + w := newWebDAV(f, &httpflags.Opt) + w.serve() + return nil }) }, } -// serve the remote -func serveWebDav(f fs.Fs) error { - fs.Logf(f, "WebDav Server started on %v", bindAddress) - - webdavFS := &WebDAV{ - f: f, - vfs: vfs.New(f, &vfsflags.Opt), - } - - handler := &webdav.Handler{ - FileSystem: webdavFS, - LockSystem: webdav.NewMemLS(), - Logger: webdavFS.logRequest, // FIXME - } - - // FIXME use our HTTP transport - http.Handle("/", handler) - return http.ListenAndServe(bindAddress, nil) -} - // WebDAV is a webdav.FileSystem interface // // A FileSystem implements access to a collection of named files. The elements @@ -85,11 +63,35 @@ func serveWebDav(f fs.Fs) error { type WebDAV struct { f fs.Fs vfs *vfs.VFS + srv *httplib.Server } // check interface var _ webdav.FileSystem = (*WebDAV)(nil) +// Make a new WebDAV to serve the remote +func newWebDAV(f fs.Fs, opt *httplib.Options) *WebDAV { + w := &WebDAV{ + f: f, + vfs: vfs.New(f, &vfsflags.Opt), + } + + handler := &webdav.Handler{ + FileSystem: w, + LockSystem: webdav.NewMemLS(), + Logger: w.logRequest, // FIXME + } + + w.srv = httplib.NewServer(handler, opt) + return w +} + +// serve runs the http server - doesn't return +func (w *WebDAV) serve() { + fs.Logf(w.f, "WebDav Server started on %s", w.srv.URL()) + w.srv.Serve() +} + // logRequest is called by the webdav module on every request func (w *WebDAV) logRequest(r *http.Request, err error) { fs.Infof(r.URL.Path, "%s from %s", r.Method, r.RemoteAddr) diff --git a/cmd/serve/webdav/webdav_test.go b/cmd/serve/webdav/webdav_test.go index 616078924..37d67eeff 100644 --- a/cmd/serve/webdav/webdav_test.go +++ b/cmd/serve/webdav/webdav_test.go @@ -13,13 +13,22 @@ import ( "testing" _ "github.com/ncw/rclone/backend/local" + "github.com/ncw/rclone/cmd/serve/httplib" "github.com/ncw/rclone/fstest" "github.com/stretchr/testify/assert" ) +const ( + testBindAddress = "localhost:51778" + testURL = "http://" + testBindAddress + "/" +) + // TestWebDav runs the webdav server then runs the unit tests for the // webdav remote against it. func TestWebDav(t *testing.T) { + opt := httplib.DefaultOpt + opt.ListenAddr = testBindAddress + fstest.Initialise() fremote, _, clean, err := fstest.RandomRemote(*fstest.RemoteName, *fstest.SubDir) @@ -31,8 +40,8 @@ func TestWebDav(t *testing.T) { // Start the server go func() { - err := serveWebDav(fremote) - assert.NoError(t, err) + w := newWebDAV(fremote, &opt) + w.serve() }() // FIXME shut it down somehow? @@ -52,7 +61,7 @@ func TestWebDav(t *testing.T) { cmd := exec.Command("go", args...) cmd.Env = append(os.Environ(), "RCLONE_CONFIG_WEBDAVTEST_TYPE=webdav", - "RCLONE_CONFIG_WEBDAVTEST_URL=http://localhost:8081/", + "RCLONE_CONFIG_WEBDAVTEST_URL="+testURL, "RCLONE_CONFIG_WEBDAVTEST_VENDOR=other", ) out, err := cmd.CombinedOutput()