Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
0890e330
Commit
0890e330
authored
Apr 17, 2016
by
W. Mark Kubacki
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #759 from mholt/paths-cleanup
Tell usage of 'path' from 'filepath' and fix *path checking
parents
b75016e6
c05c5163
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
177 additions
and
140 deletions
+177
-140
caddy/restart.go
caddy/restart.go
+2
-2
caddy/setup/ext.go
caddy/setup/ext.go
+2
-1
middleware/browse/browse.go
middleware/browse/browse.go
+119
-109
middleware/fileserver.go
middleware/fileserver.go
+10
-8
middleware/fileserver_test.go
middleware/fileserver_test.go
+32
-15
server/server.go
server/server.go
+12
-5
No files found.
caddy/restart.go
View file @
0890e330
...
@@ -11,7 +11,7 @@ import (
...
@@ -11,7 +11,7 @@ import (
"net"
"net"
"os"
"os"
"os/exec"
"os/exec"
"path"
"path
/filepath
"
"sync/atomic"
"sync/atomic"
"github.com/mholt/caddy/caddy/https"
"github.com/mholt/caddy/caddy/https"
...
@@ -138,7 +138,7 @@ func Restart(newCaddyfile Input) error {
...
@@ -138,7 +138,7 @@ func Restart(newCaddyfile Input) error {
func
getCertsForNewCaddyfile
(
newCaddyfile
Input
)
error
{
func
getCertsForNewCaddyfile
(
newCaddyfile
Input
)
error
{
// parse the new caddyfile only up to (and including) TLS
// parse the new caddyfile only up to (and including) TLS
// so we can know what we need to get certs for.
// so we can know what we need to get certs for.
configs
,
_
,
_
,
err
:=
loadConfigsUpToIncludingTLS
(
path
.
Base
(
newCaddyfile
.
Path
()),
bytes
.
NewReader
(
newCaddyfile
.
Body
()))
configs
,
_
,
_
,
err
:=
loadConfigsUpToIncludingTLS
(
file
path
.
Base
(
newCaddyfile
.
Path
()),
bytes
.
NewReader
(
newCaddyfile
.
Body
()))
if
err
!=
nil
{
if
err
!=
nil
{
return
errors
.
New
(
"loading Caddyfile: "
+
err
.
Error
())
return
errors
.
New
(
"loading Caddyfile: "
+
err
.
Error
())
}
}
...
...
caddy/setup/ext.go
View file @
0890e330
...
@@ -2,6 +2,7 @@ package setup
...
@@ -2,6 +2,7 @@ package setup
import
(
import
(
"os"
"os"
"path/filepath"
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/extensions"
"github.com/mholt/caddy/middleware/extensions"
...
@@ -47,7 +48,7 @@ func extParse(c *Controller) ([]string, error) {
...
@@ -47,7 +48,7 @@ func extParse(c *Controller) ([]string, error) {
// resourceExists returns true if the file specified at
// resourceExists returns true if the file specified at
// root + path exists; false otherwise.
// root + path exists; false otherwise.
func
resourceExists
(
root
,
path
string
)
bool
{
func
resourceExists
(
root
,
path
string
)
bool
{
_
,
err
:=
os
.
Stat
(
root
+
path
)
_
,
err
:=
os
.
Stat
(
filepath
.
Join
(
root
,
path
)
)
// technically we should use os.IsNotExist(err)
// technically we should use os.IsNotExist(err)
// but we don't handle any other kinds of errors anyway
// but we don't handle any other kinds of errors anyway
return
err
==
nil
return
err
==
nil
...
...
middleware/browse/browse.go
View file @
0890e330
...
@@ -10,6 +10,7 @@ import (
...
@@ -10,6 +10,7 @@ import (
"net/url"
"net/url"
"os"
"os"
"path"
"path"
"path/filepath"
"sort"
"sort"
"strconv"
"strconv"
"strings"
"strings"
...
@@ -173,9 +174,11 @@ func (l Listing) applySort() {
...
@@ -173,9 +174,11 @@ func (l Listing) applySort() {
}
}
func
directoryListing
(
files
[]
os
.
FileInfo
,
r
*
http
.
Request
,
canGoUp
bool
,
root
string
,
ignoreIndexes
bool
,
vars
interface
{})
(
Listing
,
error
)
{
func
directoryListing
(
files
[]
os
.
FileInfo
,
r
*
http
.
Request
,
canGoUp
bool
,
root
string
,
ignoreIndexes
bool
,
vars
interface
{})
(
Listing
,
error
)
{
var
fileinfos
[]
FileInfo
var
(
var
dirCount
,
fileCount
int
fileinfos
[]
FileInfo
var
urlPath
=
r
.
URL
.
Path
dirCount
,
fileCount
int
urlPath
=
r
.
URL
.
Path
)
for
_
,
f
:=
range
files
{
for
_
,
f
:=
range
files
{
name
:=
f
.
Name
()
name
:=
f
.
Name
()
...
@@ -226,143 +229,150 @@ func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root s
...
@@ -226,143 +229,150 @@ func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root s
// ServeHTTP implements the middleware.Handler interface.
// ServeHTTP implements the middleware.Handler interface.
func
(
b
Browse
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
func
(
b
Browse
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
filename
:=
b
.
Root
+
r
.
URL
.
Path
var
bc
*
Config
info
,
err
:=
os
.
Stat
(
filename
)
// See if there's a browse configuration to match the path
if
err
!=
nil
{
for
i
:=
range
b
.
Configs
{
if
middleware
.
Path
(
r
.
URL
.
Path
)
.
Matches
(
b
.
Configs
[
i
]
.
PathScope
)
{
bc
=
&
b
.
Configs
[
i
]
break
}
}
if
bc
==
nil
{
return
b
.
Next
.
ServeHTTP
(
w
,
r
)
return
b
.
Next
.
ServeHTTP
(
w
,
r
)
}
}
// Browse works on existing directories; delegate everything else
requestedFilepath
:=
filepath
.
Join
(
b
.
Root
,
r
.
URL
.
Path
)
info
,
err
:=
os
.
Stat
(
requestedFilepath
)
if
err
!=
nil
{
switch
{
case
os
.
IsPermission
(
err
)
:
return
http
.
StatusForbidden
,
err
case
os
.
IsExist
(
err
)
:
return
http
.
StatusNotFound
,
err
default
:
return
b
.
Next
.
ServeHTTP
(
w
,
r
)
}
}
if
!
info
.
IsDir
()
{
if
!
info
.
IsDir
()
{
return
b
.
Next
.
ServeHTTP
(
w
,
r
)
return
b
.
Next
.
ServeHTTP
(
w
,
r
)
}
}
// See if there's a browse configuration to match the path
// Do not reply to anything else because it might be nonsensical
for
_
,
bc
:=
range
b
.
Configs
{
switch
r
.
Method
{
if
!
middleware
.
Path
(
r
.
URL
.
Path
)
.
Matches
(
bc
.
PathScope
)
{
case
http
.
MethodGet
,
http
.
MethodHead
:
continue
default
:
}
return
http
.
StatusMethodNotAllowed
,
nil
switch
r
.
Method
{
}
case
http
.
MethodGet
,
http
.
MethodHead
:
default
:
return
http
.
StatusMethodNotAllowed
,
nil
}
// Browsing navigation gets messed up if browsing a directory
// Browsing navigation gets messed up if browsing a directory
// that doesn't end in "/" (which it should, anyway)
// that doesn't end in "/" (which it should, anyway)
if
r
.
URL
.
Path
[
len
(
r
.
URL
.
Path
)
-
1
]
!=
'/'
{
if
!
strings
.
HasSuffix
(
r
.
URL
.
Path
,
"/"
)
{
http
.
Redirect
(
w
,
r
,
r
.
URL
.
Path
+
"/"
,
http
.
StatusTemporaryRedirect
)
http
.
Redirect
(
w
,
r
,
r
.
URL
.
Path
+
"/"
,
http
.
StatusTemporaryRedirect
)
return
0
,
nil
return
0
,
nil
}
}
// Load directory contents
// Load directory contents
file
,
err
:=
os
.
Open
(
b
.
Root
+
r
.
URL
.
Path
)
file
,
err
:=
os
.
Open
(
requestedFilepath
)
if
err
!=
nil
{
if
err
!=
nil
{
if
os
.
IsPermission
(
err
)
{
if
os
.
IsPermission
(
err
)
{
return
http
.
StatusForbidden
,
err
return
http
.
StatusForbidden
,
err
}
return
http
.
StatusNotFound
,
err
}
}
defer
file
.
Close
()
return
http
.
StatusInternalServerError
,
err
}
defer
file
.
Close
()
files
,
err
:=
file
.
Readdir
(
-
1
)
files
,
err
:=
file
.
Readdir
(
-
1
)
if
err
!=
nil
{
if
err
!=
nil
{
if
os
.
IsPermission
(
err
)
{
return
http
.
StatusForbidden
,
err
return
http
.
StatusForbidden
,
err
}
}
return
http
.
StatusInternalServerError
,
err
}
// Determine if user can browse up another folder
// Determine if user can browse up another folder
var
canGoUp
bool
var
canGoUp
bool
curPath
:=
strings
.
TrimSuffix
(
r
.
URL
.
Path
,
"/"
)
curPath
:=
strings
.
TrimSuffix
(
r
.
URL
.
Path
,
"/"
)
for
_
,
other
:=
range
b
.
Configs
{
for
_
,
other
:=
range
b
.
Configs
{
if
strings
.
HasPrefix
(
path
.
Dir
(
curPath
),
other
.
PathScope
)
{
if
strings
.
HasPrefix
(
path
.
Dir
(
curPath
),
other
.
PathScope
)
{
canGoUp
=
true
canGoUp
=
true
break
break
}
}
// Assemble listing of directory contents
listing
,
err
:=
directoryListing
(
files
,
r
,
canGoUp
,
b
.
Root
,
b
.
IgnoreIndexes
,
bc
.
Variables
)
if
err
!=
nil
{
// directory isn't browsable
continue
}
}
}
// Assemble listing of directory contents
listing
,
err
:=
directoryListing
(
files
,
r
,
canGoUp
,
b
.
Root
,
b
.
IgnoreIndexes
,
bc
.
Variables
)
if
err
!=
nil
{
// directory isn't browsable
return
http
.
StatusInternalServerError
,
err
}
// Get the query vales and store them in the Listing struct
// Copy the query values into the Listing struct
listing
.
Sort
,
listing
.
Order
=
r
.
URL
.
Query
()
.
Get
(
"sort"
),
r
.
URL
.
Query
()
.
Get
(
"order"
)
listing
.
Sort
,
listing
.
Order
=
r
.
URL
.
Query
()
.
Get
(
"sort"
),
r
.
URL
.
Query
()
.
Get
(
"order"
)
// If the query 'sort' or 'order' is empty, check the cookies
// If the query 'sort' or 'order' is empty, use defaults or any values previously saved in Cookies
if
listing
.
Sort
==
""
{
if
listing
.
Sort
==
""
{
sortCookie
,
sortErr
:=
r
.
Cookie
(
"sort"
)
listing
.
Sort
=
"name"
// if there's no sorting values in the cookies, default to "name" and "asc"
if
sortCookie
,
sortErr
:=
r
.
Cookie
(
"sort"
);
sortErr
==
nil
{
if
sortErr
!=
nil
{
listing
.
Sort
=
sortCookie
.
Value
listing
.
Sort
=
"name"
}
else
{
// if we have values in the cookies, use them
listing
.
Sort
=
sortCookie
.
Value
}
}
else
{
// save the query value of 'sort' and 'order' as cookies
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"sort"
,
Value
:
listing
.
Sort
,
Path
:
"/"
})
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"order"
,
Value
:
listing
.
Order
,
Path
:
"/"
})
}
}
}
else
{
// Save the query value of 'sort' and 'order' as cookies.
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"sort"
,
Value
:
listing
.
Sort
,
Path
:
bc
.
PathScope
,
Secure
:
r
.
TLS
!=
nil
})
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"order"
,
Value
:
listing
.
Order
,
Path
:
bc
.
PathScope
,
Secure
:
r
.
TLS
!=
nil
})
}
if
listing
.
Order
==
""
{
if
listing
.
Order
==
""
{
orderCookie
,
orderErr
:=
r
.
Cookie
(
"order"
)
listing
.
Order
=
"asc"
// if there's no sorting values in the cookies, default to "name" and "asc"
if
orderCookie
,
orderErr
:=
r
.
Cookie
(
"order"
);
orderErr
==
nil
{
if
orderErr
!=
nil
{
listing
.
Order
=
orderCookie
.
Value
listing
.
Order
=
"asc"
}
else
{
// if we have values in the cookies, use them
listing
.
Order
=
orderCookie
.
Value
}
}
else
{
// save the query value of 'sort' and 'order' as cookies
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"order"
,
Value
:
listing
.
Order
,
Path
:
"/"
})
}
}
}
else
{
http
.
SetCookie
(
w
,
&
http
.
Cookie
{
Name
:
"order"
,
Value
:
listing
.
Order
,
Path
:
bc
.
PathScope
,
Secure
:
r
.
TLS
!=
nil
})
}
// Apply the sorting
listing
.
applySort
()
listing
.
applySort
()
var
buf
bytes
.
Buffer
var
buf
bytes
.
Buffer
// Check if we should provide json
// check if we should provide json
acceptHeader
:=
strings
.
Join
(
r
.
Header
[
"Accept"
],
","
)
acceptHeader
:=
strings
.
Join
(
r
.
Header
[
"Accept"
],
","
)
if
strings
.
Contains
(
strings
.
ToLower
(
acceptHeader
),
"application/json"
)
{
if
strings
.
Contains
(
strings
.
ToLower
(
acceptHeader
),
"application/json"
)
{
var
marsh
[]
byte
var
marsh
[]
byte
// Check if we are limited
// check if we are limited
if
limitQuery
:=
r
.
URL
.
Query
()
.
Get
(
"limit"
);
limitQuery
!=
""
{
if
limitQuery
:=
r
.
URL
.
Query
()
.
Get
(
"limit"
);
limitQuery
!=
""
{
limit
,
err
:=
strconv
.
Atoi
(
limitQuery
)
limit
,
err
:=
strconv
.
Atoi
(
limitQuery
)
if
err
!=
nil
{
// if the 'limit' query can't be interpreted as a number, return err
if
err
!=
nil
{
// if the 'limit' query can't be interpreted as a number, return err
return
http
.
StatusBadRequest
,
err
return
http
.
StatusBadRequest
,
err
}
}
// if `limit` is equal or less than len(listing.Items) and bigger than 0, list them
// if `limit` is equal or less than len(listing.Items) and bigger than 0, list them
if
limit
<=
len
(
listing
.
Items
)
&&
limit
>
0
{
if
limit
<=
len
(
listing
.
Items
)
&&
limit
>
0
{
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
[
:
limit
])
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
[
:
limit
])
}
else
{
// if the 'limit' query is empty, or has the wrong value, list everything
}
else
{
// if the 'limit' query is empty, or has the wrong value, list everything
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
)
}
if
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
}
}
else
{
// there's no 'limit' query, list them all
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
)
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
)
if
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
}
}
}
if
err
!=
nil
{
// write the marshaled json to buf
if
_
,
err
=
buf
.
Write
(
marsh
);
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
return
http
.
StatusInternalServerError
,
err
}
}
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json; charset=utf-8"
)
}
else
{
// There's no 'limit' query; list them all
marsh
,
err
=
json
.
Marshal
(
listing
.
Items
)
}
else
{
// there's no 'application/json' in the 'Accept' header, browse normally
err
=
bc
.
Template
.
Execute
(
&
buf
,
listing
)
if
err
!=
nil
{
if
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
return
http
.
StatusInternalServerError
,
err
}
}
w
.
Header
()
.
Set
(
"Content-Type"
,
"text/html; charset=utf-8"
)
}
// Write the marshaled json to buf
if
_
,
err
=
buf
.
Write
(
marsh
);
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
}
}
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json; charset=utf-8"
)
buf
.
WriteTo
(
w
)
}
else
{
// There's no 'application/json' in the 'Accept' header; browse normally
err
=
bc
.
Template
.
Execute
(
&
buf
,
listing
)
if
err
!=
nil
{
return
http
.
StatusInternalServerError
,
err
}
w
.
Header
()
.
Set
(
"Content-Type"
,
"text/html; charset=utf-8"
)
return
http
.
StatusOK
,
nil
}
}
// Didn't qualify; pass-thru
buf
.
WriteTo
(
w
)
return
b
.
Next
.
ServeHTTP
(
w
,
r
)
return
http
.
StatusOK
,
nil
}
}
middleware/fileserver.go
View file @
0890e330
...
@@ -2,10 +2,12 @@ package middleware
...
@@ -2,10 +2,12 @@ package middleware
import
(
import
(
"fmt"
"fmt"
"math/rand"
"net/http"
"net/http"
"os"
"os"
"path"
"path"
"path/filepath"
"path/filepath"
"strconv"
"strings"
"strings"
)
)
...
@@ -40,12 +42,11 @@ type fileHandler struct {
...
@@ -40,12 +42,11 @@ type fileHandler struct {
}
}
func
(
fh
*
fileHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
func
(
fh
*
fileHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
upath
:=
r
.
URL
.
Path
// r.URL.Path has already been cleaned in caddy/server by path.Clean().
if
!
strings
.
HasPrefix
(
upath
,
"/"
)
{
if
r
.
URL
.
Path
==
""
{
upath
=
"/"
+
upath
r
.
URL
.
Path
=
"/"
r
.
URL
.
Path
=
upath
}
}
return
fh
.
serveFile
(
w
,
r
,
path
.
Clean
(
upath
)
)
return
fh
.
serveFile
(
w
,
r
,
r
.
URL
.
Path
)
}
}
// serveFile writes the specified file to the HTTP response.
// serveFile writes the specified file to the HTTP response.
...
@@ -66,7 +67,8 @@ func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name st
...
@@ -66,7 +67,8 @@ func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name st
return
http
.
StatusForbidden
,
err
return
http
.
StatusForbidden
,
err
}
}
// Likely the server is under load and ran out of file descriptors
// Likely the server is under load and ran out of file descriptors
w
.
Header
()
.
Set
(
"Retry-After"
,
"5"
)
// TODO: 5 seconds enough delay? Or too much?
backoff
:=
int
(
3
+
rand
.
Int31
()
%
3
)
// 3–5 seconds to prevent a stampede
w
.
Header
()
.
Set
(
"Retry-After"
,
strconv
.
Itoa
(
backoff
))
return
http
.
StatusServiceUnavailable
,
err
return
http
.
StatusServiceUnavailable
,
err
}
}
defer
f
.
Close
()
defer
f
.
Close
()
...
@@ -86,13 +88,13 @@ func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name st
...
@@ -86,13 +88,13 @@ func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name st
url
:=
r
.
URL
.
Path
url
:=
r
.
URL
.
Path
if
d
.
IsDir
()
{
if
d
.
IsDir
()
{
// Ensure / at end of directory url
// Ensure / at end of directory url
if
url
[
len
(
url
)
-
1
]
!=
'/'
{
if
!
strings
.
HasSuffix
(
url
,
"/"
)
{
redirect
(
w
,
r
,
path
.
Base
(
url
)
+
"/"
)
redirect
(
w
,
r
,
path
.
Base
(
url
)
+
"/"
)
return
http
.
StatusMovedPermanently
,
nil
return
http
.
StatusMovedPermanently
,
nil
}
}
}
else
{
}
else
{
// Ensure no / at end of file url
// Ensure no / at end of file url
if
url
[
len
(
url
)
-
1
]
==
'/'
{
if
strings
.
HasSuffix
(
url
,
"/"
)
{
redirect
(
w
,
r
,
"../"
+
path
.
Base
(
url
))
redirect
(
w
,
r
,
"../"
+
path
.
Base
(
url
))
return
http
.
StatusMovedPermanently
,
nil
return
http
.
StatusMovedPermanently
,
nil
}
}
...
...
middleware/fileserver_test.go
View file @
0890e330
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"errors"
"errors"
"net/http"
"net/http"
"net/http/httptest"
"net/http/httptest"
"net/url"
"os"
"os"
"path/filepath"
"path/filepath"
"strings"
"strings"
...
@@ -11,23 +12,30 @@ import (
...
@@ -11,23 +12,30 @@ import (
"time"
"time"
)
)
var
testDir
=
filepath
.
Join
(
os
.
TempDir
(),
"caddy_testdir"
)
var
(
var
ErrCustom
=
errors
.
New
(
"Custom Error"
)
ErrCustom
=
errors
.
New
(
"Custom Error"
)
testDir
=
filepath
.
Join
(
os
.
TempDir
(),
"caddy_testdir"
)
testWebRoot
=
filepath
.
Join
(
testDir
,
"webroot"
)
)
// testFiles is a map with relative paths to test files as keys and file content as values.
// testFiles is a map with relative paths to test files as keys and file content as values.
// The map represents the following structure:
// The map represents the following structure:
// - $TEMP/caddy_testdir/
// - $TEMP/caddy_testdir/
// '-- file1.html
// '-- unreachable.html
// '-- dirwithindex/
// '-- webroot/
// '---- index.html
// '---- file1.html
// '-- dir/
// '---- dirwithindex/
// '---- file2.html
// '------ index.html
// '---- hidden.html
// '---- dir/
// '------ file2.html
// '------ hidden.html
var
testFiles
=
map
[
string
]
string
{
var
testFiles
=
map
[
string
]
string
{
"file1.html"
:
"<h1>file1.html</h1>"
,
"unreachable.html"
:
"<h1>must not leak</h1>"
,
filepath
.
Join
(
"dirwithindex"
,
"index.html"
)
:
"<h1>dirwithindex/index.html</h1>"
,
filepath
.
Join
(
"webroot"
,
"file1.html"
)
:
"<h1>file1.html</h1>"
,
filepath
.
Join
(
"dir"
,
"file2.html"
)
:
"<h1>dir/file2.html</h1>"
,
filepath
.
Join
(
"webroot"
,
"dirwithindex"
,
"index.html"
)
:
"<h1>dirwithindex/index.html</h1>"
,
filepath
.
Join
(
"dir"
,
"hidden.html"
)
:
"<h1>dir/hidden.html</h1>"
,
filepath
.
Join
(
"webroot"
,
"dir"
,
"file2.html"
)
:
"<h1>dir/file2.html</h1>"
,
filepath
.
Join
(
"webroot"
,
"dir"
,
"hidden.html"
)
:
"<h1>dir/hidden.html</h1>"
,
}
}
// TestServeHTTP covers positive scenarios when serving files.
// TestServeHTTP covers positive scenarios when serving files.
...
@@ -36,7 +44,7 @@ func TestServeHTTP(t *testing.T) {
...
@@ -36,7 +44,7 @@ func TestServeHTTP(t *testing.T) {
beforeServeHTTPTest
(
t
)
beforeServeHTTPTest
(
t
)
defer
afterServeHTTPTest
(
t
)
defer
afterServeHTTPTest
(
t
)
fileserver
:=
FileServer
(
http
.
Dir
(
test
Dir
),
[]
string
{
"dir/hidden.html"
})
fileserver
:=
FileServer
(
http
.
Dir
(
test
WebRoot
),
[]
string
{
"dir/hidden.html"
})
movedPermanently
:=
"Moved Permanently"
movedPermanently
:=
"Moved Permanently"
...
@@ -142,11 +150,20 @@ func TestServeHTTP(t *testing.T) {
...
@@ -142,11 +150,20 @@ func TestServeHTTP(t *testing.T) {
url
:
"https://foo/hidden.html"
,
url
:
"https://foo/hidden.html"
,
expectedStatus
:
http
.
StatusNotFound
,
expectedStatus
:
http
.
StatusNotFound
,
},
},
// Test 17 - try to get below the root directory.
{
url
:
"https://foo/%2f..%2funreachable.html"
,
expectedStatus
:
http
.
StatusNotFound
,
},
}
}
for
i
,
test
:=
range
tests
{
for
i
,
test
:=
range
tests
{
responseRecorder
:=
httptest
.
NewRecorder
()
responseRecorder
:=
httptest
.
NewRecorder
()
request
,
err
:=
http
.
NewRequest
(
"GET"
,
test
.
url
,
strings
.
NewReader
(
""
))
request
,
err
:=
http
.
NewRequest
(
"GET"
,
test
.
url
,
nil
)
// prevent any URL sanitization within Go: we need unmodified paths here
if
u
,
_
:=
url
.
Parse
(
test
.
url
);
u
.
RawPath
!=
""
{
request
.
URL
.
Path
=
u
.
RawPath
}
status
,
err
:=
fileserver
.
ServeHTTP
(
responseRecorder
,
request
)
status
,
err
:=
fileserver
.
ServeHTTP
(
responseRecorder
,
request
)
etag
:=
responseRecorder
.
Header
()
.
Get
(
"Etag"
)
etag
:=
responseRecorder
.
Header
()
.
Get
(
"Etag"
)
...
@@ -176,7 +193,7 @@ func TestServeHTTP(t *testing.T) {
...
@@ -176,7 +193,7 @@ func TestServeHTTP(t *testing.T) {
// beforeServeHTTPTest creates a test directory with the structure, defined in the variable testFiles
// beforeServeHTTPTest creates a test directory with the structure, defined in the variable testFiles
func
beforeServeHTTPTest
(
t
*
testing
.
T
)
{
func
beforeServeHTTPTest
(
t
*
testing
.
T
)
{
// make the root test dir
// make the root test dir
err
:=
os
.
Mkdir
(
testDir
,
os
.
ModePerm
)
err
:=
os
.
Mkdir
All
(
testWebRoot
,
os
.
ModePerm
)
if
err
!=
nil
{
if
err
!=
nil
{
if
!
os
.
IsExist
(
err
)
{
if
!
os
.
IsExist
(
err
)
{
t
.
Fatalf
(
"Failed to create test dir. Error was: %v"
,
err
)
t
.
Fatalf
(
"Failed to create test dir. Error was: %v"
,
err
)
...
...
server/server.go
View file @
0890e330
...
@@ -14,7 +14,7 @@ import (
...
@@ -14,7 +14,7 @@ import (
"net"
"net"
"net/http"
"net/http"
"os"
"os"
"path
/filepath
"
"path"
"runtime"
"runtime"
"strings"
"strings"
"sync"
"sync"
...
@@ -336,11 +336,18 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
@@ -336,11 +336,18 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Use URL.RawPath If you need the original, "raw" URL.Path in your middleware.
// Use URL.RawPath If you need the original, "raw" URL.Path in your middleware.
// Collapse any ./ ../ /// madness here instead of doing that in every plugin.
// Collapse any ./ ../ /// madness here instead of doing that in every plugin.
if
r
.
URL
.
Path
!=
"/"
{
if
r
.
URL
.
Path
!=
"/"
{
path
:=
filepath
.
Clean
(
r
.
URL
.
Path
)
cleanedPath
:=
path
.
Clean
(
r
.
URL
.
Path
)
if
!
strings
.
HasPrefix
(
path
,
"/"
)
{
if
cleanedPath
==
"."
{
path
=
"/"
+
path
r
.
URL
.
Path
=
"/"
}
else
{
if
!
strings
.
HasPrefix
(
cleanedPath
,
"/"
)
{
cleanedPath
=
"/"
+
cleanedPath
}
if
strings
.
HasSuffix
(
r
.
URL
.
Path
,
"/"
)
&&
!
strings
.
HasSuffix
(
cleanedPath
,
"/"
)
{
cleanedPath
=
cleanedPath
+
"/"
}
r
.
URL
.
Path
=
cleanedPath
}
}
r
.
URL
.
Path
=
path
}
}
// Execute the optional request callback if it exists and it's not disabled
// Execute the optional request callback if it exists and it's not disabled
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment