Add SSR filter
This commit is contained in:
parent
f079778754
commit
d62ec114ae
|
@ -14,4 +14,6 @@ clientSecret = xxx
|
|||
jwtSecret = CasdoorSecret
|
||||
casdoorOrganization = "casbin"
|
||||
casdoorApplication = "app-confita"
|
||||
casdoorDbName =
|
||||
casdoorDbName =
|
||||
cacheExpireSeconds = 60
|
||||
chromeCtxNum = 1
|
1
go.mod
1
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
|
||||
github.com/casdoor/casdoor-go-sdk v0.3.3
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chromedp/chromedp v0.8.6
|
||||
github.com/cristalhq/jwt/v4 v4.0.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
|
|
24
go.sum
24
go.sum
|
@ -65,6 +65,12 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
|||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chromedp/cdproto v0.0.0-20220924210414-0e3390be1777 h1:nEnjcdmVQjhtQm0RFJxRINMw7lsQ8gidtbpsidiDqpY=
|
||||
github.com/chromedp/cdproto v0.0.0-20220924210414-0e3390be1777/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0=
|
||||
github.com/chromedp/chromedp v0.8.6 h1:KobeeqR2dpfKSG1prS3Y6+FbffMmGC6xmAobRXA9QEQ=
|
||||
github.com/chromedp/chromedp v0.8.6/go.mod h1:nBYHoD6YSNzrr82cIeuOzhw1Jo/s2o0QQ+ifTeoCZ+c=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
|
@ -109,6 +115,12 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG
|
|||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
|
||||
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0=
|
||||
|
@ -186,6 +198,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
|||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
@ -207,8 +221,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
|
@ -246,6 +264,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
|||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
|
@ -449,6 +469,7 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -456,8 +477,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
4
main.go
4
main.go
|
@ -52,8 +52,8 @@ func main() {
|
|||
//beego.DelStaticPath("/static")
|
||||
beego.SetStaticPath("/static", "web/build/static")
|
||||
// https://studygolang.com/articles/2303
|
||||
beego.InsertFilter("/", beego.BeforeRouter, routers.TransparentStatic) // must has this for default page
|
||||
beego.InsertFilter("/*", beego.BeforeRouter, routers.TransparentStatic)
|
||||
beego.InsertFilter("*", beego.BeforeRouter, routers.BotFilter)
|
||||
beego.InsertFilter("*", beego.BeforeRouter, routers.Static)
|
||||
|
||||
if conf.GetConfigString("redisEndpoint") == "" {
|
||||
beego.BConfig.WebConfig.Session.SessionProvider = "file"
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"github.com/casbin/confita/util"
|
||||
)
|
||||
|
||||
func TransparentStatic(ctx *context.Context) {
|
||||
func Static(ctx *context.Context) {
|
||||
urlPath := ctx.Request.URL.Path
|
||||
if strings.HasPrefix(urlPath, "/api/") {
|
||||
return
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
package routers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
)
|
||||
|
||||
// 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, _ := beego.AppConfig.Int("chromeCtxNum")
|
||||
if chromeCtxNum <= 0 {
|
||||
chromeCtxNum = 1 // default
|
||||
}
|
||||
chromeCtxPool = NewSsrPool(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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package routers
|
||||
|
||||
import (
|
||||
ctx "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/chromedp/chromedp"
|
||||
)
|
||||
|
||||
var renderTimeout = 20 * time.Second
|
||||
|
||||
type RenderTask struct {
|
||||
HttpCtx *context.Context
|
||||
Url string
|
||||
Render func(chromeCtx ctx.Context, url string) (string, error)
|
||||
Wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
type SsrPool struct {
|
||||
TaskChannel chan *RenderTask
|
||||
JobsChannel chan *RenderTask
|
||||
AddWorkerChannel chan bool
|
||||
WorkerNum int
|
||||
}
|
||||
|
||||
func NewRenderTask(httpCtx *context.Context, url string, wg *sync.WaitGroup) *RenderTask {
|
||||
return &RenderTask{
|
||||
HttpCtx: httpCtx,
|
||||
Url: url,
|
||||
Render: render,
|
||||
Wg: wg,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSsrPool(cap int) *SsrPool {
|
||||
pool := SsrPool{
|
||||
TaskChannel: make(chan *RenderTask),
|
||||
JobsChannel: make(chan *RenderTask),
|
||||
WorkerNum: cap,
|
||||
}
|
||||
return &pool
|
||||
}
|
||||
|
||||
func render(chromeCtx ctx.Context, url string) (string, error) {
|
||||
cacheExpireSeconds, err := beego.AppConfig.Int64("cacheExpireSeconds")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
res, cacheHit := cacheRestore(url, cacheExpireSeconds)
|
||||
if cacheHit {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// set timeout for render page
|
||||
done := make(chan bool, 1)
|
||||
go func() {
|
||||
err = chromedp.Run(chromeCtx,
|
||||
chromedp.Navigate(url),
|
||||
chromedp.OuterHTML("html", &res),
|
||||
)
|
||||
if err != nil {
|
||||
done <- false
|
||||
} else {
|
||||
done <- true
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case success := <-done:
|
||||
if success {
|
||||
cacheSave(url, res)
|
||||
return res, nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
case <-time.After(renderTimeout):
|
||||
err := chromedp.Cancel(chromeCtx)
|
||||
if err != nil {
|
||||
return "", errors.New("context cancel failed")
|
||||
}
|
||||
return "", ctx.Canceled
|
||||
}
|
||||
}
|
||||
|
||||
func (pool *SsrPool) worker() {
|
||||
// chromeCtx, _ := chromedp.NewExecAllocator(ctx.Background(), append(
|
||||
// chromedp.DefaultExecAllocatorOptions[:],
|
||||
// chromedp.Flag("headless", false))...)
|
||||
// chromeCtx, _ = chromedp.NewContext(chromeCtx)
|
||||
chromeCtx, _ := chromedp.NewContext(ctx.Background()) // set default context with headless mode
|
||||
for task := range pool.JobsChannel {
|
||||
cancel := func() bool {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
handleErr(task.HttpCtx, err.(error))
|
||||
task.Wg.Done()
|
||||
}
|
||||
}()
|
||||
urlStr, err := task.Render(chromeCtx, task.Url)
|
||||
if err != nil {
|
||||
if err == ctx.Canceled { // when browser process has terminated
|
||||
handleErr(task.HttpCtx, err)
|
||||
task.Wg.Done()
|
||||
return true
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
_, err = task.HttpCtx.ResponseWriter.Write([]byte(urlStr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
task.Wg.Done()
|
||||
return false
|
||||
}()
|
||||
// if canceled, break the loop
|
||||
if cancel {
|
||||
break
|
||||
}
|
||||
}
|
||||
// if break, add a new worker
|
||||
pool.AddWorkerChannel <- true
|
||||
}
|
||||
|
||||
func (pool *SsrPool) Run() {
|
||||
pool.AddWorkerChannel = make(chan bool, pool.WorkerNum)
|
||||
for i := 0; i < pool.WorkerNum; i++ {
|
||||
pool.AddWorkerChannel <- true
|
||||
}
|
||||
go func() {
|
||||
for j := range pool.AddWorkerChannel {
|
||||
if j == true {
|
||||
go pool.worker()
|
||||
}
|
||||
}
|
||||
}()
|
||||
for task := range pool.TaskChannel {
|
||||
pool.JobsChannel <- task
|
||||
}
|
||||
}
|
||||
|
||||
func handleErr(ctx *context.Context, err error) {
|
||||
var stack string
|
||||
logs.Critical("the request url is ", ctx.Input.URL())
|
||||
logs.Critical("Handler crashed with error:", err)
|
||||
for i := 1; ; i++ {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
logs.Critical(fmt.Sprintf("%s:%d", file, line))
|
||||
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
if ctx.Output.Status != 0 {
|
||||
ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
|
||||
} else {
|
||||
ctx.ResponseWriter.WriteHeader(500)
|
||||
}
|
||||
_, _err := ctx.ResponseWriter.Write([]byte(err.(error).Error()))
|
||||
if _err != nil {
|
||||
logs.Critical("write response error:", err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue