diff --git a/common/pages.go b/common/pages.go index 097e9a23..41227120 100644 --- a/common/pages.go +++ b/common/pages.go @@ -10,10 +10,10 @@ import ( "github.com/Azareal/Gosora/common/phrases" ) -type HResource struct { +/*type HResource struct { Name string Hash string -} +}*/ // TODO: Allow resources in spots other than /static/ and possibly even external domains (e.g. CDNs) // TODO: Preload Trumboyg on Cosora on the forum list @@ -21,11 +21,11 @@ type Header struct { Title string //Title []byte // Experimenting with []byte for increased efficiency, let's avoid converting too many things to []byte, as it involves a lot of extra boilerplate NoticeList []string - Scripts []HResource - PreScriptsAsync []HResource - ScriptsAsync []HResource + Scripts []string + PreScriptsAsync []string + ScriptsAsync []string //Preload []string - Stylesheets []HResource + Stylesheets []string Widgets PageWidgets Site *site Settings SettingMap @@ -51,40 +51,48 @@ type Header struct { func (header *Header) AddScript(name string) { fname := "/static/" + name - var hash string + var oname string if fname[0] == '/' && fname[1] != '/' { file, ok := StaticFiles.Get(fname) if ok { - hash = file.Sha256 + oname = name + "?h=" + file.Sha256 } } - //log.Print("name:", name) - //log.Print("hash:", hash) - header.Scripts = append(header.Scripts, HResource{name, hash}) + if oname == "" { + oname = name + } + //log.Print("oname:", oname) + header.Scripts = append(header.Scripts, oname) } func (header *Header) AddPreScriptAsync(name string) { fname := "/static/" + name - var hash string + var oname string if fname[0] == '/' && fname[1] != '/' { file, ok := StaticFiles.Get(fname) if ok { - hash = file.Sha256 + oname = name + "?h=" + file.Sha256 } } - header.PreScriptsAsync = append(header.PreScriptsAsync, HResource{name, hash}) + if oname == "" { + oname = name + } + header.PreScriptsAsync = append(header.PreScriptsAsync, oname) } func (header *Header) AddScriptAsync(name string) { fname := "/static/" + name - var hash string + var oname string if fname[0] == '/' && fname[1] != '/' { file, ok := StaticFiles.Get(fname) if ok { - hash = file.Sha256 + oname = name + "?h=" + file.Sha256 } } - header.ScriptsAsync = append(header.ScriptsAsync, HResource{name, hash}) + if oname == "" { + oname = name + } + header.ScriptsAsync = append(header.ScriptsAsync, oname) } /*func (header *Header) Preload(name string) { @@ -93,14 +101,17 @@ func (header *Header) AddScriptAsync(name string) { func (header *Header) AddSheet(name string) { fname := "/static/" + name - var hash string + var oname string if fname[0] == '/' && fname[1] != '/' { file, ok := StaticFiles.Get(fname) if ok { - hash = file.Sha256 + oname = name + "?h=" + file.Sha256 } } - header.Stylesheets = append(header.Stylesheets, HResource{name, hash}) + if oname == "" { + oname = name + } + header.Stylesheets = append(header.Stylesheets, oname) } func (header *Header) AddNotice(name string) { diff --git a/common/site.go b/common/site.go index 8e95f7bb..dc8ceef4 100644 --- a/common/site.go +++ b/common/site.go @@ -90,6 +90,8 @@ type config struct { DisableLiveTopicList bool DisableJSAntispam bool //LooseCSP bool + DisableServerPush bool + EnableCDNPush bool Noavatar string // ? - Move this into the settings table? ItemsPerPage int // ? - Move this into the settings table? diff --git a/common/template_init.go b/common/template_init.go index a4d56ff6..b112d1bb 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -89,14 +89,14 @@ var Template_account_handle = genIntTmpl("account") func tmplInitUsers() (User, User, User) { avatar, microAvatar := BuildAvatar(62, "") - user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", avatar, microAvatar, "", "", "", "", 0, 0, 0, "0.0.0.0.0", 0} + user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", avatar, microAvatar, "", "", "", "", 0, 0, 0, "0.0.0.0.0", "", 0} // TODO: Do a more accurate level calculation for this? avatar, microAvatar = BuildAvatar(1, "") - user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 58, 1000, 0, "127.0.0.1", 0} + user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 58, 1000, 0, "127.0.0.1", "", 0} avatar, microAvatar = BuildAvatar(2, "") - user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 42, 900, 0, "::1", 0} + user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 42, 900, 0, "::1", "", 0} return user, user2, user3 } @@ -108,10 +108,10 @@ func tmplInitHeaders(user User, user2 User, user3 User) (*Header, *Header, *Head Theme: Themes[DefaultThemeBox.Load().(string)], CurrentUser: user, NoticeList: []string{"test"}, - Stylesheets: []HResource{HResource{"panel.css", "d"}}, - Scripts: []HResource{HResource{"whatever.js", "d"}}, - PreScriptsAsync: []HResource{HResource{"whatever.js", "d"}}, - ScriptsAsync: []HResource{HResource{"whatever.js", "d"}}, + Stylesheets: []string{"panel.css"}, + Scripts: []string{"whatever.js"}, + PreScriptsAsync: []string{"whatever.js"}, + ScriptsAsync: []string{"whatever.js"}, Widgets: PageWidgets{ LeftSidebar: template.HTML("lalala"), }, diff --git a/common/user.go b/common/user.go index d5962379..36607e3e 100644 --- a/common/user.go +++ b/common/user.go @@ -53,6 +53,7 @@ type User struct { Score int Liked int LastIP string // ! This part of the UserCache data might fall out of date + LastAgent string // ! Temporary hack, don't use TempGroup int } diff --git a/docs/configuration.md b/docs/configuration.md index a2efc9bb..8492219d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -76,6 +76,10 @@ DisableLiveTopicList - This switch allows you to disable the live topic list. DisableJSAntispam - This switch lets you disable the JS anti-spam feature. It may be useful if you primarily get users who for one reason or another have decided to disable JavaScript. +DisableServerPush - This switch lets you disable the HTTP/2 server push feature. + +EnableCDNPush - This switch lets you enable the HTTP/2 CDN Server Push feature. This operates by sending a Link header on every request and may also work with reverse-proxies like Nginx for doing HTTP/2 server pushes. + NoAvatar - The default avatar to use for users when they don't have their own. The default for this may change in the near future to better utilise HTTP/2. Example: https://api.adorable.io/avatars/{width}/{id}.png ItemsPerPage - The number of posts, topics, etc. you want on each page. diff --git a/gen_router.go b/gen_router.go index ba467035..ccf0ae02 100644 --- a/gen_router.go +++ b/gen_router.go @@ -824,6 +824,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // TODO: Add a setting to disable this? // TODO: Use a more efficient detector instead of smashing every possible combination in ua := strings.TrimSpace(strings.Replace(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36","",-1)) // Noise, no one's going to be running this and it would require some sort of agent ranking system to determine which identifier should be prioritised over another + var agent string if ua == "" { counters.AgentViewCounter.Bump(26) if common.Dev.DebugMode { @@ -878,7 +879,6 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // Iterate over this in reverse as the real UA tends to be on the right side - var agent string for i := len(items) - 1; i >= 0; i-- { fAgent, ok := markToAgent[items[i]] if ok { @@ -974,6 +974,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { if !ok { return } + user.LastAgent = agent if common.Dev.SuperDebug { r.requestLogger.Print( "after PreRoute\n" + diff --git a/router_gen/main.go b/router_gen/main.go index b185e04a..af2ce40d 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -603,6 +603,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // TODO: Add a setting to disable this? // TODO: Use a more efficient detector instead of smashing every possible combination in ua := strings.TrimSpace(strings.Replace(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36","",-1)) // Noise, no one's going to be running this and it would require some sort of agent ranking system to determine which identifier should be prioritised over another + var agent string if ua == "" { counters.AgentViewCounter.Bump({{.AllAgentMap.blank}}) if common.Dev.DebugMode { @@ -657,7 +658,6 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // Iterate over this in reverse as the real UA tends to be on the right side - var agent string for i := len(items) - 1; i >= 0; i-- { fAgent, ok := markToAgent[items[i]] if ok { @@ -753,6 +753,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { if !ok { return } + user.LastAgent = agent if common.Dev.SuperDebug { r.requestLogger.Print( "after PreRoute\n" + diff --git a/routes/common.go b/routes/common.go index 7e0e0fab..e9812729 100644 --- a/routes/common.go +++ b/routes/common.go @@ -1,6 +1,7 @@ package routes import ( + //"fmt" "net/http" "strconv" "strings" @@ -20,6 +21,60 @@ func ParseSEOURL(urlBit string) (slug string, id int, err error) { return halves[0], tid, err } +func doPush(w http.ResponseWriter, header *common.Header) { + //fmt.Println("in doPush") + if common.Config.EnableCDNPush { + // TODO: Faster string building... + var sbuf string + var push = func(in []string) { + for _, path := range in { + sbuf += "; rel=preload; as=script," + } + } + push(header.Scripts) + //push(header.PreScriptsAsync) + push(header.ScriptsAsync) + + if len(header.Stylesheets) > 0 { + for _, path := range header.Stylesheets { + sbuf += "; rel=preload; as=style," + } + } + // TODO: Push avatars? + + if len(sbuf) > 0 { + sbuf = sbuf[:len(sbuf)-1] + w.Header().Set("Link", sbuf) + } + } else if !common.Config.DisableServerPush { + //fmt.Println("push enabled") + gzw, ok := w.(common.GzipResponseWriter) + if ok { + w = gzw.ResponseWriter + } + pusher, ok := w.(http.Pusher) + if !ok { + return + } + //fmt.Println("has pusher") + + var push = func(in []string) { + for _, path := range in { + //fmt.Println("pushing /static/" + path) + err := pusher.Push("/static/"+path, nil) + if err != nil { + break + } + } + } + push(header.Scripts) + //push(header.PreScriptsAsync) + push(header.ScriptsAsync) + push(header.Stylesheets) + // TODO: Push avatars? + } +} + func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, header *common.Header, pi interface{}) common.RouteError { if header.CurrentUser.Loggedin { header.MetaDesc = "" @@ -32,6 +87,14 @@ func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, hea w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self' www.youtube-nocookie.com;upgrade-insecure-requests") } header.AddScript("global.js") + + // Server pushes can backfire on certain browsers, so we want to make sure it's only triggered for ones where it'll help + lastAgent := header.CurrentUser.LastAgent + //fmt.Println("lastAgent:", lastAgent) + if lastAgent == "chrome" || lastAgent == "firefox" { + doPush(w, header) + } + if header.CurrentUser.IsAdmin { header.Elapsed1 = time.Since(header.StartedAt).String() } diff --git a/templates/header.html b/templates/header.html index 75e89936..55b5b63d 100644 --- a/templates/header.html +++ b/templates/header.html @@ -3,16 +3,16 @@ {{.Title}} | {{.Header.Site.Name}} {{range .Header.Stylesheets}} - {{end}} + {{end}} {{range .Header.PreScriptsAsync}} - {{end}} + {{end}} {{range .Header.ScriptsAsync}} - {{end}} + {{end}} {{range .Header.Scripts}} - {{end}} + {{end}} {{if .Header.MetaDesc}}{{end}} {{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}}