def asgi_track_request_data(scope, tracked_request):
    """
    Track request data from an ASGI HTTP or Websocket scope.
    """
    path = scope.get("root_path", "") + scope["path"]
    query_params = parse_qsl(scope.get("query_string", b"").decode("utf-8"))
    tracked_request.tag("path", create_filtered_path(path, query_params))
    if ignore_path(path):
        tracked_request.tag("ignore_transaction", True)

    # We only care about the last values of headers so don't care that we use
    # a plain dict rather than a multi-value dict
    headers = {k.lower(): v for k, v in scope.get("headers", ())}

    if scout_config.value("collect_remote_ip"):
        user_ip = (
            headers.get(b"x-forwarded-for", b"").decode("latin1").split(",")[0]
            or headers.get(b"client-ip", b"").decode("latin1").split(",")[0]
            or scope.get("client", ("", ))[0])
        tracked_request.tag("user_ip", user_ip)

    queue_time = headers.get(b"x-queue-start", b"") or headers.get(
        b"x-request-start", b"")
    tracked_queue_time = track_request_queue_time(queue_time.decode("latin1"),
                                                  tracked_request)
    if not tracked_queue_time:
        amazon_queue_time = headers.get(b"x-amzn-trace-id", b"")
        track_amazon_request_queue_time(amazon_queue_time.decode("latin1"),
                                        tracked_request)
Example #2
0
    def after_request(self):
        if self._do_nothing:
            return
        request = cherrypy.request
        tracked_request = getattr(request, "_scout_tracked_request", None)
        if tracked_request is None:
            return

        # Rename controller span now routing has been done
        operation_name = get_operation_name(request)
        if operation_name is not None:
            request._scout_controller_span.operation = operation_name

        # Grab general request data now it has been parsed
        path = request.path_info
        # Parse params ourselves because we want only GET params but CherryPy
        # parses POST params (nearly always sensitive) into the same dict.
        params = parse_qsl(request.query_string)
        tracked_request.tag("path", create_filtered_path(path, params))
        if ignore_path(path):
            tracked_request.tag("ignore_transaction", True)

        if scout_config.value("collect_remote_ip"):
            # Determine a remote IP to associate with the request. The value is
            # spoofable by the requester so this is not suitable to use in any
            # security sensitive context.
            user_ip = (
                request.headers.get("x-forwarded-for", "").split(",")[0]
                or request.headers.get("client-ip", "").split(",")[0]
                or (request.remote.ip or None)
            )
            tracked_request.tag("user_ip", user_ip)

        queue_time = request.headers.get("x-queue-start", "") or request.headers.get(
            "x-request-start", ""
        )
        tracked_queue_time = track_request_queue_time(queue_time, tracked_request)
        if not tracked_queue_time:
            amazon_queue_time = request.headers.get("x-amzn-trace-id", "")
            track_amazon_request_queue_time(amazon_queue_time, tracked_request)

        response = cherrypy.response
        status = response.status
        if isinstance(status, int):
            status_int = status
        else:
            status_first = status.split(" ", 1)[0]
            try:
                status_int = int(status_first)
            except ValueError:
                # Assume OK
                status_int = 200
        if 500 <= status_int <= 599:
            tracked_request.tag("error", "true")
        elif status_int == 404:
            tracked_request.is_real_request = False

        tracked_request.stop_span()