From 84d1c7e741522d43cedb276dbcec955f2bb652f2 Mon Sep 17 00:00:00 2001 From: Martin Thielecke Date: Thu, 28 Dec 2017 01:40:20 +0000 Subject: [PATCH] introduced context to all http handlers and use a specific http client for S3 requests --- ajax.go | 21 +++++++++++---------- httpHelper.go | 12 +++++++----- login.go | 11 ++++++----- overview.go | 7 ++++--- register.go | 7 ++++--- storage.go | 9 +++++---- storageLocal.go | 9 +++++---- storageRedis.go | 13 +++++++------ storageS3.go | 15 ++++++++++----- 9 files changed, 59 insertions(+), 45 deletions(-) diff --git a/ajax.go b/ajax.go index 2ae60f7..fbd8159 100644 --- a/ajax.go +++ b/ajax.go @@ -1,6 +1,7 @@ package main import ( + "context" "crypto/sha1" "encoding/json" "fmt" @@ -23,16 +24,16 @@ func (a ajaxResponse) Bytes() []byte { return out } -func ajaxGetHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { +func ajaxGetHandler(c context.Context, res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { res.Header().Set("Content-Type", "application/json") - user, _ := checkLogin(r, session) + user, _ := checkLogin(c, r, session) - if user == nil || !storage.IsPresent(user.UserFile) { + if user == nil || !storage.IsPresent(c, user.UserFile) { res.Write(ajaxResponse{Error: true}.Bytes()) return nil, nil } - userFileRaw, err := storage.Read(user.UserFile) + userFileRaw, err := storage.Read(c, user.UserFile) if err != nil { log.WithError(err).Error("Could not read user file from storage") res.Write(ajaxResponse{Error: true}.Bytes()) @@ -45,21 +46,21 @@ func ajaxGetHandler(res http.ResponseWriter, r *http.Request, session *sessions. return nil, nil } -func ajaxPostHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { +func ajaxPostHandler(c context.Context, res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { res.Header().Set("Content-Type", "application/json") - user, _ := checkLogin(r, session) + user, _ := checkLogin(c, r, session) if user == nil { res.Write(ajaxResponse{Error: true, Type: "login"}.Bytes()) return nil, nil } - if !storage.IsPresent(user.UserFile) { + if !storage.IsPresent(c, user.UserFile) { res.Write(ajaxResponse{Error: true, Type: "register"}.Bytes()) return nil, nil } - userFileRaw, err := storage.Read(user.UserFile) + userFileRaw, err := storage.Read(c, user.UserFile) if err != nil { log.WithError(err).Error("Could not read user file from storage") res.Write(ajaxResponse{Error: true, Type: "storage_error"}.Bytes()) @@ -84,7 +85,7 @@ func ajaxPostHandler(res http.ResponseWriter, r *http.Request, session *sessions return nil, nil } - if err := storage.Backup(user.UserFile); err != nil { + if err := storage.Backup(c, user.UserFile); err != nil { log.WithError(err).Error("Could not create backup of user file") res.Write(ajaxResponse{Error: true, Type: "storage_error"}.Bytes()) return nil, nil @@ -95,7 +96,7 @@ func ajaxPostHandler(res http.ResponseWriter, r *http.Request, session *sessions d, _ := userFile.GetData() - if err := storage.Write(user.UserFile, d); err != nil { + if err := storage.Write(c, user.UserFile, d); err != nil { log.WithError(err).Error("Could not write user file to storage") res.Write(ajaxResponse{Error: true, Type: "storage_error"}.Bytes()) return nil, nil diff --git a/httpHelper.go b/httpHelper.go index 4a0176c..9ec6206 100644 --- a/httpHelper.go +++ b/httpHelper.go @@ -1,6 +1,7 @@ package main import ( + "context" "net/http" "github.com/flosch/pongo2" @@ -8,7 +9,7 @@ import ( log "github.com/sirupsen/logrus" ) -type httpHelperFunc func(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) +type httpHelperFunc func(c context.Context, res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) func httpHelper(f httpHelperFunc) http.HandlerFunc { return func(res http.ResponseWriter, r *http.Request) { @@ -19,7 +20,9 @@ func httpHelper(f httpHelperFunc) http.HandlerFunc { ctx["error"] = errFlash[0].(string) } - template, err := f(res, r, sess, &ctx) + c := getContext(r) + + template, err := f(c, res, r, sess, &ctx) if err != nil { http.Error(res, "An error ocurred.", http.StatusInternalServerError) log.WithError(err).Error("Unable to execute template") @@ -27,8 +30,7 @@ func httpHelper(f httpHelperFunc) http.HandlerFunc { } if template != nil { - ts := pongo2.NewSet("frontend") - ts.SetBaseDirectory("templates") + ts := pongo2.NewSet("frontend", pongo2.MustNewLocalFileSystemLoader("templates")) tpl, err := ts.FromFile(*template) if err != nil { log.WithError(err).WithFields(log.Fields{ @@ -52,7 +54,7 @@ func httpHelper(f httpHelperFunc) http.HandlerFunc { } func simpleTemplateOutput(template string) httpHelperFunc { - return func(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { + return func(c context.Context, res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { return &template, nil } } diff --git a/login.go b/login.go index 870c00d..d372b57 100644 --- a/login.go +++ b/login.go @@ -1,6 +1,7 @@ package main import ( + "context" "crypto/sha1" "fmt" "net/http" @@ -15,19 +16,19 @@ import ( log "github.com/sirupsen/logrus" ) -func loginHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { +func loginHandler(c context.Context, res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { var ( username = strings.ToLower(r.FormValue("username")) password = r.FormValue("password") deprecatedPassword = fmt.Sprintf("%x", sha1.Sum([]byte(cfg.PasswordSalt+r.FormValue("password")))) // Here for backwards compatibility ) - if !storage.IsPresent(createUserFilename(username)) { + if !storage.IsPresent(c, createUserFilename(username)) { (*ctx)["error"] = true return stringPointer("login.html"), nil } - userFileRaw, err := storage.Read(createUserFilename(username)) + userFileRaw, err := storage.Read(c, createUserFilename(username)) if err != nil { log.WithError(err).Error("Unable to read user file") (*ctx)["error"] = true @@ -68,14 +69,14 @@ func loginHandler(res http.ResponseWriter, r *http.Request, session *sessions.Se return nil, nil } -func logoutHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { +func logoutHandler(c context.Context, res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { session.Values["authorizedAccounts"] = authorizedAccounts{} session.Save(r, res) http.Redirect(res, r, "overview", http.StatusFound) return nil, nil } -func checkLogin(r *http.Request, session *sessions.Session) (*authorizedAccount, error) { +func checkLogin(c context.Context, r *http.Request, session *sessions.Session) (*authorizedAccount, error) { vars := mux.Vars(r) idx, err := strconv.ParseInt(vars["userIndex"], 10, 64) if err != nil { diff --git a/overview.go b/overview.go index d92f378..c49dcaf 100644 --- a/overview.go +++ b/overview.go @@ -1,16 +1,17 @@ package main import ( + "context" "net/http" "github.com/flosch/pongo2" "github.com/gorilla/sessions" ) -func overviewHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { - user, _ := checkLogin(r, session) +func overviewHandler(c context.Context, res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { + user, _ := checkLogin(c, r, session) - if user == nil || !storage.IsPresent(user.UserFile) { + if user == nil || !storage.IsPresent(c, user.UserFile) { http.Redirect(res, r, "../../login", http.StatusFound) return nil, nil } diff --git a/register.go b/register.go index cd1d868..c2333e6 100644 --- a/register.go +++ b/register.go @@ -1,6 +1,7 @@ package main import ( + "context" "net/http" "strings" @@ -11,7 +12,7 @@ import ( log "github.com/sirupsen/logrus" ) -func registerHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { +func registerHandler(c context.Context, res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) { var ( username = strings.ToLower(r.FormValue("username")) password = r.FormValue("password") @@ -22,7 +23,7 @@ func registerHandler(res http.ResponseWriter, r *http.Request, session *sessions return stringPointer("register.html"), nil } - if storage.IsPresent(createUserFilename(username)) { + if storage.IsPresent(c, createUserFilename(username)) { (*ctx)["exists"] = true return stringPointer("register.html"), nil } @@ -38,7 +39,7 @@ func registerHandler(res http.ResponseWriter, r *http.Request, session *sessions d.MetaData.Password = string(hashedPassword) data, _ := d.GetData() - if err := storage.Write(createUserFilename(username), data); err != nil { + if err := storage.Write(c, createUserFilename(username), data); err != nil { log.WithError(err).Error("Could not write user file to storage") (*ctx)["error"] = true return stringPointer("register.html"), nil diff --git a/storage.go b/storage.go index b087c5a..45393e5 100644 --- a/storage.go +++ b/storage.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "io" "net/url" @@ -11,10 +12,10 @@ var ( ) type storageAdapter interface { - Write(identifier string, data io.Reader) error - Read(identifier string) (io.Reader, error) - IsPresent(identifier string) bool - Backup(identifier string) error + Write(ctx context.Context, identifier string, data io.Reader) error + Read(ctx context.Context, identifier string) (io.Reader, error) + IsPresent(ctx context.Context, identifier string) bool + Backup(ctx context.Context, identifier string) error } type storageAdapterInitializer func(*url.URL) (storageAdapter, error) diff --git a/storageLocal.go b/storageLocal.go index 57e2b34..82ba4c6 100644 --- a/storageLocal.go +++ b/storageLocal.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "io" "net/url" @@ -37,7 +38,7 @@ func newLocalStorage(u *url.URL) (storageAdapter, error) { } // Write store the data of a dataObject into the storage -func (l *LocalStorage) Write(identifier string, data io.Reader) error { +func (l *LocalStorage) Write(ctx context.Context, identifier string, data io.Reader) error { f, err := os.Create(path.Join(l.path, identifier)) if err != nil { return err @@ -49,18 +50,18 @@ func (l *LocalStorage) Write(identifier string, data io.Reader) error { } // Read reads the data of a dataObject from the storage -func (l *LocalStorage) Read(identifier string) (io.Reader, error) { +func (l *LocalStorage) Read(ctx context.Context, identifier string) (io.Reader, error) { return os.Open(path.Join(l.path, identifier)) } // IsPresent checks for the presence of an userfile identifier -func (l *LocalStorage) IsPresent(identifier string) bool { +func (l *LocalStorage) IsPresent(ctx context.Context, identifier string) bool { _, err := os.Stat(path.Join(l.path, identifier)) return err == nil } // Backup creates a backup of the old data -func (l *LocalStorage) Backup(identifier string) error { +func (l *LocalStorage) Backup(ctx context.Context, identifier string) error { ts := strconv.FormatInt(time.Now().Unix(), 10) o, err := os.Open(path.Join(l.path, identifier)) diff --git a/storageRedis.go b/storageRedis.go index dd4e116..ebc7d82 100644 --- a/storageRedis.go +++ b/storageRedis.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "io" "io/ioutil" "net/url" @@ -38,7 +39,7 @@ func newRedisStorage(u *url.URL) (storageAdapter, error) { } // Write store the data of a dataObject into the storage -func (r *RedisStorage) Write(identifier string, data io.Reader) error { +func (r *RedisStorage) Write(ctx context.Context, identifier string, data io.Reader) error { d, err := ioutil.ReadAll(data) if err != nil { return err @@ -48,13 +49,13 @@ func (r *RedisStorage) Write(identifier string, data io.Reader) error { } // Read reads the data of a dataObject from the storage -func (r *RedisStorage) Read(identifier string) (io.Reader, error) { +func (r *RedisStorage) Read(ctx context.Context, identifier string) (io.Reader, error) { content, err := r.conn.Get(r.prefix + identifier) return bytes.NewReader(content), err } // IsPresent checks for the presence of an userfile identifier -func (r *RedisStorage) IsPresent(identifier string) bool { +func (r *RedisStorage) IsPresent(ctx context.Context, identifier string) bool { e, err := r.conn.Exists(r.prefix + identifier) if err != nil { log.WithError(err).WithFields(log.Fields{ @@ -66,12 +67,12 @@ func (r *RedisStorage) IsPresent(identifier string) bool { } // Backup creates a backup of the old data -func (r *RedisStorage) Backup(identifier string) error { +func (r *RedisStorage) Backup(ctx context.Context, identifier string) error { ts := strconv.FormatInt(time.Now().Unix(), 10) - data, err := r.Read(identifier) + data, err := r.Read(ctx, identifier) if err != nil { return err } - return r.Write(identifier+":backup:"+ts, data) + return r.Write(ctx, identifier+":backup:"+ts, data) } diff --git a/storageS3.go b/storageS3.go index 7f7b5ff..7768e79 100644 --- a/storageS3.go +++ b/storageS3.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "fmt" "io" "net/url" @@ -35,10 +36,11 @@ func newS3Storage(u *url.URL) (storageAdapter, error) { } // Write store the data of a dataObject into the storage -func (s *S3Storage) Write(identifier string, data io.Reader) error { +func (s *S3Storage) Write(ctx context.Context, identifier string, data io.Reader) error { buf := bytes.NewBuffer([]byte{}) io.Copy(buf, data) + s.conn.Config.HTTPClient = getHTTPClient(ctx) _, err := s.conn.PutObject(&s3.PutObjectInput{ Bucket: aws.String(s.bucket), Body: bytes.NewReader(buf.Bytes()), @@ -48,7 +50,8 @@ func (s *S3Storage) Write(identifier string, data io.Reader) error { } // Read reads the data of a dataObject from the storage -func (s *S3Storage) Read(identifier string) (io.Reader, error) { +func (s *S3Storage) Read(ctx context.Context, identifier string) (io.Reader, error) { + s.conn.Config.HTTPClient = getHTTPClient(ctx) out, err := s.conn.GetObject(&s3.GetObjectInput{ Bucket: aws.String(s.bucket), Key: aws.String(path.Join(s.path, identifier)), @@ -62,7 +65,8 @@ func (s *S3Storage) Read(identifier string) (io.Reader, error) { } // IsPresent checks for the presence of an userfile identifier -func (s *S3Storage) IsPresent(identifier string) bool { +func (s *S3Storage) IsPresent(ctx context.Context, identifier string) bool { + s.conn.Config.HTTPClient = getHTTPClient(ctx) out, err := s.conn.HeadObject(&s3.HeadObjectInput{ Bucket: aws.String(s.bucket), Key: aws.String(path.Join(s.path, identifier)), @@ -72,12 +76,13 @@ func (s *S3Storage) IsPresent(identifier string) bool { return false } - return *out.ContentLength > 0 + return aws.Int64Value(out.ContentLength) > 0 || aws.StringValue(out.ContentType) == "binary/octet-stream" } // Backup creates a backup of the old data -func (s *S3Storage) Backup(identifier string) error { +func (s *S3Storage) Backup(ctx context.Context, identifier string) error { ts := strconv.FormatInt(time.Now().Unix(), 10) + s.conn.Config.HTTPClient = getHTTPClient(ctx) _, err := s.conn.CopyObject(&s3.CopyObjectInput{ Bucket: aws.String(s.bucket), Key: aws.String(path.Join(s.path, "backup", fmt.Sprintf("%s.%s", identifier, ts))),