181 lines
4.4 KiB
Go
Raw Normal View History

package engine
import (
"net/url"
"strings"
2021-12-30 12:59:42 +01:00
"sync"
2021-03-01 14:20:56 +05:30
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/proto"
)
// Page is a single page in an isolated browser instance
type Page struct {
2022-01-31 08:52:36 +01:00
page *rod.Page
rules []rule
2022-01-31 08:52:36 +01:00
instance *Instance
hijackRouter *rod.HijackRouter
hijackNative *Hijack
2022-01-31 08:52:36 +01:00
mutex *sync.RWMutex
History []HistoryData
InteractshURLs []string
payloads map[string]interface{}
}
2021-12-29 09:51:50 +01:00
// HistoryData contains the page request/response pairs
type HistoryData struct {
RawRequest string
RawResponse string
}
// Run runs a list of actions by creating a new page in the browser.
func (i *Instance) Run(baseURL *url.URL, actions []*Action, payloads map[string]interface{}, timeout time.Duration) (map[string]string, *Page, error) {
page, err := i.engine.Page(proto.TargetCreateTarget{})
if err != nil {
return nil, nil, err
}
2021-03-01 14:20:56 +05:30
page = page.Timeout(timeout)
if i.browser.customAgent != "" {
2021-02-26 13:13:11 +05:30
if userAgentErr := page.SetUserAgent(&proto.NetworkSetUserAgentOverride{UserAgent: i.browser.customAgent}); userAgentErr != nil {
return nil, nil, userAgentErr
}
}
createdPage := &Page{page: page, instance: i, mutex: &sync.RWMutex{}, payloads: payloads}
// in case the page has request/response modification rules - enable global hijacking
if createdPage.hasModificationRules() || containsModificationActions(actions...) {
hijackRouter := page.HijackRequests()
if err := hijackRouter.Add("*", "", createdPage.routingRuleHandler); err != nil {
return nil, nil, err
}
createdPage.hijackRouter = hijackRouter
go hijackRouter.Run()
} else {
hijackRouter := NewHijack(page)
hijackRouter.SetPattern(&proto.FetchRequestPattern{
URLPattern: "*",
RequestStage: proto.FetchRequestStageResponse,
})
createdPage.hijackNative = hijackRouter
hijackRouterHandler := hijackRouter.Start(createdPage.routingRuleHandlerNative)
go func() {
_ = hijackRouterHandler()
}()
}
if err := page.SetViewport(&proto.EmulationSetDeviceMetricsOverride{Viewport: &proto.PageViewport{
Scale: 1,
Width: float64(1920),
Height: float64(1080),
}}); err != nil {
return nil, nil, err
}
if _, err := page.SetExtraHeaders([]string{"Accept-Language", "en, en-GB, en-us;"}); err != nil {
return nil, nil, err
}
data, err := createdPage.ExecuteActions(baseURL, actions)
if err != nil {
return nil, nil, err
}
return data, createdPage, nil
}
// Close closes a browser page
func (p *Page) Close() {
if p.hijackRouter != nil {
_ = p.hijackRouter.Stop()
}
if p.hijackNative != nil {
_ = p.hijackNative.Stop()
}
p.page.Close()
}
// Page returns the current page for the actions
func (p *Page) Page() *rod.Page {
return p.page
}
// Browser returns the browser that created the current page
func (p *Page) Browser() *rod.Browser {
return p.instance.engine
}
// URL returns the URL for the current page.
func (p *Page) URL() string {
info, err := p.page.Info()
if err != nil {
return ""
}
return info.URL
}
2021-12-29 09:51:50 +01:00
// DumpHistory returns the full page navigation history
func (p *Page) DumpHistory() string {
2022-01-31 08:52:36 +01:00
p.mutex.RLock()
defer p.mutex.RUnlock()
2021-12-30 12:59:42 +01:00
var historyDump strings.Builder
for _, historyData := range p.History {
historyDump.WriteString(historyData.RawRequest)
historyDump.WriteString(historyData.RawResponse)
}
return historyDump.String()
}
2021-12-30 12:59:42 +01:00
// addToHistory adds a request/response pair to the page history
2022-01-31 08:52:36 +01:00
func (p *Page) addToHistory(historyData ...HistoryData) {
p.mutex.Lock()
defer p.mutex.Unlock()
2021-12-30 12:59:42 +01:00
2022-01-31 08:52:36 +01:00
p.History = append(p.History, historyData...)
}
func (p *Page) addInteractshURL(URLs ...string) {
p.mutex.Lock()
defer p.mutex.Unlock()
p.InteractshURLs = append(p.InteractshURLs, URLs...)
2021-12-30 12:59:42 +01:00
}
func (p *Page) hasModificationRules() bool {
for _, rule := range p.rules {
if containsAnyModificationActionType(rule.Action) {
return true
}
}
return false
}
func containsModificationActions(actions ...*Action) bool {
for _, action := range actions {
if containsAnyModificationActionType(action.ActionType.ActionType) {
return true
}
}
return false
}
func containsAnyModificationActionType(actionTypes ...ActionType) bool {
for _, actionType := range actionTypes {
switch actionType {
case ActionSetMethod:
return true
case ActionAddHeader:
return true
case ActionSetHeader:
return true
case ActionDeleteHeader:
return true
case ActionSetBody:
return true
}
}
return false
}