2017-09-23 13:56:58 +08:00
|
|
|
// Copyright 2015 Light Code Labs, LLC
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2016-06-06 12:39:23 +08:00
|
|
|
package browse
|
|
|
|
|
|
|
|
import (
|
2017-03-16 00:17:12 +08:00
|
|
|
"context"
|
2016-06-06 12:39:23 +08:00
|
|
|
"encoding/json"
|
2017-08-26 21:14:40 +08:00
|
|
|
"fmt"
|
2017-08-09 01:59:01 +08:00
|
|
|
"io/ioutil"
|
2016-06-06 12:39:23 +08:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-08-26 06:52:44 +08:00
|
|
|
"runtime"
|
2016-06-06 12:39:23 +08:00
|
|
|
"sort"
|
2017-08-09 01:59:01 +08:00
|
|
|
"strings"
|
2016-06-06 12:39:23 +08:00
|
|
|
"testing"
|
|
|
|
"text/template"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
2016-12-18 02:30:08 +08:00
|
|
|
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
2016-06-06 12:39:23 +08:00
|
|
|
)
|
|
|
|
|
2017-08-09 01:59:01 +08:00
|
|
|
const testDirPrefix = "caddy_browse_test"
|
|
|
|
|
2016-06-06 12:39:23 +08:00
|
|
|
func TestSort(t *testing.T) {
|
|
|
|
// making up []fileInfo with bogus values;
|
|
|
|
// to be used to make up our "listing"
|
|
|
|
fileInfos := []FileInfo{
|
|
|
|
{
|
|
|
|
Name: "fizz",
|
|
|
|
Size: 4,
|
|
|
|
ModTime: time.Now().AddDate(-1, 1, 0),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "buzz",
|
|
|
|
Size: 2,
|
|
|
|
ModTime: time.Now().AddDate(0, -3, 3),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "bazz",
|
|
|
|
Size: 1,
|
|
|
|
ModTime: time.Now().AddDate(0, -2, -23),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "jazz",
|
|
|
|
Size: 3,
|
|
|
|
ModTime: time.Now(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
listing := Listing{
|
|
|
|
Name: "foobar",
|
|
|
|
Path: "/fizz/buzz",
|
|
|
|
CanGoUp: false,
|
|
|
|
Items: fileInfos,
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort by name
|
|
|
|
listing.Sort = "name"
|
|
|
|
listing.applySort()
|
|
|
|
if !sort.IsSorted(byName(listing)) {
|
|
|
|
t.Errorf("The listing isn't name sorted: %v", listing.Items)
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort by size
|
|
|
|
listing.Sort = "size"
|
|
|
|
listing.applySort()
|
|
|
|
if !sort.IsSorted(bySize(listing)) {
|
|
|
|
t.Errorf("The listing isn't size sorted: %v", listing.Items)
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort by Time
|
|
|
|
listing.Sort = "time"
|
|
|
|
listing.applySort()
|
|
|
|
if !sort.IsSorted(byTime(listing)) {
|
|
|
|
t.Errorf("The listing isn't time sorted: %v", listing.Items)
|
|
|
|
}
|
|
|
|
|
2017-04-03 03:38:14 +08:00
|
|
|
// sort by name dir first
|
|
|
|
listing.Sort = "namedirfirst"
|
|
|
|
listing.applySort()
|
|
|
|
if !sort.IsSorted(byNameDirFirst(listing)) {
|
|
|
|
t.Errorf("The listing isn't namedirfirst sorted: %v", listing.Items)
|
|
|
|
}
|
|
|
|
|
2016-06-06 12:39:23 +08:00
|
|
|
// reverse by name
|
|
|
|
listing.Sort = "name"
|
|
|
|
listing.Order = "desc"
|
|
|
|
listing.applySort()
|
|
|
|
if !isReversed(byName(listing)) {
|
|
|
|
t.Errorf("The listing isn't reversed by name: %v", listing.Items)
|
|
|
|
}
|
|
|
|
|
|
|
|
// reverse by size
|
|
|
|
listing.Sort = "size"
|
|
|
|
listing.Order = "desc"
|
|
|
|
listing.applySort()
|
|
|
|
if !isReversed(bySize(listing)) {
|
|
|
|
t.Errorf("The listing isn't reversed by size: %v", listing.Items)
|
|
|
|
}
|
|
|
|
|
|
|
|
// reverse by time
|
|
|
|
listing.Sort = "time"
|
|
|
|
listing.Order = "desc"
|
|
|
|
listing.applySort()
|
|
|
|
if !isReversed(byTime(listing)) {
|
|
|
|
t.Errorf("The listing isn't reversed by time: %v", listing.Items)
|
|
|
|
}
|
2017-04-03 03:38:14 +08:00
|
|
|
|
|
|
|
// reverse by name dir first
|
|
|
|
listing.Sort = "namedirfirst"
|
|
|
|
listing.Order = "desc"
|
|
|
|
listing.applySort()
|
|
|
|
if !isReversed(byNameDirFirst(listing)) {
|
|
|
|
t.Errorf("The listing isn't reversed by namedirfirst: %v", listing.Items)
|
|
|
|
}
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestBrowseHTTPMethods(t *testing.T) {
|
|
|
|
tmpl, err := template.ParseFiles("testdata/photos.tpl")
|
|
|
|
if err != nil {
|
2016-09-06 00:20:34 +08:00
|
|
|
t.Fatalf("An error occurred while parsing the template: %v", err)
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
b := Browse{
|
|
|
|
Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
|
|
|
return http.StatusTeapot, nil // not t.Fatalf, or we will not see what other methods yield
|
|
|
|
}),
|
|
|
|
Configs: []Config{
|
|
|
|
{
|
|
|
|
PathScope: "/photos",
|
2016-12-18 02:30:08 +08:00
|
|
|
Fs: staticfiles.FileServer{
|
|
|
|
Root: http.Dir("./testdata"),
|
|
|
|
},
|
|
|
|
Template: tmpl,
|
2016-06-06 12:39:23 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
for method, expected := range map[string]int{
|
|
|
|
http.MethodGet: http.StatusOK,
|
|
|
|
http.MethodHead: http.StatusOK,
|
|
|
|
http.MethodOptions: http.StatusNotImplemented,
|
|
|
|
"PROPFIND": http.StatusNotImplemented,
|
|
|
|
} {
|
|
|
|
req, err := http.NewRequest(method, "/photos/", nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test: Could not create HTTP request: %v", err)
|
|
|
|
}
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
ctx := context.WithValue(req.Context(), httpserver.OriginalURLCtxKey, *req.URL)
|
|
|
|
req = req.WithContext(ctx)
|
2016-06-06 12:39:23 +08:00
|
|
|
|
|
|
|
code, _ := b.ServeHTTP(rec, req)
|
|
|
|
if code != expected {
|
|
|
|
t.Errorf("Wrong status with HTTP Method %s: expected %d, got %d", method, expected, code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBrowseTemplate(t *testing.T) {
|
|
|
|
tmpl, err := template.ParseFiles("testdata/photos.tpl")
|
|
|
|
if err != nil {
|
2016-09-06 00:20:34 +08:00
|
|
|
t.Fatalf("An error occurred while parsing the template: %v", err)
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
b := Browse{
|
|
|
|
Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
|
|
|
t.Fatalf("Next shouldn't be called")
|
|
|
|
return 0, nil
|
|
|
|
}),
|
|
|
|
Configs: []Config{
|
|
|
|
{
|
|
|
|
PathScope: "/photos",
|
2016-12-18 02:30:08 +08:00
|
|
|
Fs: staticfiles.FileServer{
|
|
|
|
Root: http.Dir("./testdata"),
|
|
|
|
Hide: []string{"photos/hidden.html"},
|
|
|
|
},
|
|
|
|
Template: tmpl,
|
2016-06-06 12:39:23 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", "/photos/", nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test: Could not create HTTP request: %v", err)
|
|
|
|
}
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
ctx := context.WithValue(req.Context(), httpserver.OriginalURLCtxKey, *req.URL)
|
|
|
|
req = req.WithContext(ctx)
|
2016-06-06 12:39:23 +08:00
|
|
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
code, _ := b.ServeHTTP(rec, req)
|
|
|
|
if code != http.StatusOK {
|
|
|
|
t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, code)
|
|
|
|
}
|
|
|
|
|
|
|
|
respBody := rec.Body.String()
|
|
|
|
expectedBody := `<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Template</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>Header</h1>
|
|
|
|
|
|
|
|
<h1>/photos/</h1>
|
|
|
|
|
2017-03-25 12:54:16 +08:00
|
|
|
<a href="./test1/">test1</a><br>
|
|
|
|
|
2016-06-06 12:39:23 +08:00
|
|
|
<a href="./test.html">test.html</a><br>
|
|
|
|
|
|
|
|
<a href="./test2.html">test2.html</a><br>
|
|
|
|
|
|
|
|
<a href="./test3.html">test3.html</a><br>
|
|
|
|
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
`
|
|
|
|
|
|
|
|
if respBody != expectedBody {
|
2017-03-25 12:54:16 +08:00
|
|
|
t.Fatalf("Expected body: '%v' got: '%v'", expectedBody, respBody)
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBrowseJson(t *testing.T) {
|
|
|
|
b := Browse{
|
|
|
|
Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
2017-09-08 21:19:52 +08:00
|
|
|
t.Fatalf("Next shouldn't be called: %s", r.URL)
|
2016-06-06 12:39:23 +08:00
|
|
|
return 0, nil
|
|
|
|
}),
|
|
|
|
Configs: []Config{
|
|
|
|
{
|
|
|
|
PathScope: "/photos/",
|
2016-12-18 02:30:08 +08:00
|
|
|
Fs: staticfiles.FileServer{
|
|
|
|
Root: http.Dir("./testdata"),
|
|
|
|
},
|
2016-06-06 12:39:23 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
//Getting the listing from the ./testdata/photos, the listing returned will be used to validate test results
|
|
|
|
testDataPath := filepath.Join("./testdata", "photos")
|
|
|
|
file, err := os.Open(testDataPath)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsPermission(err) {
|
|
|
|
t.Fatalf("Os Permission Error")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
files, err := file.Readdir(-1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Unable to Read Contents of the directory")
|
|
|
|
}
|
|
|
|
var fileinfos []FileInfo
|
|
|
|
|
|
|
|
for i, f := range files {
|
|
|
|
name := f.Name()
|
|
|
|
|
|
|
|
// Tests fail in CI environment because all file mod times are the same for
|
|
|
|
// some reason, making the sorting unpredictable. To hack around this,
|
|
|
|
// we ensure here that each file has a different mod time.
|
|
|
|
chTime := f.ModTime().UTC().Add(-(time.Duration(i) * time.Second))
|
|
|
|
if err := os.Chtimes(filepath.Join(testDataPath, name), chTime, chTime); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.IsDir() {
|
|
|
|
name += "/"
|
|
|
|
}
|
|
|
|
|
|
|
|
url := url.URL{Path: "./" + name}
|
|
|
|
|
|
|
|
fileinfos = append(fileinfos, FileInfo{
|
|
|
|
IsDir: f.IsDir(),
|
|
|
|
Name: f.Name(),
|
|
|
|
Size: f.Size(),
|
|
|
|
URL: url.String(),
|
|
|
|
ModTime: chTime,
|
|
|
|
Mode: f.Mode(),
|
|
|
|
})
|
|
|
|
}
|
2017-04-03 03:38:14 +08:00
|
|
|
|
|
|
|
// Test that sort=name returns correct listing.
|
|
|
|
|
2016-06-06 12:39:23 +08:00
|
|
|
listing := Listing{Items: fileinfos} // this listing will be used for validation inside the tests
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
QueryURL string
|
|
|
|
SortBy string
|
|
|
|
OrderBy string
|
|
|
|
Limit int
|
|
|
|
shouldErr bool
|
|
|
|
expectedResult []FileInfo
|
|
|
|
}{
|
|
|
|
//test case 1: testing for default sort and order and without the limit parameter, default sort is by name and the default order is ascending
|
|
|
|
//without the limit query entire listing will be produced
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?sort=name", "", "", -1, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 2: limit is set to 1, orderBy and sortBy is default
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?limit=1&sort=name", "", "", 1, false, listing.Items[:1]},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 3 : if the listing request is bigger than total size of listing then it should return everything
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?limit=100000000&sort=name", "", "", 100000000, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 4 : testing for negative limit
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?limit=-1&sort=name", "", "", -1, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 5 : testing with limit set to -1 and order set to descending
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?limit=-1&order=desc&sort=name", "", "desc", -1, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 6 : testing with limit set to 2 and order set to descending
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?limit=2&order=desc&sort=name", "", "desc", 2, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 7 : testing with limit set to 3 and order set to descending
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?limit=3&order=desc&sort=name", "", "desc", 3, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 8 : testing with limit set to 3 and order set to ascending
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?limit=3&order=asc&sort=name", "", "asc", 3, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 9 : testing with limit set to 1111111 and order set to ascending
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?limit=1111111&order=asc&sort=name", "", "asc", 1111111, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 10 : testing with limit set to default and order set to ascending and sorting by size
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?order=asc&sort=size&sort=name", "size", "asc", -1, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 11 : testing with limit set to default and order set to ascending and sorting by last modified
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?order=asc&sort=time&sort=name", "time", "asc", -1, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 12 : testing with limit set to 1 and order set to ascending and sorting by last modified
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?order=asc&sort=time&limit=1&sort=name", "time", "asc", 1, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 13 : testing with limit set to -100 and order set to ascending and sorting by last modified
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?order=asc&sort=time&limit=-100&sort=name", "time", "asc", -100, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
//test case 14 : testing with limit set to -100 and order set to ascending and sorting by size
|
2017-04-03 03:38:14 +08:00
|
|
|
{"/?order=asc&sort=size&limit=-100&sort=name", "size", "asc", -100, false, listing.Items},
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
var marsh []byte
|
|
|
|
req, err := http.NewRequest("GET", "/photos"+test.QueryURL, nil)
|
2016-09-06 00:30:46 +08:00
|
|
|
if err != nil && !test.shouldErr {
|
|
|
|
t.Errorf("Test %d errored when making request, but it shouldn't have; got '%v'", i, err)
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
ctx := context.WithValue(req.Context(), httpserver.OriginalURLCtxKey, *req.URL)
|
|
|
|
req = req.WithContext(ctx)
|
2016-06-06 12:39:23 +08:00
|
|
|
|
|
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
code, err := b.ServeHTTP(rec, req)
|
2016-09-06 00:30:46 +08:00
|
|
|
if err == nil && test.shouldErr {
|
|
|
|
t.Errorf("Test %d didn't error, but it should have", i)
|
|
|
|
} else if err != nil && !test.shouldErr {
|
|
|
|
t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
|
|
|
|
}
|
2016-06-06 12:39:23 +08:00
|
|
|
if code != http.StatusOK {
|
|
|
|
t.Fatalf("In test %d: Wrong status, expected %d, got %d", i, http.StatusOK, code)
|
|
|
|
}
|
|
|
|
if rec.HeaderMap.Get("Content-Type") != "application/json; charset=utf-8" {
|
|
|
|
t.Fatalf("Expected Content type to be application/json; charset=utf-8, but got %s ", rec.HeaderMap.Get("Content-Type"))
|
|
|
|
}
|
|
|
|
|
|
|
|
actualJSONResponse := rec.Body.String()
|
|
|
|
copyOflisting := listing
|
|
|
|
if test.SortBy == "" {
|
|
|
|
copyOflisting.Sort = "name"
|
|
|
|
} else {
|
|
|
|
copyOflisting.Sort = test.SortBy
|
|
|
|
}
|
|
|
|
if test.OrderBy == "" {
|
|
|
|
copyOflisting.Order = "asc"
|
|
|
|
} else {
|
|
|
|
copyOflisting.Order = test.OrderBy
|
|
|
|
}
|
|
|
|
|
|
|
|
copyOflisting.applySort()
|
|
|
|
|
|
|
|
limit := test.Limit
|
|
|
|
if limit <= len(copyOflisting.Items) && limit > 0 {
|
|
|
|
marsh, err = json.Marshal(copyOflisting.Items[:limit])
|
|
|
|
} else { // if the 'limit' query is empty, or has the wrong value, list everything
|
|
|
|
marsh, err = json.Marshal(copyOflisting.Items)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Unable to Marshal the listing ")
|
|
|
|
}
|
|
|
|
expectedJSON := string(marsh)
|
|
|
|
|
|
|
|
if actualJSONResponse != expectedJSON {
|
|
|
|
t.Errorf("JSON response doesn't match the expected for test number %d with sort=%s, order=%s\nExpected response %s\nActual response = %s\n",
|
|
|
|
i+1, test.SortBy, test.OrderBy, expectedJSON, actualJSONResponse)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// "sort" package has "IsSorted" function, but no "IsReversed";
|
|
|
|
func isReversed(data sort.Interface) bool {
|
|
|
|
n := data.Len()
|
|
|
|
for i := n - 1; i > 0; i-- {
|
|
|
|
if !data.Less(i, i-1) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
2017-03-16 00:17:12 +08:00
|
|
|
|
|
|
|
func TestBrowseRedirect(t *testing.T) {
|
|
|
|
testCases := []struct {
|
|
|
|
url string
|
|
|
|
statusCode int
|
|
|
|
returnCode int
|
|
|
|
location string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"http://www.example.com/photos",
|
|
|
|
http.StatusMovedPermanently,
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
http.StatusMovedPermanently,
|
2017-03-16 00:17:12 +08:00
|
|
|
"http://www.example.com/photos/",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"/photos",
|
|
|
|
http.StatusMovedPermanently,
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
http.StatusMovedPermanently,
|
2017-03-16 00:17:12 +08:00
|
|
|
"/photos/",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range testCases {
|
|
|
|
b := Browse{
|
|
|
|
Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
|
|
|
t.Fatalf("Test %d - Next shouldn't be called", i)
|
|
|
|
return 0, nil
|
|
|
|
}),
|
|
|
|
Configs: []Config{
|
|
|
|
{
|
|
|
|
PathScope: "/photos",
|
|
|
|
Fs: staticfiles.FileServer{
|
|
|
|
Root: http.Dir("./testdata"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", tc.url, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test %d - could not create HTTP request: %v", i, err)
|
|
|
|
}
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
ctx := context.WithValue(req.Context(), httpserver.OriginalURLCtxKey, *req.URL)
|
|
|
|
req = req.WithContext(ctx)
|
2017-03-16 00:17:12 +08:00
|
|
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
returnCode, _ := b.ServeHTTP(rec, req)
|
|
|
|
if returnCode != tc.returnCode {
|
|
|
|
t.Fatalf("Test %d - wrong return code, expected %d, got %d",
|
|
|
|
i, tc.returnCode, returnCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
if got := rec.Code; got != tc.statusCode {
|
|
|
|
t.Errorf("Test %d - wrong status, expected %d, got %d",
|
|
|
|
i, tc.statusCode, got)
|
|
|
|
}
|
|
|
|
|
|
|
|
if got := rec.Header().Get("Location"); got != tc.location {
|
|
|
|
t.Errorf("Test %d - wrong Location header, expected %s, got %s",
|
|
|
|
i, tc.location, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-09 01:59:01 +08:00
|
|
|
|
|
|
|
func TestDirSymlink(t *testing.T) {
|
2017-08-26 06:52:44 +08:00
|
|
|
if runtime.GOOS == "windows" {
|
2017-08-26 21:14:40 +08:00
|
|
|
// Windows support for symlinks is limited, and we had a hard time getting
|
|
|
|
// all these tests to pass with the permissions of CI; so just skip them
|
|
|
|
fmt.Println("Skipping browse symlink tests on Windows...")
|
|
|
|
return
|
2017-08-26 06:52:44 +08:00
|
|
|
}
|
|
|
|
|
2017-08-09 01:59:01 +08:00
|
|
|
testCases := []struct {
|
|
|
|
source string
|
|
|
|
target string
|
|
|
|
pathScope string
|
|
|
|
url string
|
|
|
|
expectedName string
|
|
|
|
expectedURL string
|
|
|
|
}{
|
|
|
|
// test case can expect a directory "dir" and a symlink to it called "symlink"
|
|
|
|
|
|
|
|
{"dir", "$TMP/rel_symlink_to_dir", "/", "/",
|
|
|
|
"rel_symlink_to_dir", "./rel_symlink_to_dir/"},
|
|
|
|
{"$TMP/dir", "$TMP/abs_symlink_to_dir", "/", "/",
|
|
|
|
"abs_symlink_to_dir", "./abs_symlink_to_dir/"},
|
|
|
|
|
|
|
|
{"../../dir", "$TMP/sub/dir/rel_symlink_to_dir", "/", "/sub/dir/",
|
|
|
|
"rel_symlink_to_dir", "./rel_symlink_to_dir/"},
|
|
|
|
{"$TMP/dir", "$TMP/sub/dir/abs_symlink_to_dir", "/", "/sub/dir/",
|
|
|
|
"abs_symlink_to_dir", "./abs_symlink_to_dir/"},
|
|
|
|
|
|
|
|
{"../../dir", "$TMP/with/scope/rel_symlink_to_dir", "/with/scope", "/with/scope/",
|
|
|
|
"rel_symlink_to_dir", "./rel_symlink_to_dir/"},
|
|
|
|
{"$TMP/dir", "$TMP/with/scope/abs_symlink_to_dir", "/with/scope", "/with/scope/",
|
|
|
|
"abs_symlink_to_dir", "./abs_symlink_to_dir/"},
|
|
|
|
|
|
|
|
{"../../../../dir", "$TMP/with/scope/sub/dir/rel_symlink_to_dir", "/with/scope", "/with/scope/sub/dir/",
|
|
|
|
"rel_symlink_to_dir", "./rel_symlink_to_dir/"},
|
|
|
|
{"$TMP/dir", "$TMP/with/scope/sub/dir/abs_symlink_to_dir", "/with/scope", "/with/scope/sub/dir/",
|
|
|
|
"abs_symlink_to_dir", "./abs_symlink_to_dir/"},
|
|
|
|
|
|
|
|
{"symlink", "$TMP/rel_symlink_to_symlink", "/", "/",
|
|
|
|
"rel_symlink_to_symlink", "./rel_symlink_to_symlink/"},
|
|
|
|
{"$TMP/symlink", "$TMP/abs_symlink_to_symlink", "/", "/",
|
|
|
|
"abs_symlink_to_symlink", "./abs_symlink_to_symlink/"},
|
|
|
|
|
|
|
|
{"../../symlink", "$TMP/sub/dir/rel_symlink_to_symlink", "/", "/sub/dir/",
|
|
|
|
"rel_symlink_to_symlink", "./rel_symlink_to_symlink/"},
|
|
|
|
{"$TMP/symlink", "$TMP/sub/dir/abs_symlink_to_symlink", "/", "/sub/dir/",
|
|
|
|
"abs_symlink_to_symlink", "./abs_symlink_to_symlink/"},
|
|
|
|
|
|
|
|
{"../../symlink", "$TMP/with/scope/rel_symlink_to_symlink", "/with/scope", "/with/scope/",
|
|
|
|
"rel_symlink_to_symlink", "./rel_symlink_to_symlink/"},
|
|
|
|
{"$TMP/symlink", "$TMP/with/scope/abs_symlink_to_symlink", "/with/scope", "/with/scope/",
|
|
|
|
"abs_symlink_to_symlink", "./abs_symlink_to_symlink/"},
|
|
|
|
|
|
|
|
{"../../../../symlink", "$TMP/with/scope/sub/dir/rel_symlink_to_symlink", "/with/scope", "/with/scope/sub/dir/",
|
|
|
|
"rel_symlink_to_symlink", "./rel_symlink_to_symlink/"},
|
|
|
|
{"$TMP/symlink", "$TMP/with/scope/sub/dir/abs_symlink_to_symlink", "/with/scope", "/with/scope/sub/dir/",
|
|
|
|
"abs_symlink_to_symlink", "./abs_symlink_to_symlink/"},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range testCases {
|
|
|
|
func() {
|
|
|
|
tmpdir, err := ioutil.TempDir("", testDirPrefix)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to create test directory: %v", err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
|
|
|
|
if err := os.MkdirAll(filepath.Join(tmpdir, "dir"), 0755); err != nil {
|
|
|
|
t.Fatalf("failed to create test dir 'dir': %v", err)
|
|
|
|
}
|
|
|
|
if err := os.Symlink("dir", filepath.Join(tmpdir, "symlink")); err != nil {
|
|
|
|
t.Fatalf("failed to create test symlink 'symlink': %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
sourceResolved := strings.Replace(tc.source, "$TMP", tmpdir, -1)
|
|
|
|
targetResolved := strings.Replace(tc.target, "$TMP", tmpdir, -1)
|
|
|
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(sourceResolved), 0755); err != nil {
|
|
|
|
t.Fatalf("failed to create source symlink dir: %v", err)
|
|
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(targetResolved), 0755); err != nil {
|
|
|
|
t.Fatalf("failed to create target symlink dir: %v", err)
|
|
|
|
}
|
|
|
|
if err := os.Symlink(sourceResolved, targetResolved); err != nil {
|
|
|
|
t.Fatalf("failed to create test symlink: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
b := Browse{
|
|
|
|
Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
|
|
|
t.Fatalf("Test %d - Next shouldn't be called", i)
|
|
|
|
return 0, nil
|
|
|
|
}),
|
|
|
|
Configs: []Config{
|
|
|
|
{
|
|
|
|
PathScope: tc.pathScope,
|
|
|
|
Fs: staticfiles.FileServer{
|
|
|
|
Root: http.Dir(tmpdir),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", tc.url, nil)
|
|
|
|
req.Header.Add("Accept", "application/json")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test %d - could not create HTTP request: %v", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
returnCode, _ := b.ServeHTTP(rec, req)
|
|
|
|
if returnCode != http.StatusOK {
|
|
|
|
t.Fatalf("Test %d - wrong return code, expected %d, got %d",
|
|
|
|
i, http.StatusOK, returnCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
type jsonEntry struct {
|
|
|
|
Name string
|
|
|
|
IsDir bool
|
|
|
|
IsSymlink bool
|
|
|
|
URL string
|
|
|
|
}
|
|
|
|
var entries []jsonEntry
|
|
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &entries); err != nil {
|
|
|
|
t.Fatalf("Test %d - failed to parse json: %v", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
found := false
|
|
|
|
for _, e := range entries {
|
|
|
|
if e.Name != tc.expectedName {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
found = true
|
|
|
|
if !e.IsDir {
|
2017-08-26 06:52:44 +08:00
|
|
|
t.Errorf("Test %d - expected to be a dir, got %v", i, e.IsDir)
|
2017-08-09 01:59:01 +08:00
|
|
|
}
|
|
|
|
if !e.IsSymlink {
|
2017-08-26 06:52:44 +08:00
|
|
|
t.Errorf("Test %d - expected to be a symlink, got %v", i, e.IsSymlink)
|
2017-08-09 01:59:01 +08:00
|
|
|
}
|
|
|
|
if e.URL != tc.expectedURL {
|
2017-08-26 06:52:44 +08:00
|
|
|
t.Errorf("Test %d - wrong URL, expected %v, got %v", i, tc.expectedURL, e.URL)
|
2017-08-09 01:59:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
2017-08-26 06:52:44 +08:00
|
|
|
t.Errorf("Test %d - failed, could not find name %v", i, tc.expectedName)
|
2017-08-09 01:59:01 +08:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|