private function request_response()
    {
        // Must not block while reading
        stream_set_blocking($this->stream, false);
        
        $response = '';
        while (true) {
            $buffer = fgets($this->stream);
            $response .= $buffer;
            if (strstr($buffer, "\n")) {
                break;
            } else {
                usleep(1000);
            }
        }

        $data = '';
        if (!$response) {
            $this->close();
            $desc = $this->request_description();
            throw new ProtocolException("Unable to read response from server ({$desc})");
        } else if (null === ($data = json_decode(trim($response), true))) {
            $desc = $this->request_description();
            throw new ProtocolException("Unable to parse response from server ({$desc}): {$response}");
        } else if (!is_array($data)) {
            $desc = $this->request_description();
            throw new ProtocolException("Invalid response from server ({$desc}): ".json_encode($data));
        }

        if (isset($data['$error'])) {
            throw new ServerException((string)$data['$error']);
        }
        if (isset($data['$end'])) {
            $this->close();
        }

        return $data;
    }