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  }