Skip to content

Commit

Permalink
bug #30876 [Mime][BrowserKit] Add FormUrlEncoded - Fix content-type i…
Browse files Browse the repository at this point in the history
…n HttpBrowserPost (jderusse)

This PR was merged into the 4.3-dev branch.

Discussion
----------

[Mime][BrowserKit] Add FormUrlEncoded - Fix content-type in HttpBrowserPost

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #30867
| License       | MIT
| Doc PR        | NA

This PR use the content-type `x-www-form-urlencoded` when posting a form with the HttpBrowser.

Commits
-------

aecca97 Use FormUrlEncoded when posting non-binary data
  • Loading branch information
fabpot committed Apr 6, 2019
2 parents 05e488f + aecca97 commit 8a62892
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 10 deletions.
35 changes: 25 additions & 10 deletions src/Symfony/Component/BrowserKit/HttpBrowser.php
Expand Up @@ -13,6 +13,7 @@

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;
Expand Down Expand Up @@ -43,44 +44,58 @@ public function __construct(HttpClientInterface $client = null, History $history
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,
]);

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)) {
throw new \LogicException('You cannot pass non-empty bodies as the Mime component is not installed. Try running "composer require symfony/mime".');
}

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
Expand Down
60 changes: 60 additions & 0 deletions src/Symfony/Component/BrowserKit/Tests/HttpBrowserTest.php
Expand Up @@ -17,6 +17,8 @@
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
{
Expand Down Expand Up @@ -112,4 +114,62 @@ public function testGetInternalResponse()
$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']]);
}
}

0 comments on commit 8a62892

Please sign in to comment.