Use FormUrlEncoded when posting non-binary data

This commit is contained in:
Jérémy Derussé 2019-04-05 19:29:13 +02:00
parent 8da76862fa
commit aecca9778e
No known key found for this signature in database
GPG Key ID: 2083FA5758C473D2
2 changed files with 85 additions and 10 deletions

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\BrowserKit;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\Mime\Part\AbstractPart;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\FormDataPart;
use Symfony\Component\Mime\Part\TextPart;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@ -43,13 +44,10 @@ class HttpBrowser extends AbstractBrowser
protected function doRequest($request)
{
$headers = $this->getHeaders($request);
$body = '';
if (null !== $part = $this->getBody($request)) {
$headers = array_merge($headers, $part->getPreparedHeaders()->toArray());
$body = $part->bodyToIterable();
}
[$body, $extraHeaders] = $this->getBodyAndExtraHeaders($request);
$response = $this->client->request($request->getMethod(), $request->getUri(), [
'headers' => $headers,
'headers' => array_merge($headers, $extraHeaders),
'body' => $body,
'max_redirects' => 0,
]);
@ -57,10 +55,13 @@ class HttpBrowser extends AbstractBrowser
return new Response($response->getContent(false), $response->getStatusCode(), $response->getHeaders(false));
}
private function getBody(Request $request): ?AbstractPart
/**
* @return array [$body, $headers]
*/
private function getBodyAndExtraHeaders(Request $request): array
{
if (\in_array($request->getMethod(), ['GET', 'HEAD'])) {
return null;
return ['', []];
}
if (!class_exists(AbstractPart::class)) {
@ -68,19 +69,33 @@ class HttpBrowser extends AbstractBrowser
}
if (null !== $content = $request->getContent()) {
return new TextPart($content, 'utf-8', 'plain', '8bit');
$part = new TextPart($content, 'utf-8', 'plain', '8bit');
return [$part->bodyToString(), $part->getPreparedHeaders()->toArray()];
}
$fields = $request->getParameters();
$hasFile = false;
foreach ($request->getFiles() as $name => $file) {
if (!isset($file['tmp_name'])) {
continue;
}
$hasFile = true;
$fields[$name] = DataPart::fromPath($file['tmp_name'], $file['name']);
}
return new FormDataPart($fields);
if ($hasFile) {
$part = new FormDataPart($fields);
return [$part->bodyToIterable(), $part->getPreparedHeaders()->toArray()];
}
if (empty($fields)) {
return ['', []];
}
return [http_build_query($fields, '', '&', PHP_QUERY_RFC1738), ['Content-Type' => 'application/x-www-form-urlencoded']];
}
private function getHeaders(Request $request): array

View File

@ -17,6 +17,8 @@ use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\BrowserKit\Response;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
class SpecialHttpResponse extends Response
{
@ -112,4 +114,62 @@ class HttpBrowserTest extends AbstractBrowserTest
$this->assertNotInstanceOf('Symfony\Component\BrowserKit\Tests\SpecialHttpResponse', $client->getInternalResponse());
$this->assertInstanceOf('Symfony\Component\BrowserKit\Tests\SpecialHttpResponse', $client->getResponse());
}
/**
* @dataProvider validContentTypes
*/
public function testRequestHeaders(array $request, array $exepectedCall)
{
$client = $this->createMock(HttpClientInterface::class);
$client
->expects($this->once())
->method('request')
->with(...$exepectedCall)
->willReturn($this->createMock(ResponseInterface::class));
$browser = new HttpBrowser($client);
$browser->request(...$request);
}
public function validContentTypes()
{
$defaultHeaders = ['user-agent' => 'Symfony BrowserKit', 'host' => 'example.com'];
yield 'GET/HEAD' => [
['GET', 'http://example.com/', ['key' => 'value']],
['GET', 'http://example.com/', ['headers' => $defaultHeaders, 'body' => '', 'max_redirects' => 0]],
];
yield 'empty form' => [
['POST', 'http://example.com/'],
['POST', 'http://example.com/', ['headers' => $defaultHeaders, 'body' => '', 'max_redirects' => 0]],
];
yield 'form' => [
['POST', 'http://example.com/', ['key' => 'value', 'key2' => 'value']],
['POST', 'http://example.com/', ['headers' => $defaultHeaders + ['Content-Type' => 'application/x-www-form-urlencoded'], 'body' => 'key=value&key2=value', 'max_redirects' => 0]],
];
yield 'content' => [
['POST', 'http://example.com/', [], [], [], 'content'],
['POST', 'http://example.com/', ['headers' => $defaultHeaders + ['Content-Type: text/plain; charset=utf-8', 'Content-Transfer-Encoding: 8bit'], 'body' => 'content', 'max_redirects' => 0]],
];
}
public function testMultiPartRequest()
{
$client = $this->createMock(HttpClientInterface::class);
$client
->expects($this->once())
->method('request')
->with('POST', 'http://example.com/', $this->callback(function ($options) {
$this->assertContains('Content-Type: multipart/form-data', implode('', $options['headers']));
$this->assertInstanceOf('\Generator', $options['body']);
$this->assertContains('my_file', implode('', iterator_to_array($options['body'])));
return true;
}))
->willReturn($this->createMock(ResponseInterface::class));
$browser = new HttpBrowser($client);
$path = tempnam(sys_get_temp_dir(), 'http');
file_put_contents($path, 'my_file');
$browser->request('POST', 'http://example.com/', [], ['file' => ['tmp_name' => $path, 'name' => 'foo']]);
}
}