2017-06-19 16:36:14 +00:00
package http
import (
2019-06-17 08:34:30 +00:00
"context"
2017-06-19 16:36:14 +00:00
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"sort"
2019-08-12 14:29:35 +00:00
"strings"
2017-06-19 16:36:14 +00:00
"testing"
"time"
2019-07-28 17:47:38 +00:00
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config"
"github.com/rclone/rclone/fs/config/configmap"
"github.com/rclone/rclone/fstest"
"github.com/rclone/rclone/lib/rest"
2017-06-19 16:36:14 +00:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
remoteName = "TestHTTP"
testPath = "test"
filesPath = filepath . Join ( testPath , "files" )
2019-08-12 14:29:35 +00:00
headers = [ ] string { "X-Potato" , "sausage" , "X-Rhubarb" , "cucumber" }
2017-06-19 16:36:14 +00:00
)
// prepareServer the test server and return a function to tidy it up afterwards
2018-05-14 17:06:57 +00:00
func prepareServer ( t * testing . T ) ( configmap . Simple , func ( ) ) {
2017-06-19 16:36:14 +00:00
// file server for test/files
fileServer := http . FileServer ( http . Dir ( filesPath ) )
2019-08-12 14:29:35 +00:00
// 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 )
} )
2017-06-19 16:36:14 +00:00
// Make the test server
2019-08-12 14:29:35 +00:00
ts := httptest . NewServer ( handler )
2017-06-19 16:36:14 +00:00
// Configure the remote
2018-01-12 16:30:54 +00:00
config . LoadConfig ( )
2017-06-19 16:36:14 +00:00
// fs.Config.LogLevel = fs.LogLevelDebug
// fs.Config.DumpHeaders = true
// fs.Config.DumpBodies = true
2018-05-14 17:06:57 +00:00
// config.FileSet(remoteName, "type", "http")
// config.FileSet(remoteName, "url", ts.URL)
m := configmap . Simple {
2019-08-12 14:29:35 +00:00
"type" : "http" ,
"url" : ts . URL ,
"headers" : strings . Join ( headers , "," ) ,
2018-05-14 17:06:57 +00:00
}
2017-06-19 16:36:14 +00:00
// return a function to tidy up
2018-05-14 17:06:57 +00:00
return m , ts . Close
2017-06-19 16:36:14 +00:00
}
// prepare the test server and return a function to tidy it up afterwards
func prepare ( t * testing . T ) ( fs . Fs , func ( ) ) {
2018-05-14 17:06:57 +00:00
m , tidy := prepareServer ( t )
2017-06-19 16:36:14 +00:00
// Instantiate it
2018-05-14 17:06:57 +00:00
f , err := NewFs ( remoteName , "" , m )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
return f , tidy
}
2019-02-08 13:58:47 +00:00
func testListRoot ( t * testing . T , f fs . Fs , noSlash bool ) {
2019-06-17 08:34:30 +00:00
entries , err := f . List ( context . Background ( ) , "" )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
sort . Sort ( entries )
require . Equal ( t , 4 , len ( entries ) )
e := entries [ 0 ]
assert . Equal ( t , "four" , e . Remote ( ) )
2017-06-30 12:37:29 +00:00
assert . Equal ( t , int64 ( - 1 ) , e . Size ( ) )
_ , ok := e . ( fs . Directory )
2017-06-19 16:36:14 +00:00
assert . True ( t , ok )
e = entries [ 1 ]
2017-08-02 12:19:36 +00:00
assert . Equal ( t , "one%.txt" , e . Remote ( ) )
2017-06-19 16:36:14 +00:00
assert . Equal ( t , int64 ( 6 ) , e . Size ( ) )
_ , ok = e . ( * Object )
assert . True ( t , ok )
e = entries [ 2 ]
assert . Equal ( t , "three" , e . Remote ( ) )
2017-06-30 12:37:29 +00:00
assert . Equal ( t , int64 ( - 1 ) , e . Size ( ) )
_ , ok = e . ( fs . Directory )
2017-06-19 16:36:14 +00:00
assert . True ( t , ok )
e = entries [ 3 ]
assert . Equal ( t , "two.html" , e . Remote ( ) )
2019-02-08 13:58:47 +00:00
if noSlash {
assert . Equal ( t , int64 ( - 1 ) , e . Size ( ) )
_ , ok = e . ( fs . Directory )
assert . True ( t , ok )
} else {
assert . Equal ( t , int64 ( 41 ) , e . Size ( ) )
_ , ok = e . ( * Object )
assert . True ( t , ok )
}
2017-06-19 16:36:14 +00:00
}
func TestListRoot ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
2019-02-08 13:58:47 +00:00
testListRoot ( t , f , false )
}
func TestListRootNoSlash ( t * testing . T ) {
f , tidy := prepare ( t )
f . ( * Fs ) . opt . NoSlash = true
defer tidy ( )
testListRoot ( t , f , true )
2017-06-19 16:36:14 +00:00
}
func TestListSubDir ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
2019-06-17 08:34:30 +00:00
entries , err := f . List ( context . Background ( ) , "three" )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
sort . Sort ( entries )
assert . Equal ( t , 1 , len ( entries ) )
e := entries [ 0 ]
assert . Equal ( t , "three/underthree.txt" , e . Remote ( ) )
assert . Equal ( t , int64 ( 9 ) , e . Size ( ) )
_ , ok := e . ( * Object )
assert . True ( t , ok )
}
func TestNewObject ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
2019-06-17 08:34:30 +00:00
o , err := f . NewObject ( context . Background ( ) , "four/under four.txt" )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
2017-07-31 22:15:31 +00:00
assert . Equal ( t , "four/under four.txt" , o . Remote ( ) )
2017-06-19 16:36:14 +00:00
assert . Equal ( t , int64 ( 9 ) , o . Size ( ) )
_ , ok := o . ( * Object )
assert . True ( t , ok )
// Test the time is correct on the object
2019-06-17 08:34:30 +00:00
tObj := o . ModTime ( context . Background ( ) )
2017-06-19 16:36:14 +00:00
2017-07-31 22:15:31 +00:00
fi , err := os . Stat ( filepath . Join ( filesPath , "four" , "under four.txt" ) )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
tFile := fi . ModTime ( )
dt , ok := fstest . CheckTimeEqualWithPrecision ( tObj , tFile , time . Second )
assert . True ( t , ok , fmt . Sprintf ( "%s: Modification time difference too big |%s| > %s (%s vs %s) (precision %s)" , o . Remote ( ) , dt , time . Second , tObj , tFile , time . Second ) )
2018-12-04 17:40:44 +00:00
// check object not found
2019-06-17 08:34:30 +00:00
o , err = f . NewObject ( context . Background ( ) , "not found.txt" )
2018-12-04 17:40:44 +00:00
assert . Nil ( t , o )
assert . Equal ( t , fs . ErrorObjectNotFound , err )
2017-06-19 16:36:14 +00:00
}
func TestOpen ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
2019-06-17 08:34:30 +00:00
o , err := f . NewObject ( context . Background ( ) , "four/under four.txt" )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
// Test normal read
2019-06-17 08:34:30 +00:00
fd , err := o . Open ( context . Background ( ) )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
data , err := ioutil . ReadAll ( fd )
2018-05-04 14:19:50 +00:00
require . NoError ( t , err )
2017-06-19 16:36:14 +00:00
require . NoError ( t , fd . Close ( ) )
assert . Equal ( t , "beetroot\n" , string ( data ) )
// Test with range request
2019-06-17 08:34:30 +00:00
fd , err = o . Open ( context . Background ( ) , & fs . RangeOption { Start : 1 , End : 5 } )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
data , err = ioutil . ReadAll ( fd )
2018-05-04 14:19:50 +00:00
require . NoError ( t , err )
2017-06-19 16:36:14 +00:00
require . NoError ( t , fd . Close ( ) )
assert . Equal ( t , "eetro" , string ( data ) )
}
func TestMimeType ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
2019-06-17 08:34:30 +00:00
o , err := f . NewObject ( context . Background ( ) , "four/under four.txt" )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
do , ok := o . ( fs . MimeTyper )
require . True ( t , ok )
2019-06-17 08:34:30 +00:00
assert . Equal ( t , "text/plain; charset=utf-8" , do . MimeType ( context . Background ( ) ) )
2017-06-19 16:36:14 +00:00
}
func TestIsAFileRoot ( t * testing . T ) {
2018-05-14 17:06:57 +00:00
m , tidy := prepareServer ( t )
2017-06-19 16:36:14 +00:00
defer tidy ( )
2018-05-14 17:06:57 +00:00
f , err := NewFs ( remoteName , "one%.txt" , m )
2017-06-19 16:36:14 +00:00
assert . Equal ( t , err , fs . ErrorIsFile )
2019-02-08 13:58:47 +00:00
testListRoot ( t , f , false )
2017-06-19 16:36:14 +00:00
}
func TestIsAFileSubDir ( t * testing . T ) {
2018-05-14 17:06:57 +00:00
m , tidy := prepareServer ( t )
2017-06-19 16:36:14 +00:00
defer tidy ( )
2018-05-14 17:06:57 +00:00
f , err := NewFs ( remoteName , "three/underthree.txt" , m )
2017-06-19 16:36:14 +00:00
assert . Equal ( t , err , fs . ErrorIsFile )
2019-06-17 08:34:30 +00:00
entries , err := f . List ( context . Background ( ) , "" )
2017-06-19 16:36:14 +00:00
require . NoError ( t , err )
sort . Sort ( entries )
assert . Equal ( t , 1 , len ( entries ) )
e := entries [ 0 ]
assert . Equal ( t , "underthree.txt" , e . Remote ( ) )
assert . Equal ( t , int64 ( 9 ) , e . Size ( ) )
_ , ok := e . ( * Object )
assert . True ( t , ok )
}
func TestParseName ( t * testing . T ) {
for i , test := range [ ] struct {
2018-02-14 11:26:37 +00:00
base string
val string
wantErr error
want string
2017-06-19 16:36:14 +00:00
} {
2018-02-14 11:26:37 +00:00
{ "http://example.com/" , "potato" , nil , "potato" } ,
{ "http://example.com/dir/" , "potato" , nil , "potato" } ,
{ "http://example.com/dir/" , "potato?download=true" , errFoundQuestionMark , "" } ,
{ "http://example.com/dir/" , "../dir/potato" , nil , "potato" } ,
{ "http://example.com/dir/" , ".." , errNotUnderRoot , "" } ,
{ "http://example.com/dir/" , "http://example.com/" , errNotUnderRoot , "" } ,
{ "http://example.com/dir/" , "http://example.com/dir/" , errNameIsEmpty , "" } ,
{ "http://example.com/dir/" , "http://example.com/dir/potato" , nil , "potato" } ,
{ "http://example.com/dir/" , "https://example.com/dir/potato" , errSchemeMismatch , "" } ,
{ "http://example.com/dir/" , "http://notexample.com/dir/potato" , errHostMismatch , "" } ,
{ "http://example.com/dir/" , "/dir/" , errNameIsEmpty , "" } ,
{ "http://example.com/dir/" , "/dir/potato" , nil , "potato" } ,
{ "http://example.com/dir/" , "subdir/potato" , errNameContainsSlash , "" } ,
{ "http://example.com/dir/" , "With percent %25.txt" , nil , "With percent %.txt" } ,
{ "http://example.com/dir/" , "With colon :" , errURLJoinFailed , "" } ,
{ "http://example.com/dir/" , rest . URLPathEscape ( "With colon :" ) , nil , "With colon :" } ,
{ "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)/" } ,
2017-06-19 16:36:14 +00:00
} {
u , err := url . Parse ( test . base )
require . NoError ( t , err )
2018-02-14 11:26:37 +00:00
got , gotErr := parseName ( u , test . val )
2017-06-19 16:36:14 +00:00
what := fmt . Sprintf ( "test %d base=%q, val=%q" , i , test . base , test . val )
2018-02-14 11:26:37 +00:00
assert . Equal ( t , test . wantErr , gotErr , what )
2017-06-19 16:36:14 +00:00
assert . Equal ( t , test . want , got , what )
}
}
// Load HTML from the file given and parse it, checking it against the entries passed in
func parseHTML ( t * testing . T , name string , base string , want [ ] string ) {
in , err := os . Open ( filepath . Join ( testPath , "index_files" , name ) )
require . NoError ( t , err )
defer func ( ) {
require . NoError ( t , in . Close ( ) )
} ( )
if base == "" {
base = "http://example.com/"
}
u , err := url . Parse ( base )
require . NoError ( t , err )
entries , err := parse ( u , in )
require . NoError ( t , err )
assert . Equal ( t , want , entries )
}
func TestParseEmpty ( t * testing . T ) {
parseHTML ( t , "empty.html" , "" , [ ] string ( nil ) )
}
func TestParseApache ( t * testing . T ) {
parseHTML ( t , "apache.html" , "http://example.com/nick/pub/" , [ ] string {
"SWIG-embed.tar.gz" ,
"avi2dvd.pl" ,
"cambert.exe" ,
"cambert.gz" ,
"fedora_demo.gz" ,
"gchq-challenge/" ,
"mandelterm/" ,
"pgp-key.txt" ,
"pymath/" ,
"rclone" ,
"readdir.exe" ,
"rush_hour_solver_cut_down.py" ,
"snake-puzzle/" ,
"stressdisk/" ,
"timer-test" ,
"words-to-regexp.pl" ,
2017-08-02 12:19:36 +00:00
"Now 100% better.mp3" ,
"Now better.mp3" ,
2017-06-19 16:36:14 +00:00
} )
}
func TestParseMemstore ( t * testing . T ) {
parseHTML ( t , "memstore.html" , "" , [ ] string {
"test/" ,
"v1.35/" ,
"v1.36-01-g503cd84/" ,
"rclone-beta-latest-freebsd-386.zip" ,
"rclone-beta-latest-freebsd-amd64.zip" ,
"rclone-beta-latest-windows-amd64.zip" ,
} )
}
func TestParseNginx ( t * testing . T ) {
parseHTML ( t , "nginx.html" , "" , [ ] string {
"deltas/" ,
"objects/" ,
"refs/" ,
"state/" ,
"config" ,
"summary" ,
} )
}
func TestParseCaddy ( t * testing . T ) {
parseHTML ( t , "caddy.html" , "" , [ ] string {
"mimetype.zip" ,
"rclone-delete-empty-dirs.py" ,
"rclone-show-empty-dirs.py" ,
"stat-windows-386.zip" ,
"v1.36-155-gcf29ee8b-team-driveβ/" ,
"v1.36-156-gca76b3fb-team-driveβ/" ,
"v1.36-156-ge1f0e0f5-team-driveβ/" ,
"v1.36-22-g06ea13a-ssh-agentβ/" ,
} )
}