Skip to content

Commit

Permalink
refactor remote browser launching
Browse files Browse the repository at this point in the history
resolve #408
  • Loading branch information
ysmood committed May 8, 2021
1 parent 1eb661b commit 443e72d
Show file tree
Hide file tree
Showing 13 changed files with 295 additions and 265 deletions.
17 changes: 7 additions & 10 deletions lib/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
# Examples:
# To build the image:
#
# docker build -t ghcr.io/go-rod/rod -f lib/docker/Dockerfile .
# docker build -t ghcr.io/go-rod/rod -f lib/docker/Dockerfile .
#
# // use mirrors
# docker build --build-arg goproxy=https://goproxy.io,direct --build-arg apt_sources=https://mirrors.tuna.tsinghua.edu.cn \
# -t ghcr.io/go-rod/rod -f lib/docker/Dockerfile .

# build rod-launcher tool
# build rod-manager
FROM golang AS go

ARG goproxy=""
ARG goproxy="https://goproxy.io,direct"

COPY . /rod
WORKDIR /rod
RUN go env -w GO111MODULE=on && go env -w GOPROXY=$goproxy
RUN go build ./lib/launcher/rod-launcher
RUN go build ./lib/launcher/rod-manager
RUN go run ./lib/utils/get-browser

FROM ubuntu:bionic
Expand Down Expand Up @@ -49,6 +46,6 @@ RUN sed -i "s|http://archive.ubuntu.com|$apt_sources|g" /etc/apt/sources.list &&
ENTRYPOINT ["dumb-init", "--"]

COPY --from=go /root/.cache/rod /root/.cache/rod
COPY --from=go /rod/rod-launcher /usr/bin/
COPY --from=go /rod/rod-manager /usr/bin/

CMD rod-launcher
CMD rod-manager
38 changes: 38 additions & 0 deletions lib/examples/launch-managed/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package main

import (
"fmt"

"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
"github.com/go-rod/rod/lib/utils"
)

func main() {
// This example is to launch a browser remotely, not connect to a running browser remotely,
// to connect to a running browser check the "../connect-browser" example.
// Rod provides a docker image for beginers, run the below to start a launcher.Manager:
//
// docker run -p 7317:7317 ghcr.io/go-rod/rod
//
// For more information, check the doc of launcher.Manager
l := launcher.MustNewManaged("")

// You can also set any flag remotely before you launch the remote browser.
// Available flags: https://peter.sh/experiments/chromium-command-line-switches
l.Set("disable-gpu").Delete("disable-gpu")

// Launch with headful mode
l.Headless(false).XVFB("--server-num=5", "--server-args=-screen 0 1600x900x16")

browser := rod.New().Client(l.Client()).MustConnect()

// You may want to start a server to watch the screenshots of the remote browser.
launcher.Open(browser.ServeMonitor(""))

fmt.Println(
browser.MustPage("https://example.com/").MustEval("() => document.title"),
)

utils.Pause()
}
38 changes: 0 additions & 38 deletions lib/examples/remote-launch/main.go

This file was deleted.

61 changes: 35 additions & 26 deletions lib/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,26 @@ const (
flagWorkingDir = "rod-working-dir"
flagEnv = "rod-env"
flagXVFB = "rod-xvfb"
flagLeakless = "rod-leakless"
flagBin = "rod-bin"
)

// Launcher is a helper to launch browser binary smartly
type Launcher struct {
logger io.Writer
Flags map[string][]string `json:"flags"`

ctx context.Context
ctxCancel func()
browser *Browser
bin string
url string
parser *URLParser
Flags map[string][]string `json:"flags"`
pid int
exit chan struct{}
remote bool // remote mode or not
leakless bool

logger io.Writer

browser *Browser
parser *URLParser
pid int
exit chan struct{}

managed bool
serviceURL string
}

// New returns the default arguments to start browser.
Expand All @@ -50,6 +54,9 @@ func New() *Launcher {
}

defaultFlags := map[string][]string{
flagBin: {defaults.Bin},
flagLeakless: nil,

"user-data-dir": {dir},

// use random port by default
Expand Down Expand Up @@ -109,9 +116,7 @@ func New() *Launcher {
Flags: defaultFlags,
exit: make(chan struct{}),
browser: NewBrowser(),
bin: defaults.Bin,
parser: NewURLParser(),
leakless: true,
logger: ioutil.Discard,
}
}
Expand All @@ -129,10 +134,10 @@ func NewUserMode() *Launcher {
Flags: map[string][]string{
"remote-debugging-port": {"37712"},
"no-startup-window": nil,
flagBin: {bin},
},
browser: NewBrowser(),
exit: make(chan struct{}),
bin: bin,
parser: NewURLParser(),
logger: ioutil.Discard,
}
Expand All @@ -159,6 +164,12 @@ func (l *Launcher) Get(name string) (string, bool) {
return "", false
}

// Has flag or not
func (l *Launcher) Has(name string) bool {
_, has := l.GetFlags(name)
return has
}

// GetFlags from settings
func (l *Launcher) GetFlags(name string) ([]string, bool) {
flag, has := l.Flags[l.normalizeFlag(name)]
Expand Down Expand Up @@ -189,8 +200,7 @@ func (l *Launcher) Delete(name string) *Launcher {

// Bin set browser executable file path. If it's empty, launcher will automatically search or download the bin.
func (l *Launcher) Bin(path string) *Launcher {
l.bin = path
return l
return l.Set(flagBin, path)
}

// Headless switch. Whether to run browser in headless mode. A mode without visible UI.
Expand Down Expand Up @@ -220,8 +230,10 @@ func (l *Launcher) XVFB(args ...string) *Launcher {
// Leakless switch. If enabled, the browser will be force killed after the Go process exits.
// The doc of leakless: https://github.com/ysmood/leakless.
func (l *Launcher) Leakless(enable bool) *Launcher {
l.leakless = enable
return l
if enable {
return l.Set(flagLeakless)
}
return l.Delete(flagLeakless)
}

// Devtools switch to auto open devtools for each tab
Expand Down Expand Up @@ -257,12 +269,8 @@ func (l *Launcher) ProfileDir(dir string) *Launcher {
}

// RemoteDebuggingPort to launch the browser. Zero for a random port. Zero is the default value.
// If it's not zero, the launcher will try to connect to it before starting a new browser process.
// For example, to reuse the same browser process for between 2 runs of a Go program, you can
// do something like:
// launcher.New().RemoteDebuggingPort(9222).MustLaunch()
//
// Related doc: https://chromedevtools.github.io/devtools-protocol/
// If it's not zero and the Launcher.Leakless is disabled, the launcher will try to reconnect to it first,
// if the reconnection fails it will launch a new browser.
func (l *Launcher) RemoteDebuggingPort(port int) *Launcher {
return l.Set("remote-debugging-port", fmt.Sprintf("%d", port))
}
Expand Down Expand Up @@ -345,7 +353,7 @@ func (l *Launcher) Launch() (string, error) {
var ll *leakless.Launcher
var cmd *exec.Cmd

if l.leakless && leakless.Support() {
if l.Has(flagLeakless) && leakless.Support() {
ll = leakless.New()
cmd = ll.Command(bin, l.FormatArgs()...)
} else {
Expand Down Expand Up @@ -400,11 +408,12 @@ func (l *Launcher) setupCmd(cmd *exec.Cmd) {
}

func (l *Launcher) getBin() (string, error) {
if l.bin == "" {
bin, _ := l.Get(flagBin)
if bin == "" {
l.browser.Context = l.ctx
return l.browser.Get()
}
return l.bin, nil
return bin, nil
}

func (l *Launcher) getURL() (u string, err error) {
Expand Down
6 changes: 3 additions & 3 deletions lib/launcher/launcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,13 @@ func (t T) Launch() {
}

{
_, err := launcher.NewRemote("")
_, err := launcher.NewManaged("")
t.Err(err)

_, err = launcher.NewRemote("1://")
_, err = launcher.NewManaged("1://")
t.Err(err)

_, err = launcher.NewRemote("ws://not-exists")
_, err = launcher.NewManaged("ws://not-exists")
t.Err(err)
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/launcher/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/ysmood/got"
)

func BenchmarkRemoteLauncher(b *testing.B) {
func BenchmarkManager(b *testing.B) {
const concurrent = 30 // how many browsers will run at the same time
const num = 300 // how many browsers we will launch

Expand Down Expand Up @@ -47,7 +47,7 @@ func BenchmarkRemoteLauncher(b *testing.B) {
}()
}()

l := launcher.MustNewRemote("")
l := launcher.MustNewManaged("")
client := l.Client()
browser := rod.New().Context(ctx).Client(client).MustConnect()
page := browser.MustPage()
Expand Down
Loading

0 comments on commit 443e72d

Please sign in to comment.