-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathservice.go
160 lines (134 loc) · 3.18 KB
/
service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package webdriver
import (
"fmt"
"io"
"net/http"
"os"
"os/exec"
"runtime"
"strconv"
"time"
"github.com/radutopala/webdriver/bin"
)
const (
DefaultServicePort = 9515
)
// Service controls a locally-running subprocess.
type Service struct {
binaryPath string
port int
addr string
cmd *exec.Cmd
output io.Writer
}
// ServiceOption configures a Service instance.
type ServiceOption func(*Service) error
// Output specifies that the WebDriver service should log to the provided
// writer.
func Output(w io.Writer) ServiceOption {
return func(s *Service) error {
s.output = w
return nil
}
}
// Set the Service port
func Port(port int) ServiceOption {
return func(s *Service) error {
s.port = port
return nil
}
}
// Set the Service binary path
func BinaryPath(binaryPath string) ServiceOption {
return func(s *Service) error {
s.binaryPath = binaryPath
return nil
}
}
// Set the Service Cmd
func Cmd(cmd *exec.Cmd) ServiceOption {
return func(s *Service) error {
s.cmd = cmd
return nil
}
}
func generateBinary(path string) error {
//Create the binary file
file, err := os.Create(path)
if err != nil {
return fmt.Errorf("error creating %q: %v", path, err)
}
defer file.Close()
//Write the binary Data to the the created file
if _, err := file.Write(bin.Data); err != nil {
return fmt.Errorf("error writing binary data %q: %v", path, err)
}
return nil
}
func DefaultBinaryPath() string {
var binaryPath string
switch runtime.GOOS {
case "windows":
binaryPath = "./chromedriver.exe"
default:
binaryPath = "./chromedriver"
}
if _, err := os.Stat(binaryPath); err == nil {
return binaryPath
}
if err := generateBinary(binaryPath); err != nil {
fmt.Printf("\nError generating binary %s: %s", binaryPath, err)
}
os.Chmod(binaryPath, 0777)
return binaryPath
}
// NewService starts a ChromeDriver instance in the background.
func NewService(opts ...ServiceOption) (*Service, error) {
s := &Service{
binaryPath: DefaultBinaryPath(),
port: DefaultServicePort,
addr: fmt.Sprintf("http://127.0.0.1:%d", DefaultServicePort),
cmd: exec.Command(DefaultBinaryPath(), "--port="+strconv.Itoa(DefaultServicePort) /*, "--verbose"*/),
}
for _, opt := range opts {
if err := opt(s); err != nil {
return nil, err
}
}
s.cmd.Stderr = s.output
s.cmd.Stdout = s.output
s.cmd.Env = os.Environ()
if err := s.start(s.port); err != nil {
return nil, err
}
return s, nil
}
func (s *Service) start(port int) error {
if err := s.cmd.Start(); err != nil {
return err
}
for i := 0; i < 30; i++ {
time.Sleep(time.Second)
resp, err := http.Get(s.addr + "/status")
if err == nil {
resp.Body.Close()
switch resp.StatusCode {
// Selenium <3 returned Forbidden and BadRequest. ChromeDriver and
// Selenium 3 return OK.
case http.StatusForbidden, http.StatusBadRequest, http.StatusOK:
return nil
}
}
}
return fmt.Errorf("server did not respond on port %d", port)
}
// Stop shuts down the WebDriver service.
func (s *Service) Stop() error {
if err := s.cmd.Process.Kill(); err != nil {
return err
}
if err := s.cmd.Wait(); err != nil && err.Error() != "signal: killed" {
return err
}
return nil
}