Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
package httpserver
|
2015-01-14 03:43:45 +08:00
|
|
|
|
2015-05-06 05:50:42 +08:00
|
|
|
import (
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
"fmt"
|
2015-05-06 05:50:42 +08:00
|
|
|
"net/http"
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
"os"
|
2015-10-14 07:49:53 +08:00
|
|
|
"path"
|
2015-10-30 00:34:47 +08:00
|
|
|
"time"
|
2015-05-06 05:50:42 +08:00
|
|
|
)
|
2015-01-14 03:43:45 +08:00
|
|
|
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
func init() {
|
|
|
|
initCaseSettings()
|
|
|
|
}
|
|
|
|
|
2015-01-19 14:11:21 +08:00
|
|
|
type (
|
|
|
|
// Middleware is the middle layer which represents the traditional
|
2015-04-03 13:30:54 +08:00
|
|
|
// idea of middleware: it chains one Handler to the next by being
|
|
|
|
// passed the next Handler in the chain.
|
|
|
|
Middleware func(Handler) Handler
|
2015-03-29 06:37:37 +08:00
|
|
|
|
2016-03-03 02:33:40 +08:00
|
|
|
// Handler is like http.Handler except ServeHTTP may return a status
|
|
|
|
// code and/or error.
|
2015-03-29 06:37:37 +08:00
|
|
|
//
|
2016-03-03 02:33:40 +08:00
|
|
|
// If ServeHTTP writes to the response body, it should return a status
|
|
|
|
// code of 0. This signals to other handlers above it that the response
|
|
|
|
// body is already written, and that they should not write to it also.
|
2015-03-30 12:01:42 +08:00
|
|
|
//
|
2016-03-03 02:33:40 +08:00
|
|
|
// If ServeHTTP encounters an error, it should return the error value
|
|
|
|
// so it can be logged by designated error-handling middleware.
|
2015-03-30 12:01:42 +08:00
|
|
|
//
|
2016-03-03 02:33:40 +08:00
|
|
|
// If writing a response after calling another ServeHTTP method, the
|
|
|
|
// returned status code SHOULD be used when writing the response.
|
|
|
|
//
|
|
|
|
// If handling errors after calling another ServeHTTP method, the
|
|
|
|
// returned error value SHOULD be logged or handled accordingly.
|
|
|
|
//
|
|
|
|
// Otherwise, return values should be propagated down the middleware
|
|
|
|
// chain by returning them unchanged.
|
2015-03-29 06:37:37 +08:00
|
|
|
Handler interface {
|
|
|
|
ServeHTTP(http.ResponseWriter, *http.Request) (int, error)
|
|
|
|
}
|
2015-01-19 14:11:21 +08:00
|
|
|
|
2015-05-05 03:40:07 +08:00
|
|
|
// HandlerFunc is a convenience type like http.HandlerFunc, except
|
|
|
|
// ServeHTTP returns a status code and an error. See Handler
|
|
|
|
// documentation for more information.
|
|
|
|
HandlerFunc func(http.ResponseWriter, *http.Request) (int, error)
|
2016-06-21 22:59:29 +08:00
|
|
|
|
|
|
|
// RequestMatcher checks to see if current request should be handled
|
|
|
|
// by underlying handler.
|
|
|
|
RequestMatcher interface {
|
|
|
|
Match(r *http.Request) bool
|
|
|
|
}
|
2016-07-09 08:12:52 +08:00
|
|
|
|
|
|
|
// HandlerConfig is a middleware configuration.
|
|
|
|
// This makes it possible for middlewares to have a common
|
|
|
|
// configuration interface.
|
|
|
|
//
|
|
|
|
// TODO The long term plan is to get all middleware implement this
|
|
|
|
// interface for configurations.
|
|
|
|
HandlerConfig interface {
|
|
|
|
RequestMatcher
|
|
|
|
BasePath() string
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConfigSelector selects a configuration.
|
|
|
|
ConfigSelector []HandlerConfig
|
2015-01-19 14:11:21 +08:00
|
|
|
)
|
2015-04-03 13:30:54 +08:00
|
|
|
|
|
|
|
// ServeHTTP implements the Handler interface.
|
|
|
|
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
|
|
|
return f(w, r)
|
|
|
|
}
|
2015-05-06 05:50:42 +08:00
|
|
|
|
2016-07-09 08:12:52 +08:00
|
|
|
// Select selects a Config.
|
|
|
|
// This chooses the config with the longest length.
|
|
|
|
func (c ConfigSelector) Select(r *http.Request) (config HandlerConfig) {
|
|
|
|
for i := range c {
|
|
|
|
if !c[i].Match(r) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if config == nil || len(c[i].BasePath()) > len(config.BasePath()) {
|
|
|
|
config = c[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
2015-05-06 05:50:42 +08:00
|
|
|
// IndexFile looks for a file in /root/fpath/indexFile for each string
|
|
|
|
// in indexFiles. If an index file is found, it returns the root-relative
|
|
|
|
// path to the file and true. If no index file is found, empty string
|
|
|
|
// and false is returned. fpath must end in a forward slash '/'
|
|
|
|
// otherwise no index files will be tried (directory paths must end
|
|
|
|
// in a forward slash according to HTTP).
|
2015-10-14 07:49:53 +08:00
|
|
|
//
|
|
|
|
// All paths passed into and returned from this function use '/' as the
|
|
|
|
// path separator, just like URLs. IndexFle handles path manipulation
|
|
|
|
// internally for systems that use different path separators.
|
2015-05-06 05:50:42 +08:00
|
|
|
func IndexFile(root http.FileSystem, fpath string, indexFiles []string) (string, bool) {
|
|
|
|
if fpath[len(fpath)-1] != '/' || root == nil {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
for _, indexFile := range indexFiles {
|
2015-10-14 07:49:53 +08:00
|
|
|
// func (http.FileSystem).Open wants all paths separated by "/",
|
|
|
|
// regardless of operating system convention, so use
|
|
|
|
// path.Join instead of filepath.Join
|
|
|
|
fp := path.Join(fpath, indexFile)
|
2015-05-06 05:50:42 +08:00
|
|
|
f, err := root.Open(fp)
|
|
|
|
if err == nil {
|
|
|
|
f.Close()
|
|
|
|
return fp, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
2015-10-30 00:34:47 +08:00
|
|
|
|
|
|
|
// SetLastModifiedHeader checks if the provided modTime is valid and if it is sets it
|
|
|
|
// as a Last-Modified header to the ResponseWriter. If the modTime is in the future
|
|
|
|
// the current time is used instead.
|
|
|
|
func SetLastModifiedHeader(w http.ResponseWriter, modTime time.Time) {
|
|
|
|
if modTime.IsZero() || modTime.Equal(time.Unix(0, 0)) {
|
|
|
|
// the time does not appear to be valid. Don't put it in the response
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// RFC 2616 - Section 14.29 - Last-Modified:
|
|
|
|
// An origin server MUST NOT send a Last-Modified date which is later than the
|
|
|
|
// server's time of message origination. In such cases, where the resource's last
|
|
|
|
// modification would indicate some time in the future, the server MUST replace
|
|
|
|
// that date with the message origination date.
|
|
|
|
now := currentTime()
|
|
|
|
if modTime.After(now) {
|
|
|
|
modTime = now
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Last-Modified", modTime.UTC().Format(http.TimeFormat))
|
|
|
|
}
|
|
|
|
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
// CaseSensitivePath determines if paths should be case sensitive.
|
|
|
|
// This is configurable via CASE_SENSITIVE_PATH environment variable.
|
|
|
|
var CaseSensitivePath = true
|
|
|
|
|
|
|
|
const caseSensitivePathEnv = "CASE_SENSITIVE_PATH"
|
|
|
|
|
|
|
|
// initCaseSettings loads case sensitivity config from environment variable.
|
|
|
|
//
|
|
|
|
// This could have been in init, but init cannot be called from tests.
|
|
|
|
func initCaseSettings() {
|
|
|
|
switch os.Getenv(caseSensitivePathEnv) {
|
|
|
|
case "0", "false":
|
|
|
|
CaseSensitivePath = false
|
|
|
|
default:
|
|
|
|
CaseSensitivePath = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-21 22:59:29 +08:00
|
|
|
// MergeRequestMatchers merges multiple RequestMatchers into one.
|
|
|
|
// This allows a middleware to use multiple RequestMatchers.
|
|
|
|
func MergeRequestMatchers(matchers ...RequestMatcher) RequestMatcher {
|
|
|
|
return requestMatchers(matchers)
|
|
|
|
}
|
|
|
|
|
|
|
|
type requestMatchers []RequestMatcher
|
|
|
|
|
|
|
|
// Match satisfies RequestMatcher interface.
|
|
|
|
func (m requestMatchers) Match(r *http.Request) bool {
|
|
|
|
for _, matcher := range m {
|
|
|
|
if !matcher.Match(r) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2016-02-14 15:10:57 +08:00
|
|
|
// currentTime, as it is defined here, returns time.Now().
|
|
|
|
// It's defined as a variable for mocking time in tests.
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
var currentTime = func() time.Time { return time.Now() }
|
|
|
|
|
|
|
|
// EmptyNext is a no-op function that can be passed into
|
|
|
|
// Middleware functions so that the assignment to the
|
|
|
|
// Next field of the Handler can be tested.
|
|
|
|
//
|
|
|
|
// Used primarily for testing but needs to be exported so
|
|
|
|
// plugins can use this as a convenience.
|
|
|
|
var EmptyNext = HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { return 0, nil })
|
|
|
|
|
|
|
|
// SameNext does a pointer comparison between next1 and next2.
|
|
|
|
//
|
|
|
|
// Used primarily for testing but needs to be exported so
|
|
|
|
// plugins can use this as a convenience.
|
|
|
|
func SameNext(next1, next2 Handler) bool {
|
|
|
|
return fmt.Sprintf("%v", next1) == fmt.Sprintf("%v", next2)
|
2015-10-30 00:34:47 +08:00
|
|
|
}
|