diff options
author | Jordan <me@jordan.im> | 2021-03-14 13:43:03 -0700 |
---|---|---|
committer | Jordan <me@jordan.im> | 2021-03-14 13:43:03 -0700 |
commit | f03d77f73be72a934a99541b30a9663cc99a1733 (patch) | |
tree | 5065869c40492ebb917e825b6bcf0075b5117a17 | |
parent | c0271039935407c232270cef74cd2f559f77a64e (diff) | |
download | crane-f03d77f73be72a934a99541b30a9663cc99a1733.tar.gz crane-f03d77f73be72a934a99541b30a9663cc99a1733.zip |
r/w map locks; concurrent safety
-rw-r--r-- | crane.go | 39 | ||||
-rw-r--r-- | http.go | 22 | ||||
-rw-r--r-- | templates/admin-edit.html | 10 | ||||
-rw-r--r-- | templates/admin.html | 6 | ||||
-rw-r--r-- | templates/index.html | 4 | ||||
-rw-r--r-- | templates/list.html | 2 |
6 files changed, 67 insertions, 16 deletions
@@ -17,6 +17,7 @@ import ( "os" "path/filepath" "strings" + "sync" "golang.org/x/net/publicsuffix" ) @@ -64,12 +65,13 @@ type Paper struct { } type Papers struct { + sync.RWMutex List map[string]map[string]*Paper Path string } type Resp struct { - Papers map[string]map[string]*Paper + Papers Papers Status string LastPaperDL string LastUsedCategory string @@ -121,6 +123,7 @@ func getPaperFileNameFromResp(resp *http.Response) string { func (papers *Papers) getUniqueName(category string, name string) string { newName := name ext := 2 + papers.RLock() for { key := filepath.Join(category, newName+".pdf") if _, exists := papers.List[category][key]; exists != true { @@ -130,6 +133,7 @@ func (papers *Papers) getUniqueName(category string, name string) string { ext++ } } + papers.RUnlock() return newName } @@ -142,6 +146,9 @@ func (papers *Papers) findPapersWalk(path string, info os.FileInfo, return nil } + papers.Lock() + defer papers.Unlock() + // derive category name (e.g. Mathematics) from directory name; used as key var category string if i, _ := os.Stat(path); i.IsDir() { @@ -246,10 +253,12 @@ func (papers *Papers) NewPaperFromDOI(doi []byte, category string) (*Paper, // if not matching, check if DOIs match (genuine duplicate) if name != uniqueName { key := filepath.Join(category, name+".pdf") + papers.RLock() if meta.DOI == papers.List[category][key].Meta.DOI { return nil, fmt.Errorf("paper %q with DOI %q already downloaded", name, string(doi)) } + papers.RUnlock() } paper.PaperName = uniqueName @@ -276,8 +285,11 @@ func (papers *Papers) NewPaperFromDOI(doi []byte, category string) (*Paper, return nil, err } paper.Meta = *meta + + papers.Lock() papers.List[category][filepath.Join(category, paper.PaperName+".pdf")] = &paper + papers.Unlock() return &paper, nil } @@ -315,8 +327,10 @@ func (papers *Papers) NewPaperFromDirectLink(resp *http.Response, meta *Meta, if err := renameFile(tmpPDF.Name(), paper.PaperPath); err != nil { return nil, err } + papers.Lock() papers.List[category][filepath.Join(category, paper.PaperName+".pdf")] = &paper + papers.Unlock() return &paper, nil } @@ -325,6 +339,7 @@ func (papers *Papers) NewPaperFromDirectLink(resp *http.Response, meta *Meta, func (papers *Papers) DeletePaper(paper string) error { // check if the category in which the paper is said to belong // exists + papers.RLock() category := filepath.Dir(paper) if _, exists := papers.List[category]; exists != true { return fmt.Errorf("category %q does not exist\n", @@ -336,9 +351,11 @@ func (papers *Papers) DeletePaper(paper string) error { return fmt.Errorf("paper %q does not exist in category %q\n", paper, category) } + papers.RUnlock() // paper and category exists and the paper belongs to the provided // category; remove it and its XML metadata + papers.Lock() if err := os.Remove(papers.List[category][paper].PaperPath); err != nil { return err } @@ -353,31 +370,40 @@ func (papers *Papers) DeletePaper(paper string) error { } } delete(papers.List[category], paper) + papers.Unlock() return nil } // DeleteCategory deletes a category and its contents from the filesystem and // the papers.List set func (papers *Papers) DeleteCategory(category string) error { + papers.RLock() if _, exists := papers.List[category]; exists != true { return fmt.Errorf("category %q does not exist in the set\n", category) } + papers.RUnlock() + + papers.Lock() if err := os.RemoveAll(filepath.Join(papers.Path, category)); err != nil { return err } + // remove subcategories (nested directories) which exist under the primary for key, _ := range papers.List { if strings.HasPrefix(key, category+"/") { delete(papers.List, key) } } + delete(papers.List, category) + papers.Unlock() return nil } // MovePaper moves a paper to the destination category on the filesystem and // the papers.List set func (papers *Papers) MovePaper(paper string, category string) error { + papers.RLock() prevCategory := filepath.Dir(paper) if _, exists := papers.List[prevCategory]; exists != true { return fmt.Errorf("category %q does not exist\n", prevCategory) @@ -393,6 +419,9 @@ func (papers *Papers) MovePaper(paper string, category string) error { return fmt.Errorf("paper %q exists in destination category %q\n", paper, category) } + papers.RUnlock() + + papers.Lock() paperDest := filepath.Join(filepath.Join(papers.Path, category), papers.List[prevCategory][paper].PaperName+".pdf") if err := os.Rename(papers.List[prevCategory][paper].PaperPath, paperDest); @@ -422,12 +451,15 @@ func (papers *Papers) MovePaper(paper string, category string) error { } } delete(papers.List[prevCategory], paper) + + papers.Unlock() return nil } // RenameCategory renames a category on the filesystem and the paper.List set func (papers *Papers) RenameCategory(oldCategory string, newCategory string) error { + papers.RLock() if _, exists := papers.List[oldCategory]; exists != true { return fmt.Errorf("category %q does not exist in the set\n", oldCategory) } @@ -438,6 +470,9 @@ func (papers *Papers) RenameCategory(oldCategory string, filepath.Join(papers.Path, newCategory)); err != nil { return err } + papers.RUnlock() + + papers.Lock() papers.List[newCategory] = make(map[string]*Paper) for k, v := range papers.List[oldCategory] { pPaperPath := filepath.Join(papers.Path, filepath.Join(newCategory, @@ -453,6 +488,8 @@ func (papers *Papers) RenameCategory(oldCategory string, } } delete(papers.List, oldCategory) + + papers.Unlock() return nil } @@ -20,9 +20,12 @@ func (papers *Papers) IndexHandler(w http.ResponseWriter, r *http.Request) { filepath.Join(templateDir, "index.html"), filepath.Join(templateDir, "list.html"), ) + papers.RLock() res := Resp{ - Papers: papers.List, + Papers: *papers, } + papers.RUnlock() + t.Execute(w, &res) } @@ -33,9 +36,12 @@ func (papers *Papers) AdminHandler(w http.ResponseWriter, r *http.Request) { filepath.Join(templateDir, "layout.html"), filepath.Join(templateDir, "list.html"), ) + papers.RLock() res := Resp{ - Papers: papers.List, + Papers: *papers, } + papers.RUnlock() + if user != "" && pass != "" { username, password, ok := r.BasicAuth() if ok && user == username && pass == password { @@ -58,9 +64,12 @@ func (papers *Papers) EditHandler(w http.ResponseWriter, r *http.Request) { filepath.Join(templateDir, "layout.html"), filepath.Join(templateDir, "list.html"), ) + papers.RLock() res := Resp{ - Papers: papers.List, + Papers: *papers, } + papers.RUnlock() + if user != "" && pass != "" { username, password, ok := r.BasicAuth() if !ok || user != username || pass != password { @@ -152,7 +161,8 @@ func (papers *Papers) AddHandler(w http.ResponseWriter, r *http.Request) { // sanitize input; we use the category to build the path used to save // papers nc = strings.Trim(strings.Replace(nc, "..", "", -1), "/.") - res := Resp{Papers: papers.List} + + res := Resp{} // paper download, both required fields populated if len(strings.TrimSpace(p)) > 0 && len(strings.TrimSpace(c)) > 0 { @@ -194,6 +204,10 @@ func (papers *Papers) AddHandler(w http.ResponseWriter, r *http.Request) { res.Status = fmt.Sprintf("category %q added successfully", nc) } } + papers.RLock() + res.Papers = *papers + papers.RUnlock() + t.Execute(w, &res) } diff --git a/templates/admin-edit.html b/templates/admin-edit.html index 57a9aaa..7b30294 100644 --- a/templates/admin-edit.html +++ b/templates/admin-edit.html @@ -3,13 +3,13 @@ <p>{{ .Status }}</p> <table id='header'> <tr> - {{ $categoryCount := len .Papers }} + {{ $categoryCount := len .Papers.List }} {{ if gt $categoryCount 0 }} <td class='inpt'> <form method='post' action='/admin/edit/'> <input type="text" id="rename-category" name="rename-to" placeholder="Mathematics"> <select class="sel" name="rename-category" id="category"> - {{ range $category, $papers := .Papers }} + {{ range $category, $papers := .Papers.List }} <option value="{{ $category }}">{{ $category }}</option> {{ end }} </select> @@ -20,7 +20,7 @@ </tr> </table> <div class="cat"> - <span>{{ range $category, $paper := .Papers }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span> + <span>{{ range $category, $paper := .Papers.List }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span> </div> <table class='tabs'> <tr> @@ -36,7 +36,7 @@ <option value="delete">Delete</option> </optgroup> <optgroup label="Move To"> - {{ range $category, $papers := .Papers }} + {{ range $category, $papers := .Papers.List }} <option value="move-{{ $category }}">{{ $category }}</option> {{ end }} </optgroup> @@ -44,7 +44,7 @@ <input class="btn" type="submit" value="Save" /> </div> <div class='papers nowrap list'> -{{ range $category, $papers := .Papers }} +{{ range $category, $papers := .Papers.List }} {{ $paperCount := len $papers }} <div class="papersection"> <input type="checkbox" id="{{ $category }}" name="category" value="{{ $category }}"> diff --git a/templates/admin.html b/templates/admin.html index 22b0e0a..05cf04f 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -14,7 +14,7 @@ </form> </td> </tr> - {{ $categoryCount := len .Papers }} + {{ $categoryCount := len .Papers.List }} {{ if gt $categoryCount 0 }} <tr> <td class='inpt'> @@ -25,7 +25,7 @@ {{ if $lastUsedCategory }} <option value="{{ .LastUsedCategory }}">{{ $lastUsedCategory }}</option> {{ end }} - {{ range $category, $papers := .Papers }} + {{ range $category, $papers := .Papers.List }} {{ if ne $category $lastUsedCategory }} <option value="{{ $category }}">{{ $category }}</option> {{ end }} @@ -39,7 +39,7 @@ </table> {{ if gt $categoryCount 0 }} <div class="cat"> - <span>{{ range $category, $paper := .Papers }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span> + <span>{{ range $category, $paper := .Papers.List }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span> </div> <table class='tabs'> <tr> diff --git a/templates/index.html b/templates/index.html index 30023cd..6397671 100644 --- a/templates/index.html +++ b/templates/index.html @@ -2,10 +2,10 @@ {{ define "content" }} <table id='header'> </table> -{{ $categoryCount := len .Papers }} +{{ $categoryCount := len .Papers.List }} {{ if gt $categoryCount 0 }} <div class="cat"> - <span>{{ range $category, $paper := .Papers }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span> + <span>{{ range $category, $paper := .Papers.List }}[<a href="#{{ $category }}">{{ $category }}</a>] {{ end }}</span> </div> <table class='tabs'> <td><a class='active' href='/admin/'>Manage</a></td> diff --git a/templates/list.html b/templates/list.html index 9e139b9..a08efd6 100644 --- a/templates/list.html +++ b/templates/list.html @@ -1,6 +1,6 @@ {{ define "list" }} <div class='papers nowrap list'> -{{ range $category, $papers := .Papers }} +{{ range $category, $papers := .Papers.List }} {{ $paperCount := len $papers }} {{ if ge $paperCount 1 }} <div class="papersection"> |