github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/routes/common.go (about) 1 package routes 2 3 import ( 4 //"fmt" 5 "net/http" 6 "strconv" 7 "strings" 8 "sync" 9 "time" 10 11 c "github.com/Azareal/Gosora/common" 12 co "github.com/Azareal/Gosora/common/counters" 13 "github.com/Azareal/Gosora/uutils" 14 ) 15 16 var successJSONBytes = []byte(`{"success":1}`) 17 18 func ParseSEOURL(urlBit string) (slug string, id int, err error) { 19 halves := strings.Split(urlBit, ".") 20 if len(halves) < 2 { 21 halves = append(halves, halves[0]) 22 } 23 tid, err := strconv.Atoi(halves[1]) 24 return halves[0], tid, err 25 } 26 27 const slen1 = len("</s/>;rel=preload;as=script,") + 6 28 const slen2 = len("</s/>;rel=preload;as=style,") + 7 29 30 var pushStrPool = sync.Pool{} 31 32 func doPush(w http.ResponseWriter, h *c.Header) { 33 //fmt.Println("in doPush") 34 if len(h.Scripts) == 0 && len(h.ScriptsAsync) == 0 && len(h.Stylesheets) == 0 { 35 return 36 } 37 if c.Config.EnableCDNPush { 38 var sb *strings.Builder = &strings.Builder{} 39 /*ii := pushStrPool.Get() 40 if ii == nil { 41 sb = &strings.Builder{} 42 } else { 43 sb = ii.(*strings.Builder) 44 sb.Reset() 45 }*/ 46 sb.Grow((slen1 * (len(h.Scripts) + len(h.ScriptsAsync))) + ((slen2 + 7) * len(h.Stylesheets))) 47 push := func(in []c.HScript) { 48 for i, s := range in { 49 if i != 0 { 50 sb.WriteString(",</s/") 51 } else { 52 sb.WriteString("</s/") 53 } 54 sb.WriteString(s.Name) 55 sb.WriteString(">;rel=preload;as=script") 56 } 57 } 58 push(h.Scripts) 59 //push(h.PreScriptsAsync) 60 push(h.ScriptsAsync) 61 62 if len(h.Stylesheets) > 0 { 63 for i, s := range h.Stylesheets { 64 if i != 0 { 65 sb.WriteString(",</s/") 66 } else { 67 sb.WriteString("</s/") 68 } 69 sb.WriteString(s.Name) 70 sb.WriteString(">;rel=preload;as=style") 71 } 72 } 73 // TODO: Push avatars? 74 75 if sb.Len() > 0 { 76 sbuf := sb.String() 77 w.Header().Set("Link", sbuf) 78 //pushStrPool.Put(sb) 79 } 80 } else if !c.Config.DisableServerPush { 81 //fmt.Println("push enabled") 82 /*if bzw, ok := w.(c.BrResponseWriter); ok { 83 w = bzw.ResponseWriter 84 } else */if gzw, ok := w.(c.GzipResponseWriter); ok { 85 w = gzw.ResponseWriter 86 } 87 pusher, ok := w.(http.Pusher) 88 if !ok { 89 return 90 } 91 //panic("has pusher") 92 //fmt.Println("has pusher") 93 94 var sb *strings.Builder = &strings.Builder{} 95 /*ii := pushStrPool.Get() 96 if ii == nil { 97 sb = &strings.Builder{} 98 } else { 99 sb = ii.(*strings.Builder) 100 sb.Reset() 101 }*/ 102 sb.Grow(6 * (len(h.Scripts) + len(h.ScriptsAsync) + len(h.Stylesheets))) 103 push := func(in []c.HScript) { 104 for _, s := range in { 105 //fmt.Println("pushing /s/" + path) 106 sb.WriteString("/s/") 107 sb.WriteString(s.Name) 108 err := pusher.Push(sb.String(), nil) 109 if err != nil { 110 break 111 } 112 sb.Reset() 113 } 114 } 115 push(h.Scripts) 116 //push(h.PreScriptsAsync) 117 push(h.ScriptsAsync) 118 push(h.Stylesheets) 119 // TODO: Push avatars? 120 //pushStrPool.Put(sb) 121 } 122 } 123 124 func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) c.RouteError { 125 return renderTemplate2(tmplName, tmplName, w, r, header, pi) 126 } 127 128 func renderTemplate2(tmplName, hookName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) c.RouteError { 129 err := renderTemplate3(tmplName, tmplName, w, r, header, pi) 130 if err != nil { 131 return c.InternalError(err, w, r) 132 } 133 return nil 134 } 135 136 func FootHeaders(w http.ResponseWriter, h *c.Header) { 137 // TODO: Only set video domain when there is a video on the page 138 if !h.LooseCSP { 139 he := w.Header() 140 if c.Config.SslSchema { 141 if h.ExternalMedia { 142 he.Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'"+c.Config.ExtraCSPOrigins+"; 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 embed.nicovideo.jp;upgrade-insecure-requests") 143 } else { 144 he.Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'"+c.Config.ExtraCSPOrigins+"; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self';upgrade-insecure-requests") 145 } 146 } else { 147 if h.ExternalMedia { 148 he.Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'"+c.Config.ExtraCSPOrigins+"; 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 embed.nicovideo.jp") 149 } else { 150 he.Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'"+c.Config.ExtraCSPOrigins+"; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self'") 151 } 152 } 153 } 154 155 // Server pushes can backfire on certain browsers, so we want to make sure it's only triggered for ones where it'll help 156 lastAgent := h.CurrentUser.LastAgent 157 //fmt.Println("lastAgent:", lastAgent) 158 if lastAgent == c.Chrome || lastAgent == c.Firefox { 159 doPush(w, h) 160 } 161 } 162 163 func renderTemplate3(tmplName, hookName string, w http.ResponseWriter, r *http.Request, h *c.Header, pi interface{}) error { 164 s := h.Stylesheets 165 h.Stylesheets = nil 166 noDescSimpleBot := h.CurrentUser.LastAgent == c.SimpleBots[0] || h.CurrentUser.LastAgent == c.SimpleBots[1] 167 var simpleBot bool 168 for _, agent := range c.SimpleBots { 169 if h.CurrentUser.LastAgent == agent { 170 simpleBot = true 171 } 172 } 173 inner := r.FormValue("i") == "1" 174 if !inner && !simpleBot { 175 c.PrepResources(h.CurrentUser, h, h.Theme) 176 for _, ss := range s { 177 h.Stylesheets = append(h.Stylesheets, ss) 178 } 179 h.AddScript("global.js") 180 if h.CurrentUser.Loggedin { 181 h.AddScriptAsync("member.js") 182 } 183 } else { 184 h.CurrentUser.LastAgent = 0 185 } 186 187 if h.CurrentUser.Loggedin || inner || noDescSimpleBot { 188 h.MetaDesc = "" 189 h.OGDesc = "" 190 } else if h.MetaDesc != "" && h.OGDesc == "" { 191 h.OGDesc = h.MetaDesc 192 } 193 194 if !simpleBot { 195 FootHeaders(w, h) 196 } else { 197 h.GoogSiteVerify = "" 198 } 199 if h.Zone != "error" { 200 since := time.Duration(uutils.Nanotime() - h.StartedAt) 201 if h.CurrentUser.IsAdmin { 202 h.Elapsed1 = since.String() 203 } 204 co.PerfCounter.Push(since /*, false*/) 205 } 206 if c.RunPreRenderHook("pre_render_"+hookName, w, r, h.CurrentUser, pi) { 207 return nil 208 } 209 /*defer func() { 210 c.StrSlicePool.Put(h.Scripts) 211 c.StrSlicePool.Put(h.PreScriptsAsync) 212 }()*/ 213 return h.Theme.RunTmpl(tmplName, pi, w) 214 } 215 216 // TODO: Rename renderTemplate to RenderTemplate instead of using this hack to avoid breaking things 217 var RenderTemplate = renderTemplate3 218 219 func actionSuccess(w http.ResponseWriter, r *http.Request, dest string, js bool) c.RouteError { 220 if !js { 221 http.Redirect(w, r, dest, http.StatusSeeOther) 222 } else { 223 _, _ = w.Write(successJSONBytes) 224 } 225 return nil 226 }