diff --git a/backend/webdav/webdav.go b/backend/webdav/webdav.go index ce2fb3ae1..86678bfad 100644 --- a/backend/webdav/webdav.go +++ b/backend/webdav/webdav.go @@ -113,6 +113,21 @@ func init() { Name: config.ConfigEncoding, Help: configEncodingHelp, Advanced: true, + }, { + Name: "headers", + Help: `Set HTTP headers for all transactions + +Use this to set additional HTTP headers for all transactions + +The input format is comma separated list of key,value pairs. Standard +[CSV encoding](https://godoc.org/encoding/csv) may be used. + +For example to set a Cookie use 'Cookie,name=value', or '"Cookie","name=value"'. + +You can set multiple headers, e.g. '"Cookie","name=value","Authorization","xxx"'. +`, + Default: fs.CommaSepList{}, + Advanced: true, }}, }) } @@ -126,6 +141,7 @@ type Options struct { BearerToken string `config:"bearer_token"` BearerTokenCommand string `config:"bearer_token_command"` Enc encoder.MultiEncoder `config:"encoding"` + Headers fs.CommaSepList `config:"headers"` } // Fs represents a remote webdav @@ -359,6 +375,12 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e if err != nil { return nil, err } + + if len(opt.Headers)%2 != 0 { + return nil, errors.New("odd number of headers supplied") + } + fs.Debugf(nil, "found headers: %v", opt.Headers) + rootIsDir := strings.HasSuffix(root, "/") root = strings.Trim(root, "/") @@ -428,6 +450,9 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e return nil, err } } + if opt.Headers != nil { + f.addHeaders(opt.Headers) + } f.srv.SetErrorHandler(errorHandler) err = f.setQuirks(ctx, opt.Vendor) if err != nil { @@ -487,6 +512,15 @@ func (f *Fs) fetchBearerToken(cmd string) (string, error) { return stdoutString, nil } +// Adds the configured headers to the request if any +func (f *Fs) addHeaders(headers fs.CommaSepList) { + for i := 0; i < len(headers); i += 2 { + key := f.opt.Headers[i] + value := f.opt.Headers[i+1] + f.srv.SetHeader(key, value) + } +} + // fetch the bearer token and set it if successful func (f *Fs) fetchAndSetBearerToken() error { if f.opt.BearerTokenCommand == "" { diff --git a/backend/webdav/webdav_internal_test.go b/backend/webdav/webdav_internal_test.go new file mode 100644 index 000000000..8789e8413 --- /dev/null +++ b/backend/webdav/webdav_internal_test.go @@ -0,0 +1,74 @@ +package webdav_test + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/rclone/rclone/backend/webdav" + "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/config/configfile" + "github.com/rclone/rclone/fs/config/configmap" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + remoteName = "TestWebDAV" + headers = []string{"X-Potato", "sausage", "X-Rhubarb", "cucumber"} +) + +// prepareServer the test server and return a function to tidy it up afterwards +// with each request the headers option tests are executed +func prepareServer(t *testing.T) (configmap.Simple, func()) { + // file server + fileServer := http.FileServer(http.Dir("")) + + // test the headers are there then pass on to fileServer + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + what := fmt.Sprintf("%s %s: Header ", r.Method, r.URL.Path) + assert.Equal(t, headers[1], r.Header.Get(headers[0]), what+headers[0]) + assert.Equal(t, headers[3], r.Header.Get(headers[2]), what+headers[2]) + fileServer.ServeHTTP(w, r) + }) + + // Make the test server + ts := httptest.NewServer(handler) + + // Configure the remote + configfile.Install() + + m := configmap.Simple{ + "type": "webdav", + "url": ts.URL, + // add headers to test the headers option + "headers": strings.Join(headers, ","), + } + + // return a function to tidy up + return m, ts.Close +} + +// prepare the test server and return a function to tidy it up afterwards +func prepare(t *testing.T) (fs.Fs, func()) { + m, tidy := prepareServer(t) + + // Instantiate the WebDAV server + f, err := webdav.NewFs(context.Background(), remoteName, "", m) + require.NoError(t, err) + + return f, tidy +} + +// TestHeaders any request will test the headers option +func TestHeaders(t *testing.T) { + f, tidy := prepare(t) + defer tidy() + + // any request will do + _, err := f.Features().About(context.Background()) + require.NoError(t, err) +} diff --git a/docs/content/webdav.md b/docs/content/webdav.md index 7a74123f1..fc3ce5a82 100644 --- a/docs/content/webdav.md +++ b/docs/content/webdav.md @@ -198,6 +198,25 @@ Default encoding is Slash,LtGt,DoubleQuote,Colon,Question,Asterisk,Pipe,Hash,Per - Type: string - Default: "" +#### --webdav-headers + +Set HTTP headers for all transactions + +Use this to set additional HTTP headers for all transactions + +The input format is comma separated list of key,value pairs. Standard +[CSV encoding](https://godoc.org/encoding/csv) may be used. + +For example to set a Cookie use 'Cookie,name=value', or '"Cookie","name=value"'. + +You can set multiple headers, e.g. '"Cookie","name=value","Authorization","xxx"'. + + +- Config: headers +- Env Var: RCLONE_WEBDAV_HEADERS +- Type: CommaSepList +- Default: + {{< rem autogenerated options stop >}} ## Provider notes ##