ETags are now set by /api/phrases and Hyperdrive on the topic list for better caching.

Initialised the fields in hyperspace properly.

Eliminate an unnecessary line in client templates.
Cleaned up some thaw code in the forum store.
Fixed a potential premature thaw in Forums.BypassGet
This commit is contained in:
Azareal 2019-04-28 20:08:05 +10:00
parent 4414957885
commit efe7a0f3f0
7 changed files with 77 additions and 54 deletions

View File

@ -55,7 +55,7 @@ func (list SFileList) JSTmplInit() error {
} }
path = strings.TrimPrefix(path, "tmpl_client/") path = strings.TrimPrefix(path, "tmpl_client/")
tmplName := strings.TrimSuffix(path, ".go") tmplName := strings.TrimSuffix(path, ".jgo")
shortName := strings.TrimPrefix(tmplName, "template_") shortName := strings.TrimPrefix(tmplName, "template_")
var replace = func(data []byte, replaceThis string, withThis string) []byte { var replace = func(data []byte, replaceThis string, withThis string) []byte {
@ -104,10 +104,6 @@ func (list SFileList) JSTmplInit() error {
} }
return out + "]" return out + "]"
}*/ }*/
data = replace(data, `)
if !ok {
return errors.New("invalid page struct value")
}`, "*/tmpl_"+shortName+"_vars = tmpl_"+shortName+"_i")
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter // ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
var each = func(phrase string, handle func(index int)) { var each = func(phrase string, handle func(index int)) {

View File

@ -143,6 +143,7 @@ func (mfs *MemoryForumStore) rebuildView() {
}) })
sort.Sort(SortForum(forumView)) sort.Sort(SortForum(forumView))
mfs.forumView.Store(forumView) mfs.forumView.Store(forumView)
TopicListThaw.Thaw()
} }
func (mfs *MemoryForumStore) DirtyGet(id int) *Forum { func (mfs *MemoryForumStore) DirtyGet(id int) *Forum {
@ -190,7 +191,7 @@ func (mfs *MemoryForumStore) BypassGet(id int) (*Forum, error) {
forum.Link = BuildForumURL(NameToSlug(forum.Name), forum.ID) forum.Link = BuildForumURL(NameToSlug(forum.Name), forum.ID)
forum.LastTopic = Topics.DirtyGet(forum.LastTopicID) forum.LastTopic = Topics.DirtyGet(forum.LastTopicID)
forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID) forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID)
TopicListThaw.Thaw() //TopicListThaw.Thaw()
return forum, err return forum, err
} }
@ -219,7 +220,6 @@ func (mfs *MemoryForumStore) Reload(id int) error {
forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID) forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID)
mfs.CacheSet(forum) mfs.CacheSet(forum)
TopicListThaw.Thaw()
return nil return nil
} }
@ -290,7 +290,6 @@ func (mfs *MemoryForumStore) Delete(id int) error {
} }
_, err := mfs.delete.Exec(id) _, err := mfs.delete.Exec(id)
mfs.CacheDelete(id) mfs.CacheDelete(id)
TopicListThaw.Thaw()
return err return err
} }

View File

@ -495,7 +495,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
if tname != "" { if tname != "" {
tname = "_" + tname tname = "_" + tname
} }
err := writeFile(dirPrefix+"template_"+name+tname+".go", content) err := writeFile(dirPrefix+"template_"+name+tname+".jgo", content)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -442,20 +442,22 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
fout += "}\n\n" fout += "}\n\n"
} }
if c.lang == "normal" {
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n" fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n"
fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `) fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `)
if !ok { if !ok {
return errors.New("invalid page struct value") return errors.New("invalid page struct value")
} }
` `
if c.lang == "normal" { fout += `var iw http.ResponseWriter
fout += `var iw http.ResponseWriter
gzw, ok := w.(common.GzipResponseWriter) gzw, ok := w.(common.GzipResponseWriter)
if ok { if ok {
iw = gzw.ResponseWriter iw = gzw.ResponseWriter
} }
_ = iw _ = iw
` `
} else {
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_vars interface{}, w io.Writer) error {\n"
} }
if len(c.langIndexToName) > 0 { if len(c.langIndexToName) > 0 {

View File

@ -5,6 +5,9 @@ import (
//"log" //"log"
"bytes" "bytes"
"errors" "errors"
"strings"
"strconv"
"time"
"sync/atomic" "sync/atomic"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -37,11 +40,14 @@ func deactivateHdrive(plugin *c.Plugin) {
type Hyperspace struct { type Hyperspace struct {
topicList atomic.Value topicList atomic.Value
gzipTopicList atomic.Value gzipTopicList atomic.Value
lastTopicListUpdate atomic.Value
} }
func newHyperspace() *Hyperspace { func newHyperspace() *Hyperspace {
pageCache := new(Hyperspace) pageCache := new(Hyperspace)
pageCache.topicList.Store([]byte("")) pageCache.topicList.Store([]byte(""))
pageCache.gzipTopicList.Store([]byte(""))
pageCache.lastTopicListUpdate.Store(int64(0))
return pageCache return pageCache
} }
@ -86,6 +92,7 @@ func tickHdrive(args ...interface{}) (skip bool, rerr c.RouteError) {
return false, nil return false, nil
} }
hyperspace.gzipTopicList.Store(gbuf) hyperspace.gzipTopicList.Store(gbuf)
hyperspace.lastTopicListUpdate.Store(time.Now().Unix())
return false, nil return false, nil
} }
@ -128,12 +135,29 @@ func jumpHdrive(args ...interface{}) (skip bool, rerr c.RouteError) {
//c.DebugLog //c.DebugLog
c.DebugLog("Successful jump") c.DebugLog("Successful jump")
var etag string
lastUpdate := hyperspace.lastTopicListUpdate.Load().(int64)
c.DebugLog("lastUpdate:",lastUpdate)
if ok {
iw.Header().Set("X-I","1")
etag = "\""+strconv.FormatInt(lastUpdate, 10)+"-g\""
} else {
etag = "\""+strconv.FormatInt(lastUpdate, 10)+"\""
}
if lastUpdate != 0 {
iw.Header().Set("ETag", etag)
if match := r.Header.Get("If-None-Match"); match != "" {
if strings.Contains(match, etag) {
iw.WriteHeader(http.StatusNotModified)
return true, nil
}
}
}
header := args[3].(*c.Header) header := args[3].(*c.Header)
routes.FootHeaders(w, header) routes.FootHeaders(w, header)
iw.Write(tList) iw.Write(tList)
if ok {
w.Header().Set("X-I","1")
}
return true, nil return true, nil
} }

View File

@ -185,7 +185,7 @@ function initPhrases(loggedIn, panel = false) {
} }
function fetchPhrases(plist) { function fetchPhrases(plist) {
fetch("/api/phrases/?query="+plist) fetch("/api/phrases/?query="+plist, {cache: "no-cache"})
.then((resp) => resp.json()) .then((resp) => resp.json())
.then((data) => { .then((data) => {
console.log("loaded phrase endpoint data"); console.log("loaded phrase endpoint data");

View File

@ -191,32 +191,50 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout
return c.PreErrorJS("You haven't requested any phrases", w, r) return c.PreErrorJS("You haven't requested any phrases", w, r)
} }
var etag string
_, ok := w.(c.GzipResponseWriter)
if ok {
etag = "\""+strconv.FormatInt(c.StartTime.Unix(), 10)+"-g\""
} else {
etag = "\""+strconv.FormatInt(c.StartTime.Unix(), 10)+"\""
}
var plist map[string]string var plist map[string]string
var posLoop = func(positive string) c.RouteError {
// ! Constrain it to a subset of phrases for now
for _, item := range phraseWhitelist {
if strings.HasPrefix(positive, item) {
// TODO: Break this down into smaller security boundaries based on control panel sections?
if strings.HasPrefix(positive,"panel") {
w.Header().Set("Cache-Control", "private")
ok = user.IsSuperMod
} else {
ok = true
w.Header().Set("ETag", etag)
if match := r.Header.Get("If-None-Match"); match != "" {
if strings.Contains(match, etag) {
w.WriteHeader(http.StatusNotModified)
return nil
}
}
}
break
}
}
if !ok {
return c.PreErrorJS("Outside of phrase prefix whitelist", w, r)
}
return nil
}
// A little optimisation to avoid copying entries from one map to the other, if we don't have to mutate it // A little optimisation to avoid copying entries from one map to the other, if we don't have to mutate it
// TODO: Reduce the amount of duplication here
if len(positives) > 1 { if len(positives) > 1 {
plist = make(map[string]string) plist = make(map[string]string)
for _, positive := range positives { for _, positive := range positives {
// ! Constrain it to a subset of phrases for now rerr := posLoop(positive)
var ok = false if rerr != nil {
for _, item := range phraseWhitelist { return rerr
if strings.HasPrefix(positive, item) {
// TODO: Break this down into smaller security boundaries based on control panel sections?
if strings.HasPrefix(positive,"panel") {
if user.IsSuperMod {
ok = true
w.Header().Set("Cache-Control", "private")
}
} else {
ok = true
}
break
}
} }
if !ok {
return c.PreErrorJS("Outside of phrase prefix whitelist", w, r)
}
pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positive) pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positive)
if !ok { if !ok {
return c.PreErrorJS("No such prefix", w, r) return c.PreErrorJS("No such prefix", w, r)
@ -226,26 +244,10 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout
} }
} }
} else { } else {
// ! Constrain it to a subset of phrases for now rerr := posLoop(positives[0])
var ok = false if rerr != nil {
for _, item := range phraseWhitelist { return rerr
if strings.HasPrefix(positives[0], item) {
// TODO: Break this down into smaller security boundaries based on control panel sections?
if strings.HasPrefix(positives[0],"panel") {
if user.IsSuperMod {
ok = true
w.Header().Set("Cache-Control", "private")
}
} else {
ok = true
}
break
}
} }
if !ok {
return c.PreErrorJS("Outside of phrase prefix whitelist", w, r)
}
pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positives[0]) pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positives[0])
if !ok { if !ok {
return c.PreErrorJS("No such prefix", w, r) return c.PreErrorJS("No such prefix", w, r)