serve http/webdav/restic: implement --prefix - fixes #3398
--prefix enables the servers to serve from a non root prefix. This enables easier proxying.
This commit is contained in:
parent
d51a970932
commit
02eb747d71
5 changed files with 51 additions and 7 deletions
|
@ -68,7 +68,7 @@ func newServer(f fs.Fs, opt *httplib.Options) *server {
|
||||||
f: f,
|
f: f,
|
||||||
vfs: vfs.New(f, &vfsflags.Opt),
|
vfs: vfs.New(f, &vfsflags.Opt),
|
||||||
}
|
}
|
||||||
mux.HandleFunc("/", s.handler)
|
mux.HandleFunc(s.Opt.Prefix+"/", s.handler)
|
||||||
return s
|
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("Accept-Ranges", "bytes")
|
||||||
w.Header().Set("Server", "rclone/"+fs.Version)
|
w.Header().Set("Server", "rclone/"+fs.Version)
|
||||||
|
|
||||||
urlPath := r.URL.Path
|
urlPath, ok := s.Path(w, r)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
isDir := strings.HasSuffix(urlPath, "/")
|
isDir := strings.HasSuffix(urlPath, "/")
|
||||||
remote := strings.Trim(urlPath, "/")
|
remote := strings.Trim(urlPath, "/")
|
||||||
if isDir {
|
if isDir {
|
||||||
|
|
|
@ -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.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.BasicUser, prefix+"user", "", Opt.BasicUser, "User name for authentication.")
|
||||||
flags.StringVarP(flagSet, &Opt.BasicPass, prefix+"pass", "", Opt.BasicPass, "Password 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
|
// AddFlags adds flags for the httplib
|
||||||
|
|
|
@ -44,6 +44,11 @@ for a transfer.
|
||||||
--max-header-bytes controls the maximum number of bytes the server will
|
--max-header-bytes controls the maximum number of bytes the server will
|
||||||
accept in the HTTP header.
|
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
|
#### Authentication
|
||||||
|
|
||||||
By default this will serve files without needing a login.
|
By default this will serve files without needing a login.
|
||||||
|
@ -81,6 +86,7 @@ certificate authority certificate.
|
||||||
// Options contains options for the http Server
|
// Options contains options for the http Server
|
||||||
type Options struct {
|
type Options struct {
|
||||||
ListenAddr string // Port to listen on
|
ListenAddr string // Port to listen on
|
||||||
|
Prefix string // prefix to strip from URLs
|
||||||
ServerReadTimeout time.Duration // Timeout for server reading data
|
ServerReadTimeout time.Duration // Timeout for server reading data
|
||||||
ServerWriteTimeout time.Duration // Timeout for server writing data
|
ServerWriteTimeout time.Duration // Timeout for server writing data
|
||||||
MaxHeaderBytes int // Maximum size of request header
|
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")
|
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?
|
// FIXME make a transport?
|
||||||
s.httpServer = &http.Server{
|
s.httpServer = &http.Server{
|
||||||
Addr: s.Opt.ListenAddr,
|
Addr: s.Opt.ListenAddr,
|
||||||
|
@ -299,10 +313,27 @@ func (s *Server) URL() string {
|
||||||
// (i.e. port assigned by operating system)
|
// (i.e. port assigned by operating system)
|
||||||
addr = s.listener.Addr().String()
|
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
|
// UsingAuth returns true if authentication is required
|
||||||
func (s *Server) UsingAuth() bool {
|
func (s *Server) UsingAuth() bool {
|
||||||
return s.usingAuth
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ func newServer(f fs.Fs, opt *httplib.Options) *server {
|
||||||
Server: httplib.NewServer(mux, opt),
|
Server: httplib.NewServer(mux, opt),
|
||||||
f: f,
|
f: f,
|
||||||
}
|
}
|
||||||
mux.HandleFunc("/", s.handler)
|
mux.HandleFunc(s.Opt.Prefix+"/", s.handler)
|
||||||
return s
|
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("Accept-Ranges", "bytes")
|
||||||
w.Header().Set("Server", "rclone/"+fs.Version)
|
w.Header().Set("Server", "rclone/"+fs.Version)
|
||||||
|
|
||||||
path := r.URL.Path
|
path, ok := s.Path(w, r)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
remote := makeRemote(path)
|
remote := makeRemote(path)
|
||||||
fs.Debugf(s.f, "%s %s", r.Method, path)
|
fs.Debugf(s.f, "%s %s", r.Method, path)
|
||||||
|
|
||||||
|
|
|
@ -114,18 +114,22 @@ func newWebDAV(f fs.Fs, opt *httplib.Options) *WebDAV {
|
||||||
f: f,
|
f: f,
|
||||||
vfs: vfs.New(f, &vfsflags.Opt),
|
vfs: vfs.New(f, &vfsflags.Opt),
|
||||||
}
|
}
|
||||||
|
w.Server = httplib.NewServer(http.HandlerFunc(w.handler), opt)
|
||||||
webdavHandler := &webdav.Handler{
|
webdavHandler := &webdav.Handler{
|
||||||
|
Prefix: w.Server.Opt.Prefix,
|
||||||
FileSystem: w,
|
FileSystem: w,
|
||||||
LockSystem: webdav.NewMemLS(),
|
LockSystem: webdav.NewMemLS(),
|
||||||
Logger: w.logRequest, // FIXME
|
Logger: w.logRequest, // FIXME
|
||||||
}
|
}
|
||||||
w.webdavhandler = webdavHandler
|
w.webdavhandler = webdavHandler
|
||||||
w.Server = httplib.NewServer(http.HandlerFunc(w.handler), opt)
|
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WebDAV) handler(rw http.ResponseWriter, r *http.Request) {
|
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, "/")
|
isDir := strings.HasSuffix(urlPath, "/")
|
||||||
remote := strings.Trim(urlPath, "/")
|
remote := strings.Trim(urlPath, "/")
|
||||||
if !disableGETDir && (r.Method == "GET" || r.Method == "HEAD") && isDir {
|
if !disableGETDir && (r.Method == "GET" || r.Method == "HEAD") && isDir {
|
||||||
|
|
Loading…
Reference in a new issue