xref: /plugin/combo/vendor/php-webdriver/webdriver/lib/Remote/HttpCommandExecutor.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeaunamespace Facebook\WebDriver\Remote;
4*04fd306cSNickeau
5*04fd306cSNickeauuse BadMethodCallException;
6*04fd306cSNickeauuse Facebook\WebDriver\Exception\WebDriverCurlException;
7*04fd306cSNickeauuse Facebook\WebDriver\Exception\WebDriverException;
8*04fd306cSNickeauuse Facebook\WebDriver\WebDriverCommandExecutor;
9*04fd306cSNickeauuse InvalidArgumentException;
10*04fd306cSNickeau
11*04fd306cSNickeau/**
12*04fd306cSNickeau * Command executor talking to the standalone server via HTTP.
13*04fd306cSNickeau */
14*04fd306cSNickeauclass HttpCommandExecutor implements WebDriverCommandExecutor
15*04fd306cSNickeau{
16*04fd306cSNickeau    const DEFAULT_HTTP_HEADERS = [
17*04fd306cSNickeau        'Content-Type: application/json;charset=UTF-8',
18*04fd306cSNickeau        'Accept: application/json',
19*04fd306cSNickeau    ];
20*04fd306cSNickeau
21*04fd306cSNickeau    /**
22*04fd306cSNickeau     * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#command-reference
23*04fd306cSNickeau     */
24*04fd306cSNickeau    protected static $commands = [
25*04fd306cSNickeau        DriverCommand::ACCEPT_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/accept_alert'],
26*04fd306cSNickeau        DriverCommand::ADD_COOKIE => ['method' => 'POST', 'url' => '/session/:sessionId/cookie'],
27*04fd306cSNickeau        DriverCommand::CLEAR_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/clear'],
28*04fd306cSNickeau        DriverCommand::CLICK_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/click'],
29*04fd306cSNickeau        DriverCommand::CLOSE => ['method' => 'DELETE', 'url' => '/session/:sessionId/window'],
30*04fd306cSNickeau        DriverCommand::DELETE_ALL_COOKIES => ['method' => 'DELETE', 'url' => '/session/:sessionId/cookie'],
31*04fd306cSNickeau        DriverCommand::DELETE_COOKIE => ['method' => 'DELETE', 'url' => '/session/:sessionId/cookie/:name'],
32*04fd306cSNickeau        DriverCommand::DISMISS_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/dismiss_alert'],
33*04fd306cSNickeau        DriverCommand::ELEMENT_EQUALS => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/equals/:other'],
34*04fd306cSNickeau        DriverCommand::FIND_CHILD_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/element'],
35*04fd306cSNickeau        DriverCommand::FIND_CHILD_ELEMENTS => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/elements'],
36*04fd306cSNickeau        DriverCommand::EXECUTE_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute'],
37*04fd306cSNickeau        DriverCommand::EXECUTE_ASYNC_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute_async'],
38*04fd306cSNickeau        DriverCommand::FIND_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element'],
39*04fd306cSNickeau        DriverCommand::FIND_ELEMENTS => ['method' => 'POST', 'url' => '/session/:sessionId/elements'],
40*04fd306cSNickeau        DriverCommand::SWITCH_TO_FRAME => ['method' => 'POST', 'url' => '/session/:sessionId/frame'],
41*04fd306cSNickeau        DriverCommand::SWITCH_TO_PARENT_FRAME => ['method' => 'POST', 'url' => '/session/:sessionId/frame/parent'],
42*04fd306cSNickeau        DriverCommand::SWITCH_TO_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window'],
43*04fd306cSNickeau        DriverCommand::GET => ['method' => 'POST', 'url' => '/session/:sessionId/url'],
44*04fd306cSNickeau        DriverCommand::GET_ACTIVE_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/active'],
45*04fd306cSNickeau        DriverCommand::GET_ALERT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/alert_text'],
46*04fd306cSNickeau        DriverCommand::GET_ALL_COOKIES => ['method' => 'GET', 'url' => '/session/:sessionId/cookie'],
47*04fd306cSNickeau        DriverCommand::GET_NAMED_COOKIE => ['method' => 'GET', 'url' => '/session/:sessionId/cookie/:name'],
48*04fd306cSNickeau        DriverCommand::GET_ALL_SESSIONS => ['method' => 'GET', 'url' => '/sessions'],
49*04fd306cSNickeau        DriverCommand::GET_AVAILABLE_LOG_TYPES => ['method' => 'GET', 'url' => '/session/:sessionId/log/types'],
50*04fd306cSNickeau        DriverCommand::GET_CURRENT_URL => ['method' => 'GET', 'url' => '/session/:sessionId/url'],
51*04fd306cSNickeau        DriverCommand::GET_CURRENT_WINDOW_HANDLE => ['method' => 'GET', 'url' => '/session/:sessionId/window_handle'],
52*04fd306cSNickeau        DriverCommand::GET_ELEMENT_ATTRIBUTE => [
53*04fd306cSNickeau            'method' => 'GET',
54*04fd306cSNickeau            'url' => '/session/:sessionId/element/:id/attribute/:name',
55*04fd306cSNickeau        ],
56*04fd306cSNickeau        DriverCommand::GET_ELEMENT_VALUE_OF_CSS_PROPERTY => [
57*04fd306cSNickeau            'method' => 'GET',
58*04fd306cSNickeau            'url' => '/session/:sessionId/element/:id/css/:propertyName',
59*04fd306cSNickeau        ],
60*04fd306cSNickeau        DriverCommand::GET_ELEMENT_LOCATION => [
61*04fd306cSNickeau            'method' => 'GET',
62*04fd306cSNickeau            'url' => '/session/:sessionId/element/:id/location',
63*04fd306cSNickeau        ],
64*04fd306cSNickeau        DriverCommand::GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW => [
65*04fd306cSNickeau            'method' => 'GET',
66*04fd306cSNickeau            'url' => '/session/:sessionId/element/:id/location_in_view',
67*04fd306cSNickeau        ],
68*04fd306cSNickeau        DriverCommand::GET_ELEMENT_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/size'],
69*04fd306cSNickeau        DriverCommand::GET_ELEMENT_TAG_NAME => ['method' => 'GET',  'url' => '/session/:sessionId/element/:id/name'],
70*04fd306cSNickeau        DriverCommand::GET_ELEMENT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/text'],
71*04fd306cSNickeau        DriverCommand::GET_LOG => ['method' => 'POST', 'url' => '/session/:sessionId/log'],
72*04fd306cSNickeau        DriverCommand::GET_PAGE_SOURCE => ['method' => 'GET', 'url' => '/session/:sessionId/source'],
73*04fd306cSNickeau        DriverCommand::GET_SCREEN_ORIENTATION => ['method' => 'GET', 'url' => '/session/:sessionId/orientation'],
74*04fd306cSNickeau        DriverCommand::GET_CAPABILITIES => ['method' => 'GET', 'url' => '/session/:sessionId'],
75*04fd306cSNickeau        DriverCommand::GET_TITLE => ['method' => 'GET', 'url' => '/session/:sessionId/title'],
76*04fd306cSNickeau        DriverCommand::GET_WINDOW_HANDLES => ['method' => 'GET', 'url' => '/session/:sessionId/window_handles'],
77*04fd306cSNickeau        DriverCommand::GET_WINDOW_POSITION => [
78*04fd306cSNickeau            'method' => 'GET',
79*04fd306cSNickeau            'url' => '/session/:sessionId/window/:windowHandle/position',
80*04fd306cSNickeau        ],
81*04fd306cSNickeau        DriverCommand::GET_WINDOW_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/window/:windowHandle/size'],
82*04fd306cSNickeau        DriverCommand::GO_BACK => ['method' => 'POST', 'url' => '/session/:sessionId/back'],
83*04fd306cSNickeau        DriverCommand::GO_FORWARD => ['method' => 'POST', 'url' => '/session/:sessionId/forward'],
84*04fd306cSNickeau        DriverCommand::IS_ELEMENT_DISPLAYED => [
85*04fd306cSNickeau            'method' => 'GET',
86*04fd306cSNickeau            'url' => '/session/:sessionId/element/:id/displayed',
87*04fd306cSNickeau        ],
88*04fd306cSNickeau        DriverCommand::IS_ELEMENT_ENABLED => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/enabled'],
89*04fd306cSNickeau        DriverCommand::IS_ELEMENT_SELECTED => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/selected'],
90*04fd306cSNickeau        DriverCommand::MAXIMIZE_WINDOW => [
91*04fd306cSNickeau            'method' => 'POST',
92*04fd306cSNickeau            'url' => '/session/:sessionId/window/:windowHandle/maximize',
93*04fd306cSNickeau        ],
94*04fd306cSNickeau        DriverCommand::MOUSE_DOWN => ['method' => 'POST', 'url' => '/session/:sessionId/buttondown'],
95*04fd306cSNickeau        DriverCommand::MOUSE_UP => ['method' => 'POST', 'url' => '/session/:sessionId/buttonup'],
96*04fd306cSNickeau        DriverCommand::CLICK => ['method' => 'POST', 'url' => '/session/:sessionId/click'],
97*04fd306cSNickeau        DriverCommand::DOUBLE_CLICK => ['method' => 'POST', 'url' => '/session/:sessionId/doubleclick'],
98*04fd306cSNickeau        DriverCommand::MOVE_TO => ['method' => 'POST', 'url' => '/session/:sessionId/moveto'],
99*04fd306cSNickeau        DriverCommand::NEW_SESSION => ['method' => 'POST', 'url' => '/session'],
100*04fd306cSNickeau        DriverCommand::QUIT => ['method' => 'DELETE', 'url' => '/session/:sessionId'],
101*04fd306cSNickeau        DriverCommand::REFRESH => ['method' => 'POST', 'url' => '/session/:sessionId/refresh'],
102*04fd306cSNickeau        DriverCommand::UPLOAD_FILE => ['method' => 'POST', 'url' => '/session/:sessionId/file'], // undocumented
103*04fd306cSNickeau        DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/keys'],
104*04fd306cSNickeau        DriverCommand::SET_ALERT_VALUE => ['method' => 'POST', 'url' => '/session/:sessionId/alert_text'],
105*04fd306cSNickeau        DriverCommand::SEND_KEYS_TO_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/value'],
106*04fd306cSNickeau        DriverCommand::IMPLICITLY_WAIT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts/implicit_wait'],
107*04fd306cSNickeau        DriverCommand::SET_SCREEN_ORIENTATION => ['method' => 'POST', 'url' => '/session/:sessionId/orientation'],
108*04fd306cSNickeau        DriverCommand::SET_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
109*04fd306cSNickeau        DriverCommand::SET_SCRIPT_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts/async_script'],
110*04fd306cSNickeau        DriverCommand::SET_WINDOW_POSITION => [
111*04fd306cSNickeau            'method' => 'POST',
112*04fd306cSNickeau            'url' => '/session/:sessionId/window/:windowHandle/position',
113*04fd306cSNickeau        ],
114*04fd306cSNickeau        DriverCommand::SET_WINDOW_SIZE => [
115*04fd306cSNickeau            'method' => 'POST',
116*04fd306cSNickeau            'url' => '/session/:sessionId/window/:windowHandle/size',
117*04fd306cSNickeau        ],
118*04fd306cSNickeau        DriverCommand::STATUS => ['method' => 'GET', 'url' => '/status'],
119*04fd306cSNickeau        DriverCommand::SUBMIT_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/submit'],
120*04fd306cSNickeau        DriverCommand::SCREENSHOT => ['method' => 'GET', 'url' => '/session/:sessionId/screenshot'],
121*04fd306cSNickeau        DriverCommand::TAKE_ELEMENT_SCREENSHOT => [
122*04fd306cSNickeau            'method' => 'GET',
123*04fd306cSNickeau            'url' => '/session/:sessionId/element/:id/screenshot',
124*04fd306cSNickeau        ],
125*04fd306cSNickeau        DriverCommand::TOUCH_SINGLE_TAP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/click'],
126*04fd306cSNickeau        DriverCommand::TOUCH_DOWN => ['method' => 'POST', 'url' => '/session/:sessionId/touch/down'],
127*04fd306cSNickeau        DriverCommand::TOUCH_DOUBLE_TAP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/doubleclick'],
128*04fd306cSNickeau        DriverCommand::TOUCH_FLICK => ['method' => 'POST', 'url' => '/session/:sessionId/touch/flick'],
129*04fd306cSNickeau        DriverCommand::TOUCH_LONG_PRESS => ['method' => 'POST', 'url' => '/session/:sessionId/touch/longclick'],
130*04fd306cSNickeau        DriverCommand::TOUCH_MOVE => ['method' => 'POST', 'url' => '/session/:sessionId/touch/move'],
131*04fd306cSNickeau        DriverCommand::TOUCH_SCROLL => ['method' => 'POST', 'url' => '/session/:sessionId/touch/scroll'],
132*04fd306cSNickeau        DriverCommand::TOUCH_UP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/up'],
133*04fd306cSNickeau        DriverCommand::CUSTOM_COMMAND => [],
134*04fd306cSNickeau    ];
135*04fd306cSNickeau    /**
136*04fd306cSNickeau     * @var array Will be merged with $commands
137*04fd306cSNickeau     */
138*04fd306cSNickeau    protected static $w3cCompliantCommands = [
139*04fd306cSNickeau        DriverCommand::ACCEPT_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/alert/accept'],
140*04fd306cSNickeau        DriverCommand::ACTIONS => ['method' => 'POST', 'url' => '/session/:sessionId/actions'],
141*04fd306cSNickeau        DriverCommand::DISMISS_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/alert/dismiss'],
142*04fd306cSNickeau        DriverCommand::EXECUTE_ASYNC_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute/async'],
143*04fd306cSNickeau        DriverCommand::EXECUTE_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute/sync'],
144*04fd306cSNickeau        DriverCommand::FULLSCREEN_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/fullscreen'],
145*04fd306cSNickeau        DriverCommand::GET_ACTIVE_ELEMENT => ['method' => 'GET', 'url' => '/session/:sessionId/element/active'],
146*04fd306cSNickeau        DriverCommand::GET_ALERT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/alert/text'],
147*04fd306cSNickeau        DriverCommand::GET_CURRENT_WINDOW_HANDLE => ['method' => 'GET', 'url' => '/session/:sessionId/window'],
148*04fd306cSNickeau        DriverCommand::GET_ELEMENT_LOCATION => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/rect'],
149*04fd306cSNickeau        DriverCommand::GET_ELEMENT_PROPERTY => [
150*04fd306cSNickeau            'method' => 'GET',
151*04fd306cSNickeau            'url' => '/session/:sessionId/element/:id/property/:name',
152*04fd306cSNickeau        ],
153*04fd306cSNickeau        DriverCommand::GET_ELEMENT_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/rect'],
154*04fd306cSNickeau        DriverCommand::GET_WINDOW_HANDLES => ['method' => 'GET', 'url' => '/session/:sessionId/window/handles'],
155*04fd306cSNickeau        DriverCommand::GET_WINDOW_POSITION => ['method' => 'GET', 'url' => '/session/:sessionId/window/rect'],
156*04fd306cSNickeau        DriverCommand::GET_WINDOW_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/window/rect'],
157*04fd306cSNickeau        DriverCommand::IMPLICITLY_WAIT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
158*04fd306cSNickeau        DriverCommand::MAXIMIZE_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/maximize'],
159*04fd306cSNickeau        DriverCommand::MINIMIZE_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/minimize'],
160*04fd306cSNickeau        DriverCommand::NEW_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/new'],
161*04fd306cSNickeau        DriverCommand::SET_ALERT_VALUE => ['method' => 'POST', 'url' => '/session/:sessionId/alert/text'],
162*04fd306cSNickeau        DriverCommand::SET_SCRIPT_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
163*04fd306cSNickeau        DriverCommand::SET_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
164*04fd306cSNickeau        DriverCommand::SET_WINDOW_SIZE => ['method' => 'POST', 'url' => '/session/:sessionId/window/rect'],
165*04fd306cSNickeau        DriverCommand::SET_WINDOW_POSITION => ['method' => 'POST', 'url' => '/session/:sessionId/window/rect'],
166*04fd306cSNickeau    ];
167*04fd306cSNickeau    /**
168*04fd306cSNickeau     * @var string
169*04fd306cSNickeau     */
170*04fd306cSNickeau    protected $url;
171*04fd306cSNickeau    /**
172*04fd306cSNickeau     * @var resource
173*04fd306cSNickeau     */
174*04fd306cSNickeau    protected $curl;
175*04fd306cSNickeau    /**
176*04fd306cSNickeau     * @var bool
177*04fd306cSNickeau     */
178*04fd306cSNickeau    protected $isW3cCompliant = true;
179*04fd306cSNickeau
180*04fd306cSNickeau    /**
181*04fd306cSNickeau     * @param string $url
182*04fd306cSNickeau     * @param string|null $http_proxy
183*04fd306cSNickeau     * @param int|null $http_proxy_port
184*04fd306cSNickeau     */
185*04fd306cSNickeau    public function __construct($url, $http_proxy = null, $http_proxy_port = null)
186*04fd306cSNickeau    {
187*04fd306cSNickeau        self::$w3cCompliantCommands = array_merge(self::$commands, self::$w3cCompliantCommands);
188*04fd306cSNickeau
189*04fd306cSNickeau        $this->url = $url;
190*04fd306cSNickeau        $this->curl = curl_init();
191*04fd306cSNickeau
192*04fd306cSNickeau        if (!empty($http_proxy)) {
193*04fd306cSNickeau            curl_setopt($this->curl, CURLOPT_PROXY, $http_proxy);
194*04fd306cSNickeau            if ($http_proxy_port !== null) {
195*04fd306cSNickeau                curl_setopt($this->curl, CURLOPT_PROXYPORT, $http_proxy_port);
196*04fd306cSNickeau            }
197*04fd306cSNickeau        }
198*04fd306cSNickeau
199*04fd306cSNickeau        // Get credentials from $url (if any)
200*04fd306cSNickeau        $matches = null;
201*04fd306cSNickeau        if (preg_match("/^(https?:\/\/)(.*):(.*)@(.*?)/U", $url, $matches)) {
202*04fd306cSNickeau            $this->url = $matches[1] . $matches[4];
203*04fd306cSNickeau            $auth_creds = $matches[2] . ':' . $matches[3];
204*04fd306cSNickeau            curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
205*04fd306cSNickeau            curl_setopt($this->curl, CURLOPT_USERPWD, $auth_creds);
206*04fd306cSNickeau        }
207*04fd306cSNickeau
208*04fd306cSNickeau        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
209*04fd306cSNickeau        curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true);
210*04fd306cSNickeau        curl_setopt($this->curl, CURLOPT_HTTPHEADER, static::DEFAULT_HTTP_HEADERS);
211*04fd306cSNickeau        $this->setRequestTimeout(30000);
212*04fd306cSNickeau        $this->setConnectionTimeout(30000);
213*04fd306cSNickeau    }
214*04fd306cSNickeau
215*04fd306cSNickeau    public function disableW3cCompliance()
216*04fd306cSNickeau    {
217*04fd306cSNickeau        $this->isW3cCompliant = false;
218*04fd306cSNickeau    }
219*04fd306cSNickeau
220*04fd306cSNickeau    /**
221*04fd306cSNickeau     * Set timeout for the connect phase
222*04fd306cSNickeau     *
223*04fd306cSNickeau     * @param int $timeout_in_ms Timeout in milliseconds
224*04fd306cSNickeau     * @return HttpCommandExecutor
225*04fd306cSNickeau     */
226*04fd306cSNickeau    public function setConnectionTimeout($timeout_in_ms)
227*04fd306cSNickeau    {
228*04fd306cSNickeau        // There is a PHP bug in some versions which didn't define the constant.
229*04fd306cSNickeau        curl_setopt(
230*04fd306cSNickeau            $this->curl,
231*04fd306cSNickeau            /* CURLOPT_CONNECTTIMEOUT_MS */
232*04fd306cSNickeau            156,
233*04fd306cSNickeau            $timeout_in_ms
234*04fd306cSNickeau        );
235*04fd306cSNickeau
236*04fd306cSNickeau        return $this;
237*04fd306cSNickeau    }
238*04fd306cSNickeau
239*04fd306cSNickeau    /**
240*04fd306cSNickeau     * Set the maximum time of a request
241*04fd306cSNickeau     *
242*04fd306cSNickeau     * @param int $timeout_in_ms Timeout in milliseconds
243*04fd306cSNickeau     * @return HttpCommandExecutor
244*04fd306cSNickeau     */
245*04fd306cSNickeau    public function setRequestTimeout($timeout_in_ms)
246*04fd306cSNickeau    {
247*04fd306cSNickeau        // There is a PHP bug in some versions (at least for PHP 5.3.3) which
248*04fd306cSNickeau        // didn't define the constant.
249*04fd306cSNickeau        curl_setopt(
250*04fd306cSNickeau            $this->curl,
251*04fd306cSNickeau            /* CURLOPT_TIMEOUT_MS */
252*04fd306cSNickeau            155,
253*04fd306cSNickeau            $timeout_in_ms
254*04fd306cSNickeau        );
255*04fd306cSNickeau
256*04fd306cSNickeau        return $this;
257*04fd306cSNickeau    }
258*04fd306cSNickeau
259*04fd306cSNickeau    /**
260*04fd306cSNickeau     * @param WebDriverCommand $command
261*04fd306cSNickeau     *
262*04fd306cSNickeau     * @throws WebDriverException
263*04fd306cSNickeau     * @return WebDriverResponse
264*04fd306cSNickeau     */
265*04fd306cSNickeau    public function execute(WebDriverCommand $command)
266*04fd306cSNickeau    {
267*04fd306cSNickeau        $http_options = $this->getCommandHttpOptions($command);
268*04fd306cSNickeau        $http_method = $http_options['method'];
269*04fd306cSNickeau        $url = $http_options['url'];
270*04fd306cSNickeau
271*04fd306cSNickeau        $sessionID = $command->getSessionID();
272*04fd306cSNickeau        $url = str_replace(':sessionId', $sessionID === null ? '' : $sessionID, $url);
273*04fd306cSNickeau        $params = $command->getParameters();
274*04fd306cSNickeau        foreach ($params as $name => $value) {
275*04fd306cSNickeau            if ($name[0] === ':') {
276*04fd306cSNickeau                $url = str_replace($name, $value, $url);
277*04fd306cSNickeau                unset($params[$name]);
278*04fd306cSNickeau            }
279*04fd306cSNickeau        }
280*04fd306cSNickeau
281*04fd306cSNickeau        if (is_array($params) && !empty($params) && $http_method !== 'POST') {
282*04fd306cSNickeau            throw new BadMethodCallException(sprintf(
283*04fd306cSNickeau                'The http method called for %s is %s but it has to be POST' .
284*04fd306cSNickeau                ' if you want to pass the JSON params %s',
285*04fd306cSNickeau                $url,
286*04fd306cSNickeau                $http_method,
287*04fd306cSNickeau                json_encode($params)
288*04fd306cSNickeau            ));
289*04fd306cSNickeau        }
290*04fd306cSNickeau
291*04fd306cSNickeau        curl_setopt($this->curl, CURLOPT_URL, $this->url . $url);
292*04fd306cSNickeau
293*04fd306cSNickeau        // https://github.com/facebook/php-webdriver/issues/173
294*04fd306cSNickeau        if ($command->getName() === DriverCommand::NEW_SESSION) {
295*04fd306cSNickeau            curl_setopt($this->curl, CURLOPT_POST, 1);
296*04fd306cSNickeau        } else {
297*04fd306cSNickeau            curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $http_method);
298*04fd306cSNickeau        }
299*04fd306cSNickeau
300*04fd306cSNickeau        if (in_array($http_method, ['POST', 'PUT'], true)) {
301*04fd306cSNickeau            // Disable sending 'Expect: 100-Continue' header, as it is causing issues with eg. squid proxy
302*04fd306cSNickeau            // https://tools.ietf.org/html/rfc7231#section-5.1.1
303*04fd306cSNickeau            curl_setopt($this->curl, CURLOPT_HTTPHEADER, array_merge(static::DEFAULT_HTTP_HEADERS, ['Expect:']));
304*04fd306cSNickeau        } else {
305*04fd306cSNickeau            curl_setopt($this->curl, CURLOPT_HTTPHEADER, static::DEFAULT_HTTP_HEADERS);
306*04fd306cSNickeau        }
307*04fd306cSNickeau
308*04fd306cSNickeau        $encoded_params = null;
309*04fd306cSNickeau
310*04fd306cSNickeau        if ($http_method === 'POST') {
311*04fd306cSNickeau            if (is_array($params) && !empty($params)) {
312*04fd306cSNickeau                $encoded_params = json_encode($params);
313*04fd306cSNickeau            } elseif ($this->isW3cCompliant) {
314*04fd306cSNickeau                // POST body must be valid JSON in W3C, even if empty: https://www.w3.org/TR/webdriver/#processing-model
315*04fd306cSNickeau                $encoded_params = '{}';
316*04fd306cSNickeau            }
317*04fd306cSNickeau        }
318*04fd306cSNickeau
319*04fd306cSNickeau        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $encoded_params);
320*04fd306cSNickeau
321*04fd306cSNickeau        $raw_results = trim(curl_exec($this->curl));
322*04fd306cSNickeau
323*04fd306cSNickeau        if ($error = curl_error($this->curl)) {
324*04fd306cSNickeau            $msg = sprintf(
325*04fd306cSNickeau                'Curl error thrown for http %s to %s',
326*04fd306cSNickeau                $http_method,
327*04fd306cSNickeau                $url
328*04fd306cSNickeau            );
329*04fd306cSNickeau            if (is_array($params) && !empty($params)) {
330*04fd306cSNickeau                $msg .= sprintf(' with params: %s', json_encode($params, JSON_UNESCAPED_SLASHES));
331*04fd306cSNickeau            }
332*04fd306cSNickeau
333*04fd306cSNickeau            throw new WebDriverCurlException($msg . "\n\n" . $error);
334*04fd306cSNickeau        }
335*04fd306cSNickeau
336*04fd306cSNickeau        $results = json_decode($raw_results, true);
337*04fd306cSNickeau
338*04fd306cSNickeau        if ($results === null && json_last_error() !== JSON_ERROR_NONE) {
339*04fd306cSNickeau            throw new WebDriverException(
340*04fd306cSNickeau                sprintf(
341*04fd306cSNickeau                    "JSON decoding of remote response failed.\n" .
342*04fd306cSNickeau                    "Error code: %d\n" .
343*04fd306cSNickeau                    "The response: '%s'\n",
344*04fd306cSNickeau                    json_last_error(),
345*04fd306cSNickeau                    $raw_results
346*04fd306cSNickeau                )
347*04fd306cSNickeau            );
348*04fd306cSNickeau        }
349*04fd306cSNickeau
350*04fd306cSNickeau        $value = null;
351*04fd306cSNickeau        if (is_array($results) && array_key_exists('value', $results)) {
352*04fd306cSNickeau            $value = $results['value'];
353*04fd306cSNickeau        }
354*04fd306cSNickeau
355*04fd306cSNickeau        $message = null;
356*04fd306cSNickeau        if (is_array($value) && array_key_exists('message', $value)) {
357*04fd306cSNickeau            $message = $value['message'];
358*04fd306cSNickeau        }
359*04fd306cSNickeau
360*04fd306cSNickeau        $sessionId = null;
361*04fd306cSNickeau        if (is_array($value) && array_key_exists('sessionId', $value)) {
362*04fd306cSNickeau            // W3C's WebDriver
363*04fd306cSNickeau            $sessionId = $value['sessionId'];
364*04fd306cSNickeau        } elseif (is_array($results) && array_key_exists('sessionId', $results)) {
365*04fd306cSNickeau            // Legacy JsonWire
366*04fd306cSNickeau            $sessionId = $results['sessionId'];
367*04fd306cSNickeau        }
368*04fd306cSNickeau
369*04fd306cSNickeau        // @see https://w3c.github.io/webdriver/webdriver-spec.html#handling-errors
370*04fd306cSNickeau        if (isset($value['error'])) {
371*04fd306cSNickeau            // W3C's WebDriver
372*04fd306cSNickeau            WebDriverException::throwException($value['error'], $message, $results);
373*04fd306cSNickeau        }
374*04fd306cSNickeau
375*04fd306cSNickeau        $status = isset($results['status']) ? $results['status'] : 0;
376*04fd306cSNickeau        if ($status !== 0) {
377*04fd306cSNickeau            // Legacy JsonWire
378*04fd306cSNickeau            WebDriverException::throwException($status, $message, $results);
379*04fd306cSNickeau        }
380*04fd306cSNickeau
381*04fd306cSNickeau        $response = new WebDriverResponse($sessionId);
382*04fd306cSNickeau
383*04fd306cSNickeau        return $response
384*04fd306cSNickeau            ->setStatus($status)
385*04fd306cSNickeau            ->setValue($value);
386*04fd306cSNickeau    }
387*04fd306cSNickeau
388*04fd306cSNickeau    /**
389*04fd306cSNickeau     * @return string
390*04fd306cSNickeau     */
391*04fd306cSNickeau    public function getAddressOfRemoteServer()
392*04fd306cSNickeau    {
393*04fd306cSNickeau        return $this->url;
394*04fd306cSNickeau    }
395*04fd306cSNickeau
396*04fd306cSNickeau    /**
397*04fd306cSNickeau     * @return array
398*04fd306cSNickeau     */
399*04fd306cSNickeau    protected function getCommandHttpOptions(WebDriverCommand $command)
400*04fd306cSNickeau    {
401*04fd306cSNickeau        $commandName = $command->getName();
402*04fd306cSNickeau        if (!isset(self::$commands[$commandName])) {
403*04fd306cSNickeau            if ($this->isW3cCompliant && !isset(self::$w3cCompliantCommands[$commandName])) {
404*04fd306cSNickeau                throw new InvalidArgumentException($command->getName() . ' is not a valid command.');
405*04fd306cSNickeau            }
406*04fd306cSNickeau        }
407*04fd306cSNickeau
408*04fd306cSNickeau        if ($this->isW3cCompliant) {
409*04fd306cSNickeau            $raw = self::$w3cCompliantCommands[$command->getName()];
410*04fd306cSNickeau        } else {
411*04fd306cSNickeau            $raw = self::$commands[$command->getName()];
412*04fd306cSNickeau        }
413*04fd306cSNickeau
414*04fd306cSNickeau        if ($command instanceof CustomWebDriverCommand) {
415*04fd306cSNickeau            $url = $command->getCustomUrl();
416*04fd306cSNickeau            $method = $command->getCustomMethod();
417*04fd306cSNickeau        } else {
418*04fd306cSNickeau            $url = $raw['url'];
419*04fd306cSNickeau            $method = $raw['method'];
420*04fd306cSNickeau        }
421*04fd306cSNickeau
422*04fd306cSNickeau        return [
423*04fd306cSNickeau            'url' => $url,
424*04fd306cSNickeau            'method' => $method,
425*04fd306cSNickeau        ];
426*04fd306cSNickeau    }
427*04fd306cSNickeau}
428