public function read(int $length = null): string
    {
        // can not call fgets with null when not specified
        $data = null === $length ? fgets($this->resource) : fgets($this->resource, $length);
        if (false === $data) {
            // fgets() returns false on eof while feof() returned false before
            // but will now return true
            if ($this->eof()) {
                return '';
            }

            if (stream_get_meta_data($this->resource)['timed_out']) {
                throw new Timeout(
                        'Reading of ' . $length . ' bytes failed: timeout of '
                        . $this->timeout . ' seconds exceeded'
                );
            }

            throw new ConnectionFailure(
                    'Reading of ' . $length . ' bytes failed.'
            );
        }

        return $data;
    }