serve http: add serve rc interface

This commit is contained in:
Nick Craig-Wood 2025-03-28 12:20:34 +00:00
parent 0b7be6ffb9
commit 780f4040ea
2 changed files with 62 additions and 12 deletions

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net"
"net/http" "net/http"
"os" "os"
"path" "path"
@ -22,6 +23,7 @@ import (
"github.com/rclone/rclone/fs/accounting" "github.com/rclone/rclone/fs/accounting"
"github.com/rclone/rclone/fs/config/configstruct" "github.com/rclone/rclone/fs/config/configstruct"
"github.com/rclone/rclone/fs/config/flags" "github.com/rclone/rclone/fs/config/flags"
"github.com/rclone/rclone/fs/rc"
libhttp "github.com/rclone/rclone/lib/http" libhttp "github.com/rclone/rclone/lib/http"
"github.com/rclone/rclone/lib/http/serve" "github.com/rclone/rclone/lib/http/serve"
"github.com/rclone/rclone/lib/systemd" "github.com/rclone/rclone/lib/systemd"
@ -68,6 +70,28 @@ func init() {
vfsflags.AddFlags(flagSet) vfsflags.AddFlags(flagSet)
proxyflags.AddFlags(flagSet) proxyflags.AddFlags(flagSet)
cmdserve.Command.AddCommand(Command) cmdserve.Command.AddCommand(Command)
cmdserve.AddRc("http", func(ctx context.Context, f fs.Fs, in rc.Params) (cmdserve.Handle, error) {
// Read VFS Opts
var vfsOpt = vfscommon.Opt // set default opts
err := configstruct.SetAny(in, &vfsOpt)
if err != nil {
return nil, err
}
// Read Proxy Opts
var proxyOpt = proxy.Opt // set default opts
err = configstruct.SetAny(in, &proxyOpt)
if err != nil {
return nil, err
}
// Read opts
var opt = Opt // set default opts
err = configstruct.SetAny(in, &opt)
if err != nil {
return nil, err
}
// Create server
return newServer(ctx, f, &opt, &vfsOpt, &proxyOpt)
})
} }
// Command definition for cobra // Command definition for cobra
@ -101,14 +125,12 @@ control the stats printing.
} }
cmd.Run(false, true, command, func() error { cmd.Run(false, true, command, func() error {
s, err := run(context.Background(), f, Opt) s, err := newServer(context.Background(), f, &Opt, &vfscommon.Opt, &proxy.Opt)
if err != nil { if err != nil {
fs.Fatal(nil, fmt.Sprint(err)) fs.Fatal(nil, fmt.Sprint(err))
} }
defer systemd.Notify()() defer systemd.Notify()()
s.server.Wait() return s.Serve()
return nil
}) })
}, },
} }
@ -148,19 +170,19 @@ func (s *HTTP) auth(user, pass string) (value any, err error) {
return VFS, err return VFS, err
} }
func run(ctx context.Context, f fs.Fs, opt Options) (s *HTTP, err error) { func newServer(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Options, proxyOpt *proxy.Options) (s *HTTP, err error) {
s = &HTTP{ s = &HTTP{
f: f, f: f,
ctx: ctx, ctx: ctx,
opt: opt, opt: *opt,
} }
if proxy.Opt.AuthProxy != "" { if proxyOpt.AuthProxy != "" {
s.proxy = proxy.New(ctx, &proxy.Opt, &vfscommon.Opt) s.proxy = proxy.New(ctx, proxyOpt, vfsOpt)
// override auth // override auth
s.opt.Auth.CustomAuthFn = s.auth s.opt.Auth.CustomAuthFn = s.auth
} else { } else {
s._vfs = vfs.New(f, &vfscommon.Opt) s._vfs = vfs.New(f, vfsOpt)
} }
s.server, err = libhttp.NewServer(ctx, s.server, err = libhttp.NewServer(ctx,
@ -180,11 +202,26 @@ func run(ctx context.Context, f fs.Fs, opt Options) (s *HTTP, err error) {
router.Get("/*", s.handler) router.Get("/*", s.handler)
router.Head("/*", s.handler) router.Head("/*", s.handler)
s.server.Serve()
return s, nil return s, nil
} }
// Serve HTTP until the server is shutdown
func (s *HTTP) Serve() error {
s.server.Serve()
s.server.Wait()
return nil
}
// Addr returns the first address of the server
func (s *HTTP) Addr() net.Addr {
return s.server.Addr()
}
// Shutdown the server
func (s *HTTP) Shutdown() error {
return s.server.Shutdown()
}
// handler reads incoming requests and dispatches them // handler reads incoming requests and dispatches them
func (s *HTTP) handler(w http.ResponseWriter, r *http.Request) { func (s *HTTP) handler(w http.ResponseWriter, r *http.Request) {
isDir := strings.HasSuffix(r.URL.Path, "/") isDir := strings.HasSuffix(r.URL.Path, "/")

View File

@ -13,9 +13,12 @@ import (
_ "github.com/rclone/rclone/backend/local" _ "github.com/rclone/rclone/backend/local"
"github.com/rclone/rclone/cmd/serve/proxy" "github.com/rclone/rclone/cmd/serve/proxy"
"github.com/rclone/rclone/cmd/serve/servetest"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/filter" "github.com/rclone/rclone/fs/filter"
"github.com/rclone/rclone/fs/rc"
libhttp "github.com/rclone/rclone/lib/http" libhttp "github.com/rclone/rclone/lib/http"
"github.com/rclone/rclone/vfs/vfscommon"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -44,8 +47,11 @@ func start(ctx context.Context, t *testing.T, f fs.Fs) (s *HTTP, testURL string)
opts.Auth.BasicPass = testPass opts.Auth.BasicPass = testPass
} }
s, err := run(ctx, f, opts) s, err := newServer(ctx, f, &opts, &vfscommon.Opt, &proxy.Opt)
require.NoError(t, err, "failed to start server") require.NoError(t, err, "failed to start server")
go func() {
require.NoError(t, s.Serve())
}()
urls := s.server.URLs() urls := s.server.URLs()
require.Len(t, urls, 1, "expected one URL") require.Len(t, urls, 1, "expected one URL")
@ -267,3 +273,10 @@ func TestGET(t *testing.T) {
func TestAuthProxy(t *testing.T) { func TestAuthProxy(t *testing.T) {
testGET(t, true) testGET(t, true)
} }
func TestRc(t *testing.T) {
servetest.TestRc(t, rc.Params{
"type": "http",
"vfs_cache_mode": "off",
})
}