From b25697a3f0954aa05a6c829099ac6055fc016255 Mon Sep 17 00:00:00 2001 From: Ando Roots Date: Thu, 28 Aug 2014 23:33:42 +0300 Subject: [PATCH] Replace Curl with SocketHandler in FleepHookHandler --- src/Monolog/Handler/FleepHookHandler.php | 125 +++++++--------- .../Monolog/Handler/FleepHookHandlerTest.php | 133 +----------------- 2 files changed, 55 insertions(+), 203 deletions(-) diff --git a/src/Monolog/Handler/FleepHookHandler.php b/src/Monolog/Handler/FleepHookHandler.php index 8f8a8d3d..2d466f37 100644 --- a/src/Monolog/Handler/FleepHookHandler.php +++ b/src/Monolog/Handler/FleepHookHandler.php @@ -22,32 +22,18 @@ use Monolog\Logger; * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation * @author Ando Roots */ -class FleepHookHandler extends AbstractProcessingHandler +class FleepHookHandler extends SocketHandler { - const HOOK_ENDPOINT = 'https://fleep.io/hook/'; + const FLEEP_HOST = 'fleep.io'; + + const FLEEP_HOOK_URI = '/hook/'; /** * @var string Webhook token (specifies the conversation where logs are sent) */ protected $token; - /** - * @var string Full URI to the webhook endpoint (HOOK_ENDPOINT + token) - */ - protected $url; - - /** - * @var array Default options to Curl - */ - protected $curlOptions = array( - CURLOPT_POST => true, - CURLOPT_HTTPHEADER => array( - 'Content-Type: application/x-www-form-urlencoded' - ), - CURLOPT_RETURNTRANSFER => true - ); - /** * Construct a new Fleep.io Handler. * @@ -57,26 +43,18 @@ class FleepHookHandler extends AbstractProcessingHandler * @param string $token Webhook token * @param bool|int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not - * @throws \LogicException + * @throws MissingExtensionException */ public function __construct($token, $level = Logger::DEBUG, $bubble = true) { - if (!extension_loaded('curl')) { - throw new \LogicException('The curl extension is needed to use FleepHookHandler'); + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); } $this->token = $token; - $this->url = self::HOOK_ENDPOINT . $this->token; - parent::__construct($level, $bubble); - } - - /** - * @return array - */ - public function getCurlOptions() - { - return $this->curlOptions; + $connectionString = 'ssl://' . self::FLEEP_HOST . ':443'; + parent::__construct($connectionString, $level, $bubble); } /** @@ -97,59 +75,58 @@ class FleepHookHandler extends AbstractProcessingHandler * * @param array $record */ - protected function write(array $record) + public function write(array $record) { - $this->send($record['formatted']); + parent::write($record); + $this->closeSocket(); } /** - * Prepares the record for sending to Fleep + * {@inheritdoc} * - * @param string $message The formatted log message to send + * @param array $record + * @return string */ - protected function send($message) + protected function generateDataStream($record) { - $this->addCurlOptions( - array( - CURLOPT_POSTFIELDS => http_build_query(array('message' => $message)), - CURLOPT_URL => $this->url, - ) + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + $header = "POST " . self::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; + $header .= "Host: " . self::FLEEP_HOST . "\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + $dataArray = array( + 'message' => $record['formatted'] ); - $this->execCurl($this->curlOptions); - + return http_build_query($dataArray); } - /** - * Sends a new Curl request - * - * @param array $options Curl parameters, including the endpoint URL and POST payload - */ - protected function execCurl(array $options) - { - $curl = curl_init(); - - curl_setopt_array($curl, $options); - - curl_exec($curl); - curl_close($curl); - } - - - /** - * Adds or overwrites a curl option - * - * @param array $options An assoc array of Curl options, indexed by CURL_* constants - * @return $this - */ - public function addCurlOptions(array $options) - { - $this->curlOptions = array_replace( - $this->curlOptions, - $options - ); - - return $this; - } } diff --git a/tests/Monolog/Handler/FleepHookHandlerTest.php b/tests/Monolog/Handler/FleepHookHandlerTest.php index 2eeb167a..2df96954 100644 --- a/tests/Monolog/Handler/FleepHookHandlerTest.php +++ b/tests/Monolog/Handler/FleepHookHandlerTest.php @@ -33,24 +33,16 @@ class FleepHookHandlerTest extends TestCase */ private $handler; - /** - * @var Logger - */ - private $logger; - public function setUp() { parent::setUp(); - if (!extension_loaded('curl')) { - $this->markTestSkipped('This test requires curl extension to run'); + if (!extension_loaded('openssl')) { + $this->markTestSkipped('This test requires openssl extension to run'); } // Create instances of the handler and logger for convenience $this->handler = new FleepHookHandler(self::TOKEN); - $this->logger = new Logger('test'); - $this->logger->pushHandler($this->handler); - } /** @@ -58,47 +50,10 @@ class FleepHookHandlerTest extends TestCase */ public function testConstructorSetsExpectedDefaults() { - // Test that the $token is saved when calling the constructor - $token = self::TOKEN; - $handler = $this->mockHandler(array('execCurl')); - $handler->expects($this->once()) - ->method('execCurl') - ->with( - $this->callback( - function ($curlOpts) use ($token) { - return substr($curlOpts[CURLOPT_URL], -strlen($token)) === $token; - } - ) - ); - - $this->sendLog($handler); - - // Test that default values are assigned to $level and $bubble $this->assertEquals(Logger::DEBUG, $this->handler->getLevel()); $this->assertEquals(true, $this->handler->getBubble()); } - /** - * @covers ::write - */ - public function testWriteSendsFormattedMessageToFleep() - { - $handler = $this->mockHandler(array('send')); - - $message = 'theCakeIsALie'; - $handler->expects($this->once()) - ->method('send') - ->with( - $this->callback( - function ($message) { - return strstr($message, 'theCakeIsALie') && strstr($message, 'channel.ALERT'); - } - ) - ); - - $this->sendLog($handler, $message); - } - /** * @covers ::getDefaultFormatter */ @@ -125,91 +80,11 @@ class FleepHookHandlerTest extends TestCase } /** - * Tests that the URL to which the message is posted is of correct format - * - * Example: https://fleep.io/hook/mTZG6s-XRfKdNTJtpVyVaV * @covers ::__construct */ - public function testFleepEndpointUrlIsConstructedCorrectly() + public function testConnectionStringisConstructedCorrectly() { - $handler = $this->mockHandler(array('execCurl')); - - $token = self::TOKEN; - - // Set up expectation to execCurl: receive curlOpts array where URL is correct - $handler->expects($this->once()) - ->method('execCurl') - ->with( - $this->callback( - function (array $curlOpts) use ($token) { - return $curlOpts[CURLOPT_URL] === FleepHookHandler::HOOK_ENDPOINT . $token; - } - ) - ); - $this->sendLog($handler); - } - - /** - * Tests that the log message is added to the POST content, under the 'message' key - * - * @covers ::send - */ - public function testSendAddsMessageToCurlOpts() - { - $handler = $this->mockHandler(array('execCurl')); - $handler->expects($this->once()) - ->method('execCurl') - ->with( - $this->callback( - function ($curlOpts) { - parse_str($curlOpts[CURLOPT_POSTFIELDS], $body); - return isset($body['message']) && strstr($body['message'], 'msg'); - } - ) - ); - - $this->sendLog($handler, 'msg'); - } - - /** - * @covers ::addCurlOptions - */ - public function testAddCurlOptionsLeavesUnspecifiedOptionsIntact() - { - $this->handler->addCurlOptions(array(CURLOPT_PROXY => 'http://localhost:3128')); - $this->assertArrayHasKey(CURLOPT_POST, $this->handler->getCurlOptions(), 'addCurlOpts deleted a key!'); - } - - public function testAddCurlOptionsAddsANewCurlOption() - { - $proxy = 'http://localhost:3128'; - $this->handler->addCurlOptions(array(CURLOPT_PROXY => $proxy)); - $options = $this->handler->getCurlOptions(); - $this->assertEquals($options[CURLOPT_PROXY], $proxy); - } - - /** - * Helper method for simulating a long sending event from the logger - * - * @param FleepHookHandler $handler - * @param string $message - */ - private function sendLog(FleepHookHandler $handler, $message = 'test') - { - $logger = new Logger('channel'); - $logger->pushHandler($handler); - $logger->addAlert($message); - } - - /** - * Helper method for constructing a new mock of FleepHookHandler - * - * @param array $methods - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function mockHandler(array $methods) - { - return $this->getMock('Monolog\Handler\FleepHookHandler', $methods, array(self::TOKEN)); + $this->assertEquals('ssl://' . FleepHookHandler::FLEEP_HOST . ':443', $this->handler->getConnectionString()); } }