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
8baead61
Commit
8baead61
authored
Sep 25, 2015
by
Mathias Beke
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'upstream/master'
parents
aa5a5957
4f5a29d6
Changes
15
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
255 additions
and
105 deletions
+255
-105
config/setup/browse.go
config/setup/browse.go
+3
-2
config/setup/controller_test.go
config/setup/controller_test.go
+0
-0
config/setup/errors.go
config/setup/errors.go
+30
-16
config/setup/errors_test.go
config/setup/errors_test.go
+25
-6
dist/CHANGES.txt
dist/CHANGES.txt
+6
-0
middleware/browse/browse.go
middleware/browse/browse.go
+16
-17
middleware/context.go
middleware/context.go
+47
-0
middleware/errors/errors.go
middleware/errors/errors.go
+32
-12
middleware/errors/errors_test.go
middleware/errors/errors_test.go
+45
-6
middleware/fileserver.go
middleware/fileserver.go
+28
-8
middleware/proxy/proxy.go
middleware/proxy/proxy.go
+2
-3
middleware/replacer.go
middleware/replacer.go
+9
-0
middleware/rewrite/rewrite.go
middleware/rewrite/rewrite.go
+10
-33
middleware/rewrite/rewrite_test.go
middleware/rewrite/rewrite_test.go
+1
-1
server/virtualhost.go
server/virtualhost.go
+1
-1
No files found.
config/setup/browse.go
View file @
8baead61
...
@@ -19,6 +19,7 @@ func Browse(c *Controller) (middleware.Middleware, error) {
...
@@ -19,6 +19,7 @@ func Browse(c *Controller) (middleware.Middleware, error) {
browse
:=
browse
.
Browse
{
browse
:=
browse
.
Browse
{
Root
:
c
.
Root
,
Root
:
c
.
Root
,
Configs
:
configs
,
Configs
:
configs
,
IgnoreIndexes
:
false
,
}
}
return
func
(
next
middleware
.
Handler
)
middleware
.
Handler
{
return
func
(
next
middleware
.
Handler
)
middleware
.
Handler
{
...
...
config/setup/controllertest.go
→
config/setup/controller
_
test.go
View file @
8baead61
File moved
config/setup/errors.go
View file @
8baead61
...
@@ -25,16 +25,24 @@ func Errors(c *Controller) (middleware.Middleware, error) {
...
@@ -25,16 +25,24 @@ func Errors(c *Controller) (middleware.Middleware, error) {
var
err
error
var
err
error
var
writer
io
.
Writer
var
writer
io
.
Writer
if
handler
.
LogFile
==
"stdout"
{
switch
handler
.
LogFile
{
case
"visible"
:
handler
.
Debug
=
true
case
"stdout"
:
writer
=
os
.
Stdout
writer
=
os
.
Stdout
}
else
if
handler
.
LogFile
==
"stderr"
{
case
"stderr"
:
writer
=
os
.
Stderr
writer
=
os
.
Stderr
}
else
if
handler
.
LogFile
==
"syslog"
{
case
"syslog"
:
writer
,
err
=
gsyslog
.
NewLogger
(
gsyslog
.
LOG_ERR
,
"LOCAL0"
,
"caddy"
)
writer
,
err
=
gsyslog
.
NewLogger
(
gsyslog
.
LOG_ERR
,
"LOCAL0"
,
"caddy"
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
}
else
if
handler
.
LogFile
!=
""
{
default
:
if
handler
.
LogFile
==
""
{
writer
=
os
.
Stderr
// default
break
}
var
file
*
os
.
File
var
file
*
os
.
File
file
,
err
=
os
.
OpenFile
(
handler
.
LogFile
,
os
.
O_RDWR
|
os
.
O_CREATE
|
os
.
O_APPEND
,
0644
)
file
,
err
=
os
.
OpenFile
(
handler
.
LogFile
,
os
.
O_RDWR
|
os
.
O_CREATE
|
os
.
O_APPEND
,
0644
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -80,6 +88,9 @@ func errorsParse(c *Controller) (*errors.ErrorHandler, error) {
...
@@ -80,6 +88,9 @@ func errorsParse(c *Controller) (*errors.ErrorHandler, error) {
where
:=
c
.
Val
()
where
:=
c
.
Val
()
if
what
==
"log"
{
if
what
==
"log"
{
if
where
==
"visible"
{
handler
.
Debug
=
true
}
else
{
handler
.
LogFile
=
where
handler
.
LogFile
=
where
if
c
.
NextArg
()
{
if
c
.
NextArg
()
{
if
c
.
Val
()
==
"{"
{
if
c
.
Val
()
==
"{"
{
...
@@ -91,6 +102,7 @@ func errorsParse(c *Controller) (*errors.ErrorHandler, error) {
...
@@ -91,6 +102,7 @@ func errorsParse(c *Controller) (*errors.ErrorHandler, error) {
handler
.
LogRoller
=
logRoller
handler
.
LogRoller
=
logRoller
}
}
}
}
}
}
else
{
}
else
{
// Error page; ensure it exists
// Error page; ensure it exists
where
=
path
.
Join
(
c
.
Root
,
where
)
where
=
path
.
Join
(
c
.
Root
,
where
)
...
@@ -121,12 +133,14 @@ func errorsParse(c *Controller) (*errors.ErrorHandler, error) {
...
@@ -121,12 +133,14 @@ func errorsParse(c *Controller) (*errors.ErrorHandler, error) {
return
handler
,
err
return
handler
,
err
}
}
// Otherwise, the only argument would be an error log file name
// Otherwise, the only argument would be an error log file name
or 'visible'
if
!
hadBlock
{
if
!
hadBlock
{
if
c
.
NextArg
()
{
if
c
.
NextArg
()
{
handler
.
LogFile
=
c
.
Val
()
if
c
.
Val
()
==
"visible"
{
handler
.
Debug
=
true
}
else
{
}
else
{
handler
.
LogFile
=
errors
.
DefaultLogFilename
handler
.
LogFile
=
c
.
Val
()
}
}
}
}
}
}
}
...
...
config/setup/errors_test.go
View file @
8baead61
...
@@ -8,9 +8,7 @@ import (
...
@@ -8,9 +8,7 @@ import (
)
)
func
TestErrors
(
t
*
testing
.
T
)
{
func
TestErrors
(
t
*
testing
.
T
)
{
c
:=
NewTestController
(
`errors`
)
c
:=
NewTestController
(
`errors`
)
mid
,
err
:=
Errors
(
c
)
mid
,
err
:=
Errors
(
c
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -28,8 +26,8 @@ func TestErrors(t *testing.T) {
...
@@ -28,8 +26,8 @@ func TestErrors(t *testing.T) {
t
.
Fatalf
(
"Expected handler to be type ErrorHandler, got: %#v"
,
handler
)
t
.
Fatalf
(
"Expected handler to be type ErrorHandler, got: %#v"
,
handler
)
}
}
if
myHandler
.
LogFile
!=
errors
.
DefaultLogFilename
{
if
myHandler
.
LogFile
!=
""
{
t
.
Errorf
(
"Expected
%s as the default LogFile"
,
errors
.
DefaultLogFilename
)
t
.
Errorf
(
"Expected
'%s' as the default LogFile"
,
""
)
}
}
if
myHandler
.
LogRoller
!=
nil
{
if
myHandler
.
LogRoller
!=
nil
{
t
.
Errorf
(
"Expected LogRoller to be nil, got: %v"
,
*
myHandler
.
LogRoller
)
t
.
Errorf
(
"Expected LogRoller to be nil, got: %v"
,
*
myHandler
.
LogRoller
)
...
@@ -37,6 +35,15 @@ func TestErrors(t *testing.T) {
...
@@ -37,6 +35,15 @@ func TestErrors(t *testing.T) {
if
!
SameNext
(
myHandler
.
Next
,
EmptyNext
)
{
if
!
SameNext
(
myHandler
.
Next
,
EmptyNext
)
{
t
.
Error
(
"'Next' field of handler was not set properly"
)
t
.
Error
(
"'Next' field of handler was not set properly"
)
}
}
// Test Startup function
if
len
(
c
.
Startup
)
==
0
{
t
.
Fatal
(
"Expected 1 startup function, had 0"
)
}
err
=
c
.
Startup
[
0
]()
if
myHandler
.
Log
==
nil
{
t
.
Error
(
"Expected Log to be non-nil after startup because Debug is not enabled"
)
}
}
}
func
TestErrorsParse
(
t
*
testing
.
T
)
{
func
TestErrorsParse
(
t
*
testing
.
T
)
{
...
@@ -46,11 +53,19 @@ func TestErrorsParse(t *testing.T) {
...
@@ -46,11 +53,19 @@ func TestErrorsParse(t *testing.T) {
expectedErrorHandler
errors
.
ErrorHandler
expectedErrorHandler
errors
.
ErrorHandler
}{
}{
{
`errors`
,
false
,
errors
.
ErrorHandler
{
{
`errors`
,
false
,
errors
.
ErrorHandler
{
LogFile
:
errors
.
DefaultLogFilename
,
LogFile
:
""
,
}},
}},
{
`errors errors.txt`
,
false
,
errors
.
ErrorHandler
{
{
`errors errors.txt`
,
false
,
errors
.
ErrorHandler
{
LogFile
:
"errors.txt"
,
LogFile
:
"errors.txt"
,
}},
}},
{
`errors visible`
,
false
,
errors
.
ErrorHandler
{
LogFile
:
""
,
Debug
:
true
,
}},
{
`errors { log visible }`
,
false
,
errors
.
ErrorHandler
{
LogFile
:
""
,
Debug
:
true
,
}},
{
`errors { log errors.txt
{
`errors { log errors.txt
404 404.html
404 404.html
500 500.html
500 500.html
...
@@ -101,9 +116,13 @@ func TestErrorsParse(t *testing.T) {
...
@@ -101,9 +116,13 @@ func TestErrorsParse(t *testing.T) {
t
.
Errorf
(
"Test %d errored, but it shouldn't have; got '%v'"
,
i
,
err
)
t
.
Errorf
(
"Test %d errored, but it shouldn't have; got '%v'"
,
i
,
err
)
}
}
if
actualErrorsRule
.
LogFile
!=
test
.
expectedErrorHandler
.
LogFile
{
if
actualErrorsRule
.
LogFile
!=
test
.
expectedErrorHandler
.
LogFile
{
t
.
Errorf
(
"Test %d expected LogFile to be
%s
, but got %s"
,
t
.
Errorf
(
"Test %d expected LogFile to be
%s
, but got %s"
,
i
,
test
.
expectedErrorHandler
.
LogFile
,
actualErrorsRule
.
LogFile
)
i
,
test
.
expectedErrorHandler
.
LogFile
,
actualErrorsRule
.
LogFile
)
}
}
if
actualErrorsRule
.
Debug
!=
test
.
expectedErrorHandler
.
Debug
{
t
.
Errorf
(
"Test %d expected Debug to be %v, but got %v"
,
i
,
test
.
expectedErrorHandler
.
Debug
,
actualErrorsRule
.
Debug
)
}
if
actualErrorsRule
.
LogRoller
!=
nil
&&
test
.
expectedErrorHandler
.
LogRoller
==
nil
||
actualErrorsRule
.
LogRoller
==
nil
&&
test
.
expectedErrorHandler
.
LogRoller
!=
nil
{
if
actualErrorsRule
.
LogRoller
!=
nil
&&
test
.
expectedErrorHandler
.
LogRoller
==
nil
||
actualErrorsRule
.
LogRoller
==
nil
&&
test
.
expectedErrorHandler
.
LogRoller
!=
nil
{
t
.
Fatalf
(
"Test %d expected LogRoller to be %v, but got %v"
,
t
.
Fatalf
(
"Test %d expected LogRoller to be %v, but got %v"
,
i
,
test
.
expectedErrorHandler
.
LogRoller
,
actualErrorsRule
.
LogRoller
)
i
,
test
.
expectedErrorHandler
.
LogRoller
,
actualErrorsRule
.
LogRoller
)
...
...
dist/CHANGES.txt
View file @
8baead61
...
@@ -3,6 +3,12 @@ CHANGES
...
@@ -3,6 +3,12 @@ CHANGES
<master>
<master>
- basicauth: Support for legacy htpasswd files
- basicauth: Support for legacy htpasswd files
- browse: JSON response with file listing given Accept header
- browse: JSON response with file listing given Accept header
- core: Caddyfile as command line argument
- errors: Can write full stack trace to HTTP response for debugging
- errors, log: Roll log files after certain size or age
- proxy: Fix for 32-bit architectures
- templates: Added .StripExt and .StripHTML methods
- Internal improvements and minor bug fixes
0.7.5 (August 5, 2015)
0.7.5 (August 5, 2015)
...
...
middleware/browse/browse.go
View file @
8baead61
...
@@ -26,11 +26,13 @@ type Browse struct {
...
@@ -26,11 +26,13 @@ type Browse struct {
Next
middleware
.
Handler
Next
middleware
.
Handler
Root
string
Root
string
Configs
[]
Config
Configs
[]
Config
IgnoreIndexes
bool
}
}
// Config is a configuration for browsing in a particular path.
// Config is a configuration for browsing in a particular path.
type
Config
struct
{
type
Config
struct
{
PathScope
string
PathScope
string
Variables
interface
{}
Template
*
template
.
Template
Template
*
template
.
Template
}
}
...
@@ -54,6 +56,9 @@ type Listing struct {
...
@@ -54,6 +56,9 @@ type Listing struct {
// And which order
// And which order
Order
string
Order
string
// Optional custom variables for use in browse templates
User
interface
{}
middleware
.
Context
middleware
.
Context
}
}
...
@@ -133,27 +138,20 @@ func (fi FileInfo) HumanModTime(format string) string {
...
@@ -133,27 +138,20 @@ func (fi FileInfo) HumanModTime(format string) string {
return
fi
.
ModTime
.
Format
(
format
)
return
fi
.
ModTime
.
Format
(
format
)
}
}
var
IndexPages
=
[]
string
{
func
directoryListing
(
files
[]
os
.
FileInfo
,
r
*
http
.
Request
,
canGoUp
bool
,
root
string
,
ignoreIndexes
bool
,
vars
interface
{})
(
Listing
,
error
)
{
"index.html"
,
"index.htm"
,
"index.txt"
,
"default.html"
,
"default.htm"
,
"default.txt"
,
}
func
directoryListing
(
files
[]
os
.
FileInfo
,
r
*
http
.
Request
,
canGoUp
bool
,
root
string
)
(
Listing
,
error
)
{
var
fileinfos
[]
FileInfo
var
fileinfos
[]
FileInfo
var
urlPath
=
r
.
URL
.
Path
var
urlPath
=
r
.
URL
.
Path
for
_
,
f
:=
range
files
{
for
_
,
f
:=
range
files
{
name
:=
f
.
Name
()
name
:=
f
.
Name
()
// Directory is not browsable if it contains index file
// Directory is not browsable if it contains index file
for
_
,
indexName
:=
range
IndexPages
{
if
!
ignoreIndexes
{
for
_
,
indexName
:=
range
middleware
.
IndexPages
{
if
name
==
indexName
{
if
name
==
indexName
{
return
Listing
{},
errors
.
New
(
"Directory contains index file, not browsable!"
)
return
Listing
{},
errors
.
New
(
"Directory contains index file, not browsable!"
)
}
}
}
}
}
if
f
.
IsDir
()
{
if
f
.
IsDir
()
{
name
+=
"/"
name
+=
"/"
...
@@ -181,6 +179,7 @@ func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root s
...
@@ -181,6 +179,7 @@ func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root s
Req
:
r
,
Req
:
r
,
URL
:
r
.
URL
,
URL
:
r
.
URL
,
},
},
User
:
vars
,
},
nil
},
nil
}
}
...
@@ -234,7 +233,7 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
...
@@ -234,7 +233,7 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
}
}
}
}
// Assemble listing of directory contents
// Assemble listing of directory contents
listing
,
err
:=
directoryListing
(
files
,
r
,
canGoUp
,
b
.
Root
)
listing
,
err
:=
directoryListing
(
files
,
r
,
canGoUp
,
b
.
Root
,
b
.
IgnoreIndexes
,
bc
.
Variables
)
if
err
!=
nil
{
// directory isn't browsable
if
err
!=
nil
{
// directory isn't browsable
continue
continue
}
}
...
...
middleware/context.go
View file @
8baead61
...
@@ -131,6 +131,53 @@ func (c Context) Truncate(input string, length int) string {
...
@@ -131,6 +131,53 @@ func (c Context) Truncate(input string, length int) string {
return
input
return
input
}
}
// StripHTML returns s without HTML tags. It is fairly naive
// but works with most valid HTML inputs.
func
(
c
Context
)
StripHTML
(
s
string
)
string
{
var
buf
bytes
.
Buffer
var
inTag
,
inQuotes
bool
var
tagStart
int
for
i
,
ch
:=
range
s
{
if
inTag
{
if
ch
==
'>'
&&
!
inQuotes
{
inTag
=
false
}
else
if
ch
==
'<'
&&
!
inQuotes
{
// false start
buf
.
WriteString
(
s
[
tagStart
:
i
])
tagStart
=
i
}
else
if
ch
==
'"'
{
inQuotes
=
!
inQuotes
}
continue
}
if
ch
==
'<'
{
inTag
=
true
tagStart
=
i
continue
}
buf
.
WriteRune
(
ch
)
}
if
inTag
{
// false start
buf
.
WriteString
(
s
[
tagStart
:
])
inTag
=
false
}
return
buf
.
String
()
}
// StripExt returns the input string without the extension,
// which is the suffix starting with the final '.' character
// but not before the final path separator ('/') character.
// If there is no extension, the whole input is returned.
func
(
c
Context
)
StripExt
(
path
string
)
string
{
for
i
:=
len
(
path
)
-
1
;
i
>=
0
&&
path
[
i
]
!=
'/'
;
i
--
{
if
path
[
i
]
==
'.'
{
return
path
[
:
i
]
}
}
return
path
}
// Replace replaces instances of find in input with replacement.
// Replace replaces instances of find in input with replacement.
func
(
c
Context
)
Replace
(
input
,
find
,
replacement
string
)
string
{
func
(
c
Context
)
Replace
(
input
,
find
,
replacement
string
)
string
{
return
strings
.
Replace
(
input
,
find
,
replacement
,
-
1
)
return
strings
.
Replace
(
input
,
find
,
replacement
,
-
1
)
...
...
middleware/errors/errors.go
View file @
8baead61
...
@@ -14,13 +14,14 @@ import (
...
@@ -14,13 +14,14 @@ import (
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware"
)
)
// ErrorHandler handles HTTP errors (
or
errors from other middleware).
// ErrorHandler handles HTTP errors (
and
errors from other middleware).
type
ErrorHandler
struct
{
type
ErrorHandler
struct
{
Next
middleware
.
Handler
Next
middleware
.
Handler
ErrorPages
map
[
int
]
string
// map of status code to filename
ErrorPages
map
[
int
]
string
// map of status code to filename
LogFile
string
LogFile
string
Log
*
log
.
Logger
Log
*
log
.
Logger
LogRoller
*
middleware
.
LogRoller
LogRoller
*
middleware
.
LogRoller
Debug
bool
// if true, errors are written out to client rather than to a log
}
}
func
(
h
ErrorHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
func
(
h
ErrorHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
...
@@ -29,12 +30,21 @@ func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er
...
@@ -29,12 +30,21 @@ func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er
status
,
err
:=
h
.
Next
.
ServeHTTP
(
w
,
r
)
status
,
err
:=
h
.
Next
.
ServeHTTP
(
w
,
r
)
if
err
!=
nil
{
if
err
!=
nil
{
h
.
Log
.
Printf
(
"%s [ERROR %d %s] %v"
,
time
.
Now
()
.
Format
(
timeFormat
),
status
,
r
.
URL
.
Path
,
err
)
errMsg
:=
fmt
.
Sprintf
(
"%s [ERROR %d %s] %v"
,
time
.
Now
()
.
Format
(
timeFormat
),
status
,
r
.
URL
.
Path
,
err
)
if
h
.
Debug
{
// Write error to response instead of to log
w
.
WriteHeader
(
status
)
fmt
.
Fprintln
(
w
,
errMsg
)
return
0
,
err
// returning < 400 signals that a response has been written
}
else
{
h
.
Log
.
Println
(
errMsg
)
}
}
}
if
status
>=
400
{
if
status
>=
400
{
h
.
errorPage
(
w
,
status
)
h
.
errorPage
(
w
,
r
,
status
)
return
0
,
err
// status < 400 signals that a response has been written
return
0
,
err
}
}
return
status
,
err
return
status
,
err
...
@@ -43,7 +53,7 @@ func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er
...
@@ -43,7 +53,7 @@ func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er
// errorPage serves a static error page to w according to the status
// errorPage serves a static error page to w according to the status
// code. If there is an error serving the error page, a plaintext error
// code. If there is an error serving the error page, a plaintext error
// message is written instead, and the extra error is logged.
// message is written instead, and the extra error is logged.
func
(
h
ErrorHandler
)
errorPage
(
w
http
.
ResponseWriter
,
code
int
)
{
func
(
h
ErrorHandler
)
errorPage
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
code
int
)
{
defaultBody
:=
fmt
.
Sprintf
(
"%d %s"
,
code
,
http
.
StatusText
(
code
))
defaultBody
:=
fmt
.
Sprintf
(
"%d %s"
,
code
,
http
.
StatusText
(
code
))
// See if an error page for this status code was specified
// See if an error page for this status code was specified
...
@@ -52,8 +62,9 @@ func (h ErrorHandler) errorPage(w http.ResponseWriter, code int) {
...
@@ -52,8 +62,9 @@ func (h ErrorHandler) errorPage(w http.ResponseWriter, code int) {
// Try to open it
// Try to open it
errorPage
,
err
:=
os
.
Open
(
pagePath
)
errorPage
,
err
:=
os
.
Open
(
pagePath
)
if
err
!=
nil
{
if
err
!=
nil
{
// An error handling an error... <insert grumpy cat here>
// An additional error handling an error... <insert grumpy cat here>
h
.
Log
.
Printf
(
"HTTP %d could not load error page %s: %v"
,
code
,
pagePath
,
err
)
h
.
Log
.
Printf
(
"%s [NOTICE %d %s] could not load error page: %v"
,
time
.
Now
()
.
Format
(
timeFormat
),
code
,
r
.
URL
.
String
(),
err
)
http
.
Error
(
w
,
defaultBody
,
code
)
http
.
Error
(
w
,
defaultBody
,
code
)
return
return
}
}
...
@@ -66,7 +77,8 @@ func (h ErrorHandler) errorPage(w http.ResponseWriter, code int) {
...
@@ -66,7 +77,8 @@ func (h ErrorHandler) errorPage(w http.ResponseWriter, code int) {
if
err
!=
nil
{
if
err
!=
nil
{
// Epic fail... sigh.
// Epic fail... sigh.
h
.
Log
.
Printf
(
"HTTP %d could not respond with %s: %v"
,
code
,
pagePath
,
err
)
h
.
Log
.
Printf
(
"%s [NOTICE %d %s] could not respond with %s: %v"
,
time
.
Now
()
.
Format
(
timeFormat
),
code
,
r
.
URL
.
String
(),
pagePath
,
err
)
http
.
Error
(
w
,
defaultBody
,
code
)
http
.
Error
(
w
,
defaultBody
,
code
)
}
}
...
@@ -108,10 +120,18 @@ func (h ErrorHandler) recovery(w http.ResponseWriter, r *http.Request) {
...
@@ -108,10 +120,18 @@ func (h ErrorHandler) recovery(w http.ResponseWriter, r *http.Request) {
file
=
file
[
pkgPathPos
+
len
(
delim
)
:
]
file
=
file
[
pkgPathPos
+
len
(
delim
)
:
]
}
}
// Currently we don't use the function name, as file:line is more conventional
panicMsg
:=
fmt
.
Sprintf
(
"%s [PANIC %s] %s:%d - %v"
,
time
.
Now
()
.
Format
(
timeFormat
),
r
.
URL
.
String
(),
file
,
line
,
rec
)
h
.
Log
.
Printf
(
"%s [PANIC %s] %s:%d - %v"
,
time
.
Now
()
.
Format
(
timeFormat
),
r
.
URL
.
String
(),
file
,
line
,
rec
)
if
h
.
Debug
{
h
.
errorPage
(
w
,
http
.
StatusInternalServerError
)
// Write error and stack trace to the response rather than to a log
var
stackBuf
[
4096
]
byte
stack
:=
stackBuf
[
:
runtime
.
Stack
(
stackBuf
[
:
],
false
)]
w
.
WriteHeader
(
http
.
StatusInternalServerError
)
fmt
.
Fprintf
(
w
,
"%s
\n\n
%s"
,
panicMsg
,
stack
)
}
else
{
// Currently we don't use the function name, since file:line is more conventional
h
.
Log
.
Printf
(
panicMsg
)
h
.
errorPage
(
w
,
r
,
http
.
StatusInternalServerError
)
}
}
}
const
DefaultLogFilename
=
"error.log"
const
timeFormat
=
"02/Jan/2006:15:04:05 -0700"
const
timeFormat
=
"02/Jan/2006:15:04:05 -0700"
middleware/errors/errors_test.go
View file @
8baead61
...
@@ -33,11 +33,12 @@ func TestErrors(t *testing.T) {
...
@@ -33,11 +33,12 @@ func TestErrors(t *testing.T) {
buf
:=
bytes
.
Buffer
{}
buf
:=
bytes
.
Buffer
{}
em
:=
ErrorHandler
{
em
:=
ErrorHandler
{
ErrorPages
:
make
(
map
[
int
]
string
),
ErrorPages
:
map
[
int
]
string
{
http
.
StatusNotFound
:
path
,
http
.
StatusForbidden
:
"not_exist_file"
,
},
Log
:
log
.
New
(
&
buf
,
""
,
0
),
Log
:
log
.
New
(
&
buf
,
""
,
0
),
}
}
em
.
ErrorPages
[
http
.
StatusNotFound
]
=
path
em
.
ErrorPages
[
http
.
StatusForbidden
]
=
"not_exist_file"
_
,
notExistErr
:=
os
.
Open
(
"not_exist_file"
)
_
,
notExistErr
:=
os
.
Open
(
"not_exist_file"
)
testErr
:=
errors
.
New
(
"test error"
)
testErr
:=
errors
.
New
(
"test error"
)
...
@@ -82,8 +83,8 @@ func TestErrors(t *testing.T) {
...
@@ -82,8 +83,8 @@ func TestErrors(t *testing.T) {
expectedCode
:
0
,
expectedCode
:
0
,
expectedBody
:
fmt
.
Sprintf
(
"%d %s
\n
"
,
http
.
StatusForbidden
,
expectedBody
:
fmt
.
Sprintf
(
"%d %s
\n
"
,
http
.
StatusForbidden
,
http
.
StatusText
(
http
.
StatusForbidden
)),
http
.
StatusText
(
http
.
StatusForbidden
)),
expectedLog
:
fmt
.
Sprintf
(
"
HTTP %d could not load error page %s
: %v
\n
"
,
expectedLog
:
fmt
.
Sprintf
(
"
[NOTICE %d /] could not load error page
: %v
\n
"
,
http
.
StatusForbidden
,
"not_exist_file"
,
notExistErr
),
http
.
StatusForbidden
,
notExistErr
),
expectedErr
:
nil
,
expectedErr
:
nil
,
},
},
}
}
...
@@ -117,6 +118,44 @@ func TestErrors(t *testing.T) {
...
@@ -117,6 +118,44 @@ func TestErrors(t *testing.T) {
}
}
}
}
func
TestVisibleErrorWithPanic
(
t
*
testing
.
T
)
{
const
panicMsg
=
"I'm a panic"
eh
:=
ErrorHandler
{
ErrorPages
:
make
(
map
[
int
]
string
),
Debug
:
true
,
Next
:
middleware
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
panic
(
panicMsg
)
}),
}
req
,
err
:=
http
.
NewRequest
(
"GET"
,
"/"
,
nil
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
rec
:=
httptest
.
NewRecorder
()
code
,
err
:=
eh
.
ServeHTTP
(
rec
,
req
)
if
code
!=
0
{
t
.
Errorf
(
"Expected error handler to return 0 (it should write to response), got status %d"
,
code
)
}
if
err
!=
nil
{
t
.
Errorf
(
"Expected error handler to return nil error (it should panic!), but got '%v'"
,
err
)
}
body
:=
rec
.
Body
.
String
()
if
!
strings
.
Contains
(
body
,
"[PANIC /] middleware/errors/errors_test.go"
)
{
t
.
Errorf
(
"Expected response body to contain error log line, but it didn't:
\n
%s"
,
body
)
}
if
!
strings
.
Contains
(
body
,
panicMsg
)
{
t
.
Errorf
(
"Expected response body to contain panic message, but it didn't:
\n
%s"
,
body
)
}
if
len
(
body
)
<
500
{
t
.
Errorf
(
"Expected response body to contain stack trace, but it was too short: len=%d"
,
len
(
body
))
}
}
func
genErrorHandler
(
status
int
,
err
error
,
body
string
)
middleware
.
Handler
{
func
genErrorHandler
(
status
int
,
err
error
,
body
string
)
middleware
.
Handler
{
return
middleware
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
return
middleware
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
(
int
,
error
)
{
fmt
.
Fprint
(
w
,
body
)
fmt
.
Fprint
(
w
,
body
)
...
...
server
/fileserver.go
→
middleware
/fileserver.go
View file @
8baead61
package
server
package
middleware
import
(
import
(
"net/http"
"net/http"
"os"
"os"
"path"
"path"
"strings"
"strings"
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/browse"
)
)
// This file contains a standard way for Caddy middleware
// to load files from the file system given a request
// URI and path to site root. Other middleware that load
// files should use these facilities.
// FileServer implements a production-ready file server
// and is the 'default' handler for all requests to Caddy.
// It simply loads and serves the URI requested. If Caddy is
// run without any extra configuration/directives, this is the
// only middleware handler that runs. It is not in its own
// folder like most other middleware handlers because it does
// not require a directive. It is a special case.
//
// FileServer is adapted from the one in net/http by
// FileServer is adapted from the one in net/http by
// the Go authors. Significant modifications have been made.
// the Go authors. Significant modifications have been made.
//
//
//
// Original license:
// License:
//
//
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// license that can be found in the LICENSE file.
func
FileServer
(
root
http
.
FileSystem
,
hide
[]
string
)
middleware
.
Handler
{
func
FileServer
(
root
http
.
FileSystem
,
hide
[]
string
)
Handler
{
return
&
fileHandler
{
root
:
root
,
hide
:
hide
}
return
&
fileHandler
{
root
:
root
,
hide
:
hide
}
}
}
...
@@ -82,7 +91,7 @@ func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name st
...
@@ -82,7 +91,7 @@ func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name st
// use contents of an index file, if present, for directory
// use contents of an index file, if present, for directory
if
d
.
IsDir
()
{
if
d
.
IsDir
()
{
for
_
,
indexPage
:=
range
browse
.
IndexPages
{
for
_
,
indexPage
:=
range
IndexPages
{
index
:=
strings
.
TrimSuffix
(
name
,
"/"
)
+
"/"
+
indexPage
index
:=
strings
.
TrimSuffix
(
name
,
"/"
)
+
"/"
+
indexPage
ff
,
err
:=
fh
.
root
.
Open
(
index
)
ff
,
err
:=
fh
.
root
.
Open
(
index
)
if
err
==
nil
{
if
err
==
nil
{
...
@@ -134,3 +143,14 @@ func redirect(w http.ResponseWriter, r *http.Request, newPath string) {
...
@@ -134,3 +143,14 @@ func redirect(w http.ResponseWriter, r *http.Request, newPath string) {
}
}
http
.
Redirect
(
w
,
r
,
newPath
,
http
.
StatusMovedPermanently
)
http
.
Redirect
(
w
,
r
,
newPath
,
http
.
StatusMovedPermanently
)
}
}
// IndexPages is a list of pages that may be understood as
// the "index" files to directories.
var
IndexPages
=
[]
string
{
"index.html"
,
"index.htm"
,
"index.txt"
,
"default.html"
,
"default.htm"
,
"default.txt"
,
}
middleware/proxy/proxy.go
View file @
8baead61
...
@@ -33,10 +33,9 @@ type UpstreamHostDownFunc func(*UpstreamHost) bool
...
@@ -33,10 +33,9 @@ type UpstreamHostDownFunc func(*UpstreamHost) bool
// UpstreamHost represents a single proxy upstream
// UpstreamHost represents a single proxy upstream
type
UpstreamHost
struct
{
type
UpstreamHost
struct
{
// The hostname of this upstream host
Conns
int64
// must be first field to be 64-bit aligned on 32-bit systems
Name
string
Name
string
// hostname of this upstream host
ReverseProxy
*
ReverseProxy
ReverseProxy
*
ReverseProxy
Conns
int64
Fails
int32
Fails
int32
FailTimeout
time
.
Duration
FailTimeout
time
.
Duration
Unhealthy
bool
Unhealthy
bool
...
...
middleware/replacer.go
View file @
8baead61
...
@@ -3,6 +3,7 @@ package middleware
...
@@ -3,6 +3,7 @@ package middleware
import
(
import
(
"net"
"net"
"net/http"
"net/http"
"path"
"strconv"
"strconv"
"strings"
"strings"
"time"
"time"
...
@@ -63,6 +64,14 @@ func NewReplacer(r *http.Request, rr *responseRecorder, emptyValue string) Repla
...
@@ -63,6 +64,14 @@ func NewReplacer(r *http.Request, rr *responseRecorder, emptyValue string) Repla
"{when}"
:
func
()
string
{
"{when}"
:
func
()
string
{
return
time
.
Now
()
.
Format
(
timeFormat
)
return
time
.
Now
()
.
Format
(
timeFormat
)
}(),
}(),
"{file}"
:
func
()
string
{
_
,
file
:=
path
.
Split
(
r
.
URL
.
Path
)
return
file
}(),
"{dir}"
:
func
()
string
{
dir
,
_
:=
path
.
Split
(
r
.
URL
.
Path
)
return
dir
}(),
},
},
emptyValue
:
emptyValue
,
emptyValue
:
emptyValue
,
}
}
...
...
middleware/rewrite/rewrite.go
View file @
8baead61
...
@@ -3,9 +3,8 @@
...
@@ -3,9 +3,8 @@
package
rewrite
package
rewrite
import
(
import
(
"net/http"
"fmt"
"fmt"
"net/http"
"net/url"
"net/url"
"path"
"path"
"path/filepath"
"path/filepath"
...
@@ -96,15 +95,6 @@ func NewRegexpRule(base, pattern, to string, ext []string) (*RegexpRule, error)
...
@@ -96,15 +95,6 @@ func NewRegexpRule(base, pattern, to string, ext []string) (*RegexpRule, error)
},
nil
},
nil
}
}
// regexpVars are variables that can be used for To (rewrite destination path).
var
regexpVars
=
[]
string
{
"{path}"
,
"{query}"
,
"{file}"
,
"{dir}"
,
"{frag}"
,
}
// Rewrite rewrites the internal location of the current request.
// Rewrite rewrites the internal location of the current request.
func
(
r
*
RegexpRule
)
Rewrite
(
req
*
http
.
Request
)
bool
{
func
(
r
*
RegexpRule
)
Rewrite
(
req
*
http
.
Request
)
bool
{
rPath
:=
req
.
URL
.
Path
rPath
:=
req
.
URL
.
Path
...
@@ -119,32 +109,19 @@ func (r *RegexpRule) Rewrite(req *http.Request) bool {
...
@@ -119,32 +109,19 @@ func (r *RegexpRule) Rewrite(req *http.Request) bool {
return
false
return
false
}
}
// include trailing slash in regexp if present
start
:=
len
(
r
.
Base
)
if
strings
.
HasSuffix
(
r
.
Base
,
"/"
)
{
start
-=
1
}
// validate regexp
// validate regexp
if
!
r
.
MatchString
(
rPath
[
len
(
r
.
Base
)
:
])
{
if
!
r
.
MatchString
(
rPath
[
start
:
])
{
return
false
return
false
}
}
to
:=
r
.
To
// replace variables
to
:=
path
.
Clean
(
middleware
.
NewReplacer
(
req
,
nil
,
""
)
.
Replace
(
r
.
To
))
// check variables
for
_
,
v
:=
range
regexpVars
{
if
strings
.
Contains
(
r
.
To
,
v
)
{
switch
v
{
case
"{path}"
:
to
=
strings
.
Replace
(
to
,
v
,
req
.
URL
.
Path
[
1
:
],
-
1
)
case
"{query}"
:
to
=
strings
.
Replace
(
to
,
v
,
req
.
URL
.
RawQuery
,
-
1
)
case
"{frag}"
:
to
=
strings
.
Replace
(
to
,
v
,
req
.
URL
.
Fragment
,
-
1
)
case
"{file}"
:
_
,
file
:=
path
.
Split
(
req
.
URL
.
Path
)
to
=
strings
.
Replace
(
to
,
v
,
file
,
-
1
)
case
"{dir}"
:
dir
,
_
:=
path
.
Split
(
req
.
URL
.
Path
)
to
=
path
.
Clean
(
strings
.
Replace
(
to
,
v
,
dir
,
-
1
))
}
}
}
// validate resulting path
// validate resulting path
url
,
err
:=
url
.
Parse
(
to
)
url
,
err
:=
url
.
Parse
(
to
)
...
...
middleware/rewrite/rewrite_test.go
View file @
8baead61
...
@@ -28,7 +28,7 @@ func TestRewrite(t *testing.T) {
...
@@ -28,7 +28,7 @@ func TestRewrite(t *testing.T) {
[]
string
{
"/ab/"
,
"ab"
,
"/ab?type=html&{query}"
,
".html|"
},
[]
string
{
"/ab/"
,
"ab"
,
"/ab?type=html&{query}"
,
".html|"
},
[]
string
{
"/abc/"
,
"ab"
,
"/abc/{file}"
,
".html|"
},
[]
string
{
"/abc/"
,
"ab"
,
"/abc/{file}"
,
".html|"
},
[]
string
{
"/abcd/"
,
"ab"
,
"/a/{dir}/{file}"
,
".html|"
},
[]
string
{
"/abcd/"
,
"ab"
,
"/a/{dir}/{file}"
,
".html|"
},
[]
string
{
"/abcde/"
,
"ab"
,
"/a#{frag}"
,
".html|"
},
[]
string
{
"/abcde/"
,
"ab"
,
"/a#{frag
ment
}"
,
".html|"
},
[]
string
{
"/ab/"
,
`.*\.jpg`
,
"/ajpg"
,
""
},
[]
string
{
"/ab/"
,
`.*\.jpg`
,
"/ajpg"
,
""
},
}
}
...
...
server/virtualhost.go
View file @
8baead61
...
@@ -20,7 +20,7 @@ type virtualHost struct {
...
@@ -20,7 +20,7 @@ type virtualHost struct {
// on its config. This method should be called last before
// on its config. This method should be called last before
// ListenAndServe begins.
// ListenAndServe begins.
func
(
vh
*
virtualHost
)
buildStack
()
error
{
func
(
vh
*
virtualHost
)
buildStack
()
error
{
vh
.
fileServer
=
FileServer
(
http
.
Dir
(
vh
.
config
.
Root
),
[]
string
{
vh
.
config
.
ConfigFile
})
vh
.
fileServer
=
middleware
.
FileServer
(
http
.
Dir
(
vh
.
config
.
Root
),
[]
string
{
vh
.
config
.
ConfigFile
})
// TODO: We only compile middleware for the "/" scope.
// TODO: We only compile middleware for the "/" scope.
// Partial support for multiple location contexts already
// Partial support for multiple location contexts already
...
...
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