From 3ffa47ea166805c3f93803f44c9a71d4453d7ddb Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Fri, 24 May 2024 11:45:10 +0200 Subject: [PATCH] webdav: add --webdav-unix-socket-path to connect to a unix socket This adds a new optional parameter to the backend, to specify a path to a unix domain socket to connect to, instead the specified URL. The URL itself is still used for the rest of the HTTP client, allowing host and subpath to stay intact. This allows using rclone with the webdav backend to connect to a WebDAV server provided at a Unix Domain socket: rclone serve webdav --addr unix:///tmp/my.socket remote:path rclone --webdav-unix-socket /tmp/my.socket --webdav-url http://localhost lsf :webdav: --- backend/webdav/webdav.go | 12 ++++++++++-- cmd/serve/webdav/webdav.go | 13 +++++++++++++ docs/content/webdav.md | 11 +++++++++++ fs/fshttp/http.go | 27 ++++++++++++++++++++++++++- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/backend/webdav/webdav.go b/backend/webdav/webdav.go index 4611e3370..f3e2ee905 100644 --- a/backend/webdav/webdav.go +++ b/backend/webdav/webdav.go @@ -159,7 +159,9 @@ Set to 0 to disable chunked uploading. Help: "Exclude ownCloud mounted storages", Advanced: true, Default: false, - }}, + }, + fshttp.UnixSocketConfig, + }, }) } @@ -177,6 +179,7 @@ type Options struct { ChunkSize fs.SizeSuffix `config:"nextcloud_chunk_size"` ExcludeShares bool `config:"owncloud_exclude_shares"` ExcludeMounts bool `config:"owncloud_exclude_mounts"` + UnixSocket string `config:"unix_socket"` } // Fs represents a remote webdav @@ -458,7 +461,12 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e precision: fs.ModTimeNotSupported, } - client := fshttp.NewClient(ctx) + var client *http.Client + if opt.UnixSocket == "" { + client = fshttp.NewClient(ctx) + } else { + client = fshttp.NewClientWithUnixSocket(ctx, opt.UnixSocket) + } if opt.Vendor == "sharepoint-ntlm" { // Disable transparent HTTP/2 support as per https://golang.org/pkg/net/http/ , // otherwise any connection to IIS 10.0 fails with 'stream error: stream ID 39; HTTP_1_1_REQUIRED' diff --git a/cmd/serve/webdav/webdav.go b/cmd/serve/webdav/webdav.go index c28b13e69..622575e8d 100644 --- a/cmd/serve/webdav/webdav.go +++ b/cmd/serve/webdav/webdav.go @@ -115,6 +115,19 @@ Create a new DWORD BasicAuthLevel with value 2. https://learn.microsoft.com/en-us/office/troubleshoot/powerpoint/office-opens-blank-from-sharepoint +### Serving over a unix socket + +You can serve the webdav on a unix socket like this: + + rclone serve webdav --addr unix:///tmp/my.socket remote:path + +and connect to it like this using rclone and the webdav backend: + + rclone --webdav-unix-socket /tmp/my.socket --webdav-url http://localhost lsf :webdav: + +Note that there is no authentication on http protocol - this is expected to be +done by the permissions on the socket. + ` + libhttp.Help(flagPrefix) + libhttp.TemplateHelp(flagPrefix) + libhttp.AuthHelp(flagPrefix) + vfs.Help() + proxy.Help, Annotations: map[string]string{ "versionIntroduced": "v1.39", diff --git a/docs/content/webdav.md b/docs/content/webdav.md index 458a3488c..c42fcd57b 100644 --- a/docs/content/webdav.md +++ b/docs/content/webdav.md @@ -294,6 +294,17 @@ Properties: - Type: bool - Default: false +#### --webdav-unix-socket + +Path to a unix domain socket to dial to, instead of opening a TCP connection directly + +Properties: + +- Config: unix_socket +- Env Var: RCLONE_WEBDAV_UNIX_SOCKET +- Type: string +- Required: false + #### --webdav-description Description of the remote. diff --git a/fs/fshttp/http.go b/fs/fshttp/http.go index be9e3aee3..5a3fea073 100644 --- a/fs/fshttp/http.go +++ b/fs/fshttp/http.go @@ -31,6 +31,14 @@ var ( noTransport = new(sync.Once) cookieJar, _ = cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) logMutex sync.Mutex + + // UnixSocketConfig describes the option to configure the path to a unix domain socket to connect to + UnixSocketConfig = fs.Option{ + Name: "unix_socket", + Help: "Path to a unix domain socket to dial to, instead of opening a TCP connection directly", + Advanced: true, + Default: "", + } ) // ResetTransport resets the existing transport, allowing it to take new settings. @@ -134,9 +142,15 @@ func NewTransport(ctx context.Context) http.RoundTripper { // NewClient returns an http.Client with the correct timeouts func NewClient(ctx context.Context) *http.Client { + return NewClientCustom(ctx, nil) +} + +// NewClientCustom returns an http.Client with the correct timeouts. +// It allows customizing the transport, using NewTransportCustom. +func NewClientCustom(ctx context.Context, customize func(*http.Transport)) *http.Client { ci := fs.GetConfig(ctx) client := &http.Client{ - Transport: NewTransport(ctx), + Transport: NewTransportCustom(ctx, customize), } if ci.Cookie { client.Jar = cookieJar @@ -144,6 +158,17 @@ func NewClient(ctx context.Context) *http.Client { return client } +// NewClientWithUnixSocket returns an http.Client with the correct timeout. +// It internally uses NewClientCustom with a custom dialer connecting to +// the specified unix domain socket. +func NewClientWithUnixSocket(ctx context.Context, path string) *http.Client { + return NewClientCustom(ctx, func(t *http.Transport) { + t.DialContext = func(reqCtx context.Context, network, addr string) (net.Conn, error) { + return NewDialer(ctx).DialContext(reqCtx, "unix", path) + } + }) +} + // Transport is our http Transport which wraps an http.Transport // * Sets the User Agent // * Does logging