From 0be8403272913c4cee85350f70d4e56bd49283ef Mon Sep 17 00:00:00 2001
From: Natan Felles <natanfelles@gmail.com>
Date: Sat, 17 Nov 2018 21:40:31 -0200
Subject: [PATCH 1/2] Add CURLRequest helper methods

---
 system/HTTP/CURLRequest.php           |  64 ++++++++++-
 tests/system/HTTP/CURLRequestTest.php | 155 +++++++++++++++++++-------
 2 files changed, 177 insertions(+), 42 deletions(-)

diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php
index 6cbbc17459ef..5aa1665fa27b 100644
--- a/system/HTTP/CURLRequest.php
+++ b/system/HTTP/CURLRequest.php
@@ -47,8 +47,6 @@
  * A lightweight HTTP client for sending synchronous HTTP requests
  * via cURL.
  *
- * @todo Add a few helpers for dealing with JSON, forms, files, etc.
- *
  * @package CodeIgniter\HTTP
  */
 class CURLRequest extends Request
@@ -267,6 +265,68 @@ public function put(string $url, array $options = []): ResponseInterface
 
 	//--------------------------------------------------------------------
 
+	/**
+	 * Set the HTTP Authentication.
+	 *
+	 * @param string $username
+	 * @param string $password
+	 * @param string $type     basic or digest
+	 *
+	 * @return $this
+	 */
+	public function setAuth(string $username, string $password, string $type = 'basic')
+	{
+		$this->config['auth'] = [
+			$username,
+			$password,
+			$type,
+		];
+
+		return $this;
+	}
+
+	//--------------------------------------------------------------------
+
+	/**
+	 * Set form data to be sent.
+	 *
+	 * @param array   $params
+	 * @param boolean $multipart Set TRUE if you are sending CURLFiles
+	 *
+	 * @return $this
+	 */
+	public function setForm(array $params, bool $multipart = false)
+	{
+		if ($multipart)
+		{
+			$this->config['multipart'] = $params;
+		}
+		else
+		{
+			$this->config['form_params'] = $params;
+		}
+
+		return $this;
+	}
+
+	//--------------------------------------------------------------------
+
+	/**
+	 * Set JSON data to be sent.
+	 *
+	 * @param mixed $data
+	 *
+	 * @return $this
+	 */
+	public function setJSON($data)
+	{
+		$this->config['json'] = $data;
+
+		return $this;
+	}
+
+	//--------------------------------------------------------------------
+
 	/**
 	 * Sets the correct settings based on the options array
 	 * passed in.
diff --git a/tests/system/HTTP/CURLRequestTest.php b/tests/system/HTTP/CURLRequestTest.php
index 1b50fb4c3e94..7ee3506b6383 100644
--- a/tests/system/HTTP/CURLRequestTest.php
+++ b/tests/system/HTTP/CURLRequestTest.php
@@ -8,7 +8,9 @@
 
 class CURLRequestTest extends \CIUnitTestCase
 {
-
+	/**
+	 * @var MockCURLRequest
+	 */
 	protected $request;
 
 	public function setUp()
@@ -23,7 +25,7 @@ protected function getRequest(array $options = [])
 	{
 		$uri = isset($options['base_uri']) ? new URI($options['base_uri']) : new URI();
 
-		return new MockCURLRequest(new App(), $uri, new Response(new \Config\App()), $options);
+		return new MockCURLRequest(($app = new App()), $uri, new Response($app), $options);
 	}
 
 	//--------------------------------------------------------------------
@@ -37,7 +39,7 @@ public function testGetRemembersBaseURI()
 			'base_uri' => 'http://www.foo.com/api/v1/',
 		]);
 
-		$response = $request->get('products');
+		$request->get('products');
 
 		$options = $request->curl_options;
 
@@ -75,7 +77,7 @@ public function testSendReturnsResponse()
 
 	public function testGetSetsCorrectMethod()
 	{
-		$response = $this->request->get('http://example.com');
+		$this->request->get('http://example.com');
 
 		$this->assertEquals('get', $this->request->getMethod());
 
@@ -89,7 +91,7 @@ public function testGetSetsCorrectMethod()
 
 	public function testDeleteSetsCorrectMethod()
 	{
-		$response = $this->request->delete('http://example.com');
+		$this->request->delete('http://example.com');
 
 		$this->assertEquals('delete', $this->request->getMethod());
 
@@ -103,7 +105,7 @@ public function testDeleteSetsCorrectMethod()
 
 	public function testHeadSetsCorrectMethod()
 	{
-		$response = $this->request->head('http://example.com');
+		$this->request->head('http://example.com');
 
 		$this->assertEquals('head', $this->request->getMethod());
 
@@ -117,7 +119,7 @@ public function testHeadSetsCorrectMethod()
 
 	public function testOptionsSetsCorrectMethod()
 	{
-		$response = $this->request->options('http://example.com');
+		$this->request->options('http://example.com');
 
 		$this->assertEquals('options', $this->request->getMethod());
 
@@ -184,7 +186,7 @@ public function testOptionsDelay()
 
 	public function testPatchSetsCorrectMethod()
 	{
-		$response = $this->request->patch('http://example.com');
+		$this->request->patch('http://example.com');
 
 		$this->assertEquals('patch', $this->request->getMethod());
 
@@ -198,7 +200,7 @@ public function testPatchSetsCorrectMethod()
 
 	public function testPostSetsCorrectMethod()
 	{
-		$response = $this->request->post('http://example.com');
+		$this->request->post('http://example.com');
 
 		$this->assertEquals('post', $this->request->getMethod());
 
@@ -212,7 +214,7 @@ public function testPostSetsCorrectMethod()
 
 	public function testPutSetsCorrectMethod()
 	{
-		$response = $this->request->put('http://example.com');
+		$this->request->put('http://example.com');
 
 		$this->assertEquals('put', $this->request->getMethod());
 
@@ -226,7 +228,7 @@ public function testPutSetsCorrectMethod()
 
 	public function testCustomMethodSetsCorrectMethod()
 	{
-		$response = $this->request->request('custom', 'http://example.com');
+		$this->request->request('custom', 'http://example.com');
 
 		$this->assertEquals('custom', $this->request->getMethod());
 
@@ -240,7 +242,7 @@ public function testCustomMethodSetsCorrectMethod()
 
 	public function testRequestMethodGetsSanitized()
 	{
-		$response = $this->request->request('<script>Custom</script>', 'http://example.com');
+		$this->request->request('<script>Custom</script>', 'http://example.com');
 
 		$this->assertEquals('custom', $this->request->getMethod());
 
@@ -254,7 +256,7 @@ public function testRequestMethodGetsSanitized()
 
 	public function testRequestSetsBasicCurlOptions()
 	{
-		$response = $this->request->request('get', 'http://example.com');
+		$this->request->request('get', 'http://example.com');
 
 		$options = $this->request->curl_options;
 
@@ -281,7 +283,7 @@ public function testRequestSetsBasicCurlOptions()
 
 	public function testAuthBasicOption()
 	{
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'auth' => [
 				'username',
 				'password',
@@ -301,7 +303,7 @@ public function testAuthBasicOption()
 
 	public function testAuthBasicOptionExplicit()
 	{
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'auth' => [
 				'username',
 				'password',
@@ -322,7 +324,7 @@ public function testAuthBasicOptionExplicit()
 
 	public function testAuthDigestOption()
 	{
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'auth' => [
 				'username',
 				'password',
@@ -341,11 +343,39 @@ public function testAuthDigestOption()
 
 	//--------------------------------------------------------------------
 
+	public function testSetAuthBasic()
+	{
+		$this->request->setAuth('username', 'password')->get('http://example.com');
+
+		$options = $this->request->curl_options;
+
+		$this->assertArrayHasKey(CURLOPT_USERPWD, $options);
+		$this->assertEquals('username:password', $options[CURLOPT_USERPWD]);
+
+		$this->assertArrayHasKey(CURLOPT_HTTPAUTH, $options);
+		$this->assertEquals(CURLAUTH_BASIC, $options[CURLOPT_HTTPAUTH]);
+	}
+
+	public function testSetAuthDigest()
+	{
+		$this->request->setAuth('username', 'password', 'digest')->get('http://example.com');
+
+		$options = $this->request->curl_options;
+
+		$this->assertArrayHasKey(CURLOPT_USERPWD, $options);
+		$this->assertEquals('username:password', $options[CURLOPT_USERPWD]);
+
+		$this->assertArrayHasKey(CURLOPT_HTTPAUTH, $options);
+		$this->assertEquals(CURLAUTH_DIGEST, $options[CURLOPT_HTTPAUTH]);
+	}
+
+	//--------------------------------------------------------------------
+
 	public function testCertOption()
 	{
 		$file = __FILE__;
 
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'cert' => $file,
 		]);
 
@@ -359,7 +389,7 @@ public function testCertOptionWithPassword()
 	{
 		$file = __FILE__;
 
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'cert' => [
 				$file,
 				'password',
@@ -380,7 +410,7 @@ public function testMissingCertOption()
 		$file = 'something_obviously_bogus';
 		$this->expectException(Exceptions\HTTPException::class);
 
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'cert' => $file,
 		]);
 	}
@@ -391,7 +421,7 @@ public function testSSLVerification()
 	{
 		$file = __FILE__;
 
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'verify'  => 'yes',
 			'ssl_key' => $file,
 		]);
@@ -410,7 +440,7 @@ public function testSSLWithBadKey()
 		$file = 'something_obviously_bogus';
 		$this->expectException(Exceptions\HTTPException::class);
 
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'verify'  => 'yes',
 			'ssl_key' => $file,
 		]);
@@ -420,7 +450,7 @@ public function testSSLWithBadKey()
 
 	public function testDebugOption()
 	{
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'debug' => true,
 		]);
 
@@ -437,7 +467,7 @@ public function testDebugOption()
 	public function testDecodeContent()
 	{
 		$this->request->setHeader('Accept-Encoding', 'cobol');
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'decode_content' => true,
 		]);
 
@@ -450,7 +480,7 @@ public function testDecodeContent()
 	public function testDecodeContentWithoutAccept()
 	{
 		//      $this->request->setHeader('Accept-Encoding', 'cobol');
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'decode_content' => true,
 		]);
 
@@ -466,7 +496,7 @@ public function testDecodeContentWithoutAccept()
 
 	public function testAllowRedirectsOptionFalse()
 	{
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'allow_redirects' => false,
 		]);
 
@@ -481,7 +511,7 @@ public function testAllowRedirectsOptionFalse()
 
 	public function testAllowRedirectsOptionTrue()
 	{
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'allow_redirects' => true,
 		]);
 
@@ -498,7 +528,7 @@ public function testAllowRedirectsOptionTrue()
 
 	public function testAllowRedirectsOptionDefaults()
 	{
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'allow_redirects' => true,
 		]);
 
@@ -513,7 +543,7 @@ public function testAllowRedirectsOptionDefaults()
 
 	public function testAllowRedirectsArray()
 	{
-		$response = $this->request->request('get', 'http://example.com', [
+		$this->request->request('get', 'http://example.com', [
 			'allow_redirects' => ['max' => 2],
 		]);
 
@@ -535,7 +565,7 @@ public function testSendWithQuery()
 			'query'    => ['name' => 'Henry'],
 		]);
 
-		$response = $request->get('products');
+		$request->get('products');
 
 		$options = $request->curl_options;
 
@@ -550,7 +580,7 @@ public function testSendWithDelay()
 			'delay'    => 1000,
 		]);
 
-		$response = $request->get('products');
+		$request->get('products');
 
 		// we still need to check the code coverage to make sure this was done
 		$this->assertEquals(1.0, $request->getDelay());
@@ -617,14 +647,14 @@ public function testResponseHeaders()
 
 	public function testPostFormEncoded()
 	{
-		$params   = [
+		$params = [
 			'foo' => 'bar',
 			'baz' => [
 				'hi',
 				'there',
 			],
 		];
-		$response = $this->request->request('POST', '/post', [
+		$this->request->request('POST', '/post', [
 			'form_params' => $params,
 		]);
 
@@ -639,7 +669,7 @@ public function testPostFormEncoded()
 
 	public function testPostFormMultipart()
 	{
-		$params   = [
+		$params = [
 			'foo'   => 'bar',
 			'baz'   => [
 				'hi',
@@ -647,7 +677,7 @@ public function testPostFormMultipart()
 			],
 			'afile' => new \CURLFile(__FILE__),
 		];
-		$response = $this->request->request('POST', '/post', [
+		$this->request->request('POST', '/post', [
 			'multipart' => $params,
 		]);
 
@@ -661,16 +691,45 @@ public function testPostFormMultipart()
 
 	//--------------------------------------------------------------------
 
+	public function testSetForm()
+	{
+		$params = [
+			'foo' => 'bar',
+			'baz' => [
+				'hi',
+				'there',
+			],
+		];
+
+		$this->request->setForm($params)->post('/post');
+
+		$this->assertEquals(
+			http_build_query($params),
+			$this->request->curl_options[CURLOPT_POSTFIELDS]
+		);
+
+		$params['afile'] = new \CURLFile(__FILE__);
+
+		$this->request->setForm($params, true)->post('/post');
+
+		$this->assertEquals(
+			$params,
+			$this->request->curl_options[CURLOPT_POSTFIELDS]
+		);
+	}
+
+	//--------------------------------------------------------------------
+
 	public function testJSONData()
 	{
-		$params   = [
+		$params = [
 			'foo' => 'bar',
 			'baz' => [
 				'hi',
 				'there',
 			],
 		];
-		$response = $this->request->request('POST', '/post', [
+		$this->request->request('POST', '/post', [
 			'json' => $params,
 		]);
 
@@ -682,9 +741,25 @@ public function testJSONData()
 
 	//--------------------------------------------------------------------
 
+	public function testSetJSON()
+	{
+		$params = [
+			'foo' => 'bar',
+			'baz' => [
+				'hi',
+				'there',
+			],
+		];
+		$this->request->setJSON($params)->post('/post');
+
+		$this->assertEquals(json_encode($params), $this->request->getBody());
+	}
+
+	//--------------------------------------------------------------------
+
 	public function testHTTPv1()
 	{
-		$response = $this->request->request('POST', '/post', [
+		$this->request->request('POST', '/post', [
 			'version' => 1.0,
 		]);
 
@@ -696,7 +771,7 @@ public function testHTTPv1()
 
 	public function testHTTPv11()
 	{
-		$response = $this->request->request('POST', '/post', [
+		$this->request->request('POST', '/post', [
 			'version' => 1.1,
 		]);
 
@@ -710,8 +785,8 @@ public function testHTTPv11()
 
 	public function testCookieOption()
 	{
-		$holder   = SUPPORTPATH . 'HTTP/Files/CookiesHolder.txt';
-		$response = $this->request->request('POST', '/post', [
+		$holder = SUPPORTPATH . 'HTTP/Files/CookiesHolder.txt';
+		$this->request->request('POST', '/post', [
 			'cookie' => $holder,
 		]);
 

From 42605e9ac3784b52f5b0c20825a39d0664795fd0 Mon Sep 17 00:00:00 2001
From: Natan Felles <natanfelles@gmail.com>
Date: Wed, 28 Nov 2018 21:05:51 -0200
Subject: [PATCH 2/2] Test json content-type header

---
 tests/system/HTTP/CURLRequestTest.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/system/HTTP/CURLRequestTest.php b/tests/system/HTTP/CURLRequestTest.php
index 7ee3506b6383..5eeb75930872 100644
--- a/tests/system/HTTP/CURLRequestTest.php
+++ b/tests/system/HTTP/CURLRequestTest.php
@@ -753,6 +753,7 @@ public function testSetJSON()
 		$this->request->setJSON($params)->post('/post');
 
 		$this->assertEquals(json_encode($params), $this->request->getBody());
+		$this->assertEquals('application/json', $this->request->getHeaderLine('Content-Type'));
 	}
 
 	//--------------------------------------------------------------------