/**
  * Run react/http server for incoming GitHub webhook events
  *
  * @param LoopInterface $loop
  * @param int $port
  * @param string $ip
  */
 private function runServer(LoopInterface $loop, $port = 8080, $ip = '0.0.0.0')
 {
     // Set up react HTTP server to listen for github webhooks
     $socket = new \React\Socket\Server($loop);
     $http = new \React\Http\Server($socket, $loop);
     $http->on('request', function ($request, $response) {
         $headers = $request->getHeaders();
         $path = $request->getPath();
         $hook = substr($path, 1);
         // Basic check if we got event and signature headers
         if (empty($headers['X-GitHub-Event'])) {
             $response->writeHead(500, ['Content-Type' => 'text/plain']);
             $response->end("Missing event header\n");
             $this->plugin->getLogger()->error("Received request with missing event header");
             return;
         }
         if (empty($headers['X-Hub-Signature'])) {
             $response->writeHead(500, ['Content-Type' => 'text/plain']);
             $response->end("Missing signature\n");
             $this->plugin->getLogger()->error("Received request with missing signature header");
             return;
         }
         // Check if the hook actually exists
         if (empty($hook)) {
             $response->writeHead(500, ['Content-Type' => 'text/plain']);
             $response->end("Missing hook name\n");
             $this->plugin->getLogger()->error("Received request with missing hook name, check out the plugin README");
             return;
         }
         $hooks = $this->plugin->getHooks();
         if (!isset($hooks[$hook])) {
             $response->writeHead(500, ['Content-Type' => 'text/plain']);
             $response->end("Missing hook name\n");
             $this->plugin->getLogger()->error("{$hook} does not match any defined hooks in the plugin configuration, check out the plugin README");
             return;
         }
         // Okay the request
         $response->writeHead(200, ['Content-Type' => 'text/plain']);
         $response->end("OK\n");
         // Get incoming JSON payload
         $payload = "";
         $request->on('data', function ($data) use(&$payload) {
             $payload .= $data;
         });
         // Wait for the end of data burst
         $request->on('end', function () use(&$payload, $headers, $hook) {
             $raw_payload = $payload;
             // Parse json payload to PHP array
             $payload = json_decode($payload, TRUE);
             if (!$payload) {
                 $this->plugin->getLogger()->error(sprintf("Unable to parse payload: %s", json_last_error_msg()));
                 $this->plugin->getLogger()->debug('Payload: ' . var_export($payload, TRUE));
                 return;
             }
             // Verify signature
             $hooks = $this->plugin->getHooks();
             if (!empty($hooks[$hook]['secret'])) {
                 list($algo, $signature) = explode("=", $headers['X-Hub-Signature']);
                 if (hash_hmac($algo, $raw_payload, $hooks[$hook]['secret']) != $signature) {
                     $this->plugin->getLogger()->error("Invalid signature, void request");
                     return;
                 }
             } else {
                 $this->plugin->getLogger()->warning("No secret set for {$hook}, seriously consider setting one");
             }
             $this->plugin->getEventEmitter()->emit("githubhooks.{$hook}.{$headers['X-GitHub-Event']}", [$payload]);
         });
     });
     $socket->listen($port, $ip);
 }
 /**
  * Tests that getSubscribedEvents() returns an array.
  */
 public function testGetSubscribedEvents()
 {
     $plugin = new Plugin();
     $this->assertInternalType('array', $plugin->getSubscribedEvents());
 }