From 02eb747d7156fcb2f8660decd080796218c02ef1 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Sun, 4 Aug 2019 10:56:38 +0100 Subject: [PATCH] serve http/webdav/restic: implement --prefix - fixes #3398 --prefix enables the servers to serve from a non root prefix. This enables easier proxying. --- cmd/serve/http/http.go | 7 +++-- cmd/serve/httplib/httpflags/httpflags.go | 3 +++ cmd/serve/httplib/httplib.go | 33 +++++++++++++++++++++++- cmd/serve/restic/restic.go | 7 +++-- cmd/serve/webdav/webdav.go | 8 ++++-- 5 files changed, 51 insertions(+), 7 deletions(-) diff --git a/cmd/serve/http/http.go b/cmd/serve/http/http.go index 2cce22a91..6a1649e82 100644 --- a/cmd/serve/http/http.go +++ b/cmd/serve/http/http.go @@ -68,7 +68,7 @@ func newServer(f fs.Fs, opt *httplib.Options) *server { f: f, vfs: vfs.New(f, &vfsflags.Opt), } - mux.HandleFunc("/", s.handler) + mux.HandleFunc(s.Opt.Prefix+"/", s.handler) return s } @@ -93,7 +93,10 @@ func (s *server) handler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Accept-Ranges", "bytes") w.Header().Set("Server", "rclone/"+fs.Version) - urlPath := r.URL.Path + urlPath, ok := s.Path(w, r) + if !ok { + return + } isDir := strings.HasSuffix(urlPath, "/") remote := strings.Trim(urlPath, "/") if isDir { diff --git a/cmd/serve/httplib/httpflags/httpflags.go b/cmd/serve/httplib/httpflags/httpflags.go index 05c98fe50..46383e212 100644 --- a/cmd/serve/httplib/httpflags/httpflags.go +++ b/cmd/serve/httplib/httpflags/httpflags.go @@ -26,6 +26,9 @@ func AddFlagsPrefix(flagSet *pflag.FlagSet, prefix string, Opt *httplib.Options) flags.StringVarP(flagSet, &Opt.Realm, prefix+"realm", "", Opt.Realm, "realm for authentication") flags.StringVarP(flagSet, &Opt.BasicUser, prefix+"user", "", Opt.BasicUser, "User name for authentication.") flags.StringVarP(flagSet, &Opt.BasicPass, prefix+"pass", "", Opt.BasicPass, "Password for authentication.") + if prefix == "" { + flags.StringVarP(flagSet, &Opt.Prefix, prefix+"prefix", "", Opt.Prefix, "Prefix for URLs.") + } } // AddFlags adds flags for the httplib diff --git a/cmd/serve/httplib/httplib.go b/cmd/serve/httplib/httplib.go index fd5beb9e0..03302e044 100644 --- a/cmd/serve/httplib/httplib.go +++ b/cmd/serve/httplib/httplib.go @@ -44,6 +44,11 @@ for a transfer. --max-header-bytes controls the maximum number of bytes the server will accept in the HTTP header. +--prefix controls the URL prefix that rclone serves from. By default +rclone will serve from the root. If you used --prefix "rclone" then +rclone would serve from a URL starting with "/rclone/". This is +useful if you wish to proxy rclone serve. + #### Authentication By default this will serve files without needing a login. @@ -81,6 +86,7 @@ certificate authority certificate. // Options contains options for the http Server type Options struct { ListenAddr string // Port to listen on + Prefix string // prefix to strip from URLs ServerReadTimeout time.Duration // Timeout for server reading data ServerWriteTimeout time.Duration // Timeout for server writing data MaxHeaderBytes int // Maximum size of request header @@ -190,6 +196,14 @@ func NewServer(handler http.Handler, opt *Options) *Server { log.Fatalf("Need both -cert and -key to use SSL") } + // If a Path is set then serve from there + if strings.HasSuffix(s.Opt.Prefix, "/") { + s.Opt.Prefix = s.Opt.Prefix[:len(s.Opt.Prefix)-1] + } + if s.Opt.Prefix != "" && !strings.HasPrefix(s.Opt.Prefix, "/") { + s.Opt.Prefix = "/" + s.Opt.Prefix + } + // FIXME make a transport? s.httpServer = &http.Server{ Addr: s.Opt.ListenAddr, @@ -299,10 +313,27 @@ func (s *Server) URL() string { // (i.e. port assigned by operating system) addr = s.listener.Addr().String() } - return fmt.Sprintf("%s://%s/", proto, addr) + return fmt.Sprintf("%s://%s%s/", proto, addr, s.Opt.Prefix) } // UsingAuth returns true if authentication is required func (s *Server) UsingAuth() bool { return s.usingAuth } + +// Path returns the current path with the Prefix stripped +// +// If it returns false, then the path was invalid and the handler +// should exit as the error response has already been sent +func (s *Server) Path(w http.ResponseWriter, r *http.Request) (Path string, ok bool) { + Path = r.URL.Path + if s.Opt.Prefix == "" { + return Path, true + } + if !strings.HasPrefix(Path, s.Opt.Prefix+"/") { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return Path, false + } + Path = Path[len(s.Opt.Prefix):] + return Path, true +} diff --git a/cmd/serve/restic/restic.go b/cmd/serve/restic/restic.go index 39454288f..5ffdca5d0 100644 --- a/cmd/serve/restic/restic.go +++ b/cmd/serve/restic/restic.go @@ -171,7 +171,7 @@ func newServer(f fs.Fs, opt *httplib.Options) *server { Server: httplib.NewServer(mux, opt), f: f, } - mux.HandleFunc("/", s.handler) + mux.HandleFunc(s.Opt.Prefix+"/", s.handler) return s } @@ -211,7 +211,10 @@ func (s *server) handler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Accept-Ranges", "bytes") w.Header().Set("Server", "rclone/"+fs.Version) - path := r.URL.Path + path, ok := s.Path(w, r) + if !ok { + return + } remote := makeRemote(path) fs.Debugf(s.f, "%s %s", r.Method, path) diff --git a/cmd/serve/webdav/webdav.go b/cmd/serve/webdav/webdav.go index 1790d4a5f..0f4609b22 100644 --- a/cmd/serve/webdav/webdav.go +++ b/cmd/serve/webdav/webdav.go @@ -114,18 +114,22 @@ func newWebDAV(f fs.Fs, opt *httplib.Options) *WebDAV { f: f, vfs: vfs.New(f, &vfsflags.Opt), } + w.Server = httplib.NewServer(http.HandlerFunc(w.handler), opt) webdavHandler := &webdav.Handler{ + Prefix: w.Server.Opt.Prefix, FileSystem: w, LockSystem: webdav.NewMemLS(), Logger: w.logRequest, // FIXME } w.webdavhandler = webdavHandler - w.Server = httplib.NewServer(http.HandlerFunc(w.handler), opt) return w } func (w *WebDAV) handler(rw http.ResponseWriter, r *http.Request) { - urlPath := r.URL.Path + urlPath, ok := w.Path(rw, r) + if !ok { + return + } isDir := strings.HasSuffix(urlPath, "/") remote := strings.Trim(urlPath, "/") if !disableGETDir && (r.Method == "GET" || r.Method == "HEAD") && isDir {