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:
This commit is contained in:
Florian Klink 2024-05-24 11:45:10 +02:00 committed by Nick Craig-Wood
parent 70e8ad456f
commit 3ffa47ea16
4 changed files with 60 additions and 3 deletions

View File

@ -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'

View File

@ -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",

View File

@ -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.

View File

@ -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