diff --git a/src/Httpful/Request.php b/src/Httpful/Request.php
index bf40b44..96e6f18 100644
--- a/src/Httpful/Request.php
+++ b/src/Httpful/Request.php
@@ -27,6 +27,7 @@ class Request
public $uri,
$method = Http::GET,
$headers = array(),
+ $raw_headers = '',
$strict_ssl = false,
$content_type,
$expected_type,
@@ -685,15 +686,49 @@ public function _curlPrep()
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- $headers = array("Content-Type: {$this->content_type}");
-
- $headers[] = !empty($this->expected_type) ?
- "Accept: {$this->expected_type}, text/plain" :
- "Accept: */*";
+ $headers = array();
+ if (!isset($this->headers['User-Agent'])) {
+ $user_agent = 'User-Agent: HttpFul/1.0 (cURL/';
+ $curl = \curl_version();
+ if (isset($curl['version']))
+ $user_agent .= $curl['version'];
+ else
+ $user_agent .= '?.?.?';
+ $user_agent .= ' PHP/'.PHP_VERSION.' ('.PHP_OS.')';
+ if (isset($_SERVER['SERVER_SOFTWARE']))
+ $user_agent .= ' '.\preg_replace('~PHP/[\d\.]+~U', '', $_SERVER['SERVER_SOFTWARE']);
+ else {
+ if (isset($_SERVER['TERM_PROGRAM']))
+ $user_agent .= " {$_SERVER['TERM_PROGRAM']}";
+ if (isset($_SERVER['TERM_PROGRAM_VERSION']))
+ $user_agent .= "/{$_SERVER['TERM_PROGRAM_VERSION']}";
+ }
+ if (isset($_SERVER['HTTP_USER_AGENT']))
+ $user_agent .= " {$_SERVER['HTTP_USER_AGENT']}";
+ $user_agent .= ')';
+ $headers[] = $user_agent;
+ }
+ $headers[] = "Content-Type: {$this->content_type}";
+
+ // http://pretty-rfc.herokuapp.com/RFC2616#header.accept
+ $accept = "Accept: */*; q=0.5, text/plain; q=0.8,\r\n\t" .
+ 'text/html;level=3; q=0.9';
+ if (!empty($this->expected_type))
+ $accept .= ", {$this->expected_type}";
+ $headers[] = $accept;
foreach ($this->headers as $header => $value) {
$headers[] = "$header: $value";
}
+
+ $url = \parse_url($this->uri);
+ $path = (isset($url['path']) ? $url['path'] : '/').(isset($url['query']) ? '?'.$url['query'] : '');
+ $this->raw_headers = "{$this->method} $path HTTP/1.1\r\n";
+ $host = (isset($url['host']) ? $url['host'] : 'localhost').(isset($url['port']) ? ':'.$url['port'] : '');
+ $this->raw_headers .= "Host: $host\r\n";
+ $this->raw_headers .= \implode("\r\n", $headers);
+ $this->raw_headers .= "\r\n";
+
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if (isset($this->payload)) {
diff --git a/src/Httpful/Response.php b/src/Httpful/Response.php
index 5778e76..1075405 100644
--- a/src/Httpful/Response.php
+++ b/src/Httpful/Response.php
@@ -13,6 +13,7 @@ class Response
public $body,
$raw_body,
$headers,
+ $raw_headers,
$request,
$code = 0,
$content_type,
@@ -42,11 +43,21 @@ public function __construct($body, $headers, Request $request)
}
/**
- * @return bool Did we receive a 400 or 500?
+ * Status Code Definitions
+ *
+ * Informational 1xx
+ * Successful 2xx
+ * Redirection 3xx
+ * Client Error 4xx
+ * Server Error 5xx
+ *
+ * http://pretty-rfc.herokuapp.com/RFC2616#status.codes
+ *
+ * @return bool Did we receive a 4xx or 5xx?
*/
public function hasErrors()
{
- return $this->code < 100 || $this->code >= 400;
+ return $this->code >= 400;
}
/**
@@ -100,7 +111,8 @@ public function _parse($body)
*/
public function _parseHeaders($headers)
{
- $headers = preg_split("/(\r|\n)+/", $headers);
+ $headers = preg_split("/(\r|\n)+/", $headers, -1, \PREG_SPLIT_NO_EMPTY);
+ $parse_headers = array();
for ($i = 1; $i < count($headers); $i++) {
list($key, $raw_value) = explode(':', $headers[$i], 2);
$parse_headers[trim($key)] = trim($raw_value);
diff --git a/tests/Httpful/HttpfulTest.php b/tests/Httpful/HttpfulTest.php
index 03d7ecb..524edca 100644
--- a/tests/Httpful/HttpfulTest.php
+++ b/tests/Httpful/HttpfulTest.php
@@ -28,13 +28,13 @@ class HttpfulTest extends \PHPUnit_Framework_TestCase
"HTTP/1.1 200 OK
Content-Type: application/json
Connection: keep-alive
-Transfer-Encoding: chunked";
+Transfer-Encoding: chunked\r\n";
const SAMPLE_JSON_RESPONSE = '{"key":"value","object":{"key":"value"},"array":[1,2,3,4]}';
const SAMPLE_CSV_HEADER =
"HTTP/1.1 200 OK
Content-Type: text/csv
Connection: keep-alive
-Transfer-Encoding: chunked";
+Transfer-Encoding: chunked\r\n";
const SAMPLE_CSV_RESPONSE =
"Key1,Key2
Value1,Value2
@@ -44,12 +44,12 @@ class HttpfulTest extends \PHPUnit_Framework_TestCase
"HTTP/1.1 200 OK
Content-Type: application/xml
Connection: keep-alive
-Transfer-Encoding: chunked";
+Transfer-Encoding: chunked\r\n";
const SAMPLE_VENDOR_HEADER =
"HTTP/1.1 200 OK
Content-Type: application/vnd.nategood.message+xml
Connection: keep-alive
-Transfer-Encoding: chunked";
+Transfer-Encoding: chunked\r\n";
const SAMPLE_VENDOR_TYPE = "application/vnd.nategood.message+xml";
function testInit()
@@ -169,7 +169,35 @@ function testIni()
Request::resetIni();
}
- function testAuthSetup()
+ function testAccept()
+ {
+ $r = Request::get('http://example.com/')
+ ->expectsType(Mime::JSON);
+
+ $this->assertEquals(Mime::JSON, $r->expected_type);
+ $r->_curlPrep();
+ $this->assertContains('application/json', $r->raw_headers);
+ }
+ function testUserAgent()
+ {
+ $r = Request::get('http://example.com/')
+ ->withUserAgent('ACME/1.2.3');
+
+ $this->assertArrayHasKey('User-Agent', $r->headers);
+ $r->_curlPrep();
+ $this->assertContains('User-Agent: ACME/1.2.3', $r->raw_headers);
+ $this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers);
+
+ $r = Request::get('http://example.com/')
+ ->withUserAgent('');
+
+ $this->assertArrayHasKey('User-Agent', $r->headers);
+ $r->_curlPrep();
+ $this->assertContains('User-Agent:', $r->raw_headers);
+ $this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers);
+ }
+
+ function testAuthSetup()
{
$username = 'nathan';
$password = 'opensesame';
@@ -228,7 +256,7 @@ function testParsingContentTypeCharset()
// $response = new Response(SAMPLE_JSON_RESPONSE, "", $req);
// // Check default content type of iso-8859-1
$response = new Response(self::SAMPLE_JSON_RESPONSE, "HTTP/1.1 200 OK
-Content-Type: text/plain; charset=utf-8", $req);
+Content-Type: text/plain; charset=utf-8\r\n", $req);
$this->assertInternalType('array', $response->headers);
$this->assertEquals($response->headers['Content-Type'], 'text/plain; charset=utf-8');
$this->assertEquals($response->content_type, 'text/plain');
@@ -263,6 +291,53 @@ function testParseHeaders()
$this->assertEquals('application/json', $response->headers['Content-Type']);
}
+ function testRawHeaders()
+ {
+ $req = Request::init()->sendsAndExpects(Mime::JSON);
+ $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
+ $this->assertContains('Content-Type: application/json', $response->raw_headers);
+ }
+
+ function testHasErrors()
+ {
+ $req = Request::init()->sendsAndExpects(Mime::JSON);
+ $response = new Response('', "HTTP/1.1 100 Continue\r\n", $req);
+ $this->assertFalse($response->hasErrors());
+ $response = new Response('', "HTTP/1.1 200 OK\r\n", $req);
+ $this->assertFalse($response->hasErrors());
+ $response = new Response('', "HTTP/1.1 300 Multiple Choices\r\n", $req);
+ $this->assertFalse($response->hasErrors());
+ $response = new Response('', "HTTP/1.1 400 Bad Request\r\n", $req);
+ $this->assertTrue($response->hasErrors());
+ $response = new Response('', "HTTP/1.1 500 Internal Server Error\r\n", $req);
+ $this->assertTrue($response->hasErrors());
+ }
+
+ function test_parseCode()
+ {
+ $req = Request::init()->sendsAndExpects(Mime::JSON);
+ $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
+ $code = $response->_parseCode("HTTP/1.1 406 Not Acceptable\r\n");
+ $this->assertEquals(406, $code);
+ }
+
+ function testToString()
+ {
+ $req = Request::init()->sendsAndExpects(Mime::JSON);
+ $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
+ $this->assertEquals(self::SAMPLE_JSON_RESPONSE, (string)$response);
+ }
+
+ function test_parseHeaders()
+ {
+ $req = Request::init();
+ $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
+ $parse_headers = $response->_parseHeaders(self::SAMPLE_JSON_HEADER);
+ $this->assertEquals(3, count($parse_headers));
+ $this->assertEquals('application/json', $parse_headers['Content-Type']);
+ $this->assertArrayHasKey('Connection', $parse_headers);
+ }
+
function testDetectContentType()
{
$req = Request::init();
@@ -298,7 +373,7 @@ function testMissingContentType()
$response = new Response('Nathan',
"HTTP/1.1 200 OK
Connection: keep-alive
-Transfer-Encoding: chunked", $request);
+Transfer-Encoding: chunked\r\n", $request);
$this->assertEquals("", $response->content_type);
}
@@ -342,4 +417,5 @@ class DemoMimeHandler extends \Httpful\Handlers\MimeHandlerAdapter {
public function parse($body) {
return 'custom parse';
}
-}
\ No newline at end of file
+}
+
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index 1df78ad..8f62e80 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -1,7 +1,9 @@
-
-
- Httpful
-
-
-
\ No newline at end of file
+
+ .
+
+
+
+
+
+