Skip to content

Commit d7b17b2

Browse files
committed
Renamed Guzzle6 connector to Guzzle
1 parent d509f38 commit d7b17b2

File tree

3 files changed

+365
-6
lines changed

3 files changed

+365
-6
lines changed
Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
<?php
2+
namespace Codeception\Lib\Connector;
3+
4+
use Aws\Credentials\Credentials;
5+
use Aws\Signature\SignatureV4;
6+
use Codeception\Util\Uri;
7+
use GuzzleHttp\Client as GuzzleClient;
8+
use GuzzleHttp\Cookie\CookieJar;
9+
use GuzzleHttp\Cookie\SetCookie;
10+
use GuzzleHttp\Exception\RequestException;
11+
use GuzzleHttp\Handler\CurlHandler;
12+
use GuzzleHttp\Handler\StreamHandler;
13+
use GuzzleHttp\HandlerStack;
14+
use GuzzleHttp\Psr7\Request as Psr7Request;
15+
use GuzzleHttp\Psr7\Response as Psr7Response;
16+
use GuzzleHttp\Psr7\Uri as Psr7Uri;
17+
use Symfony\Component\BrowserKit\Client;
18+
use Symfony\Component\BrowserKit\Cookie;
19+
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
20+
use Symfony\Component\BrowserKit\Request;
21+
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;
22+
23+
class Guzzle extends Client
24+
{
25+
protected $requestOptions = [
26+
'allow_redirects' => false,
27+
'headers' => [],
28+
];
29+
protected $refreshMaxInterval = 0;
30+
31+
protected $awsCredentials = null;
32+
protected $awsSignature = null;
33+
34+
/** @var \GuzzleHttp\Client */
35+
protected $client;
36+
37+
/**
38+
* Sets the maximum allowable timeout interval for a meta tag refresh to
39+
* automatically redirect a request.
40+
*
41+
* A meta tag detected with an interval equal to or greater than $seconds
42+
* would not result in a redirect. A meta tag without a specified interval
43+
* or one with a value less than $seconds would result in the client
44+
* automatically redirecting to the specified URL
45+
*
46+
* @param int $seconds Number of seconds
47+
*/
48+
public function setRefreshMaxInterval($seconds)
49+
{
50+
$this->refreshMaxInterval = $seconds;
51+
}
52+
53+
public function setClient(GuzzleClient &$client)
54+
{
55+
$this->client = $client;
56+
}
57+
58+
/**
59+
* Sets the request header to the passed value. The header will be
60+
* sent along with the next request.
61+
*
62+
* Passing an empty value clears the header, which is the equivalent
63+
* of calling deleteHeader.
64+
*
65+
* @param string $name the name of the header
66+
* @param string $value the value of the header
67+
*/
68+
public function setHeader($name, $value)
69+
{
70+
if (strval($value) === '') {
71+
$this->deleteHeader($name);
72+
} else {
73+
$this->requestOptions['headers'][$name] = $value;
74+
}
75+
}
76+
77+
/**
78+
* Deletes the header with the passed name from the list of headers
79+
* that will be sent with the request.
80+
*
81+
* @param string $name the name of the header to delete.
82+
*/
83+
public function deleteHeader($name)
84+
{
85+
unset($this->requestOptions['headers'][$name]);
86+
}
87+
88+
/**
89+
* @param string $username
90+
* @param string $password
91+
* @param string $type Default: 'basic'
92+
*/
93+
public function setAuth($username, $password, $type = 'basic')
94+
{
95+
if (!$username) {
96+
unset($this->requestOptions['auth']);
97+
return;
98+
}
99+
$this->requestOptions['auth'] = [$username, $password, $type];
100+
}
101+
102+
/**
103+
* Taken from Mink\BrowserKitDriver
104+
*
105+
* @param Response $response
106+
*
107+
* @return \Symfony\Component\BrowserKit\Response
108+
*/
109+
protected function createResponse(Psr7Response $response)
110+
{
111+
$body = (string) $response->getBody();
112+
$headers = $response->getHeaders();
113+
114+
$contentType = null;
115+
116+
if (isset($headers['Content-Type'])) {
117+
$contentType = reset($headers['Content-Type']);
118+
}
119+
if (!$contentType) {
120+
$contentType = 'text/html';
121+
}
122+
123+
if (strpos($contentType, 'charset=') === false) {
124+
if (preg_match('/\<meta[^\>]+charset *= *["\']?([a-zA-Z\-0-9]+)/i', $body, $matches)) {
125+
$contentType .= ';charset=' . $matches[1];
126+
}
127+
$headers['Content-Type'] = [$contentType];
128+
}
129+
130+
$status = $response->getStatusCode();
131+
if ($status < 300 || $status >= 400) {
132+
$matches = [];
133+
134+
$matchesMeta = preg_match(
135+
'/\<meta[^\>]+http-equiv="refresh" content="\s*(\d*)\s*;\s*url=(.*?)"/i',
136+
$body,
137+
$matches
138+
);
139+
140+
if (!$matchesMeta && isset($headers['Refresh'])) {
141+
// match by header
142+
preg_match(
143+
'/^\s*(\d*)\s*;\s*url=(.*)/i',
144+
(string) reset($headers['Refresh']),
145+
$matches
146+
);
147+
}
148+
149+
if ((!empty($matches)) && (empty($matches[1]) || $matches[1] < $this->refreshMaxInterval)) {
150+
$uri = new Psr7Uri($this->getAbsoluteUri($matches[2]));
151+
$currentUri = new Psr7Uri($this->getHistory()->current()->getUri());
152+
153+
if ($uri->withFragment('') != $currentUri->withFragment('')) {
154+
$status = 302;
155+
$headers['Location'] = $matchesMeta ? htmlspecialchars_decode($uri) : (string)$uri;
156+
}
157+
}
158+
}
159+
160+
return new BrowserKitResponse($body, $status, $headers);
161+
}
162+
163+
public function getAbsoluteUri($uri)
164+
{
165+
$baseUri = $this->client->getConfig('base_uri');
166+
if (strpos($uri, '://') === false && strpos($uri, '//') !== 0) {
167+
if (strpos($uri, '/') === 0) {
168+
$baseUriPath = $baseUri->getPath();
169+
if (!empty($baseUriPath) && strpos($uri, $baseUriPath) === 0) {
170+
$uri = substr($uri, strlen($baseUriPath));
171+
}
172+
173+
return Uri::appendPath((string)$baseUri, $uri);
174+
}
175+
// relative url
176+
if (!$this->getHistory()->isEmpty()) {
177+
return Uri::mergeUrls((string)$this->getHistory()->current()->getUri(), $uri);
178+
}
179+
}
180+
return Uri::mergeUrls($baseUri, $uri);
181+
}
182+
183+
protected function doRequest($request)
184+
{
185+
/** @var $request BrowserKitRequest **/
186+
$guzzleRequest = new Psr7Request(
187+
$request->getMethod(),
188+
$request->getUri(),
189+
$this->extractHeaders($request),
190+
$request->getContent()
191+
);
192+
$options = $this->requestOptions;
193+
$options['cookies'] = $this->extractCookies($guzzleRequest->getUri()->getHost());
194+
$multipartData = $this->extractMultipartFormData($request);
195+
if (!empty($multipartData)) {
196+
$options['multipart'] = $multipartData;
197+
}
198+
199+
$formData = $this->extractFormData($request);
200+
if (empty($multipartData) and $formData) {
201+
$options['form_params'] = $formData;
202+
}
203+
204+
try {
205+
if (null !== $this->awsCredentials) {
206+
$response = $this->client->send($this->awsSignature->signRequest($guzzleRequest, $this->awsCredentials), $options);
207+
} else {
208+
$response = $this->client->send($guzzleRequest, $options);
209+
}
210+
} catch (RequestException $e) {
211+
if (!$e->hasResponse()) {
212+
throw $e;
213+
}
214+
$response = $e->getResponse();
215+
}
216+
return $this->createResponse($response);
217+
}
218+
219+
protected function extractHeaders(BrowserKitRequest $request)
220+
{
221+
$headers = [];
222+
$server = $request->getServer();
223+
224+
$contentHeaders = ['Content-Length' => true, 'Content-Md5' => true, 'Content-Type' => true];
225+
foreach ($server as $header => $val) {
226+
$header = html_entity_decode(implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $header))))), ENT_NOQUOTES);
227+
if (strpos($header, 'Http-') === 0) {
228+
$headers[substr($header, 5)] = $val;
229+
} elseif (isset($contentHeaders[$header])) {
230+
$headers[$header] = $val;
231+
}
232+
}
233+
return $headers;
234+
}
235+
236+
protected function extractFormData(BrowserKitRequest $request)
237+
{
238+
if (!in_array(strtoupper($request->getMethod()), ['POST', 'PUT', 'PATCH', 'DELETE'])) {
239+
return null;
240+
}
241+
242+
// guessing if it is a form data
243+
$headers = $request->getServer();
244+
if (isset($headers['HTTP_CONTENT_TYPE'])) {
245+
// not a form
246+
if ($headers['HTTP_CONTENT_TYPE'] !== 'application/x-www-form-urlencoded') {
247+
return null;
248+
}
249+
}
250+
if ($request->getContent() !== null) {
251+
return null;
252+
}
253+
return $request->getParameters();
254+
}
255+
256+
protected function extractMultipartFormData(Request $request)
257+
{
258+
if (!in_array(strtoupper($request->getMethod()), ['POST', 'PUT', 'PATCH'])) {
259+
return [];
260+
}
261+
262+
$parts = $this->mapFiles($request->getFiles());
263+
if (empty($parts)) {
264+
return [];
265+
}
266+
267+
foreach ($request->getParameters() as $k => $v) {
268+
$parts = $this->formatMultipart($parts, $k, $v);
269+
}
270+
return $parts;
271+
}
272+
273+
protected function formatMultipart($parts, $key, $value)
274+
{
275+
if (is_array($value)) {
276+
foreach ($value as $subKey => $subValue) {
277+
$parts = array_merge($this->formatMultipart([], $key."[$subKey]", $subValue), $parts);
278+
}
279+
return $parts;
280+
}
281+
$parts[] = ['name' => $key, 'contents' => (string) $value];
282+
return $parts;
283+
}
284+
285+
protected function mapFiles($requestFiles, $arrayName = '')
286+
{
287+
$files = [];
288+
foreach ($requestFiles as $name => $info) {
289+
if (!empty($arrayName)) {
290+
$name = $arrayName . '[' . $name . ']';
291+
}
292+
293+
if (is_array($info)) {
294+
if (isset($info['tmp_name'])) {
295+
if ($info['tmp_name']) {
296+
$handle = fopen($info['tmp_name'], 'r');
297+
$filename = isset($info['name']) ? $info['name'] : null;
298+
299+
$files[] = [
300+
'name' => $name,
301+
'contents' => $handle,
302+
'filename' => $filename
303+
];
304+
}
305+
} else {
306+
$files = array_merge($files, $this->mapFiles($info, $name));
307+
}
308+
} else {
309+
$files[] = [
310+
'name' => $name,
311+
'contents' => fopen($info, 'r')
312+
];
313+
}
314+
}
315+
316+
return $files;
317+
}
318+
319+
protected function extractCookies($host)
320+
{
321+
$jar = [];
322+
$cookies = $this->getCookieJar()->all();
323+
foreach ($cookies as $cookie) {
324+
/** @var $cookie Cookie **/
325+
$setCookie = SetCookie::fromString((string)$cookie);
326+
if (!$setCookie->getDomain()) {
327+
$setCookie->setDomain($host);
328+
}
329+
$jar[] = $setCookie;
330+
}
331+
return new CookieJar(false, $jar);
332+
}
333+
334+
public static function createHandler($handler)
335+
{
336+
if ($handler instanceof HandlerStack) {
337+
return $handler;
338+
}
339+
if ($handler === 'curl') {
340+
return HandlerStack::create(new CurlHandler());
341+
}
342+
if ($handler === 'stream') {
343+
return HandlerStack::create(new StreamHandler());
344+
}
345+
if (is_string($handler) && class_exists($handler)) {
346+
return HandlerStack::create(new $handler);
347+
}
348+
if (is_callable($handler)) {
349+
return HandlerStack::create($handler);
350+
}
351+
return HandlerStack::create();
352+
}
353+
354+
public function setAwsAuth($config)
355+
{
356+
$this->awsCredentials = new Credentials($config['key'], $config['secret']);
357+
$this->awsSignature = new SignatureV4($config['service'], $config['region']);
358+
}
359+
}

0 commit comments

Comments
 (0)