github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/http/http_internal_test.go (about) 1 package http 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "net/http/httptest" 9 "net/url" 10 "os" 11 "path/filepath" 12 "sort" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/rclone/rclone/fs" 18 "github.com/rclone/rclone/fs/config" 19 "github.com/rclone/rclone/fs/config/configmap" 20 "github.com/rclone/rclone/fstest" 21 "github.com/rclone/rclone/lib/rest" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 ) 25 26 var ( 27 remoteName = "TestHTTP" 28 testPath = "test" 29 filesPath = filepath.Join(testPath, "files") 30 headers = []string{"X-Potato", "sausage", "X-Rhubarb", "cucumber"} 31 ) 32 33 // prepareServer the test server and return a function to tidy it up afterwards 34 func prepareServer(t *testing.T) (configmap.Simple, func()) { 35 // file server for test/files 36 fileServer := http.FileServer(http.Dir(filesPath)) 37 38 // test the headers are there then pass on to fileServer 39 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 40 what := fmt.Sprintf("%s %s: Header ", r.Method, r.URL.Path) 41 assert.Equal(t, headers[1], r.Header.Get(headers[0]), what+headers[0]) 42 assert.Equal(t, headers[3], r.Header.Get(headers[2]), what+headers[2]) 43 fileServer.ServeHTTP(w, r) 44 }) 45 46 // Make the test server 47 ts := httptest.NewServer(handler) 48 49 // Configure the remote 50 config.LoadConfig() 51 // fs.Config.LogLevel = fs.LogLevelDebug 52 // fs.Config.DumpHeaders = true 53 // fs.Config.DumpBodies = true 54 // config.FileSet(remoteName, "type", "http") 55 // config.FileSet(remoteName, "url", ts.URL) 56 57 m := configmap.Simple{ 58 "type": "http", 59 "url": ts.URL, 60 "headers": strings.Join(headers, ","), 61 } 62 63 // return a function to tidy up 64 return m, ts.Close 65 } 66 67 // prepare the test server and return a function to tidy it up afterwards 68 func prepare(t *testing.T) (fs.Fs, func()) { 69 m, tidy := prepareServer(t) 70 71 // Instantiate it 72 f, err := NewFs(remoteName, "", m) 73 require.NoError(t, err) 74 75 return f, tidy 76 } 77 78 func testListRoot(t *testing.T, f fs.Fs, noSlash bool) { 79 entries, err := f.List(context.Background(), "") 80 require.NoError(t, err) 81 82 sort.Sort(entries) 83 84 require.Equal(t, 4, len(entries)) 85 86 e := entries[0] 87 assert.Equal(t, "four", e.Remote()) 88 assert.Equal(t, int64(-1), e.Size()) 89 _, ok := e.(fs.Directory) 90 assert.True(t, ok) 91 92 e = entries[1] 93 assert.Equal(t, "one%.txt", e.Remote()) 94 assert.Equal(t, int64(6), e.Size()) 95 _, ok = e.(*Object) 96 assert.True(t, ok) 97 98 e = entries[2] 99 assert.Equal(t, "three", e.Remote()) 100 assert.Equal(t, int64(-1), e.Size()) 101 _, ok = e.(fs.Directory) 102 assert.True(t, ok) 103 104 e = entries[3] 105 assert.Equal(t, "two.html", e.Remote()) 106 if noSlash { 107 assert.Equal(t, int64(-1), e.Size()) 108 _, ok = e.(fs.Directory) 109 assert.True(t, ok) 110 } else { 111 assert.Equal(t, int64(41), e.Size()) 112 _, ok = e.(*Object) 113 assert.True(t, ok) 114 } 115 } 116 117 func TestListRoot(t *testing.T) { 118 f, tidy := prepare(t) 119 defer tidy() 120 testListRoot(t, f, false) 121 } 122 123 func TestListRootNoSlash(t *testing.T) { 124 f, tidy := prepare(t) 125 f.(*Fs).opt.NoSlash = true 126 defer tidy() 127 128 testListRoot(t, f, true) 129 } 130 131 func TestListSubDir(t *testing.T) { 132 f, tidy := prepare(t) 133 defer tidy() 134 135 entries, err := f.List(context.Background(), "three") 136 require.NoError(t, err) 137 138 sort.Sort(entries) 139 140 assert.Equal(t, 1, len(entries)) 141 142 e := entries[0] 143 assert.Equal(t, "three/underthree.txt", e.Remote()) 144 assert.Equal(t, int64(9), e.Size()) 145 _, ok := e.(*Object) 146 assert.True(t, ok) 147 } 148 149 func TestNewObject(t *testing.T) { 150 f, tidy := prepare(t) 151 defer tidy() 152 153 o, err := f.NewObject(context.Background(), "four/under four.txt") 154 require.NoError(t, err) 155 156 assert.Equal(t, "four/under four.txt", o.Remote()) 157 assert.Equal(t, int64(9), o.Size()) 158 _, ok := o.(*Object) 159 assert.True(t, ok) 160 161 // Test the time is correct on the object 162 163 tObj := o.ModTime(context.Background()) 164 165 fi, err := os.Stat(filepath.Join(filesPath, "four", "under four.txt")) 166 require.NoError(t, err) 167 tFile := fi.ModTime() 168 169 fstest.AssertTimeEqualWithPrecision(t, o.Remote(), tFile, tObj, time.Second) 170 171 // check object not found 172 o, err = f.NewObject(context.Background(), "not found.txt") 173 assert.Nil(t, o) 174 assert.Equal(t, fs.ErrorObjectNotFound, err) 175 } 176 177 func TestOpen(t *testing.T) { 178 f, tidy := prepare(t) 179 defer tidy() 180 181 o, err := f.NewObject(context.Background(), "four/under four.txt") 182 require.NoError(t, err) 183 184 // Test normal read 185 fd, err := o.Open(context.Background()) 186 require.NoError(t, err) 187 data, err := ioutil.ReadAll(fd) 188 require.NoError(t, err) 189 require.NoError(t, fd.Close()) 190 assert.Equal(t, "beetroot\n", string(data)) 191 192 // Test with range request 193 fd, err = o.Open(context.Background(), &fs.RangeOption{Start: 1, End: 5}) 194 require.NoError(t, err) 195 data, err = ioutil.ReadAll(fd) 196 require.NoError(t, err) 197 require.NoError(t, fd.Close()) 198 assert.Equal(t, "eetro", string(data)) 199 } 200 201 func TestMimeType(t *testing.T) { 202 f, tidy := prepare(t) 203 defer tidy() 204 205 o, err := f.NewObject(context.Background(), "four/under four.txt") 206 require.NoError(t, err) 207 208 do, ok := o.(fs.MimeTyper) 209 require.True(t, ok) 210 assert.Equal(t, "text/plain; charset=utf-8", do.MimeType(context.Background())) 211 } 212 213 func TestIsAFileRoot(t *testing.T) { 214 m, tidy := prepareServer(t) 215 defer tidy() 216 217 f, err := NewFs(remoteName, "one%.txt", m) 218 assert.Equal(t, err, fs.ErrorIsFile) 219 220 testListRoot(t, f, false) 221 } 222 223 func TestIsAFileSubDir(t *testing.T) { 224 m, tidy := prepareServer(t) 225 defer tidy() 226 227 f, err := NewFs(remoteName, "three/underthree.txt", m) 228 assert.Equal(t, err, fs.ErrorIsFile) 229 230 entries, err := f.List(context.Background(), "") 231 require.NoError(t, err) 232 233 sort.Sort(entries) 234 235 assert.Equal(t, 1, len(entries)) 236 237 e := entries[0] 238 assert.Equal(t, "underthree.txt", e.Remote()) 239 assert.Equal(t, int64(9), e.Size()) 240 _, ok := e.(*Object) 241 assert.True(t, ok) 242 } 243 244 func TestParseName(t *testing.T) { 245 for i, test := range []struct { 246 base string 247 val string 248 wantErr error 249 want string 250 }{ 251 {"http://example.com/", "potato", nil, "potato"}, 252 {"http://example.com/dir/", "potato", nil, "potato"}, 253 {"http://example.com/dir/", "potato?download=true", errFoundQuestionMark, ""}, 254 {"http://example.com/dir/", "../dir/potato", nil, "potato"}, 255 {"http://example.com/dir/", "..", errNotUnderRoot, ""}, 256 {"http://example.com/dir/", "http://example.com/", errNotUnderRoot, ""}, 257 {"http://example.com/dir/", "http://example.com/dir/", errNameIsEmpty, ""}, 258 {"http://example.com/dir/", "http://example.com/dir/potato", nil, "potato"}, 259 {"http://example.com/dir/", "https://example.com/dir/potato", errSchemeMismatch, ""}, 260 {"http://example.com/dir/", "http://notexample.com/dir/potato", errHostMismatch, ""}, 261 {"http://example.com/dir/", "/dir/", errNameIsEmpty, ""}, 262 {"http://example.com/dir/", "/dir/potato", nil, "potato"}, 263 {"http://example.com/dir/", "subdir/potato", errNameContainsSlash, ""}, 264 {"http://example.com/dir/", "With percent %25.txt", nil, "With percent %.txt"}, 265 {"http://example.com/dir/", "With colon :", errURLJoinFailed, ""}, 266 {"http://example.com/dir/", rest.URLPathEscape("With colon :"), nil, "With colon :"}, 267 {"http://example.com/Dungeons%20%26%20Dragons/", "/Dungeons%20&%20Dragons/D%26D%20Basic%20%28Holmes%2C%20B%2C%20X%2C%20BECMI%29/", nil, "D&D Basic (Holmes, B, X, BECMI)/"}, 268 } { 269 u, err := url.Parse(test.base) 270 require.NoError(t, err) 271 got, gotErr := parseName(u, test.val) 272 what := fmt.Sprintf("test %d base=%q, val=%q", i, test.base, test.val) 273 assert.Equal(t, test.wantErr, gotErr, what) 274 assert.Equal(t, test.want, got, what) 275 } 276 } 277 278 // Load HTML from the file given and parse it, checking it against the entries passed in 279 func parseHTML(t *testing.T, name string, base string, want []string) { 280 in, err := os.Open(filepath.Join(testPath, "index_files", name)) 281 require.NoError(t, err) 282 defer func() { 283 require.NoError(t, in.Close()) 284 }() 285 if base == "" { 286 base = "http://example.com/" 287 } 288 u, err := url.Parse(base) 289 require.NoError(t, err) 290 entries, err := parse(u, in) 291 require.NoError(t, err) 292 assert.Equal(t, want, entries) 293 } 294 295 func TestParseEmpty(t *testing.T) { 296 parseHTML(t, "empty.html", "", []string(nil)) 297 } 298 299 func TestParseApache(t *testing.T) { 300 parseHTML(t, "apache.html", "http://example.com/nick/pub/", []string{ 301 "SWIG-embed.tar.gz", 302 "avi2dvd.pl", 303 "cambert.exe", 304 "cambert.gz", 305 "fedora_demo.gz", 306 "gchq-challenge/", 307 "mandelterm/", 308 "pgp-key.txt", 309 "pymath/", 310 "rclone", 311 "readdir.exe", 312 "rush_hour_solver_cut_down.py", 313 "snake-puzzle/", 314 "stressdisk/", 315 "timer-test", 316 "words-to-regexp.pl", 317 "Now 100% better.mp3", 318 "Now better.mp3", 319 }) 320 } 321 322 func TestParseMemstore(t *testing.T) { 323 parseHTML(t, "memstore.html", "", []string{ 324 "test/", 325 "v1.35/", 326 "v1.36-01-g503cd84/", 327 "rclone-beta-latest-freebsd-386.zip", 328 "rclone-beta-latest-freebsd-amd64.zip", 329 "rclone-beta-latest-windows-amd64.zip", 330 }) 331 } 332 333 func TestParseNginx(t *testing.T) { 334 parseHTML(t, "nginx.html", "", []string{ 335 "deltas/", 336 "objects/", 337 "refs/", 338 "state/", 339 "config", 340 "summary", 341 }) 342 } 343 344 func TestParseCaddy(t *testing.T) { 345 parseHTML(t, "caddy.html", "", []string{ 346 "mimetype.zip", 347 "rclone-delete-empty-dirs.py", 348 "rclone-show-empty-dirs.py", 349 "stat-windows-386.zip", 350 "v1.36-155-gcf29ee8b-team-driveβ/", 351 "v1.36-156-gca76b3fb-team-driveβ/", 352 "v1.36-156-ge1f0e0f5-team-driveβ/", 353 "v1.36-22-g06ea13a-ssh-agentβ/", 354 }) 355 }