tailscale/tailfs/tailfsimpl/local_impl.go
Percy Wegmann 50fb8b9123 tailfs: replace webdavfs with reverse proxies
Instead of modeling remote WebDAV servers as actual
webdav.FS instances, we now just proxy traffic to them.
This not only simplifies the code, but it also allows
WebDAV locking to work correctly by making sure locks are
handled by the servers that need to (i.e. the ones actually
serving the files).

Updates tailscale/corp#16827

Signed-off-by: Percy Wegmann <percy@tailscale.com>
2024-02-26 09:30:22 -06:00

94 lines
2.6 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package tailfsimpl provides an implementation of package tailfs.
package tailfsimpl
import (
"log"
"net"
"net/http"
"time"
"tailscale.com/tailfs"
"tailscale.com/tailfs/tailfsimpl/compositedav"
"tailscale.com/tailfs/tailfsimpl/dirfs"
"tailscale.com/types/logger"
)
const (
// statCacheTTL causes the local WebDAV proxy to cache file metadata to
// avoid excessive network roundtrips. This is similar to the
// DirectoryCacheLifetime setting of Windows' built-in SMB client,
// see https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-7/ff686200(v=ws.10)
statCacheTTL = 10 * time.Second
)
// NewFileSystemForLocal starts serving a filesystem for local clients.
// Inbound connections must be handed to HandleConn.
func NewFileSystemForLocal(logf logger.Logf) *FileSystemForLocal {
if logf == nil {
logf = log.Printf
}
fs := &FileSystemForLocal{
logf: logf,
h: &compositedav.Handler{
Logf: logf,
StatCache: &compositedav.StatCache{TTL: statCacheTTL},
},
listener: newConnListener(),
}
fs.startServing()
return fs
}
// FileSystemForLocal is the TailFS filesystem exposed to local clients. It
// provides a unified WebDAV interface to remote TailFS shares on other nodes.
type FileSystemForLocal struct {
logf logger.Logf
h *compositedav.Handler
listener *connListener
}
func (s *FileSystemForLocal) startServing() {
hs := &http.Server{Handler: s.h}
go func() {
err := hs.Serve(s.listener)
if err != nil {
// TODO(oxtoacart): should we panic or something different here?
log.Printf("serve: %v", err)
}
}()
}
// HandleConn handles connections from local WebDAV clients
func (s *FileSystemForLocal) HandleConn(conn net.Conn, remoteAddr net.Addr) error {
return s.listener.HandleConn(conn, remoteAddr)
}
// SetRemotes sets the complete set of remotes on the given tailnet domain
// using a map of name -> url. If transport is specified, that transport
// will be used to connect to these remotes.
func (s *FileSystemForLocal) SetRemotes(domain string, remotes []*tailfs.Remote, transport http.RoundTripper) {
children := make([]*compositedav.Child, 0, len(remotes))
for _, remote := range remotes {
children = append(children, &compositedav.Child{
Child: &dirfs.Child{
Name: remote.Name,
Available: remote.Available,
},
BaseURL: remote.URL,
Transport: transport,
})
}
s.h.SetChildren(domain, children...)
}
// Close() stops serving the WebDAV content
func (s *FileSystemForLocal) Close() error {
err := s.listener.Close()
s.h.Close()
return err
}