Make get request to url (might use cache)

        :param url: Url to make request to
        :type url: str | unicode
        :param timeout: Timeout for request (default: None)
            None -> infinite timeout
        :type timeout: None | int | float
        :param headers: Headers to be passed along (default: None)
        :type headers: None | dict
        :param params: Parameters to be passed along with url (default: None)
        :type params: None | dict
        :param cache_ext: External cache info
        :type cache_ext: floscraper.models.CacheInfo
        :return: Response
        :rtype: floscraper.models.Response | None
        :raises WEBConnectException: Loading failed

def get(self, url, timeout=None, headers=None, params=None, cache_ext=None):
        """
        Make get request to url (might use cache)

        :param url: Url to make request to
        :type url: str | unicode
        :param timeout: Timeout for request (default: None)
            None -> infinite timeout
        :type timeout: None | int | float
        :param headers: Headers to be passed along (default: None)
        :type headers: None | dict
        :param params: Parameters to be passed along with url (default: None)
        :type params: None | dict
        :param cache_ext: External cache info
        :type cache_ext: floscraper.models.CacheInfo
        :return: Response
        :rtype: floscraper.models.Response | None
        :raises WEBConnectException: Loading failed
        """
        if headers is None:
            headers = {}
        cached = cache_info = None
        # TODO: add params to caching key
        if self.cache:
            cached, cache_info = self.cache.get(url)

        if cache_ext:
            # Check local cache
            if not cache_info:
                cache_info = cache_ext
            if cache_info.access_time and cache_ext.access_time:
                if cache_info.access_time < cache_ext.access_time:
                    cached = None
                    cache_info = cache_ext
            if cache_info.etag != cache_ext.etag:
                cached = None
                cache_info = cache_ext
        if cached:
            # Using cached
            return Response(cached, cache_info)
        # Not using cached

        if not self.session:
            self._browser_init()

        if self.cache:
            headers = self.cache.prepare_headers(headers, cache_info)

        response = self._get(
            url,
            timeout=timeout,
            headers=headers,
            params=params
        )
        if "etag" in response.headers:
            if not cache_info:
                cache_info = CacheInfo()
            cache_info.etag = response.headers.get('etag')

        res = Response(cache_info=cache_info)

        if response.history:
            # list of responses in redirects
            code = 0

            for resp in response.history + [None]:
                if resp is None:
                    resp = response
                if code in [
                    requests.codes.FOUND,
                    requests.codes.SEE_OTHER,
                    requests.codes.TEMPORARY_REDIRECT
                ]:
                    self.info("Temporary redirect to {}".format(resp.url))
                elif code in [
                    requests.codes.MOVED_PERMANENTLY,
                    requests.codes.PERMANENT_REDIRECT
                ]:
                    self.warning("Moved to {}".format(resp.url))
                elif code != 0:
                    self.error("Code {} to {}".format(code, resp.url))
                code = resp.status_code

        if response.status_code == requests.codes.NOT_MODIFIED:
            self.info("Not modified {}".format(url))
            res.html, _ = self.cache.get(url, ignore_access_time=True)
            if self.cache:
                self.cache.update(url, cache_info)
            return res

        try:
            response.raise_for_status()
        except HTTPError as e:
            raise WEBConnectException("{} - {}".format(e, url))

        try:
            raw = response.content
            html = response.text
            if raw is None:
                self.warning("Response returned None")
                raise Exception()
            if html is None:
                self.warning("Response parsed returned None")
                raise Exception()
        except Exception:
            raise WEBConnectException("Unable to load {}".format(url))

        # TODO: cache raw / whole response object
        if self.cache:
            self.cache.put(url, html, cache_info)
        if url != response.url:
            if not response.history:
                self.warning(
                    "Response url different despite no redirects "
                    "{} - {}".format(url, response.url)
                )
            if self.cache:
                self.cache.put(response.url, html, cache_info)
        res.html = html
        res.raw = raw
        return res