X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=gocheese.go;h=26c2c27ba74673cda10e03eb6b44e31ee924920f;hb=0659f26ad2020eb9339fb4e276a44c01d897d709;hp=6ca3ad53e036236ea7dc3310e74d55641f715bbb;hpb=5ccea4614d3d499a0b183998230778e4adfc6127;p=gocheese.git diff --git a/gocheese.go b/gocheese.go index 6ca3ad5..26c2c27 100644 --- a/gocheese.go +++ b/gocheese.go @@ -1,6 +1,7 @@ /* GoCheese -- Python private package repository and caching proxy Copyright (C) 2019 Sergey Matveev + 2019 Elena Balakhonova This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,21 +21,27 @@ package main import ( "bytes" + "context" "crypto/sha256" "encoding/hex" - "errors" "flag" "fmt" "io" "io/ioutil" "log" + "net" "net/http" "net/url" "os" + "os/signal" "path/filepath" "regexp" "runtime" "strings" + "syscall" + "time" + + "golang.org/x/net/netutil" ) const ( @@ -67,6 +74,7 @@ var ( passwdPath = flag.String("passwd", "passwd", "Path to file with authenticators") passwdCheck = flag.Bool("passwd-check", false, "Test the -passwd file for syntax errors and exit") fsck = flag.Bool("fsck", false, "Check integrity of all packages") + maxClients = flag.Int("maxclients", 128, "Maximal amount of simultaneous clients") version = flag.Bool("version", false, "Print version information") warranty = flag.Bool("warranty", false, "Print warranty information") @@ -83,7 +91,7 @@ type Auther interface { func mkdirForPkg(w http.ResponseWriter, r *http.Request, dir string) bool { path := filepath.Join(*root, dir) if _, err := os.Stat(path); os.IsNotExist(err) { - if err = os.Mkdir(path, 0700); err != nil { + if err = os.Mkdir(path, os.FileMode(0777)); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return false } @@ -186,7 +194,7 @@ func refreshDir(w http.ResponseWriter, r *http.Request, dir, filenameGet string) } } log.Println(r.RemoteAddr, "pypi touch", filename) - if err = ioutil.WriteFile(path, digest, os.FileMode(0600)); err != nil { + if err = ioutil.WriteFile(path, digest, os.FileMode(0666)); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return false } @@ -265,25 +273,6 @@ func servePkg(w http.ResponseWriter, r *http.Request, dir, filename string) { http.ServeFile(w, r, path) } -func strToAuther(verifier string) (string, Auther, error) { - st := strings.SplitN(verifier, "$", 3) - if len(st) != 3 || st[0] != "" { - return "", nil, errors.New("invalid verifier structure") - } - algorithm := st[1] - var auther Auther - var err error - switch algorithm { - case "argon2i": - auther, err = parseArgon2i(st[2]) - case "sha256": - auther, err = parseSHA256(st[2]) - default: - err = errors.New("unknown hashing algorithm") - } - return algorithm, auther, err -} - func serveUpload(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if !ok { @@ -355,11 +344,7 @@ func serveUpload(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } - if err = ioutil.WriteFile( - path+SHA256Ext, - hasher.Sum(nil), - os.FileMode(0600), - ); err != nil { + if err = ioutil.WriteFile(path+SHA256Ext, hasher.Sum(nil), os.FileMode(0666)); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -462,7 +447,38 @@ func main() { } refreshPasswd() log.Println("root:", *root, "bind:", *bind) + needsRefreshPasswd := make(chan os.Signal, 0) + needsShutdown := make(chan os.Signal, 0) + killed := make(chan error, 0) http.HandleFunc(*norefreshURLPath, handler) http.HandleFunc(*refreshURLPath, handler) - log.Fatal(http.ListenAndServe(*bind, nil)) + ln, err := net.Listen("tcp", *bind) + if err != nil { + log.Fatal(err) + } + s := &http.Server{ + ReadTimeout: time.Minute, + WriteTimeout: time.Minute, + } + signal.Notify(needsRefreshPasswd, syscall.SIGHUP) + signal.Notify(needsShutdown, syscall.SIGTERM, syscall.SIGINT) + go func() { + for range needsRefreshPasswd { + log.Println("Refreshing passwords") + refreshPasswd() + } + }() + go func(s *http.Server) { + <-needsShutdown + log.Println("Shutting down") + ctx, cancel := context.WithTimeout(context.TODO(), time.Minute) + killed <- s.Shutdown(ctx) + cancel() + }(s) + if err := s.Serve(netutil.LimitListener(ln, *maxClients)); err != http.ErrServerClosed { + log.Fatal(err) + } + if err := <-killed; err != nil { + log.Fatal(err) + } }