2016-12-06 18:00:24 +00:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
2020-05-05 09:41:08 +01:00
|
|
|
"context"
|
2016-12-06 18:00:24 +00:00
|
|
|
"flag"
|
2022-08-20 16:38:02 +02:00
|
|
|
"io"
|
2016-12-06 18:00:24 +00:00
|
|
|
"net/http"
|
2022-08-20 16:38:02 +02:00
|
|
|
"os"
|
2023-02-23 09:55:00 +00:00
|
|
|
"path/filepath"
|
2016-12-06 18:00:24 +00:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2019-07-28 18:47:38 +01:00
|
|
|
_ "github.com/rclone/rclone/backend/local"
|
2023-02-23 09:55:00 +00:00
|
|
|
"github.com/rclone/rclone/cmd/serve/proxy/proxyflags"
|
2019-07-28 18:47:38 +01:00
|
|
|
"github.com/rclone/rclone/fs"
|
|
|
|
"github.com/rclone/rclone/fs/filter"
|
2022-11-08 07:49:19 -04:00
|
|
|
libhttp "github.com/rclone/rclone/lib/http"
|
2016-12-06 18:00:24 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2018-02-19 14:02:19 +00:00
|
|
|
var (
|
|
|
|
updateGolden = flag.Bool("updategolden", false, "update golden files for regression test")
|
|
|
|
)
|
2016-12-06 18:00:24 +00:00
|
|
|
|
|
|
|
const (
|
2019-05-01 11:16:22 +01:00
|
|
|
testBindAddress = "localhost:0"
|
2023-02-23 09:55:00 +00:00
|
|
|
testUser = "user"
|
|
|
|
testPass = "pass"
|
2020-05-08 11:15:21 -04:00
|
|
|
testTemplate = "testdata/golden/testindex.html"
|
2016-12-06 18:00:24 +00:00
|
|
|
)
|
|
|
|
|
2023-02-23 09:55:00 +00:00
|
|
|
func start(ctx context.Context, t *testing.T, f fs.Fs) (s *HTTP, testURL string) {
|
2022-11-08 07:49:19 -04:00
|
|
|
opts := Options{
|
2022-11-08 08:03:26 -04:00
|
|
|
HTTP: libhttp.DefaultCfg(),
|
2022-11-08 07:49:19 -04:00
|
|
|
Template: libhttp.TemplateConfig{
|
|
|
|
Path: testTemplate,
|
|
|
|
},
|
2021-04-19 22:14:21 -07:00
|
|
|
}
|
2022-11-08 07:49:19 -04:00
|
|
|
opts.HTTP.ListenAddr = []string{testBindAddress}
|
2023-02-23 09:55:00 +00:00
|
|
|
if proxyflags.Opt.AuthProxy == "" {
|
|
|
|
opts.Auth.BasicUser = testUser
|
|
|
|
opts.Auth.BasicPass = testPass
|
|
|
|
}
|
2022-11-08 07:49:19 -04:00
|
|
|
|
|
|
|
s, err := run(ctx, f, opts)
|
|
|
|
require.NoError(t, err, "failed to start server")
|
|
|
|
|
|
|
|
urls := s.server.URLs()
|
|
|
|
require.Len(t, urls, 1, "expected one URL")
|
|
|
|
|
|
|
|
testURL = urls[0]
|
2016-12-06 18:00:24 +00:00
|
|
|
|
|
|
|
// try to connect to the test server
|
|
|
|
pause := time.Millisecond
|
build: modernize Go usage
This commit modernizes Go usage. This was done with:
go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...
Then files needed to be `go fmt`ed and a few comments needed to be
restored.
The modernizations include replacing
- if/else conditional assignment by a call to the built-in min or max functions added in go1.21
- sort.Slice(x, func(i, j int) bool) { return s[i] < s[j] } by a call to slices.Sort(s), added in go1.21
- interface{} by the 'any' type added in go1.18
- append([]T(nil), s...) by slices.Clone(s) or slices.Concat(s), added in go1.21
- loop around an m[k]=v map update by a call to one of the Collect, Copy, Clone, or Insert functions from the maps package, added in go1.21
- []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...), added in go1.19
- append(s[:i], s[i+1]...) by slices.Delete(s, i, i+1), added in go1.21
- a 3-clause for i := 0; i < n; i++ {} loop by for i := range n {}, added in go1.22
2025-02-26 21:08:12 +00:00
|
|
|
for range 10 {
|
2019-05-01 11:16:22 +01:00
|
|
|
resp, err := http.Head(testURL)
|
2016-12-06 18:00:24 +00:00
|
|
|
if err == nil {
|
2019-05-01 11:16:22 +01:00
|
|
|
_ = resp.Body.Close()
|
2016-12-06 18:00:24 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
// t.Logf("couldn't connect, sleeping for %v: %v", pause, err)
|
|
|
|
time.Sleep(pause)
|
|
|
|
pause *= 2
|
|
|
|
}
|
|
|
|
t.Fatal("couldn't connect to server")
|
2023-02-23 09:55:00 +00:00
|
|
|
|
|
|
|
return s, testURL
|
2016-12-06 18:00:24 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 09:41:08 +01:00
|
|
|
var (
|
|
|
|
datedObject = "two.txt"
|
|
|
|
expectedTime = time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC)
|
|
|
|
)
|
|
|
|
|
2016-12-06 18:00:24 +00:00
|
|
|
// check body against the file, or re-write body if -updategolden is
|
|
|
|
// set.
|
|
|
|
func checkGolden(t *testing.T, fileName string, got []byte) {
|
|
|
|
if *updateGolden {
|
|
|
|
t.Logf("Updating golden file %q", fileName)
|
2022-08-20 16:38:02 +02:00
|
|
|
err := os.WriteFile(fileName, got, 0666)
|
2016-12-06 18:00:24 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
} else {
|
2022-08-20 16:38:02 +02:00
|
|
|
want, err := os.ReadFile(fileName)
|
2016-12-06 18:00:24 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
wants := strings.Split(string(want), "\n")
|
|
|
|
gots := strings.Split(string(got), "\n")
|
|
|
|
assert.Equal(t, wants, gots, fileName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-23 09:55:00 +00:00
|
|
|
func testGET(t *testing.T, useProxy bool) {
|
|
|
|
ctx := context.Background()
|
|
|
|
// ci := fs.GetConfig(ctx)
|
|
|
|
// ci.LogLevel = fs.LogLevelDebug
|
|
|
|
|
|
|
|
// exclude files called hidden.txt and directories called hidden
|
|
|
|
fi := filter.GetConfig(ctx)
|
|
|
|
require.NoError(t, fi.AddRule("- hidden.txt"))
|
|
|
|
require.NoError(t, fi.AddRule("- hidden/**"))
|
|
|
|
|
|
|
|
var f fs.Fs
|
|
|
|
if useProxy {
|
|
|
|
// the backend config will be made by the proxy
|
|
|
|
prog, err := filepath.Abs("../servetest/proxy_code.go")
|
|
|
|
require.NoError(t, err)
|
|
|
|
files, err := filepath.Abs("testdata/files")
|
|
|
|
require.NoError(t, err)
|
|
|
|
cmd := "go run " + prog + " " + files
|
|
|
|
|
|
|
|
// FIXME this is untidy setting a global variable!
|
|
|
|
proxyflags.Opt.AuthProxy = cmd
|
|
|
|
defer func() {
|
|
|
|
proxyflags.Opt.AuthProxy = ""
|
|
|
|
}()
|
|
|
|
|
|
|
|
f = nil
|
|
|
|
} else {
|
|
|
|
// Create a test Fs
|
|
|
|
var err error
|
|
|
|
f, err = fs.NewFs(context.Background(), "testdata/files")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// set date of datedObject to expectedTime
|
|
|
|
obj, err := f.NewObject(context.Background(), datedObject)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, obj.SetModTime(context.Background(), expectedTime))
|
|
|
|
}
|
|
|
|
|
|
|
|
s, testURL := start(ctx, t, f)
|
|
|
|
defer func() {
|
|
|
|
assert.NoError(t, s.server.Shutdown())
|
|
|
|
}()
|
|
|
|
|
2016-12-06 18:00:24 +00:00
|
|
|
for _, test := range []struct {
|
|
|
|
URL string
|
|
|
|
Status int
|
|
|
|
Golden string
|
|
|
|
Method string
|
2017-10-27 20:56:31 +01:00
|
|
|
Range string
|
2016-12-06 18:00:24 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
URL: "",
|
|
|
|
Status: http.StatusOK,
|
|
|
|
Golden: "testdata/golden/index.html",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "notfound",
|
|
|
|
Status: http.StatusNotFound,
|
|
|
|
Golden: "testdata/golden/notfound.html",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "dirnotfound/",
|
|
|
|
Status: http.StatusNotFound,
|
|
|
|
Golden: "testdata/golden/dirnotfound.html",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "hidden/",
|
|
|
|
Status: http.StatusNotFound,
|
|
|
|
Golden: "testdata/golden/hiddendir.html",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "one%25.txt",
|
|
|
|
Status: http.StatusOK,
|
|
|
|
Golden: "testdata/golden/one.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "hidden.txt",
|
|
|
|
Status: http.StatusNotFound,
|
|
|
|
Golden: "testdata/golden/hidden.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "three/",
|
|
|
|
Status: http.StatusOK,
|
|
|
|
Golden: "testdata/golden/three.html",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "three/a.txt",
|
|
|
|
Status: http.StatusOK,
|
|
|
|
Golden: "testdata/golden/a.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "",
|
|
|
|
Method: "HEAD",
|
|
|
|
Status: http.StatusOK,
|
|
|
|
Golden: "testdata/golden/indexhead.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "one%25.txt",
|
|
|
|
Method: "HEAD",
|
|
|
|
Status: http.StatusOK,
|
|
|
|
Golden: "testdata/golden/onehead.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "",
|
|
|
|
Method: "POST",
|
|
|
|
Status: http.StatusMethodNotAllowed,
|
|
|
|
Golden: "testdata/golden/indexpost.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "one%25.txt",
|
|
|
|
Method: "POST",
|
|
|
|
Status: http.StatusMethodNotAllowed,
|
|
|
|
Golden: "testdata/golden/onepost.txt",
|
|
|
|
},
|
2017-10-27 20:56:31 +01:00
|
|
|
{
|
|
|
|
URL: "two.txt",
|
|
|
|
Status: http.StatusOK,
|
|
|
|
Golden: "testdata/golden/two.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "two.txt",
|
|
|
|
Status: http.StatusPartialContent,
|
|
|
|
Range: "bytes=2-5",
|
|
|
|
Golden: "testdata/golden/two2-5.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "two.txt",
|
|
|
|
Status: http.StatusPartialContent,
|
|
|
|
Range: "bytes=0-6",
|
|
|
|
Golden: "testdata/golden/two-6.txt",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
URL: "two.txt",
|
|
|
|
Status: http.StatusPartialContent,
|
|
|
|
Range: "bytes=3-",
|
|
|
|
Golden: "testdata/golden/two3-.txt",
|
|
|
|
},
|
2016-12-06 18:00:24 +00:00
|
|
|
} {
|
|
|
|
method := test.Method
|
|
|
|
if method == "" {
|
|
|
|
method = "GET"
|
|
|
|
}
|
|
|
|
req, err := http.NewRequest(method, testURL+test.URL, nil)
|
|
|
|
require.NoError(t, err)
|
2017-10-27 20:56:31 +01:00
|
|
|
if test.Range != "" {
|
|
|
|
req.Header.Add("Range", test.Range)
|
|
|
|
}
|
2023-02-23 09:55:00 +00:00
|
|
|
req.SetBasicAuth(testUser, testPass)
|
2016-12-06 18:00:24 +00:00
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, test.Status, resp.StatusCode, test.Golden)
|
2022-08-20 16:38:02 +02:00
|
|
|
body, err := io.ReadAll(resp.Body)
|
2016-12-06 18:00:24 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-08 20:17:24 -04:00
|
|
|
// Check we got a Last-Modified header and that it is a valid date
|
2020-05-05 09:41:08 +01:00
|
|
|
if test.Status == http.StatusOK || test.Status == http.StatusPartialContent {
|
|
|
|
lastModified := resp.Header.Get("Last-Modified")
|
|
|
|
assert.NotEqual(t, "", lastModified, test.Golden)
|
|
|
|
modTime, err := http.ParseTime(lastModified)
|
|
|
|
assert.NoError(t, err, test.Golden)
|
|
|
|
// check the actual date on our special file
|
|
|
|
if test.URL == datedObject {
|
|
|
|
assert.Equal(t, expectedTime, modTime, test.Golden)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-06 18:00:24 +00:00
|
|
|
checkGolden(t, test.Golden, body)
|
|
|
|
}
|
|
|
|
}
|
2023-02-23 09:55:00 +00:00
|
|
|
|
|
|
|
func TestGET(t *testing.T) {
|
|
|
|
testGET(t, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAuthProxy(t *testing.T) {
|
|
|
|
testGET(t, true)
|
|
|
|
}
|