Server push is back. But only for Chrome and Firefox.

Added the DisableServerPush and EnableCDNPush config.json settings.
This commit is contained in:
Azareal 2019-04-02 17:43:11 +10:00
parent 167bb230b4
commit af9a56a9a9
9 changed files with 116 additions and 33 deletions

View File

@ -10,10 +10,10 @@ import (
"github.com/Azareal/Gosora/common/phrases" "github.com/Azareal/Gosora/common/phrases"
) )
type HResource struct { /*type HResource struct {
Name string Name string
Hash string Hash string
} }*/
// TODO: Allow resources in spots other than /static/ and possibly even external domains (e.g. CDNs) // 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 // TODO: Preload Trumboyg on Cosora on the forum list
@ -21,11 +21,11 @@ type Header struct {
Title string 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 //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 NoticeList []string
Scripts []HResource Scripts []string
PreScriptsAsync []HResource PreScriptsAsync []string
ScriptsAsync []HResource ScriptsAsync []string
//Preload []string //Preload []string
Stylesheets []HResource Stylesheets []string
Widgets PageWidgets Widgets PageWidgets
Site *site Site *site
Settings SettingMap Settings SettingMap
@ -51,40 +51,48 @@ type Header struct {
func (header *Header) AddScript(name string) { func (header *Header) AddScript(name string) {
fname := "/static/" + name fname := "/static/" + name
var hash string var oname string
if fname[0] == '/' && fname[1] != '/' { if fname[0] == '/' && fname[1] != '/' {
file, ok := StaticFiles.Get(fname) file, ok := StaticFiles.Get(fname)
if ok { if ok {
hash = file.Sha256 oname = name + "?h=" + file.Sha256
} }
} }
//log.Print("name:", name) if oname == "" {
//log.Print("hash:", hash) oname = name
header.Scripts = append(header.Scripts, HResource{name, hash}) }
//log.Print("oname:", oname)
header.Scripts = append(header.Scripts, oname)
} }
func (header *Header) AddPreScriptAsync(name string) { func (header *Header) AddPreScriptAsync(name string) {
fname := "/static/" + name fname := "/static/" + name
var hash string var oname string
if fname[0] == '/' && fname[1] != '/' { if fname[0] == '/' && fname[1] != '/' {
file, ok := StaticFiles.Get(fname) file, ok := StaticFiles.Get(fname)
if ok { 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) { func (header *Header) AddScriptAsync(name string) {
fname := "/static/" + name fname := "/static/" + name
var hash string var oname string
if fname[0] == '/' && fname[1] != '/' { if fname[0] == '/' && fname[1] != '/' {
file, ok := StaticFiles.Get(fname) file, ok := StaticFiles.Get(fname)
if ok { 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) { /*func (header *Header) Preload(name string) {
@ -93,14 +101,17 @@ func (header *Header) AddScriptAsync(name string) {
func (header *Header) AddSheet(name string) { func (header *Header) AddSheet(name string) {
fname := "/static/" + name fname := "/static/" + name
var hash string var oname string
if fname[0] == '/' && fname[1] != '/' { if fname[0] == '/' && fname[1] != '/' {
file, ok := StaticFiles.Get(fname) file, ok := StaticFiles.Get(fname)
if ok { 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) { func (header *Header) AddNotice(name string) {

View File

@ -90,6 +90,8 @@ type config struct {
DisableLiveTopicList bool DisableLiveTopicList bool
DisableJSAntispam bool DisableJSAntispam bool
//LooseCSP bool //LooseCSP bool
DisableServerPush bool
EnableCDNPush bool
Noavatar string // ? - Move this into the settings table? Noavatar string // ? - Move this into the settings table?
ItemsPerPage int // ? - Move this into the settings table? ItemsPerPage int // ? - Move this into the settings table?

View File

@ -89,14 +89,14 @@ var Template_account_handle = genIntTmpl("account")
func tmplInitUsers() (User, User, User) { func tmplInitUsers() (User, User, User) {
avatar, microAvatar := BuildAvatar(62, "") 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? // TODO: Do a more accurate level calculation for this?
avatar, microAvatar = BuildAvatar(1, "") 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, "") 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 return user, user2, user3
} }
@ -108,10 +108,10 @@ func tmplInitHeaders(user User, user2 User, user3 User) (*Header, *Header, *Head
Theme: Themes[DefaultThemeBox.Load().(string)], Theme: Themes[DefaultThemeBox.Load().(string)],
CurrentUser: user, CurrentUser: user,
NoticeList: []string{"test"}, NoticeList: []string{"test"},
Stylesheets: []HResource{HResource{"panel.css", "d"}}, Stylesheets: []string{"panel.css"},
Scripts: []HResource{HResource{"whatever.js", "d"}}, Scripts: []string{"whatever.js"},
PreScriptsAsync: []HResource{HResource{"whatever.js", "d"}}, PreScriptsAsync: []string{"whatever.js"},
ScriptsAsync: []HResource{HResource{"whatever.js", "d"}}, ScriptsAsync: []string{"whatever.js"},
Widgets: PageWidgets{ Widgets: PageWidgets{
LeftSidebar: template.HTML("lalala"), LeftSidebar: template.HTML("lalala"),
}, },

View File

@ -53,6 +53,7 @@ type User struct {
Score int Score int
Liked int Liked int
LastIP string // ! This part of the UserCache data might fall out of date LastIP string // ! This part of the UserCache data might fall out of date
LastAgent string // ! Temporary hack, don't use
TempGroup int TempGroup int
} }

View File

@ -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. 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 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. ItemsPerPage - The number of posts, topics, etc. you want on each page.

View File

@ -824,6 +824,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// TODO: Add a setting to disable this? // TODO: Add a setting to disable this?
// TODO: Use a more efficient detector instead of smashing every possible combination in // 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 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 == "" { if ua == "" {
counters.AgentViewCounter.Bump(26) counters.AgentViewCounter.Bump(26)
if common.Dev.DebugMode { 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 // 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-- { for i := len(items) - 1; i >= 0; i-- {
fAgent, ok := markToAgent[items[i]] fAgent, ok := markToAgent[items[i]]
if ok { if ok {
@ -974,6 +974,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if !ok { if !ok {
return return
} }
user.LastAgent = agent
if common.Dev.SuperDebug { if common.Dev.SuperDebug {
r.requestLogger.Print( r.requestLogger.Print(
"after PreRoute\n" + "after PreRoute\n" +

View File

@ -603,6 +603,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// TODO: Add a setting to disable this? // TODO: Add a setting to disable this?
// TODO: Use a more efficient detector instead of smashing every possible combination in // 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 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 == "" { if ua == "" {
counters.AgentViewCounter.Bump({{.AllAgentMap.blank}}) counters.AgentViewCounter.Bump({{.AllAgentMap.blank}})
if common.Dev.DebugMode { 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 // 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-- { for i := len(items) - 1; i >= 0; i-- {
fAgent, ok := markToAgent[items[i]] fAgent, ok := markToAgent[items[i]]
if ok { if ok {
@ -753,6 +753,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if !ok { if !ok {
return return
} }
user.LastAgent = agent
if common.Dev.SuperDebug { if common.Dev.SuperDebug {
r.requestLogger.Print( r.requestLogger.Print(
"after PreRoute\n" + "after PreRoute\n" +

View File

@ -1,6 +1,7 @@
package routes package routes
import ( import (
//"fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -20,6 +21,60 @@ func ParseSEOURL(urlBit string) (slug string, id int, err error) {
return halves[0], tid, err 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 += "</static/" + path + ">; rel=preload; as=script,"
}
}
push(header.Scripts)
//push(header.PreScriptsAsync)
push(header.ScriptsAsync)
if len(header.Stylesheets) > 0 {
for _, path := range header.Stylesheets {
sbuf += "</static/" + path + ">; 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 { func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, header *common.Header, pi interface{}) common.RouteError {
if header.CurrentUser.Loggedin { if header.CurrentUser.Loggedin {
header.MetaDesc = "" 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") 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") 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 { if header.CurrentUser.IsAdmin {
header.Elapsed1 = time.Since(header.StartedAt).String() header.Elapsed1 = time.Since(header.StartedAt).String()
} }

View File

@ -3,16 +3,16 @@
<head> <head>
<title>{{.Title}} | {{.Header.Site.Name}}</title> <title>{{.Title}} | {{.Header.Site.Name}}</title>
{{range .Header.Stylesheets}} {{range .Header.Stylesheets}}
<link href="/static/{{.Name}}{{if .Hash}}?h={{.Hash}}{{end}}" rel="stylesheet" type="text/css">{{end}} <link href="/static/{{.}}" rel="stylesheet" type="text/css">{{end}}
{{range .Header.PreScriptsAsync}} {{range .Header.PreScriptsAsync}}
<script async type="text/javascript" src="/static/{{.Name}}{{if .Hash}}?h={{.Hash}}{{end}}"></script>{{end}} <script async type="text/javascript" src="/static/{{.}}"></script>{{end}}
<meta property="x-loggedin" content="{{.CurrentUser.Loggedin}}" /> <meta property="x-loggedin" content="{{.CurrentUser.Loggedin}}" />
<script type="text/javascript" src="/static/init.js"></script> <script type="text/javascript" src="/static/init.js"></script>
{{range .Header.ScriptsAsync}} {{range .Header.ScriptsAsync}}
<script async type="text/javascript" src="/static/{{.Name}}{{if .Hash}}?h={{.Hash}}{{end}}"></script>{{end}} <script async type="text/javascript" src="/static/{{.}}"></script>{{end}}
<script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script> <script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script>
{{range .Header.Scripts}} {{range .Header.Scripts}}
<script type="text/javascript" src="/static/{{.Name}}{{if .Hash}}?h={{.Hash}}{{end}}"></script>{{end}} <script type="text/javascript" src="/static/{{.}}"></script>{{end}}
<meta name="viewport" content="width=device-width,initial-scale = 1.0, maximum-scale=1.0,user-scalable=no" /> <meta name="viewport" content="width=device-width,initial-scale = 1.0, maximum-scale=1.0,user-scalable=no" />
{{if .Header.MetaDesc}}<meta name="description" content="{{.Header.MetaDesc}}" />{{end}} {{if .Header.MetaDesc}}<meta name="description" content="{{.Header.MetaDesc}}" />{{end}}
{{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}} {{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}}