casnode/routers/filter_ssr.go

131 lines
3.1 KiB
Go

package routers
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
"github.com/astaxie/beego/context"
"github.com/casbin/casnode/conf"
)
// var chromeCtx ctx.Context
var chromeCtxPool *SsrPool
var (
isChromeInstalled bool
isChromeInit bool
)
type PageCache struct {
time time.Time
html string
}
var renderCache = make(map[string]PageCache)
// modified from https://github.com/chromedp/chromedp/blob/master/allocate.go#L331
func isChromeFound() bool {
for _, path := range [...]string{
// Unix-like
"headless_shell",
"headless-shell",
"chromium",
"chromium-browser",
"google-chrome",
"google-chrome-stable",
"google-chrome-beta",
"google-chrome-unstable",
"/usr/bin/google-chrome",
// Windows
"chrome",
"chrome.exe", // in case PATHEXT is misconfigured
`C:\Program Files (x86)\Google\Chrome\Application\chrome.exe`,
`C:\Program Files\Google\Chrome\Application\chrome.exe`,
filepath.Join(os.Getenv("USERPROFILE"), `AppData\Local\Google\Chrome\Application\chrome.exe`),
// Mac
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
} {
_, err := exec.LookPath(path)
if err == nil {
// return found -> return true
// modified by Kininaru<shiftregister233@outlook.com>
return true
}
}
// return "google-chrome" -> return false
// modified by Kininaru<shiftregister233@outlook.com>
return false
}
func InitChromeDp() {
isChromeInit = true
isChromeInstalled = isChromeFound()
if isChromeInstalled {
chromeCtxNum, _ := conf.GetConfigInt64("chromeCtxNum")
if chromeCtxNum <= 0 {
chromeCtxNum = 1 // default
}
chromeCtxPool = NewSsrPool(int(chromeCtxNum))
}
go chromeCtxPool.Run() // start ssr_pool
}
func cacheSave(urlString string, res string) {
renderCache[urlString] = PageCache{time.Now(), res}
}
func cacheRestore(urlString string, cacheExpireSeconds int64) (string, bool) {
if _, ok := renderCache[urlString]; ok {
if time.Now().Sub(renderCache[urlString].time) < time.Duration(cacheExpireSeconds)*time.Second {
return renderCache[urlString].html, true
}
}
return "", false
}
var botRegex *regexp.Regexp
func isBot(userAgent string) bool {
if botRegex == nil {
botRegex, _ = regexp.Compile("bot|slurp|bing|crawler|spider")
}
userAgent = strings.ToLower(userAgent)
return botRegex.MatchString(userAgent)
}
func BotFilter(ctx *context.Context) {
if strings.HasPrefix(ctx.Request.URL.Path, "/api/") {
return
}
if isBot(ctx.Request.UserAgent()) {
ctx.ResponseWriter.WriteHeader(200)
urlStr := fmt.Sprintf("http://%s%s", ctx.Request.Host, ctx.Request.URL.Path)
if !isChromeInit {
InitChromeDp()
}
if !isChromeInstalled {
_, err := ctx.ResponseWriter.Write([]byte("Chrome is not installed in your server"))
if err != nil {
panic(err)
}
}
// the context will be canceled when the task send to channel
// sync.WaitGroup will wait for the task to be finished, it can avoid this problem
var wg sync.WaitGroup
wg.Add(1)
// create ssr_task and put it into task channel
task := NewRenderTask(ctx, urlStr, &wg)
chromeCtxPool.TaskChannel <- task
wg.Wait()
}
}