-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathwebpagetest.go
205 lines (178 loc) · 4.98 KB
/
webpagetest.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package wpt
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"time"
)
const (
defaultBaseURL = "https://www.webpagetest.org"
// Test status
testNew = "new"
testFailed = "failed"
testQueued = "queued"
testRunning = "running"
testFinished = "finished"
testComplete = "complete"
testNotFound = "notfound"
testTimedOut = "timeout"
testError = "error"
testCancelled = "cancelled"
// URL endpoints
urlLocations = "getLocations.php"
urlRunTest = "runtest.php"
urlStatus = "testStatus.php"
urlResults = "jsonResult.php"
// urlCancel = "cancelTest.php" # todo
)
var (
serverURL *url.URL
pollingInterval time.Duration
maximumMonitorPeriod time.Duration
// Valid Connection Profiles
connectionProfiles = []string{
"DSL",
"Cable",
"FIOS",
"Dial",
"3G",
"3GFast",
"Native",
"custom",
}
)
// init setup the defaults for WebPageTest server and polling intervals
func init() {
serverURL, _ = url.Parse(defaultBaseURL)
pollingInterval = 60 * time.Second
maximumMonitorPeriod = 1800 * time.Second // 30 minutes
}
// SetURL sets your own private WebPageTest server url
// this is validated and stored as a url.URL struct
func SetURL(host string) error {
var err error
serverURL, err = url.Parse(host)
if err != nil {
return errors.New("webpagetest: invalid server url")
}
return nil
}
// SetPollingInterval allows for overwriting the default 60 second polling
// interval for checking test status.
func SetPollingInterval(period int64) {
pollingInterval = time.Duration(period) * time.Second
}
// SetMaximumMonitorPeriod allows overwriting the default 600 second
// maximum period to monitor tests for status changes
func SetMaximumMonitorPeriod(period int64) {
maximumMonitorPeriod = time.Duration(period) * time.Second
}
// Config represents persistent parameters to use for running tests
type Config struct {
// Host specifies the host to send http requests to
Host string
// APIKey to send with requests to the WebPageTest instance
APIKey string
// Timeout for all http requests
// if not specified this defaults to 0 and requests will not timeout
Timeout time.Duration
// Debug prints out all requests made to WPT.
Debug bool
}
// NewDefaultConfig returns a default configuratio for the client
// all available settings are explicitly specified for clarity
func NewDefaultConfig() Config {
return Config{
Host: defaultBaseURL,
APIKey: "",
Timeout: 60 * time.Second,
Debug: false,
}
}
// Client is used for all connections to the WPT instance
type Client struct {
// URL stores the Host and standard query params use for all
// requests to the WPT server
URL url.URL
APIKey string
HTTPClient *http.Client
// Debug prints out all requests made to WPT.
Debug bool
}
// NewClient parses the provided Config and returns a Client struct pointer
func NewClient(c Config) (*Client, error) {
u, err := url.Parse(c.Host)
if err != nil {
return nil, errors.New("webpagetest: error parsing configuration")
}
v := url.Values{}
v.Add("f", "json")
if c.APIKey != "" {
v.Add("k", c.APIKey)
}
u.RawQuery = v.Encode()
client := Client{
URL: *u,
HTTPClient: &http.Client{Timeout: c.Timeout},
// Defaults to false
Debug: c.Debug,
}
return &client, nil
}
// Locations queries webpagetest using getLocation.php and parses the returned data into
// an array of Location structs and returns this.
func (c *Client) Locations() (locations Locations, err error) {
var resp LocationsResponse
err = c.query(urlLocations, "", &resp)
if err != nil {
fmt.Println(err)
return nil, err
}
for _, location := range resp.Data {
locations = append(locations, location)
}
return locations, nil
}
// GetResults retrieves the test results for a specific test ID.
func (c *Client) GetResults(id string) (results TestResults, err error) {
qs := fmt.Sprintf("test=%s", id)
err = c.query(urlResults, qs, &results)
if err != nil {
fmt.Printf("webpagetest: error loading results for test: %s", id)
return TestResults{}, err
}
return results, nil
}
// query takes a given path and query string and calls the WPT host. The response is
// then UnMarshalled into the provided struct pointer
func (c *Client) query(path string, qs string, respStruct interface{}) error {
u := c.URL
u.Path = path
u.RawQuery = fmt.Sprintf("%s&%s", u.RawQuery, qs)
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return errors.New("webpagetest: error creating request")
}
req.Close = true
resp, err := c.HTTPClient.Do(req)
if err != nil {
return errors.New("webpagetest: error querying webpagetest")
}
if resp.StatusCode != http.StatusOK {
return errors.New("webpagetest: bad response code from host")
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return errors.New("webpagetest: error ready response body")
}
if c.Debug {
log.Printf("WPT Debug Request: %s %s %s", path, qs, body)
}
err = json.Unmarshal(body, &respStruct)
return err
}