diff --git a/src/restic/cmd/restic-server/handlers.go b/src/restic/cmd/restic-server/handlers.go index 8294d454f..cd2fd518b 100644 --- a/src/restic/cmd/restic-server/handlers.go +++ b/src/restic/cmd/restic-server/handlers.go @@ -1,3 +1,5 @@ +// +build go1.4 + package main import ( @@ -10,10 +12,14 @@ import ( "time" ) +// Context contains repository meta-data. type Context struct { path string } +// AuthHandler wraps h with a http.HandlerFunc that performs basic +// authentication against the user/passwords pairs stored in f and returns the +// http.HandlerFunc. func AuthHandler(f *HtpasswdFile, h http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() @@ -29,6 +35,8 @@ func AuthHandler(f *HtpasswdFile, h http.Handler) http.HandlerFunc { } } +// CheckConfig returns a http.HandlerFunc that checks whether +// a configuration exists. func CheckConfig(c *Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { config := filepath.Join(c.path, "config") @@ -39,6 +47,8 @@ func CheckConfig(c *Context) http.HandlerFunc { } } +// GetConfig returns a http.HandlerFunc that allows for a +// config to be retrieved. func GetConfig(c *Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { config := filepath.Join(c.path, "config") @@ -51,6 +61,8 @@ func GetConfig(c *Context) http.HandlerFunc { } } +// SaveConfig returns a http.HandlerFunc that allows for a +// config to be saved. func SaveConfig(c *Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { config := filepath.Join(c.path, "config") @@ -68,6 +80,8 @@ func SaveConfig(c *Context) http.HandlerFunc { } } +// ListBlobs returns a http.HandlerFunc that lists +// all blobs of a given type in an arbitrary order. func ListBlobs(c *Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := strings.Split(r.RequestURI, "/") @@ -91,6 +105,8 @@ func ListBlobs(c *Context) http.HandlerFunc { } } +// CheckBlob reutrns a http.HandlerFunc that tests whether a blob exists +// and returns 200, if it does, or 404 otherwise. func CheckBlob(c *Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := strings.Split(r.RequestURI, "/") @@ -105,6 +121,8 @@ func CheckBlob(c *Context) http.HandlerFunc { } } +// GetBlob returns a http.HandlerFunc that retrieves a blob +// from the repository. func GetBlob(c *Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := strings.Split(r.RequestURI, "/") @@ -121,6 +139,7 @@ func GetBlob(c *Context) http.HandlerFunc { } } +// SaveBlob returns a http.HandlerFunc that saves a blob to the repository. func SaveBlob(c *Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := strings.Split(r.RequestURI, "/") @@ -141,6 +160,8 @@ func SaveBlob(c *Context) http.HandlerFunc { } } +// DeleteBlob returns a http.HandlerFunc that deletes a blob from the +// repository. func DeleteBlob(c *Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := strings.Split(r.RequestURI, "/") diff --git a/src/restic/cmd/restic-server/htpasswd.go b/src/restic/cmd/restic-server/htpasswd.go index d72a83c9a..2b7108f7f 100644 --- a/src/restic/cmd/restic-server/htpasswd.go +++ b/src/restic/cmd/restic-server/htpasswd.go @@ -1,3 +1,5 @@ +// +build go1.4 + package main /* @@ -36,10 +38,14 @@ import ( // lookup passwords in a htpasswd file // The entries must have been created with -s for SHA encryption +// HtpasswdFile is a map for usernames to passwords. type HtpasswdFile struct { Users map[string]string } +// NewHtpasswdFromFile reads the users and passwords from a htpasswd +// file and returns them. If an error is encountered, it is returned, together +// with a nil-Pointer for the HtpasswdFile. func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) { r, err := os.Open(path) if err != nil { @@ -49,13 +55,16 @@ func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) { return NewHtpasswd(r) } +// NewHtpasswd reads the users and passwords from a htpasswd +// datastream in file and returns them. If an error is encountered, +// it is returned, together with a nil-Pointer for the HtpasswdFile. func NewHtpasswd(file io.Reader) (*HtpasswdFile, error) { - csv_reader := csv.NewReader(file) - csv_reader.Comma = ':' - csv_reader.Comment = '#' - csv_reader.TrimLeadingSpace = true + cr := csv.NewReader(file) + cr.Comma = ':' + cr.Comment = '#' + cr.TrimLeadingSpace = true - records, err := csv_reader.ReadAll() + records, err := cr.ReadAll() if err != nil { return nil, err } @@ -66,6 +75,9 @@ func NewHtpasswd(file io.Reader) (*HtpasswdFile, error) { return h, nil } +// Validate returns true if password matches the stored password +// for user. If no password for user is stored, or the password +// is wrong, false is returned. func (h *HtpasswdFile) Validate(user string, password string) bool { realPassword, exists := h.Users[user] if !exists { diff --git a/src/restic/cmd/restic-server/router.go b/src/restic/cmd/restic-server/router.go index c0a302832..c48203955 100644 --- a/src/restic/cmd/restic-server/router.go +++ b/src/restic/cmd/restic-server/router.go @@ -1,3 +1,5 @@ +// +build go1.4 + package main import ( @@ -6,83 +8,103 @@ import ( "strings" ) +// Route is a handler for a path that was already split. type Route struct { path []string handler http.Handler } +// Router maps HTTP methods to a slice of Route handlers. type Router struct { routes map[string][]Route } +// NewRouter creates a new Router and returns a pointer to it. func NewRouter() *Router { return &Router{make(map[string][]Route)} } +// Options registers handler for path with method "OPTIONS". func (router *Router) Options(path string, handler http.Handler) { router.Handle("OPTIONS", path, handler) } +// OptionsFunc registers handler for path with method "OPTIONS". func (router *Router) OptionsFunc(path string, handler http.HandlerFunc) { router.Handle("OPTIONS", path, handler) } +// Get registers handler for path with method "GET". func (router *Router) Get(path string, handler http.Handler) { router.Handle("GET", path, handler) } +// GetFunc registers handler for path with method "GET". func (router *Router) GetFunc(path string, handler http.HandlerFunc) { router.Handle("GET", path, handler) } +// Head registers handler for path with method "HEAD". func (router *Router) Head(path string, handler http.Handler) { router.Handle("HEAD", path, handler) } +// HeadFunc registers handler for path with method "HEAD". func (router *Router) HeadFunc(path string, handler http.HandlerFunc) { router.Handle("HEAD", path, handler) } +// Post registers handler for path with method "POST". func (router *Router) Post(path string, handler http.Handler) { router.Handle("POST", path, handler) } +// PostFunc registers handler for path with method "POST". func (router *Router) PostFunc(path string, handler http.HandlerFunc) { router.Handle("POST", path, handler) } +// Put registers handler for path with method "PUT". func (router *Router) Put(path string, handler http.Handler) { router.Handle("PUT", path, handler) } +// PutFunc registers handler for path with method "PUT". func (router *Router) PutFunc(path string, handler http.HandlerFunc) { router.Handle("PUT", path, handler) } +// Delete registers handler for path with method "DELETE". func (router *Router) Delete(path string, handler http.Handler) { router.Handle("DELETE", path, handler) } +// DeleteFunc registers handler for path with method "DELETE". func (router *Router) DeleteFunc(path string, handler http.HandlerFunc) { router.Handle("DELETE", path, handler) } +// Trace registers handler for path with method "TRACE". func (router *Router) Trace(path string, handler http.Handler) { router.Handle("TRACE", path, handler) } +// TraceFunc registers handler for path with method "TRACE". func (router *Router) TraceFunc(path string, handler http.HandlerFunc) { router.Handle("TRACE", path, handler) } +// Connect registers handler for path with method "Connect". func (router *Router) Connect(path string, handler http.Handler) { router.Handle("Connect", path, handler) } +// ConnectFunc registers handler for path with method "Connect". func (router *Router) ConnectFunc(path string, handler http.HandlerFunc) { router.Handle("Connect", path, handler) } +// Handle registers a http.Handler for method and uri func (router *Router) Handle(method string, uri string, handler http.Handler) { routes := router.routes[method] path := strings.Split(uri, "/") diff --git a/src/restic/cmd/restic-server/router_test.go b/src/restic/cmd/restic-server/router_test.go index f7b1f4304..d4da1843d 100644 --- a/src/restic/cmd/restic-server/router_test.go +++ b/src/restic/cmd/restic-server/router_test.go @@ -1,3 +1,5 @@ +// +build go1.4 + package main import ( @@ -6,8 +8,6 @@ import ( "net/http/httptest" "strings" "testing" - - "github.com/stretchr/testify/require" ) func TestRouter(t *testing.T) { @@ -38,21 +38,37 @@ func TestRouter(t *testing.T) { getConfigResp, _ := http.Get(server.URL + "/config") getConfigBody, _ := ioutil.ReadAll(getConfigResp.Body) - require.Equal(t, 200, getConfigResp.StatusCode) - require.Equal(t, string(getConfig), string(getConfigBody)) + if getConfigResp.StatusCode != 200 { + t.Fatalf("Wanted HTTP Status 200, got %d", getConfigResp.StatusCode) + } + if string(getConfig) != string(getConfigBody) { + t.Fatalf("Config wrong:\nWanted '%s'\nGot: '%s'", string(getConfig), string(getConfigBody)) + } postConfigResp, _ := http.Post(server.URL+"/config", "binary/octet-stream", strings.NewReader("post test")) postConfigBody, _ := ioutil.ReadAll(postConfigResp.Body) - require.Equal(t, 200, postConfigResp.StatusCode) - require.Equal(t, string(postConfig), string(postConfigBody)) + if postConfigResp.StatusCode != 200 { + t.Fatalf("Wanted HTTP Status 200, got %d", postConfigResp.StatusCode) + } + if string(postConfig) != string(postConfigBody) { + t.Fatalf("Config wrong:\nWanted '%s'\nGot: '%s'", string(postConfig), string(postConfigBody)) + } getBlobsResp, _ := http.Get(server.URL + "/blobs/") getBlobsBody, _ := ioutil.ReadAll(getBlobsResp.Body) - require.Equal(t, 200, getBlobsResp.StatusCode) - require.Equal(t, string(getBlobs), string(getBlobsBody)) + if getBlobsResp.StatusCode != 200 { + t.Fatalf("Wanted HTTP Status 200, got %d", getBlobsResp.StatusCode) + } + if string(getBlobs) != string(getBlobsBody) { + t.Fatalf("Config wrong:\nWanted '%s'\nGot: '%s'", string(getBlobs), string(getBlobsBody)) + } getBlobResp, _ := http.Get(server.URL + "/blobs/test") getBlobBody, _ := ioutil.ReadAll(getBlobResp.Body) - require.Equal(t, 200, getBlobResp.StatusCode) - require.Equal(t, string(getBlob), string(getBlobBody)) + if getBlobResp.StatusCode != 200 { + t.Fatalf("Wanted HTTP Status 200, got %d", getBlobResp.StatusCode) + } + if string(getBlob) != string(getBlobBody) { + t.Fatalf("Config wrong:\nWanted '%s'\nGot: '%s'", string(getBlob), string(getBlobBody)) + } } diff --git a/src/restic/cmd/restic-server/server.go b/src/restic/cmd/restic-server/server.go index 86bcf75c0..bdad003f4 100644 --- a/src/restic/cmd/restic-server/server.go +++ b/src/restic/cmd/restic-server/server.go @@ -1,3 +1,5 @@ +// +build go1.4 + package main import ( @@ -9,8 +11,8 @@ import ( ) const ( - HTTP = ":8000" - HTTPS = ":8443" + defaultHTTPPort = ":8000" + defaultHTTPSPort = ":8443" ) func main() { @@ -56,15 +58,15 @@ func main() { // start the server if !*tls { - log.Printf("start server on port %s\n", HTTP) - http.ListenAndServe(HTTP, handler) + log.Printf("start server on port %s\n", defaultHTTPPort) + http.ListenAndServe(defaultHTTPPort, handler) } else { privateKey := filepath.Join(*path, "private_key") publicKey := filepath.Join(*path, "public_key") log.Println("TLS enabled") log.Printf("private key: %s", privateKey) log.Printf("public key: %s", publicKey) - log.Printf("start server on port %s\n", HTTPS) - http.ListenAndServeTLS(HTTPS, publicKey, privateKey, handler) + log.Printf("start server on port %s\n", defaultHTTPSPort) + http.ListenAndServeTLS(defaultHTTPSPort, publicKey, privateKey, handler) } }