diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 6a3329c..7f43bd9 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -7,6 +7,10 @@ "ImportPath": "github.com/codeskyblue/kexec", "Rev": "098ccba5e5e7e676f3631983c5ea931a3592871f" }, + { + "ImportPath": "github.com/elazarl/go-bindata-assetfs", + "Rev": "d5cac425555ca5cf00694df246e04f05e6a55150" + }, { "ImportPath": "github.com/equinox-io/equinox", "Rev": "6f97d0d3970881d3e53dd6f547a41109eb055e54" @@ -39,6 +43,10 @@ "ImportPath": "github.com/go-yaml/yaml", "Rev": "e4d366fc3c7938e2958e662b4258c7a89e1f0e3e" }, + { + "ImportPath": "github.com/goji/httpauth", + "Rev": "2da839ab0f4df05a6db5eb277995589dadbd4fb9" + }, { "ImportPath": "github.com/gorilla/context", "Rev": "1c83b3eabd45b6d76072b66b746c20815fb2872d" diff --git a/README.md b/README.md index af4a6bb..49830d5 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,31 @@ $ gosuv status Server is running ``` -Open web to see the manager page. +Open web to see the manager page. ![gosuv web](docs/gosuv.gif) ## Configuration Default config file stored in directory `$HOME/.gosuv/` +- file `programs.yml` contains all program settings. +- file `config.yml` contains server config + +File `config.yml` can be generated by `gosuv conftest` + +Example config.yaml + +``` +server: + httpauth: + enabled: false + username: uu + password: pp + addr: :11313 +client: + server_url: http://localhost:11313 +``` + ## Design HTTP is follow the RESTFul guide. diff --git a/config.go b/config.go index 0293619..1fc2c6d 100644 --- a/config.go +++ b/config.go @@ -11,6 +11,7 @@ import ( type Configuration struct { Server struct { HttpAuth struct { + Enabled bool `yaml:"enabled"` User string `yaml:"username"` Password string `yaml:"password"` } `yaml:"httpauth"` diff --git a/gosuv.go b/gosuv.go index 2e62116..ea5d720 100644 --- a/gosuv.go +++ b/gosuv.go @@ -11,6 +11,7 @@ import ( "path/filepath" "github.com/equinox-io/equinox" + "github.com/goji/httpauth" "github.com/urfave/cli" ) @@ -57,10 +58,17 @@ func equinoxUpdate(channel string) error { } func actionStartServer(c *cli.Context) error { - if err := registerHTTPHandlers(); err != nil { - return err + hdlr, err := newSupervisorHandler() + if err != nil { + log.Fatal(err) + } + auth := cfg.Server.HttpAuth + if auth.Enabled { + hdlr = httpauth.SimpleBasicAuth(auth.User, auth.Password)(hdlr) } - addr := c.String("address") + http.Handle("/", hdlr) + + addr := cfg.Server.Addr if c.Bool("foreground") { fmt.Println("added serv: ", addr) log.Fatal(http.ListenAndServe(addr, nil)) @@ -76,7 +84,7 @@ func actionStartServer(c *cli.Context) error { } func actionStatus(c *cli.Context) error { - resp, err := http.Get("http://localhost:8000/api/status") + resp, err := http.Get(cfg.Client.ServerURL + "/api/status") if err != nil { log.Fatal(err) } @@ -95,7 +103,7 @@ func actionStatus(c *cli.Context) error { } func actionShutdown(c *cli.Context) error { - resp, err := http.Get("http://localhost:8000/api/shutdown") + resp, err := http.Get(cfg.Client.ServerURL + "/api/status") if err != nil { log.Fatal(err) } @@ -114,7 +122,7 @@ func actionShutdown(c *cli.Context) error { } func actionConfigTest(c *cli.Context) error { - if err := registerHTTPHandlers(); err != nil { + if _, err := newSupervisorHandler(); err != nil { log.Fatal(err) } log.Println("test is successful") @@ -168,11 +176,6 @@ func main() { Name: "foreground, f", Usage: "start in foreground", }, - cli.StringFlag{ - Name: "address, addr", - Usage: "listen address", - Value: ":8000", - }, cli.StringFlag{ Name: "conf, c", Usage: "config file", diff --git a/vendor/github.com/elazarl/go-bindata-assetfs/LICENSE b/vendor/github.com/elazarl/go-bindata-assetfs/LICENSE new file mode 100644 index 0000000..5782c72 --- /dev/null +++ b/vendor/github.com/elazarl/go-bindata-assetfs/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2014, Elazar Leibovich +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/elazarl/go-bindata-assetfs/README.md b/vendor/github.com/elazarl/go-bindata-assetfs/README.md new file mode 100644 index 0000000..795d3d3 --- /dev/null +++ b/vendor/github.com/elazarl/go-bindata-assetfs/README.md @@ -0,0 +1,46 @@ +# go-bindata-assetfs + +Serve embedded files from [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) with `net/http`. + +[GoDoc](http://godoc.org/github.com/elazarl/go-bindata-assetfs) + +### Installation + +Install with + + $ go get github.com/jteeuwen/go-bindata/... + $ go get github.com/elazarl/go-bindata-assetfs/... + +### Creating embedded data + +Usage is identical to [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) usage, +instead of running `go-bindata` run `go-bindata-assetfs`. + +The tool will create a `bindata_assetfs.go` file, which contains the embedded data. + +A typical use case is + + $ go-bindata-assetfs data/... + +### Using assetFS in your code + +The generated file provides an `assetFS()` function that returns a `http.Filesystem` +wrapping the embedded files. What you usually want to do is: + + http.Handle("/", http.FileServer(assetFS())) + +This would run an HTTP server serving the embedded files. + +## Without running binary tool + +You can always just run the `go-bindata` tool, and then + +use + + import "github.com/elazarl/go-bindata-assetfs" + ... + http.Handle("/", + http.FileServer( + &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"})) + +to serve files embedded from the `data` directory. diff --git a/vendor/github.com/elazarl/go-bindata-assetfs/assetfs.go b/vendor/github.com/elazarl/go-bindata-assetfs/assetfs.go new file mode 100644 index 0000000..5174d5a --- /dev/null +++ b/vendor/github.com/elazarl/go-bindata-assetfs/assetfs.go @@ -0,0 +1,147 @@ +package assetfs + +import ( + "bytes" + "errors" + "io" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "time" +) + +var ( + fileTimestamp = time.Now() +) + +// FakeFile implements os.FileInfo interface for a given path and size +type FakeFile struct { + // Path is the path of this file + Path string + // Dir marks of the path is a directory + Dir bool + // Len is the length of the fake file, zero if it is a directory + Len int64 +} + +func (f *FakeFile) Name() string { + _, name := filepath.Split(f.Path) + return name +} + +func (f *FakeFile) Mode() os.FileMode { + mode := os.FileMode(0644) + if f.Dir { + return mode | os.ModeDir + } + return mode +} + +func (f *FakeFile) ModTime() time.Time { + return fileTimestamp +} + +func (f *FakeFile) Size() int64 { + return f.Len +} + +func (f *FakeFile) IsDir() bool { + return f.Mode().IsDir() +} + +func (f *FakeFile) Sys() interface{} { + return nil +} + +// AssetFile implements http.File interface for a no-directory file with content +type AssetFile struct { + *bytes.Reader + io.Closer + FakeFile +} + +func NewAssetFile(name string, content []byte) *AssetFile { + return &AssetFile{ + bytes.NewReader(content), + ioutil.NopCloser(nil), + FakeFile{name, false, int64(len(content))}} +} + +func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) { + return nil, errors.New("not a directory") +} + +func (f *AssetFile) Size() int64 { + return f.FakeFile.Size() +} + +func (f *AssetFile) Stat() (os.FileInfo, error) { + return f, nil +} + +// AssetDirectory implements http.File interface for a directory +type AssetDirectory struct { + AssetFile + ChildrenRead int + Children []os.FileInfo +} + +func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory { + fileinfos := make([]os.FileInfo, 0, len(children)) + for _, child := range children { + _, err := fs.AssetDir(filepath.Join(name, child)) + fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0}) + } + return &AssetDirectory{ + AssetFile{ + bytes.NewReader(nil), + ioutil.NopCloser(nil), + FakeFile{name, true, 0}, + }, + 0, + fileinfos} +} + +func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) { + if count <= 0 { + return f.Children, nil + } + if f.ChildrenRead+count > len(f.Children) { + count = len(f.Children) - f.ChildrenRead + } + rv := f.Children[f.ChildrenRead : f.ChildrenRead+count] + f.ChildrenRead += count + return rv, nil +} + +func (f *AssetDirectory) Stat() (os.FileInfo, error) { + return f, nil +} + +// AssetFS implements http.FileSystem, allowing +// embedded files to be served from net/http package. +type AssetFS struct { + // Asset should return content of file in path if exists + Asset func(path string) ([]byte, error) + // AssetDir should return list of files in the path + AssetDir func(path string) ([]string, error) + // Prefix would be prepended to http requests + Prefix string +} + +func (fs *AssetFS) Open(name string) (http.File, error) { + name = path.Join(fs.Prefix, name) + if len(name) > 0 && name[0] == '/' { + name = name[1:] + } + if b, err := fs.Asset(name); err == nil { + return NewAssetFile(name, b), nil + } + if children, err := fs.AssetDir(name); err == nil { + return NewAssetDirectory(name, children, fs), nil + } else { + return nil, err + } +} diff --git a/vendor/github.com/elazarl/go-bindata-assetfs/doc.go b/vendor/github.com/elazarl/go-bindata-assetfs/doc.go new file mode 100644 index 0000000..a664249 --- /dev/null +++ b/vendor/github.com/elazarl/go-bindata-assetfs/doc.go @@ -0,0 +1,13 @@ +// assetfs allows packages to serve static content embedded +// with the go-bindata tool with the standard net/http package. +// +// See https://github.com/jteeuwen/go-bindata for more information +// about embedding binary data with go-bindata. +// +// Usage example, after running +// $ go-bindata data/... +// use: +// http.Handle("/", +// http.FileServer( +// &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"})) +package assetfs diff --git a/vendor/github.com/goji/httpauth/.travis.yml b/vendor/github.com/goji/httpauth/.travis.yml new file mode 100644 index 0000000..13b50cd --- /dev/null +++ b/vendor/github.com/goji/httpauth/.travis.yml @@ -0,0 +1,22 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.2 + - go: 1.3 + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: tip + allow_failures: + - go: tip + +install: + - # skip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go vet $(go list ./... | grep -v /vendor/) + - go test -v -race ./... diff --git a/vendor/github.com/goji/httpauth/LICENSE b/vendor/github.com/goji/httpauth/LICENSE new file mode 100644 index 0000000..316bced --- /dev/null +++ b/vendor/github.com/goji/httpauth/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2014 Carl Jackson (carl@avtok.com), Matt Silverlock (matt@eatsleeprepeat.net) + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/goji/httpauth/README.md b/vendor/github.com/goji/httpauth/README.md new file mode 100644 index 0000000..a5fb22d --- /dev/null +++ b/vendor/github.com/goji/httpauth/README.md @@ -0,0 +1,163 @@ +# goji/httpauth [![GoDoc](https://godoc.org/github.com/goji/httpauth?status.svg)](https://godoc.org/github.com/goji/httpauth) [![Build Status](https://travis-ci.org/goji/httpauth.svg)](https://travis-ci.org/goji/httpauth) + +`httpauth` currently provides [HTTP Basic Authentication middleware](http://tools.ietf.org/html/rfc2617) for Go. It is compatible with Go's own `net/http`, [goji](https://goji.io), Gin & anything that speaks the `http.Handler` interface. + +## Example + +`httpauth` provides a `SimpleBasicAuth` function to get you up and running. Particularly ideal for development servers. + +Note that HTTP Basic Authentication credentials are sent over the wire "in the clear" (read: plaintext!) and therefore should not be considered a robust way to secure a HTTP server. If you're after that, you'll need to use SSL/TLS ("HTTPS") at a minimum. + +### Install It + +```sh +$ go get github.com/goji/httpauth +``` + +### Goji v2 + +#### Simple Usage + +The fastest and simplest way to get started using `httpauth` is to use the +`SimpleBasicAuth` function. + +```go + +package main + +import( + "net/http" + + "goji.io" +) + +func main() { + mux := goji.NewMux() + + mux.Use(httpauth.SimpleBasicAuth("dave", "somepassword")) + mux.Use(SomeOtherMiddleware) + + // YourHandler now requires HTTP Basic Auth + mux.Handle(pat.Get("/some-route"), YourHandler)) + + log.Fatal(http.ListenAndServe("localhost:8000", mux)) +} +``` + +#### Advanced Usage + +For more control over the process, pass a `AuthOptions` struct to `BasicAuth` instead. This allows you to: + +* Configure the authentication realm. +* Provide your own UnauthorizedHandler (anything that satisfies `http.Handler`) so you can return a better looking 401 page. +* Define a custom authentication function, which is discussed in the next section. + +```go + +func main() { + + authOpts := httpauth.AuthOptions{ + Realm: "DevCo", + User: "dave", + Password: "plaintext!", + UnauthorizedHandler: myUnauthorizedHandler, + } + + mux := goji.NewMux() + + mux.Use(BasicAuth(authOpts)) + mux.Use(SomeOtherMiddleware) + + mux.Handle(pat.Get("/some-route"), YourHandler)) + + log.Fatal(http.ListenAndServe("localhost:8000", mux)) +} +``` + +#### Custom Authentication Function + +`httpauth` will accept a custom authentication function. +Normally, you would not set `AuthOptions.User` nor `AuthOptions.Password` in this scenario. +You would instead validate the given credentials against an external system such as a database. +The contrived example below is for demonstration purposes only. + +```go +func main() { + + authOpts := httpauth.AuthOptions{ + Realm: "DevCo", + AuthFunc: myAuthFunc, + UnauthorizedHandler: myUnauthorizedHandler, + } + + mux := goji.NewMux() + + mux.Use(BasicAuth(authOpts)) + mux.Use(SomeOtherMiddleware) + + mux.Handle(pat.Get("/some-route"), YourHandler)) + + log.Fatal(http.ListenAndServe("localhost:8000", mux)) +} + +// myAuthFunc is not secure. It checks to see if the password is simply +// the username repeated three times. +func myAuthFunc(user, pass string, r *http.Request) bool { + return pass == strings.Repeat(user, 3) +} +``` + +### gorilla/mux + +Since it's all `http.Handler`, `httpauth` works with [gorilla/mux](https://github.com/gorilla/mux) (and most other routers) as well: + +```go +package main + +import ( + "net/http" + + "github.com/goji/httpauth" + "github.com/gorilla/mux" +) + +func main() { + r := mux.NewRouter() + + r.HandleFunc("/", YourHandler) + http.Handle("/", httpauth.SimpleBasicAuth("dave", "somepassword")(r)) + + http.ListenAndServe(":7000", nil) +} + +func YourHandler(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Gorilla!\n")) +} +``` + +### net/http + +If you're using vanilla `net/http`: + +```go +package main + +import( + "net/http" + + "github.com/goji/httpauth" +) + +func main() { + http.Handle("/", httpauth.SimpleBasicAuth("dave", "somepassword")(http.HandlerFunc(YourHandler))) + http.ListenAndServe(":7000", nil) +} +``` + +## Contributing + +Send a pull request! Note that features on the (informal) roadmap include HTTP Digest Auth. + +## License + +MIT Licensed. See the LICENSE file for details. diff --git a/vendor/github.com/goji/httpauth/basic_auth.go b/vendor/github.com/goji/httpauth/basic_auth.go new file mode 100644 index 0000000..a14c27a --- /dev/null +++ b/vendor/github.com/goji/httpauth/basic_auth.go @@ -0,0 +1,185 @@ +package httpauth + +import ( + "bytes" + "crypto/sha256" + "crypto/subtle" + "encoding/base64" + "fmt" + "net/http" + "strings" +) + +type basicAuth struct { + h http.Handler + opts AuthOptions +} + +// AuthOptions stores the configuration for HTTP Basic Authentication. +// +// A http.Handler may also be passed to UnauthorizedHandler to override the +// default error handler if you wish to serve a custom template/response. +type AuthOptions struct { + Realm string + User string + Password string + AuthFunc func(string, string, *http.Request) bool + UnauthorizedHandler http.Handler +} + +// Satisfies the http.Handler interface for basicAuth. +func (b basicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Check if we have a user-provided error handler, else set a default + if b.opts.UnauthorizedHandler == nil { + b.opts.UnauthorizedHandler = http.HandlerFunc(defaultUnauthorizedHandler) + } + + // Check that the provided details match + if b.authenticate(r) == false { + b.requestAuth(w, r) + return + } + + // Call the next handler on success. + b.h.ServeHTTP(w, r) +} + +// authenticate retrieves and then validates the user:password combination provided in +// the request header. Returns 'false' if the user has not successfully authenticated. +func (b *basicAuth) authenticate(r *http.Request) bool { + const basicScheme string = "Basic " + + if r == nil { + return false + } + + // In simple mode, prevent authentication with empty credentials if User is + // not set. Allow empty passwords to support non-password use-cases. + if b.opts.AuthFunc == nil && b.opts.User == "" { + return false + } + + // Confirm the request is sending Basic Authentication credentials. + auth := r.Header.Get("Authorization") + if !strings.HasPrefix(auth, basicScheme) { + return false + } + + // Get the plain-text username and password from the request. + // The first six characters are skipped - e.g. "Basic ". + str, err := base64.StdEncoding.DecodeString(auth[len(basicScheme):]) + if err != nil { + return false + } + + // Split on the first ":" character only, with any subsequent colons assumed to be part + // of the password. Note that the RFC2617 standard does not place any limitations on + // allowable characters in the password. + creds := bytes.SplitN(str, []byte(":"), 2) + + if len(creds) != 2 { + return false + } + + givenUser := string(creds[0]) + givenPass := string(creds[1]) + + // Default to Simple mode if no AuthFunc is defined. + if b.opts.AuthFunc == nil { + b.opts.AuthFunc = b.simpleBasicAuthFunc + } + + return b.opts.AuthFunc(givenUser, givenPass, r) +} + +// simpleBasicAuthFunc authenticates the supplied username and password against +// the User and Password set in the Options struct. +func (b *basicAuth) simpleBasicAuthFunc(user, pass string, r *http.Request) bool { + // Equalize lengths of supplied and required credentials + // by hashing them + givenUser := sha256.Sum256([]byte(user)) + givenPass := sha256.Sum256([]byte(pass)) + requiredUser := sha256.Sum256([]byte(b.opts.User)) + requiredPass := sha256.Sum256([]byte(b.opts.Password)) + + // Compare the supplied credentials to those set in our options + if subtle.ConstantTimeCompare(givenUser[:], requiredUser[:]) == 1 && + subtle.ConstantTimeCompare(givenPass[:], requiredPass[:]) == 1 { + return true + } + + return false +} + +// Require authentication, and serve our error handler otherwise. +func (b *basicAuth) requestAuth(w http.ResponseWriter, r *http.Request) { + w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm=%q`, b.opts.Realm)) + b.opts.UnauthorizedHandler.ServeHTTP(w, r) +} + +// defaultUnauthorizedHandler provides a default HTTP 401 Unauthorized response. +func defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) +} + +// BasicAuth provides HTTP middleware for protecting URIs with HTTP Basic Authentication +// as per RFC 2617. The server authenticates a user:password combination provided in the +// "Authorization" HTTP header. +// +// Example: +// +// package main +// +// import( +// "net/http" +// "github.com/zenazn/goji" +// "github.com/goji/httpauth" +// ) +// +// func main() { +// basicOpts := httpauth.AuthOptions{ +// Realm: "Restricted", +// User: "Dave", +// Password: "ClearText", +// } +// +// goji.Use(httpauth.BasicAuth(basicOpts), SomeOtherMiddleware) +// goji.Get("/thing", myHandler) +// } +// +// Note: HTTP Basic Authentication credentials are sent in plain text, and therefore it does +// not make for a wholly secure authentication mechanism. You should serve your content over +// HTTPS to mitigate this, noting that "Basic Authentication" is meant to be just that: basic! +func BasicAuth(o AuthOptions) func(http.Handler) http.Handler { + fn := func(h http.Handler) http.Handler { + return basicAuth{h, o} + } + return fn +} + +// SimpleBasicAuth is a convenience wrapper around BasicAuth. It takes a user and password, and +// returns a pre-configured BasicAuth handler using the "Restricted" realm and a default 401 handler. +// +// Example: +// +// package main +// +// import( +// "net/http" +// "github.com/zenazn/goji/web/httpauth" +// ) +// +// func main() { +// +// goji.Use(httpauth.SimpleBasicAuth("dave", "somepassword"), SomeOtherMiddleware) +// goji.Get("/thing", myHandler) +// } +// +func SimpleBasicAuth(user, password string) func(http.Handler) http.Handler { + opts := AuthOptions{ + Realm: "Restricted", + User: user, + Password: password, + } + return BasicAuth(opts) +} diff --git a/web.go b/web.go index ea73538..d3742d4 100644 --- a/web.go +++ b/web.go @@ -399,7 +399,7 @@ func (s *Supervisor) catchExitSignal() { }() } -func registerHTTPHandlers() error { +func newSupervisorHandler() (hdlr http.Handler, err error) { suv := &Supervisor{ ConfigDir: defaultConfigDir, pgMap: make(map[string]*Program, 0), @@ -407,8 +407,8 @@ func registerHTTPHandlers() error { // eventCs: make(map[chan string]bool), eventB: NewBroadcastString(), } - if err := suv.loadDB(); err != nil { - return err + if err = suv.loadDB(); err != nil { + return } suv.catchExitSignal() @@ -424,8 +424,5 @@ func registerHTTPHandlers() error { r.HandleFunc("/ws/events", suv.wsEvents) r.HandleFunc("/ws/logs/{name}", suv.wsLog) - // fs := http.FileServer(http.Dir("res")) - http.Handle("/", r) - // http.Handle("/res/", http.StripPrefix("/res/", fs)) - return nil + return r, nil }