Esempio n. 1
0
def wms_structure(wms_url: Url, host: str,
                  request: pyramid.request.Request) -> Dict[str, List[str]]:
    """Get a simple serializable structure of the WMS capabilities."""
    url = wms_url.clone().add_query({
        "SERVICE": "WMS",
        "VERSION": "1.1.1",
        "REQUEST": "GetCapabilities"
    })

    # Forward request to target (without Host Header)
    headers = {}
    if url.hostname == "localhost" and host is not None:
        headers["Host"] = host
    try:
        response = requests.get(url.url(),
                                headers=headers,
                                **request.registry.settings.get(
                                    "http_options", {}))
    except Exception:
        LOG.exception("Unable to GetCapabilities from wms_url '%s'", wms_url)
        raise HTTPBadGateway(  # pylint: disable=raise-missing-from
            "Unable to GetCapabilities, see logs for details")

    if not response.ok:
        raise HTTPBadGateway(
            f"GetCapabilities from wms_url {url.url()} return the error: "
            f"{response.status_code:d} {response.reason}")

    try:
        wms = WebMapService(None, xml=response.content)
        result: Dict[str, List[str]] = {}

        def _fill(name: str, parent: ContentMetadata) -> None:
            if parent is None:
                return
            if parent.name not in result:
                result[parent.name] = []
            result[parent.name].append(name)
            _fill(name, parent.parent)

        for layer in list(wms.contents.values()):
            _fill(layer.name, layer.parent)
        return result

    except AttributeError:
        error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
        error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
        LOG.exception(error)
        raise HTTPBadGateway(error)  # pylint: disable=raise-missing-from

    except SyntaxError:
        error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
        error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
        LOG.exception(error)
        raise HTTPBadGateway(error)  # pylint: disable=raise-missing-from
Esempio n. 2
0
def wms_structure(wms_url: Url, host: str, request: pyramid.request.Request) -> Dict[str, List[str]]:
    url = wms_url.clone().add_query({"SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities"})

    # Forward request to target (without Host Header)
    headers = dict()
    if url.hostname == "localhost" and host is not None:
        headers["Host"] = host
    try:
        response = requests.get(
            url.url(), headers=headers, **request.registry.settings.get("http_options", {})
        )
    except Exception:
        raise HTTPBadGateway("Unable to GetCapabilities from wms_url {}".format(wms_url))

    if not response.ok:
        raise HTTPBadGateway(
            "GetCapabilities from wms_url {} return the error: {:d} {}".format(
                url.url(), response.status_code, response.reason
            )
        )

    try:
        wms = WebMapService(None, xml=response.content)
        result: Dict[str, List[str]] = {}

        def _fill(name: str, parent: ContentMetadata) -> None:
            if parent is None:
                return
            if parent.name not in result:
                result[parent.name] = []
            result[parent.name].append(name)
            _fill(name, parent.parent)

        for layer in list(wms.contents.values()):
            _fill(layer.name, layer.parent)
        return result

    except AttributeError:
        error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
        error = "{0!s}\nurl: {1!s}\nxml:\n{2!s}".format(error, wms_url, response.text)
        LOG.exception(error)
        raise HTTPBadGateway(error)

    except SyntaxError:
        error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
        error = "{0!s}\nurl: {1!s}\nxml:\n{2!s}".format(error, wms_url, response.text)
        LOG.exception(error)
        raise HTTPBadGateway(error)
Esempio n. 3
0
    def _proxy_callback(
        self, cache_control: Cache, url: Url, params: Dict[str, str], **kwargs: Any
    ) -> Response:
        if self.request.matched_route.name.endswith("_path"):
            if self.request.matchdict["path"] == ("favicon.ico",):
                return HTTPFound("/favicon.ico")
            url = url.clone()
            url.path = self.request.path

        response = self._proxy(url=url, params=params, **kwargs)

        content = response.content
        if self.lower_params.get("request") == "getcapabilities":
            content = filter_capabilities(
                response.text,
                self.lower_params.get("service") == "wms",
                url,
                self.request.headers,
                self.request,
            ).encode("utf-8")

        content_type = response.headers["Content-Type"]

        return self._build_response(response, content, cache_control, "mapserver", content_type=content_type)
Esempio n. 4
0
    def _proxy(
        self,
        url: Url,
        params: Dict[str, str] = None,
        method: str = None,
        cache: bool = False,
        body: bytes = None,
        headers: Dict[str, str] = None,
    ) -> pyramid.response.Response:
        # Get query string
        params = dict(self.request.params) if params is None else params
        url = url.clone().add_query(params, True)

        LOG.debug("Send query to URL:\n%s.", url)

        if method is None:
            method = self.request.method

        if headers is None:
            headers = dict(self.request.headers)

        # Forward request to target (without Host Header)
        if url.hostname not in self.host_forward_host and "Host" in headers:
            headers.pop("Host")

        # Forward the request tracking ID to the other service. This will allow to follow the logs belonging
        # to a single request coming from the user
        headers.setdefault("X-Request-ID", self.request.c2c_request_id)
        # If we releay want to respect the specification, we should chain with the content of the previous
        # proxy, see also:
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
        forwarded = {
            "for": self.request.client_addr,
            "proto": self.request.scheme
        }
        if "Host" in self.request.headers:
            forwarded["host"] = self.request.headers["Host"]
        forwarded_str = ";".join(["=".join(e) for e in forwarded.items()])
        if "Forwarded" in headers:
            headers["Forwarded"] = ",".join(
                [headers["Forwarded"], forwarded_str])
        else:
            headers["Forwarded"] = forwarded_str

        if not cache:
            headers["Cache-Control"] = "no-cache"

        if method in ("POST", "PUT") and body is None:
            body = self.request.body

        try:
            if method in ("POST", "PUT"):
                response = requests.request(method,
                                            url.url(),
                                            data=body,
                                            headers=headers,
                                            **self.http_options)
            else:
                response = requests.request(method,
                                            url.url(),
                                            headers=headers,
                                            **self.http_options)
        except Exception:
            errors = [
                "Error '%s' while getting the URL:", "%s", "Method: %s",
                "--- With headers ---", "%s"
            ]
            args1 = [
                sys.exc_info()[0],
                url,
                method,
                "\n".join([
                    "{}: {}".format(
                        h,
                        v if h not in ("Authorization", "Cookies") else "***")
                    for h, v in list(headers.items())
                ]),
            ]
            if method in ("POST", "PUT") and body is not None:
                errors += ["--- Query with body ---", "%s"]
                args1.append(body.decode("utf-8"))
            LOG.error("\n".join(errors), *args1, exc_info=True)

            raise HTTPBadGateway("Error on backend, See logs for detail")

        if not response.ok:
            errors = [
                "Error '%s' in response of URL:",
                "%s",
                "Status: %d",
                "Method: %s",
                "--- With headers ---",
                "%s",
            ]
            args2: List[Union[str, int]] = [
                response.reason,
                url.url(),
                response.status_code,
                method,
                "\n".join([
                    "{}: {}".format(
                        h,
                        v if h not in ("Authorization", "Cookies") else "***")
                    for h, v in list(headers.items())
                ]),
            ]
            if method in ("POST", "PUT") and body is not None:
                errors += ["--- Query with body ---", "%s"]
                args2.append(body.decode("utf-8"))
            errors += ["--- Return content ---", "%s"]
            args2.append(response.text)
            LOG.error("\n".join(errors), *args2)

            raise exception_response(response.status_code)

        return response
Esempio n. 5
0
    def _proxy(
        self,
        url: Url,
        params: Optional[Dict[str, str]] = None,
        method: Optional[str] = None,
        cache: bool = False,
        body: Optional[bytes] = None,
        headers: Optional[Dict[str, str]] = None,
    ) -> requests.models.Response:
        # Get query string
        params = dict(self.request.params) if params is None else params
        url = url.clone().add_query(params, True)

        LOG.debug("Send query to URL:\n%s.", url)

        if method is None:
            method = self.request.method

        headers = dict(self.request.headers if headers is None else headers)

        # Forward request to target (without Host Header).
        # The original Host will be added back by pyramid.
        if url.hostname not in self.host_forward_host and "Host" in headers:
            headers.pop("Host")

        # Forward the request tracking ID to the other service. This will allow to follow the logs belonging
        # to a single request coming from the user
        headers.setdefault("X-Request-ID", self.request.c2c_request_id)
        # If we really want to respect the specification, we should chain with the content of the previous
        # proxy, see also:
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
        forwarded = {
            "for": self.request.client_addr,
            "proto": self.request.scheme
        }
        if "Host" in self.request.headers:
            forwarded["host"] = self.request.headers["Host"]
        forwarded_str = ";".join(["=".join(e) for e in forwarded.items()])
        if "Forwarded" in headers:
            headers["Forwarded"] = ",".join(
                [headers["Forwarded"], forwarded_str])
        else:
            headers["Forwarded"] = forwarded_str
        # Set alternative "X-Forwarded" headers
        for forwarded_elements in reversed(headers["Forwarded"].split(",")):
            for element in forwarded_elements.split(";"):
                key, value = element.split("=")
                header_key = f"X-Forwarded-{key.capitalize()}"
                header_value = headers.get(header_key)
                headers[
                    header_key] = value if header_value is None else ", ".join(
                        [header_value, value])

        if not cache:
            headers["Cache-Control"] = "no-cache"

        if method in ("POST", "PUT") and body is None:
            body = self.request.body

        headers = restrict_headers(headers, self.headers_whitelist,
                                   self.headers_blacklist)

        try:
            if method in ("POST", "PUT"):
                response = requests.request(method,
                                            url.url(),
                                            data=body,
                                            headers=headers,
                                            **self.http_options)
            else:
                response = requests.request(method,
                                            url.url(),
                                            headers=headers,
                                            **self.http_options)
        except Exception:
            errors = [
                "Error '%s' while getting the URL:", "%s", "Method: %s",
                "--- With headers ---", "%s"
            ]
            args1 = [
                sys.exc_info()[0],
                url,
                method,
                "\n".join([
                    f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
                    for h, v in list(headers.items())
                ]),
            ]
            if method in ("POST", "PUT") and body is not None:
                errors += ["--- Query with body ---", "%s"]
                args1.append(body.decode("utf-8"))
            LOG.exception("\n".join(errors), *args1)

            raise HTTPBadGateway(  # pylint: disable=raise-missing-from
                "Error on backend, See logs for detail")

        if not response.ok:
            errors = [
                "Error '%s' in response of URL:",
                "%s",
                "Status: %d",
                "Method: %s",
                "--- With headers ---",
                "%s",
            ]
            args2: List[Union[str, int]] = [
                response.reason,
                url.url(),
                response.status_code,
                method,
                "\n".join([
                    f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
                    for h, v in list(headers.items())
                ]),
            ]
            if method in ("POST", "PUT") and body is not None:
                errors += ["--- Query with body ---", "%s"]
                args2.append(body.decode("utf-8"))
            errors += ["--- Return content ---", "%s"]
            args2.append(response.text)
            LOG.error("\n".join(errors), *args2)

            raise exception_response(response.status_code)
        if not response.headers.get("Content-Type", "").startswith("image/"):
            LOG.debug("Get result for URL: %s:\n%s.", url, body)

        return response