mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-22 02:15:45 +03:00
commit
a881838836
27 changed files with 201 additions and 196 deletions
10
app/app.go
10
app/app.go
|
@ -16,10 +16,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Program name
|
// Name is the program name
|
||||||
Name = "Caddy"
|
Name = "Caddy"
|
||||||
|
|
||||||
// Program version
|
// Version is the program version
|
||||||
Version = "0.6.0"
|
Version = "0.6.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,13 +27,13 @@ var (
|
||||||
// Servers is a list of all the currently-listening servers
|
// Servers is a list of all the currently-listening servers
|
||||||
Servers []*server.Server
|
Servers []*server.Server
|
||||||
|
|
||||||
// This mutex protects the Servers slice during changes
|
// ServersMutex protects the Servers slice during changes
|
||||||
ServersMutex sync.Mutex
|
ServersMutex sync.Mutex
|
||||||
|
|
||||||
// Waiting on Wg will block until all listeners have shut down.
|
// Wg is used to wait for all servers to shut down
|
||||||
Wg sync.WaitGroup
|
Wg sync.WaitGroup
|
||||||
|
|
||||||
// Whether HTTP2 is enabled or not
|
// Http2 indicates whether HTTP2 is enabled or not
|
||||||
Http2 bool // TODO: temporary flag until http2 is standard
|
Http2 bool // TODO: temporary flag until http2 is standard
|
||||||
|
|
||||||
// Quiet mode hides non-error initialization output
|
// Quiet mode hides non-error initialization output
|
||||||
|
|
|
@ -19,7 +19,8 @@ const (
|
||||||
DefaultPort = "2015"
|
DefaultPort = "2015"
|
||||||
DefaultRoot = "."
|
DefaultRoot = "."
|
||||||
|
|
||||||
// The default configuration file to load if none is specified
|
// DefaultConfigFile is the name of the configuration file that is loaded
|
||||||
|
// by default if no other file is specified.
|
||||||
DefaultConfigFile = "Caddyfile"
|
DefaultConfigFile = "Caddyfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -58,9 +58,8 @@ func (l *lexer) next() bool {
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return false
|
return false
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if quoted {
|
if quoted {
|
||||||
|
|
|
@ -57,7 +57,7 @@ func gitParse(c *Controller) (*git.Repo, error) {
|
||||||
repo.Path = filepath.Clean(c.Root + string(filepath.Separator) + args[1])
|
repo.Path = filepath.Clean(c.Root + string(filepath.Separator) + args[1])
|
||||||
fallthrough
|
fallthrough
|
||||||
case 1:
|
case 1:
|
||||||
repo.Url = args[0]
|
repo.URL = args[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
for c.NextBlock() {
|
for c.NextBlock() {
|
||||||
|
@ -66,7 +66,7 @@ func gitParse(c *Controller) (*git.Repo, error) {
|
||||||
if !c.NextArg() {
|
if !c.NextArg() {
|
||||||
return nil, c.ArgErr()
|
return nil, c.ArgErr()
|
||||||
}
|
}
|
||||||
repo.Url = c.Val()
|
repo.URL = c.Val()
|
||||||
case "path":
|
case "path":
|
||||||
if !c.NextArg() {
|
if !c.NextArg() {
|
||||||
return nil, c.ArgErr()
|
return nil, c.ArgErr()
|
||||||
|
@ -103,19 +103,19 @@ func gitParse(c *Controller) (*git.Repo, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if repo is not specified, return error
|
// if repo is not specified, return error
|
||||||
if repo.Url == "" {
|
if repo.URL == "" {
|
||||||
return nil, c.ArgErr()
|
return nil, c.ArgErr()
|
||||||
}
|
}
|
||||||
|
|
||||||
// if private key is not specified, convert repository url to https
|
// if private key is not specified, convert repository URL to https
|
||||||
// to avoid ssh authentication
|
// to avoid ssh authentication
|
||||||
// else validate git url
|
// else validate git URL
|
||||||
// Note: private key support not yet available on Windows
|
// Note: private key support not yet available on Windows
|
||||||
var err error
|
var err error
|
||||||
if repo.KeyPath == "" {
|
if repo.KeyPath == "" {
|
||||||
repo.Url, repo.Host, err = sanitizeHttp(repo.Url)
|
repo.URL, repo.Host, err = sanitizeHTTP(repo.URL)
|
||||||
} else {
|
} else {
|
||||||
repo.Url, repo.Host, err = sanitizeGit(repo.Url)
|
repo.URL, repo.Host, err = sanitizeGit(repo.URL)
|
||||||
// TODO add Windows support for private repos
|
// TODO add Windows support for private repos
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
return nil, fmt.Errorf("Private repository not yet supported on Windows")
|
return nil, fmt.Errorf("Private repository not yet supported on Windows")
|
||||||
|
@ -134,12 +134,12 @@ func gitParse(c *Controller) (*git.Repo, error) {
|
||||||
return repo, repo.Prepare()
|
return repo, repo.Prepare()
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitizeHttp cleans up repository url and converts to https format
|
// sanitizeHTTP cleans up repository URL and converts to https format
|
||||||
// if currently in ssh format.
|
// if currently in ssh format.
|
||||||
// Returns sanitized url, hostName (e.g. github.com, bitbucket.com)
|
// Returns sanitized url, hostName (e.g. github.com, bitbucket.com)
|
||||||
// and possible error
|
// and possible error
|
||||||
func sanitizeHttp(repoUrl string) (string, string, error) {
|
func sanitizeHTTP(repoURL string) (string, string, error) {
|
||||||
url, err := url.Parse(repoUrl)
|
url, err := url.Parse(repoURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
@ -148,46 +148,46 @@ func sanitizeHttp(repoUrl string) (string, string, error) {
|
||||||
url.Path = url.Path[len("git@"):]
|
url.Path = url.Path[len("git@"):]
|
||||||
i := strings.Index(url.Path, ":")
|
i := strings.Index(url.Path, ":")
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return "", "", fmt.Errorf("Invalid git url %s", repoUrl)
|
return "", "", fmt.Errorf("Invalid git url %s", repoURL)
|
||||||
}
|
}
|
||||||
url.Host = url.Path[:i]
|
url.Host = url.Path[:i]
|
||||||
url.Path = "/" + url.Path[i+1:]
|
url.Path = "/" + url.Path[i+1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
repoUrl = "https://" + url.Host + url.Path
|
repoURL = "https://" + url.Host + url.Path
|
||||||
|
|
||||||
// add .git suffix if missing
|
// add .git suffix if missing
|
||||||
if !strings.HasSuffix(repoUrl, ".git") {
|
if !strings.HasSuffix(repoURL, ".git") {
|
||||||
repoUrl += ".git"
|
repoURL += ".git"
|
||||||
}
|
}
|
||||||
|
|
||||||
return repoUrl, url.Host, nil
|
return repoURL, url.Host, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitizeGit cleans up repository url and converts to ssh format for private
|
// sanitizeGit cleans up repository url and converts to ssh format for private
|
||||||
// repositories if required.
|
// repositories if required.
|
||||||
// Returns sanitized url, hostName (e.g. github.com, bitbucket.com)
|
// Returns sanitized url, hostName (e.g. github.com, bitbucket.com)
|
||||||
// and possible error
|
// and possible error
|
||||||
func sanitizeGit(repoUrl string) (string, string, error) {
|
func sanitizeGit(repoURL string) (string, string, error) {
|
||||||
repoUrl = strings.TrimSpace(repoUrl)
|
repoURL = strings.TrimSpace(repoURL)
|
||||||
|
|
||||||
// check if valid ssh format
|
// check if valid ssh format
|
||||||
if !strings.HasPrefix(repoUrl, "git@") || strings.Index(repoUrl, ":") < len("git@a:") {
|
if !strings.HasPrefix(repoURL, "git@") || strings.Index(repoURL, ":") < len("git@a:") {
|
||||||
// check if valid http format and convert to ssh
|
// check if valid http format and convert to ssh
|
||||||
if url, err := url.Parse(repoUrl); err == nil && strings.HasPrefix(url.Scheme, "http") {
|
if url, err := url.Parse(repoURL); err == nil && strings.HasPrefix(url.Scheme, "http") {
|
||||||
repoUrl = fmt.Sprintf("git@%v:%v", url.Host, url.Path[1:])
|
repoURL = fmt.Sprintf("git@%v:%v", url.Host, url.Path[1:])
|
||||||
} else {
|
} else {
|
||||||
return "", "", fmt.Errorf("Invalid git url %s", repoUrl)
|
return "", "", fmt.Errorf("Invalid git url %s", repoURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hostUrl := repoUrl[len("git@"):]
|
hostURL := repoURL[len("git@"):]
|
||||||
i := strings.Index(hostUrl, ":")
|
i := strings.Index(hostURL, ":")
|
||||||
host := hostUrl[:i]
|
host := hostURL[:i]
|
||||||
|
|
||||||
// add .git suffix if missing
|
// add .git suffix if missing
|
||||||
if !strings.HasSuffix(repoUrl, ".git") {
|
if !strings.HasSuffix(repoURL, ".git") {
|
||||||
repoUrl += ".git"
|
repoURL += ".git"
|
||||||
}
|
}
|
||||||
|
|
||||||
return repoUrl, host, nil
|
return repoURL, host, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,29 +32,29 @@ func TestGitParse(t *testing.T) {
|
||||||
expected *git.Repo
|
expected *git.Repo
|
||||||
}{
|
}{
|
||||||
{`git git@github.com:user/repo`, false, &git.Repo{
|
{`git git@github.com:user/repo`, false, &git.Repo{
|
||||||
Url: "https://github.com/user/repo.git",
|
URL: "https://github.com/user/repo.git",
|
||||||
}},
|
}},
|
||||||
{`git github.com/user/repo`, false, &git.Repo{
|
{`git github.com/user/repo`, false, &git.Repo{
|
||||||
Url: "https://github.com/user/repo.git",
|
URL: "https://github.com/user/repo.git",
|
||||||
}},
|
}},
|
||||||
{`git git@github.com/user/repo`, true, nil},
|
{`git git@github.com/user/repo`, true, nil},
|
||||||
{`git http://github.com/user/repo`, false, &git.Repo{
|
{`git http://github.com/user/repo`, false, &git.Repo{
|
||||||
Url: "https://github.com/user/repo.git",
|
URL: "https://github.com/user/repo.git",
|
||||||
}},
|
}},
|
||||||
{`git https://github.com/user/repo`, false, &git.Repo{
|
{`git https://github.com/user/repo`, false, &git.Repo{
|
||||||
Url: "https://github.com/user/repo.git",
|
URL: "https://github.com/user/repo.git",
|
||||||
}},
|
}},
|
||||||
{`git http://github.com/user/repo {
|
{`git http://github.com/user/repo {
|
||||||
key ~/.key
|
key ~/.key
|
||||||
}`, false, &git.Repo{
|
}`, false, &git.Repo{
|
||||||
KeyPath: "~/.key",
|
KeyPath: "~/.key",
|
||||||
Url: "git@github.com:user/repo.git",
|
URL: "git@github.com:user/repo.git",
|
||||||
}},
|
}},
|
||||||
{`git git@github.com:user/repo {
|
{`git git@github.com:user/repo {
|
||||||
key ~/.key
|
key ~/.key
|
||||||
}`, false, &git.Repo{
|
}`, false, &git.Repo{
|
||||||
KeyPath: "~/.key",
|
KeyPath: "~/.key",
|
||||||
Url: "git@github.com:user/repo.git",
|
URL: "git@github.com:user/repo.git",
|
||||||
}},
|
}},
|
||||||
{`git `, true, nil},
|
{`git `, true, nil},
|
||||||
{`git {
|
{`git {
|
||||||
|
@ -66,7 +66,7 @@ func TestGitParse(t *testing.T) {
|
||||||
key ~/.key
|
key ~/.key
|
||||||
}`, false, &git.Repo{
|
}`, false, &git.Repo{
|
||||||
KeyPath: "~/.key",
|
KeyPath: "~/.key",
|
||||||
Url: "git@github.com:user/repo.git",
|
URL: "git@github.com:user/repo.git",
|
||||||
}},
|
}},
|
||||||
{`git {
|
{`git {
|
||||||
repo git@github.com:user/repo
|
repo git@github.com:user/repo
|
||||||
|
@ -74,7 +74,7 @@ func TestGitParse(t *testing.T) {
|
||||||
interval 600
|
interval 600
|
||||||
}`, false, &git.Repo{
|
}`, false, &git.Repo{
|
||||||
KeyPath: "~/.key",
|
KeyPath: "~/.key",
|
||||||
Url: "git@github.com:user/repo.git",
|
URL: "git@github.com:user/repo.git",
|
||||||
Interval: time.Second * 600,
|
Interval: time.Second * 600,
|
||||||
}},
|
}},
|
||||||
{`git {
|
{`git {
|
||||||
|
@ -82,7 +82,7 @@ func TestGitParse(t *testing.T) {
|
||||||
branch dev
|
branch dev
|
||||||
}`, false, &git.Repo{
|
}`, false, &git.Repo{
|
||||||
Branch: "dev",
|
Branch: "dev",
|
||||||
Url: "https://github.com/user/repo.git",
|
URL: "https://github.com/user/repo.git",
|
||||||
}},
|
}},
|
||||||
{`git {
|
{`git {
|
||||||
key ~/.key
|
key ~/.key
|
||||||
|
@ -93,7 +93,7 @@ func TestGitParse(t *testing.T) {
|
||||||
then echo hello world
|
then echo hello world
|
||||||
}`, false, &git.Repo{
|
}`, false, &git.Repo{
|
||||||
KeyPath: "~/.key",
|
KeyPath: "~/.key",
|
||||||
Url: "git@github.com:user/repo.git",
|
URL: "git@github.com:user/repo.git",
|
||||||
Then: "echo hello world",
|
Then: "echo hello world",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ func reposEqual(expected, repo *git.Repo) bool {
|
||||||
if expected.Then != "" && expected.Then != repo.Then {
|
if expected.Then != "" && expected.Then != repo.Then {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if expected.Url != "" && expected.Url != repo.Url {
|
if expected.URL != "" && expected.URL != repo.URL {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
caddylog "github.com/mholt/caddy/middleware/log"
|
caddylog "github.com/mholt/caddy/middleware/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Log sets up the logging middleware.
|
||||||
func Log(c *Controller) (middleware.Middleware, error) {
|
func Log(c *Controller) (middleware.Middleware, error) {
|
||||||
rules, err := logParse(c)
|
rules, err := logParse(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -42,22 +43,22 @@ func Log(c *Controller) (middleware.Middleware, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func logParse(c *Controller) ([]caddylog.LogRule, error) {
|
func logParse(c *Controller) ([]caddylog.Rule, error) {
|
||||||
var rules []caddylog.LogRule
|
var rules []caddylog.Rule
|
||||||
|
|
||||||
for c.Next() {
|
for c.Next() {
|
||||||
args := c.RemainingArgs()
|
args := c.RemainingArgs()
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
// Nothing specified; use defaults
|
// Nothing specified; use defaults
|
||||||
rules = append(rules, caddylog.LogRule{
|
rules = append(rules, caddylog.Rule{
|
||||||
PathScope: "/",
|
PathScope: "/",
|
||||||
OutputFile: caddylog.DefaultLogFilename,
|
OutputFile: caddylog.DefaultLogFilename,
|
||||||
Format: caddylog.DefaultLogFormat,
|
Format: caddylog.DefaultLogFormat,
|
||||||
})
|
})
|
||||||
} else if len(args) == 1 {
|
} else if len(args) == 1 {
|
||||||
// Only an output file specified
|
// Only an output file specified
|
||||||
rules = append(rules, caddylog.LogRule{
|
rules = append(rules, caddylog.Rule{
|
||||||
PathScope: "/",
|
PathScope: "/",
|
||||||
OutputFile: args[0],
|
OutputFile: args[0],
|
||||||
Format: caddylog.DefaultLogFormat,
|
Format: caddylog.DefaultLogFormat,
|
||||||
|
@ -78,7 +79,7 @@ func logParse(c *Controller) ([]caddylog.LogRule, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rules = append(rules, caddylog.LogRule{
|
rules = append(rules, caddylog.Rule{
|
||||||
PathScope: args[0],
|
PathScope: args[0],
|
||||||
OutputFile: args[1],
|
OutputFile: args[1],
|
||||||
Format: format,
|
Format: format,
|
||||||
|
|
|
@ -41,16 +41,14 @@ func (a BasicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
|
||||||
isAuthenticated = true
|
isAuthenticated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasAuth {
|
if hasAuth {
|
||||||
if !isAuthenticated {
|
if !isAuthenticated {
|
||||||
w.Header().Set("WWW-Authenticate", "Basic")
|
w.Header().Set("WWW-Authenticate", "Basic")
|
||||||
return http.StatusUnauthorized, nil
|
return http.StatusUnauthorized, nil
|
||||||
} else {
|
|
||||||
// "It's an older code, sir, but it checks out. I was about to clear them."
|
|
||||||
return a.Next.ServeHTTP(w, r)
|
|
||||||
}
|
}
|
||||||
|
// "It's an older code, sir, but it checks out. I was about to clear them."
|
||||||
|
return a.Next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass-thru when no paths match
|
// Pass-thru when no paths match
|
||||||
|
|
|
@ -27,13 +27,10 @@ func TestBasicAuth(t *testing.T) {
|
||||||
{"/testing", http.StatusUnauthorized, "ttest:test"},
|
{"/testing", http.StatusUnauthorized, "ttest:test"},
|
||||||
{"/testing", http.StatusOK, "test:ttest"},
|
{"/testing", http.StatusOK, "test:ttest"},
|
||||||
{"/testing", http.StatusUnauthorized, ""},
|
{"/testing", http.StatusUnauthorized, ""},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", test.from, nil)
|
req, err := http.NewRequest("GET", test.from, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: Could not create HTTP request %v", i, err)
|
t.Fatalf("Test %d: Could not create HTTP request %v", i, err)
|
||||||
|
@ -54,19 +51,17 @@ func TestBasicAuth(t *testing.T) {
|
||||||
headers := rec.Header()
|
headers := rec.Header()
|
||||||
if val, ok := headers["Www-Authenticate"]; ok {
|
if val, ok := headers["Www-Authenticate"]; ok {
|
||||||
if val[0] != "Basic" {
|
if val[0] != "Basic" {
|
||||||
t.Errorf("Test %d, Www-Authenticate should be %s provided %s", i, "Basic", val[0])
|
t.Errorf("Test %d, Www-Authenticate should be %s provided %s", i, "Basic", val[0])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("Test %d, should provide a header Www-Authenticate", i)
|
t.Errorf("Test %d, should provide a header Www-Authenticate", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func TestMultipleOverlappingRules(t *testing.T) {
|
func TestMultipleOverlappingRules(t *testing.T) {
|
||||||
rw := BasicAuth{
|
rw := BasicAuth{
|
||||||
Next: middleware.HandlerFunc(contentHandler),
|
Next: middleware.HandlerFunc(contentHandler),
|
||||||
|
@ -75,7 +70,7 @@ func TestMultipleOverlappingRules(t *testing.T) {
|
||||||
{Username: "t1", Password: "p2", Resources: []string{"/t/t"}},
|
{Username: "t1", Password: "p2", Resources: []string{"/t/t"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
from string
|
from string
|
||||||
result int
|
result int
|
||||||
|
@ -89,9 +84,8 @@ func TestMultipleOverlappingRules(t *testing.T) {
|
||||||
{"/t", http.StatusUnauthorized, "t1:p2"},
|
{"/t", http.StatusUnauthorized, "t1:p2"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", test.from, nil)
|
req, err := http.NewRequest("GET", test.from, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: Could not create HTTP request %v", i, err)
|
t.Fatalf("Test %d: Could not create HTTP request %v", i, err)
|
||||||
|
@ -108,14 +102,12 @@ func TestMultipleOverlappingRules(t *testing.T) {
|
||||||
t.Errorf("Test %d: Expected Header '%d' but was '%d'",
|
t.Errorf("Test %d: Expected Header '%d' but was '%d'",
|
||||||
i, test.result, result)
|
i, test.result, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func contentHandler(w http.ResponseWriter, r *http.Request) (int, error) {
|
func contentHandler(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
fmt.Fprintf(w, r.URL.String())
|
fmt.Fprintf(w, r.URL.String())
|
||||||
return http.StatusOK, nil
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,10 +55,12 @@ type FileInfo struct {
|
||||||
Mode os.FileMode
|
Mode os.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HumanSize returns the size of the file as a human-readable string.
|
||||||
func (fi FileInfo) HumanSize() string {
|
func (fi FileInfo) HumanSize() string {
|
||||||
return humanize.Bytes(uint64(fi.Size))
|
return humanize.Bytes(uint64(fi.Size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HumanModTime returns the modified time of the file as a human-readable string.
|
||||||
func (fi FileInfo) HumanModTime(format string) string {
|
func (fi FileInfo) HumanModTime(format string) string {
|
||||||
return fi.ModTime.Format(format)
|
return fi.ModTime.Format(format)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Package extension is middleware for clean URLs.
|
// Package extensions contains middleware for clean URLs.
|
||||||
//
|
//
|
||||||
// The root path of the site is passed in as well as possible extensions
|
// The root path of the site is passed in as well as possible extensions
|
||||||
// to try internally for paths requested that don't match an existing
|
// to try internally for paths requested that don't match an existing
|
||||||
|
|
|
@ -89,10 +89,10 @@ type header struct {
|
||||||
// not synchronized because we don't care what the contents are
|
// not synchronized because we don't care what the contents are
|
||||||
var pad [maxPad]byte
|
var pad [maxPad]byte
|
||||||
|
|
||||||
func (h *header) init(recType uint8, reqId uint16, contentLength int) {
|
func (h *header) init(recType uint8, reqID uint16, contentLength int) {
|
||||||
h.Version = 1
|
h.Version = 1
|
||||||
h.Type = recType
|
h.Type = recType
|
||||||
h.Id = reqId
|
h.Id = reqID
|
||||||
h.ContentLength = uint16(contentLength)
|
h.ContentLength = uint16(contentLength)
|
||||||
h.PaddingLength = uint8(-contentLength & 7)
|
h.PaddingLength = uint8(-contentLength & 7)
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ type FCGIClient struct {
|
||||||
reqId uint16
|
reqId uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connects to the fcgi responder at the specified network address.
|
// Dial connects to the fcgi responder at the specified network address.
|
||||||
// See func net.Dial for a description of the network and address parameters.
|
// See func net.Dial for a description of the network and address parameters.
|
||||||
func Dial(network, address string) (fcgi *FCGIClient, err error) {
|
func Dial(network, address string) (fcgi *FCGIClient, err error) {
|
||||||
var conn net.Conn
|
var conn net.Conn
|
||||||
|
@ -154,43 +154,43 @@ func Dial(network, address string) (fcgi *FCGIClient, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close fcgi connnection
|
// Close closes fcgi connnection
|
||||||
func (this *FCGIClient) Close() {
|
func (c *FCGIClient) Close() {
|
||||||
this.rwc.Close()
|
c.rwc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
|
func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
|
||||||
this.mutex.Lock()
|
c.mutex.Lock()
|
||||||
defer this.mutex.Unlock()
|
defer c.mutex.Unlock()
|
||||||
this.buf.Reset()
|
c.buf.Reset()
|
||||||
this.h.init(recType, this.reqId, len(content))
|
c.h.init(recType, c.reqId, len(content))
|
||||||
if err := binary.Write(&this.buf, binary.BigEndian, this.h); err != nil {
|
if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := this.buf.Write(content); err != nil {
|
if _, err := c.buf.Write(content); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := this.buf.Write(pad[:this.h.PaddingLength]); err != nil {
|
if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = this.rwc.Write(this.buf.Bytes())
|
_, err = c.rwc.Write(c.buf.Bytes())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FCGIClient) writeBeginRequest(role uint16, flags uint8) error {
|
func (c *FCGIClient) writeBeginRequest(role uint16, flags uint8) error {
|
||||||
b := [8]byte{byte(role >> 8), byte(role), flags}
|
b := [8]byte{byte(role >> 8), byte(role), flags}
|
||||||
return this.writeRecord(FCGI_BEGIN_REQUEST, b[:])
|
return c.writeRecord(FCGI_BEGIN_REQUEST, b[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FCGIClient) writeEndRequest(appStatus int, protocolStatus uint8) error {
|
func (c *FCGIClient) writeEndRequest(appStatus int, protocolStatus uint8) error {
|
||||||
b := make([]byte, 8)
|
b := make([]byte, 8)
|
||||||
binary.BigEndian.PutUint32(b, uint32(appStatus))
|
binary.BigEndian.PutUint32(b, uint32(appStatus))
|
||||||
b[4] = protocolStatus
|
b[4] = protocolStatus
|
||||||
return this.writeRecord(FCGI_END_REQUEST, b)
|
return c.writeRecord(FCGI_END_REQUEST, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FCGIClient) writePairs(recType uint8, pairs map[string]string) error {
|
func (c *FCGIClient) writePairs(recType uint8, pairs map[string]string) error {
|
||||||
w := newWriter(this, recType)
|
w := newWriter(c, recType)
|
||||||
b := make([]byte, 8)
|
b := make([]byte, 8)
|
||||||
nn := 0
|
nn := 0
|
||||||
for k, v := range pairs {
|
for k, v := range pairs {
|
||||||
|
@ -333,32 +333,32 @@ func (w *streamReader) Read(p []byte) (n int, err error) {
|
||||||
|
|
||||||
// Do made the request and returns a io.Reader that translates the data read
|
// Do made the request and returns a io.Reader that translates the data read
|
||||||
// from fcgi responder out of fcgi packet before returning it.
|
// from fcgi responder out of fcgi packet before returning it.
|
||||||
func (this *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err error) {
|
func (c *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err error) {
|
||||||
err = this.writeBeginRequest(uint16(FCGI_RESPONDER), 0)
|
err = c.writeBeginRequest(uint16(FCGI_RESPONDER), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = this.writePairs(FCGI_PARAMS, p)
|
err = c.writePairs(FCGI_PARAMS, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body := newWriter(this, FCGI_STDIN)
|
body := newWriter(c, FCGI_STDIN)
|
||||||
if req != nil {
|
if req != nil {
|
||||||
io.Copy(body, req)
|
io.Copy(body, req)
|
||||||
}
|
}
|
||||||
body.Close()
|
body.Close()
|
||||||
|
|
||||||
r = &streamReader{c: this}
|
r = &streamReader{c: c}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request returns a HTTP Response with Header and Body
|
// Request returns a HTTP Response with Header and Body
|
||||||
// from fcgi responder
|
// from fcgi responder
|
||||||
func (this *FCGIClient) Request(p map[string]string, req io.Reader) (resp *http.Response, err error) {
|
func (c *FCGIClient) Request(p map[string]string, req io.Reader) (resp *http.Response, err error) {
|
||||||
|
|
||||||
r, err := this.Do(p, req)
|
r, err := c.Do(p, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -394,40 +394,39 @@ func (this *FCGIClient) Request(p map[string]string, req io.Reader) (resp *http.
|
||||||
} else {
|
} else {
|
||||||
resp.Body = ioutil.NopCloser(rb)
|
resp.Body = ioutil.NopCloser(rb)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get issues a GET request to the fcgi responder.
|
// Get issues a GET request to the fcgi responder.
|
||||||
func (this *FCGIClient) Get(p map[string]string) (resp *http.Response, err error) {
|
func (c *FCGIClient) Get(p map[string]string) (resp *http.Response, err error) {
|
||||||
|
|
||||||
p["REQUEST_METHOD"] = "GET"
|
p["REQUEST_METHOD"] = "GET"
|
||||||
p["CONTENT_LENGTH"] = "0"
|
p["CONTENT_LENGTH"] = "0"
|
||||||
|
|
||||||
return this.Request(p, nil)
|
return c.Request(p, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Head issues a HEAD request to the fcgi responder.
|
// Head issues a HEAD request to the fcgi responder.
|
||||||
func (this *FCGIClient) Head(p map[string]string) (resp *http.Response, err error) {
|
func (c *FCGIClient) Head(p map[string]string) (resp *http.Response, err error) {
|
||||||
|
|
||||||
p["REQUEST_METHOD"] = "HEAD"
|
p["REQUEST_METHOD"] = "HEAD"
|
||||||
p["CONTENT_LENGTH"] = "0"
|
p["CONTENT_LENGTH"] = "0"
|
||||||
|
|
||||||
return this.Request(p, nil)
|
return c.Request(p, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options issues an OPTIONS request to the fcgi responder.
|
// Options issues an OPTIONS request to the fcgi responder.
|
||||||
func (this *FCGIClient) Options(p map[string]string) (resp *http.Response, err error) {
|
func (c *FCGIClient) Options(p map[string]string) (resp *http.Response, err error) {
|
||||||
|
|
||||||
p["REQUEST_METHOD"] = "OPTIONS"
|
p["REQUEST_METHOD"] = "OPTIONS"
|
||||||
p["CONTENT_LENGTH"] = "0"
|
p["CONTENT_LENGTH"] = "0"
|
||||||
|
|
||||||
return this.Request(p, nil)
|
return c.Request(p, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post issues a POST request to the fcgi responder. with request body
|
// Post issues a POST request to the fcgi responder. with request body
|
||||||
// in the format that bodyType specified
|
// in the format that bodyType specified
|
||||||
func (this *FCGIClient) Post(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
func (c *FCGIClient) Post(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
||||||
|
|
||||||
if len(p["REQUEST_METHOD"]) == 0 || p["REQUEST_METHOD"] == "GET" {
|
if len(p["REQUEST_METHOD"]) == 0 || p["REQUEST_METHOD"] == "GET" {
|
||||||
p["REQUEST_METHOD"] = "POST"
|
p["REQUEST_METHOD"] = "POST"
|
||||||
|
@ -439,44 +438,44 @@ func (this *FCGIClient) Post(p map[string]string, bodyType string, body io.Reade
|
||||||
p["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
p["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.Request(p, body)
|
return c.Request(p, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put issues a PUT request to the fcgi responder.
|
// Put issues a PUT request to the fcgi responder.
|
||||||
func (this *FCGIClient) Put(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
func (c *FCGIClient) Put(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
||||||
|
|
||||||
p["REQUEST_METHOD"] = "PUT"
|
p["REQUEST_METHOD"] = "PUT"
|
||||||
|
|
||||||
return this.Post(p, bodyType, body, l)
|
return c.Post(p, bodyType, body, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch issues a PATCH request to the fcgi responder.
|
// Patch issues a PATCH request to the fcgi responder.
|
||||||
func (this *FCGIClient) Patch(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
func (c *FCGIClient) Patch(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
||||||
|
|
||||||
p["REQUEST_METHOD"] = "PATCH"
|
p["REQUEST_METHOD"] = "PATCH"
|
||||||
|
|
||||||
return this.Post(p, bodyType, body, l)
|
return c.Post(p, bodyType, body, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete issues a DELETE request to the fcgi responder.
|
// Delete issues a DELETE request to the fcgi responder.
|
||||||
func (this *FCGIClient) Delete(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
func (c *FCGIClient) Delete(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
||||||
|
|
||||||
p["REQUEST_METHOD"] = "DELETE"
|
p["REQUEST_METHOD"] = "DELETE"
|
||||||
|
|
||||||
return this.Post(p, bodyType, body, l)
|
return c.Post(p, bodyType, body, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostForm issues a POST to the fcgi responder, with form
|
// PostForm issues a POST to the fcgi responder, with form
|
||||||
// as a string key to a list values (url.Values)
|
// as a string key to a list values (url.Values)
|
||||||
func (this *FCGIClient) PostForm(p map[string]string, data url.Values) (resp *http.Response, err error) {
|
func (c *FCGIClient) PostForm(p map[string]string, data url.Values) (resp *http.Response, err error) {
|
||||||
body := bytes.NewReader([]byte(data.Encode()))
|
body := bytes.NewReader([]byte(data.Encode()))
|
||||||
return this.Post(p, "application/x-www-form-urlencoded", body, body.Len())
|
return c.Post(p, "application/x-www-form-urlencoded", body, body.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostFile issues a POST to the fcgi responder in multipart(RFC 2046) standard,
|
// PostFile issues a POST to the fcgi responder in multipart(RFC 2046) standard,
|
||||||
// with form as a string key to a list values (url.Values),
|
// with form as a string key to a list values (url.Values),
|
||||||
// and/or with file as a string key to a list file path.
|
// and/or with file as a string key to a list file path.
|
||||||
func (this *FCGIClient) PostFile(p map[string]string, data url.Values, file map[string]string) (resp *http.Response, err error) {
|
func (c *FCGIClient) PostFile(p map[string]string, data url.Values, file map[string]string) (resp *http.Response, err error) {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
writer := multipart.NewWriter(buf)
|
writer := multipart.NewWriter(buf)
|
||||||
bodyType := writer.FormDataContentType()
|
bodyType := writer.FormDataContentType()
|
||||||
|
@ -509,7 +508,7 @@ func (this *FCGIClient) PostFile(p map[string]string, data url.Values, file map[
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.Post(p, bodyType, buf, buf.Len())
|
return c.Post(p, bodyType, buf, buf.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether chunked is part of the encodings stack
|
// Checks whether chunked is part of the encodings stack
|
||||||
|
|
|
@ -28,7 +28,7 @@ var shell string
|
||||||
|
|
||||||
// initMutex prevents parallel attempt to validate
|
// initMutex prevents parallel attempt to validate
|
||||||
// git requirements.
|
// git requirements.
|
||||||
var initMutex sync.Mutex = sync.Mutex{}
|
var initMutex = sync.Mutex{}
|
||||||
|
|
||||||
// Logger is used to log errors; if nil, the default log.Logger is used.
|
// Logger is used to log errors; if nil, the default log.Logger is used.
|
||||||
var Logger *log.Logger
|
var Logger *log.Logger
|
||||||
|
@ -44,7 +44,7 @@ func logger() *log.Logger {
|
||||||
// Repo is the structure that holds required information
|
// Repo is the structure that holds required information
|
||||||
// of a git repository.
|
// of a git repository.
|
||||||
type Repo struct {
|
type Repo struct {
|
||||||
Url string // Repository URL
|
URL string // Repository URL
|
||||||
Path string // Directory to pull to
|
Path string // Directory to pull to
|
||||||
Host string // Git domain host e.g. github.com
|
Host string // Git domain host e.g. github.com
|
||||||
Branch string // Git branch
|
Branch string // Git branch
|
||||||
|
@ -94,7 +94,7 @@ func (r *Repo) Pull() error {
|
||||||
|
|
||||||
// Pull performs git clone, or git pull if repository exists
|
// Pull performs git clone, or git pull if repository exists
|
||||||
func (r *Repo) pull() error {
|
func (r *Repo) pull() error {
|
||||||
params := []string{"clone", "-b", r.Branch, r.Url, r.Path}
|
params := []string{"clone", "-b", r.Branch, r.URL, r.Path}
|
||||||
if r.pulled {
|
if r.pulled {
|
||||||
params = []string{"pull", "origin", r.Branch}
|
params = []string{"pull", "origin", r.Branch}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ func (r *Repo) pull() error {
|
||||||
if err = runCmd(gitBinary, params, dir); err == nil {
|
if err = runCmd(gitBinary, params, dir); err == nil {
|
||||||
r.pulled = true
|
r.pulled = true
|
||||||
r.lastPull = time.Now()
|
r.lastPull = time.Now()
|
||||||
logger().Printf("%v pulled.\n", r.Url)
|
logger().Printf("%v pulled.\n", r.URL)
|
||||||
r.lastCommit, err = r.getMostRecentCommit()
|
r.lastCommit, err = r.getMostRecentCommit()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -122,11 +122,11 @@ func (r *Repo) pull() error {
|
||||||
// pullWithKey is used for private repositories and requires an ssh key.
|
// pullWithKey is used for private repositories and requires an ssh key.
|
||||||
// Note: currently only limited to Linux and OSX.
|
// Note: currently only limited to Linux and OSX.
|
||||||
func (r *Repo) pullWithKey(params []string) error {
|
func (r *Repo) pullWithKey(params []string) error {
|
||||||
var gitSsh, script gitos.File
|
var gitSSH, script gitos.File
|
||||||
// ensure temporary files deleted after usage
|
// ensure temporary files deleted after usage
|
||||||
defer func() {
|
defer func() {
|
||||||
if gitSsh != nil {
|
if gitSSH != nil {
|
||||||
gos.Remove(gitSsh.Name())
|
gos.Remove(gitSSH.Name())
|
||||||
}
|
}
|
||||||
if script != nil {
|
if script != nil {
|
||||||
gos.Remove(script.Name())
|
gos.Remove(script.Name())
|
||||||
|
@ -135,13 +135,13 @@ func (r *Repo) pullWithKey(params []string) error {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
// write git.sh script to temp file
|
// write git.sh script to temp file
|
||||||
gitSsh, err = writeScriptFile(gitWrapperScript())
|
gitSSH, err = writeScriptFile(gitWrapperScript())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// write git clone bash script to file
|
// write git clone bash script to file
|
||||||
script, err = writeScriptFile(bashScript(gitSsh.Name(), r, params))
|
script, err = writeScriptFile(bashScript(gitSSH.Name(), r, params))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ func (r *Repo) pullWithKey(params []string) error {
|
||||||
if err = runCmd(script.Name(), nil, dir); err == nil {
|
if err = runCmd(script.Name(), nil, dir); err == nil {
|
||||||
r.pulled = true
|
r.pulled = true
|
||||||
r.lastPull = time.Now()
|
r.lastPull = time.Now()
|
||||||
logger().Printf("%v pulled.\n", r.Url)
|
logger().Printf("%v pulled.\n", r.URL)
|
||||||
r.lastCommit, err = r.getMostRecentCommit()
|
r.lastCommit, err = r.getMostRecentCommit()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -181,13 +181,13 @@ func (r *Repo) Prepare() error {
|
||||||
|
|
||||||
if isGit {
|
if isGit {
|
||||||
// check if same repository
|
// check if same repository
|
||||||
var repoUrl string
|
var repoURL string
|
||||||
if repoUrl, err = r.getRepoUrl(); err == nil {
|
if repoURL, err = r.getRepoURL(); err == nil {
|
||||||
// add .git suffix if missing for adequate comparison.
|
// add .git suffix if missing for adequate comparison.
|
||||||
if !strings.HasSuffix(repoUrl, ".git") {
|
if !strings.HasSuffix(repoURL, ".git") {
|
||||||
repoUrl += ".git"
|
repoURL += ".git"
|
||||||
}
|
}
|
||||||
if repoUrl == r.Url {
|
if repoURL == r.URL {
|
||||||
r.pulled = true
|
r.pulled = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ func (r *Repo) Prepare() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot retrieve repo url for %v Error: %v", r.Path, err)
|
return fmt.Errorf("Cannot retrieve repo url for %v Error: %v", r.Path, err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Another git repo '%v' exists at %v", repoUrl, r.Path)
|
return fmt.Errorf("Another git repo '%v' exists at %v", repoURL, r.Path)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Cannot git clone into %v, directory not empty.", r.Path)
|
return fmt.Errorf("Cannot git clone into %v, directory not empty.", r.Path)
|
||||||
}
|
}
|
||||||
|
@ -211,8 +211,8 @@ func (r *Repo) getMostRecentCommit() (string, error) {
|
||||||
return runCmdOutput(c, args, r.Path)
|
return runCmdOutput(c, args, r.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRepoUrl retrieves remote origin url for the git repository at path
|
// getRepoURL retrieves remote origin url for the git repository at path
|
||||||
func (r *Repo) getRepoUrl() (string, error) {
|
func (r *Repo) getRepoURL() (string, error) {
|
||||||
_, err := gos.Stat(r.Path)
|
_, err := gos.Stat(r.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -63,7 +63,7 @@ func TestGit(t *testing.T) {
|
||||||
// prepare
|
// prepare
|
||||||
repos := []*Repo{
|
repos := []*Repo{
|
||||||
nil,
|
nil,
|
||||||
&Repo{Path: "gitdir", Url: "success.git"},
|
&Repo{Path: "gitdir", URL: "success.git"},
|
||||||
}
|
}
|
||||||
for _, r := range repos {
|
for _, r := range repos {
|
||||||
repo := createRepo(r)
|
repo := createRepo(r)
|
||||||
|
@ -79,26 +79,26 @@ func TestGit(t *testing.T) {
|
||||||
output string
|
output string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
&Repo{Path: "gitdir", Url: "git@github.com:user/repo.git", KeyPath: "~/.key", Then: "echo Hello"},
|
&Repo{Path: "gitdir", URL: "git@github.com:user/repo.git", KeyPath: "~/.key", Then: "echo Hello"},
|
||||||
`git@github.com:user/repo.git pulled.
|
`git@github.com:user/repo.git pulled.
|
||||||
Command echo Hello successful.
|
Command echo Hello successful.
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
&Repo{Path: "gitdir", Url: "https://github.com/user/repo.git", Then: "echo Hello"},
|
&Repo{Path: "gitdir", URL: "https://github.com/user/repo.git", Then: "echo Hello"},
|
||||||
`https://github.com/user/repo.git pulled.
|
`https://github.com/user/repo.git pulled.
|
||||||
Command echo Hello successful.
|
Command echo Hello successful.
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
&Repo{Url: "git@github.com:user/repo"},
|
&Repo{URL: "git@github.com:user/repo"},
|
||||||
`git@github.com:user/repo pulled.
|
`git@github.com:user/repo pulled.
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
gittest.CmdOutput = test.repo.Url
|
gittest.CmdOutput = test.repo.URL
|
||||||
|
|
||||||
test.repo = createRepo(test.repo)
|
test.repo = createRepo(test.repo)
|
||||||
|
|
||||||
|
@ -117,8 +117,8 @@ Command echo Hello successful.
|
||||||
|
|
||||||
// pull with error
|
// pull with error
|
||||||
repos = []*Repo{
|
repos = []*Repo{
|
||||||
&Repo{Path: "gitdir", Url: "http://github.com:u/repo.git"},
|
&Repo{Path: "gitdir", URL: "http://github.com:u/repo.git"},
|
||||||
&Repo{Path: "gitdir", Url: "https://github.com/user/repo.git", Then: "echo Hello"},
|
&Repo{Path: "gitdir", URL: "https://github.com/user/repo.git", Then: "echo Hello"},
|
||||||
&Repo{Path: "gitdir"},
|
&Repo{Path: "gitdir"},
|
||||||
&Repo{Path: "gitdir", KeyPath: ".key"},
|
&Repo{Path: "gitdir", KeyPath: ".key"},
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ Command echo Hello successful.
|
||||||
|
|
||||||
func createRepo(r *Repo) *Repo {
|
func createRepo(r *Repo) *Repo {
|
||||||
repo := &Repo{
|
repo := &Repo{
|
||||||
Url: "git@github.com/user/test",
|
URL: "git@github.com/user/test",
|
||||||
Path: ".",
|
Path: ".",
|
||||||
Host: "github.com",
|
Host: "github.com",
|
||||||
Branch: "master",
|
Branch: "master",
|
||||||
|
@ -170,8 +170,8 @@ func createRepo(r *Repo) *Repo {
|
||||||
if r.Then != "" {
|
if r.Then != "" {
|
||||||
repo.Then = r.Then
|
repo.Then = r.Then
|
||||||
}
|
}
|
||||||
if r.Url != "" {
|
if r.URL != "" {
|
||||||
repo.Url = r.Url
|
repo.URL = r.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
return repo
|
return repo
|
||||||
|
|
|
@ -45,9 +45,8 @@ func (g Gzip) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
gz.WriteHeader(status)
|
gz.WriteHeader(status)
|
||||||
fmt.Fprintf(gz, "%d %s", status, http.StatusText(status))
|
fmt.Fprintf(gz, "%d %s", status, http.StatusText(status))
|
||||||
return 0, err
|
return 0, err
|
||||||
} else {
|
|
||||||
return status, err
|
|
||||||
}
|
}
|
||||||
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// gzipResponeWriter wraps the underlying Write method
|
// gzipResponeWriter wraps the underlying Write method
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// The package internal provides a simple middleware that (a) prevents access
|
// Package internal provides a simple middleware that (a) prevents access
|
||||||
// to internal locations and (b) allows to return files from internal location
|
// to internal locations and (b) allows to return files from internal location
|
||||||
// by setting a special header, e.g. in a proxy response.
|
// by setting a special header, e.g. in a proxy response.
|
||||||
package internal
|
package internal
|
||||||
|
@ -85,7 +85,6 @@ func (w internalResponseWriter) WriteHeader(code int) {
|
||||||
func (w internalResponseWriter) Write(b []byte) (int, error) {
|
func (w internalResponseWriter) Write(b []byte) (int, error) {
|
||||||
if isInternalRedirect(w) {
|
if isInternalRedirect(w) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
} else {
|
|
||||||
return w.ResponseWriter.Write(b)
|
|
||||||
}
|
}
|
||||||
|
return w.ResponseWriter.Write(b)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,10 @@ import (
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Logger is a basic request logging middleware.
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
Rules []LogRule
|
Rules []Rule
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
|
@ -26,7 +27,8 @@ func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
return l.Next.ServeHTTP(w, r)
|
return l.Next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogRule struct {
|
// Rule configures the logging middleware.
|
||||||
|
type Rule struct {
|
||||||
PathScope string
|
PathScope string
|
||||||
OutputFile string
|
OutputFile string
|
||||||
Format string
|
Format string
|
||||||
|
|
|
@ -31,9 +31,9 @@ type Markdown struct {
|
||||||
IndexFiles []string
|
IndexFiles []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to check if a file is an index file
|
// IsIndexFile checks to see if a file is an index file
|
||||||
func (m Markdown) IsIndexFile(file string) bool {
|
func (md Markdown) IsIndexFile(file string) bool {
|
||||||
for _, f := range m.IndexFiles {
|
for _, f := range md.IndexFiles {
|
||||||
if f == file {
|
if f == file {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -105,12 +105,11 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
|
||||||
if html, err := ioutil.ReadFile(filepath); err == nil {
|
if html, err := ioutil.ReadFile(filepath); err == nil {
|
||||||
w.Write(html)
|
w.Write(html)
|
||||||
return http.StatusOK, nil
|
return http.StatusOK, nil
|
||||||
} else {
|
|
||||||
if os.IsPermission(err) {
|
|
||||||
return http.StatusForbidden, err
|
|
||||||
}
|
|
||||||
return http.StatusNotFound, nil
|
|
||||||
}
|
}
|
||||||
|
if os.IsPermission(err) {
|
||||||
|
return http.StatusForbidden, err
|
||||||
|
}
|
||||||
|
return http.StatusNotFound, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,8 @@ func (j *JSONMetadataParser) Parse(b []byte) ([]byte, error) {
|
||||||
return buf[:n], nil
|
return buf[:n], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parsed metadata.
|
// Metadata returns parsed metadata. It should be called
|
||||||
// Should be called after a call to Parse returns no error
|
// only after a call to Parse returns without error.
|
||||||
func (j *JSONMetadataParser) Metadata() Metadata {
|
func (j *JSONMetadataParser) Metadata() Metadata {
|
||||||
return j.metadata
|
return j.metadata
|
||||||
}
|
}
|
||||||
|
@ -123,8 +123,8 @@ func (t *TOMLMetadataParser) Parse(b []byte) ([]byte, error) {
|
||||||
return markdown, nil
|
return markdown, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parsed metadata.
|
// Metadata returns parsed metadata. It should be called
|
||||||
// Should be called after a call to Parse returns no error
|
// only after a call to Parse returns without error.
|
||||||
func (t *TOMLMetadataParser) Metadata() Metadata {
|
func (t *TOMLMetadataParser) Metadata() Metadata {
|
||||||
return t.metadata
|
return t.metadata
|
||||||
}
|
}
|
||||||
|
@ -171,8 +171,8 @@ func (y *YAMLMetadataParser) Parse(b []byte) ([]byte, error) {
|
||||||
return markdown, nil
|
return markdown, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parsed metadata.
|
// Metadata returns parsed metadata. It should be called
|
||||||
// Should be called after a call to Parse returns no error
|
// only after a call to Parse returns without error.
|
||||||
func (y *YAMLMetadataParser) Metadata() Metadata {
|
func (y *YAMLMetadataParser) Metadata() Metadata {
|
||||||
return y.metadata
|
return y.metadata
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import "strings"
|
||||||
// Path represents a URI path, maybe with pattern characters.
|
// Path represents a URI path, maybe with pattern characters.
|
||||||
type Path string
|
type Path string
|
||||||
|
|
||||||
|
// Matches checks to see if other matches p.
|
||||||
|
//
|
||||||
// Path matching will probably not always be a direct
|
// Path matching will probably not always be a direct
|
||||||
// comparison; this method assures that paths can be
|
// comparison; this method assures that paths can be
|
||||||
// easily and consistently matched.
|
// easily and consistently matched.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HostPool is a collection of UpstreamHosts.
|
||||||
type HostPool []*UpstreamHost
|
type HostPool []*UpstreamHost
|
||||||
|
|
||||||
// Policy decides how a host will be selected from a pool.
|
// Policy decides how a host will be selected from a pool.
|
||||||
|
@ -12,9 +13,10 @@ type Policy interface {
|
||||||
Select(pool HostPool) *UpstreamHost
|
Select(pool HostPool) *UpstreamHost
|
||||||
}
|
}
|
||||||
|
|
||||||
// The random policy randomly selected an up host from the pool.
|
// Random is a policy that selects up hosts from a pool at random.
|
||||||
type Random struct{}
|
type Random struct{}
|
||||||
|
|
||||||
|
// Select selects an up host at random from the specified pool.
|
||||||
func (r *Random) Select(pool HostPool) *UpstreamHost {
|
func (r *Random) Select(pool HostPool) *UpstreamHost {
|
||||||
// instead of just generating a random index
|
// instead of just generating a random index
|
||||||
// this is done to prevent selecting a down host
|
// this is done to prevent selecting a down host
|
||||||
|
@ -37,11 +39,12 @@ func (r *Random) Select(pool HostPool) *UpstreamHost {
|
||||||
return randHost
|
return randHost
|
||||||
}
|
}
|
||||||
|
|
||||||
// The least_conn policy selects a host with the least connections.
|
// LeastConn is a policy that selects the host with the least connections.
|
||||||
// If multiple hosts have the least amount of connections, one is randomly
|
|
||||||
// chosen.
|
|
||||||
type LeastConn struct{}
|
type LeastConn struct{}
|
||||||
|
|
||||||
|
// Select selects the up host with the least number of connections in the
|
||||||
|
// pool. If more than one host has the same least number of connections,
|
||||||
|
// one of the hosts is chosen at random.
|
||||||
func (r *LeastConn) Select(pool HostPool) *UpstreamHost {
|
func (r *LeastConn) Select(pool HostPool) *UpstreamHost {
|
||||||
var bestHost *UpstreamHost
|
var bestHost *UpstreamHost
|
||||||
count := 0
|
count := 0
|
||||||
|
@ -71,11 +74,12 @@ func (r *LeastConn) Select(pool HostPool) *UpstreamHost {
|
||||||
return bestHost
|
return bestHost
|
||||||
}
|
}
|
||||||
|
|
||||||
// The round_robin policy selects a host based on round robin ordering.
|
// RoundRobin is a policy that selects hosts based on round robin ordering.
|
||||||
type RoundRobin struct {
|
type RoundRobin struct {
|
||||||
Robin uint32
|
Robin uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Select selects an up host from the pool using a round robin ordering scheme.
|
||||||
func (r *RoundRobin) Select(pool HostPool) *UpstreamHost {
|
func (r *RoundRobin) Select(pool HostPool) *UpstreamHost {
|
||||||
poolLen := uint32(len(pool))
|
poolLen := uint32(len(pool))
|
||||||
selection := atomic.AddUint32(&r.Robin, 1) % poolLen
|
selection := atomic.AddUint32(&r.Robin, 1) % poolLen
|
||||||
|
|
|
@ -19,18 +19,19 @@ type Proxy struct {
|
||||||
Upstreams []Upstream
|
Upstreams []Upstream
|
||||||
}
|
}
|
||||||
|
|
||||||
// An upstream manages a pool of proxy upstream hosts. Select should return a
|
// Upstream manages a pool of proxy upstream hosts. Select should return a
|
||||||
// suitable upstream host, or nil if no such hosts are available.
|
// suitable upstream host, or nil if no such hosts are available.
|
||||||
type Upstream interface {
|
type Upstream interface {
|
||||||
//The path this upstream host should be routed on
|
// The path this upstream host should be routed on
|
||||||
From() string
|
From() string
|
||||||
// Selects an upstream host to be routed to.
|
// Selects an upstream host to be routed to.
|
||||||
Select() *UpstreamHost
|
Select() *UpstreamHost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpstreamHostDownFunc can be used to customize how Down behaves.
|
||||||
type UpstreamHostDownFunc func(*UpstreamHost) bool
|
type UpstreamHostDownFunc func(*UpstreamHost) bool
|
||||||
|
|
||||||
// An UpstreamHost represents a single proxy upstream
|
// UpstreamHost represents a single proxy upstream
|
||||||
type UpstreamHost struct {
|
type UpstreamHost struct {
|
||||||
// The hostname of this upstream host
|
// The hostname of this upstream host
|
||||||
Name string
|
Name string
|
||||||
|
@ -43,6 +44,9 @@ type UpstreamHost struct {
|
||||||
CheckDown UpstreamHostDownFunc
|
CheckDown UpstreamHostDownFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Down checks whether the upstream host is down or not.
|
||||||
|
// Down will try to use uh.CheckDown first, and will fall
|
||||||
|
// back to some default criteria if necessary.
|
||||||
func (uh *UpstreamHost) Down() bool {
|
func (uh *UpstreamHost) Down() bool {
|
||||||
if uh.CheckDown == nil {
|
if uh.CheckDown == nil {
|
||||||
// Default settings
|
// Default settings
|
||||||
|
@ -70,10 +74,10 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
proxy := host.ReverseProxy
|
proxy := host.ReverseProxy
|
||||||
r.Host = host.Name
|
r.Host = host.Name
|
||||||
|
|
||||||
if baseUrl, err := url.Parse(host.Name); err == nil {
|
if baseURL, err := url.Parse(host.Name); err == nil {
|
||||||
r.Host = baseUrl.Host
|
r.Host = baseURL.Host
|
||||||
if proxy == nil {
|
if proxy == nil {
|
||||||
proxy = NewSingleHostReverseProxy(baseUrl)
|
proxy = NewSingleHostReverseProxy(baseURL)
|
||||||
}
|
}
|
||||||
} else if proxy == nil {
|
} else if proxy == nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mholt/caddy/config/parse"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -9,6 +8,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/config/parse"
|
||||||
)
|
)
|
||||||
|
|
||||||
type staticUpstream struct {
|
type staticUpstream struct {
|
||||||
|
@ -24,7 +25,7 @@ type staticUpstream struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newStaticUpstreams parses the configuration input and sets up
|
// NewStaticUpstreams parses the configuration input and sets up
|
||||||
// static upstreams for the proxy middleware.
|
// static upstreams for the proxy middleware.
|
||||||
func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
||||||
var upstreams []Upstream
|
var upstreams []Upstream
|
||||||
|
@ -130,8 +131,8 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
||||||
}
|
}
|
||||||
}(upstream),
|
}(upstream),
|
||||||
}
|
}
|
||||||
if baseUrl, err := url.Parse(uh.Name); err == nil {
|
if baseURL, err := url.Parse(uh.Name); err == nil {
|
||||||
uh.ReverseProxy = NewSingleHostReverseProxy(baseUrl)
|
uh.ReverseProxy = NewSingleHostReverseProxy(baseURL)
|
||||||
} else {
|
} else {
|
||||||
return upstreams, err
|
return upstreams, err
|
||||||
}
|
}
|
||||||
|
@ -152,8 +153,8 @@ func (u *staticUpstream) From() string {
|
||||||
|
|
||||||
func (u *staticUpstream) healthCheck() {
|
func (u *staticUpstream) healthCheck() {
|
||||||
for _, host := range u.Hosts {
|
for _, host := range u.Hosts {
|
||||||
hostUrl := host.Name + u.HealthCheck.Path
|
hostURL := host.Name + u.HealthCheck.Path
|
||||||
if r, err := http.Get(hostUrl); err == nil {
|
if r, err := http.Get(hostURL); err == nil {
|
||||||
io.Copy(ioutil.Discard, r.Body)
|
io.Copy(ioutil.Discard, r.Body)
|
||||||
r.Body.Close()
|
r.Body.Close()
|
||||||
host.Unhealthy = r.StatusCode < 200 || r.StatusCode >= 400
|
host.Unhealthy = r.StatusCode < 200 || r.StatusCode >= 400
|
||||||
|
@ -199,7 +200,6 @@ func (u *staticUpstream) Select() *UpstreamHost {
|
||||||
|
|
||||||
if u.Policy == nil {
|
if u.Policy == nil {
|
||||||
return (&Random{}).Select(pool)
|
return (&Random{}).Select(pool)
|
||||||
} else {
|
|
||||||
return u.Policy.Select(pool)
|
|
||||||
}
|
}
|
||||||
|
return u.Policy.Select(pool)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,13 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mholt/caddy/middleware"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Rewrite is middleware to rewrite request locations internally before being handled.
|
// Rewrite is middleware to rewrite request locations internally before being handled.
|
||||||
|
@ -96,7 +97,7 @@ func NewRegexpRule(base, pattern, to string, ext []string) (*RegexpRule, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// regexpVars are variables that can be used for To (rewrite destination path).
|
// regexpVars are variables that can be used for To (rewrite destination path).
|
||||||
var regexpVars []string = []string{
|
var regexpVars = []string{
|
||||||
"{path}",
|
"{path}",
|
||||||
"{query}",
|
"{query}",
|
||||||
"{file}",
|
"{file}",
|
||||||
|
|
|
@ -6,8 +6,9 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mholt/caddy/middleware"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRewrite(t *testing.T) {
|
func TestRewrite(t *testing.T) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ type (
|
||||||
Sockets []Config
|
Sockets []Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// WSConfig holds the configuration for a single websocket
|
// Config holds the configuration for a single websocket
|
||||||
// endpoint which may serve multiple websocket connections.
|
// endpoint which may serve multiple websocket connections.
|
||||||
Config struct {
|
Config struct {
|
||||||
Path string
|
Path string
|
||||||
|
@ -50,9 +50,11 @@ func (ws WebSockets) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// See CGI spec, 4.1.4
|
// GatewayInterface is the dialect of CGI being used by the server
|
||||||
|
// to communicate with the script. See CGI spec, 4.1.4
|
||||||
GatewayInterface string
|
GatewayInterface string
|
||||||
|
|
||||||
// See CGI spec, 4.1.17
|
// ServerSoftware is the name and version of the information server
|
||||||
|
// software making the CGI request. See CGI spec, 4.1.17
|
||||||
ServerSoftware string
|
ServerSoftware string
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/mholt/caddy/middleware/browse"
|
"github.com/mholt/caddy/middleware/browse"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This 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.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -28,15 +28,16 @@ type fileHandler struct {
|
||||||
hide []string // list of files to treat as "Not Found"
|
hide []string // list of files to treat as "Not Found"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *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
|
upath := r.URL.Path
|
||||||
if !strings.HasPrefix(upath, "/") {
|
if !strings.HasPrefix(upath, "/") {
|
||||||
upath = "/" + upath
|
upath = "/" + upath
|
||||||
r.URL.Path = upath
|
r.URL.Path = upath
|
||||||
}
|
}
|
||||||
return f.serveFile(w, r, path.Clean(upath))
|
return fh.serveFile(w, r, path.Clean(upath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serveFile writes the specified file to the HTTP response.
|
||||||
// name is '/'-separated, not filepath.Separator.
|
// name is '/'-separated, not filepath.Separator.
|
||||||
func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name string) (int, error) {
|
func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name string) (int, error) {
|
||||||
f, err := fh.root.Open(name)
|
f, err := fh.root.Open(name)
|
||||||
|
|
|
@ -97,9 +97,8 @@ func (s *Server) Serve() error {
|
||||||
tlsConfigs = append(tlsConfigs, vh.config.TLS)
|
tlsConfigs = append(tlsConfigs, vh.config.TLS)
|
||||||
}
|
}
|
||||||
return ListenAndServeTLSWithSNI(server, tlsConfigs)
|
return ListenAndServeTLSWithSNI(server, tlsConfigs)
|
||||||
} else {
|
|
||||||
return server.ListenAndServe()
|
|
||||||
}
|
}
|
||||||
|
return server.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeTLSWithSNI serves TLS with Server Name Indication (SNI) support, which allows
|
// ListenAndServeTLSWithSNI serves TLS with Server Name Indication (SNI) support, which allows
|
||||||
|
|
Loading…
Reference in a new issue