Ejemplo n.º 1
0
    def forward_request(self, method, path, data, headers):
        if method == 'OPTIONS':
            return 200

        if path.split('?')[0] == '/health':
            return serve_health_endpoint(method, path, data)
        if method == 'POST' and path == '/graph':
            return serve_resource_graph(data)

        # kill the process if we receive this header
        headers.get(HEADER_KILL_SIGNAL) and os._exit(0)

        target = headers.get('x-amz-target', '')
        auth_header = headers.get('authorization', '')
        host = headers.get('host', '')
        headers[HEADER_LOCALSTACK_EDGE_URL] = 'https://%s' % host

        # extract API details
        api, port, path, host = get_api_from_headers(headers, path)

        set_default_region_in_headers(headers)

        if port and int(port) < 0:
            return 404

        if not port:
            port = get_port_from_custom_rules(method, path, data,
                                              headers) or port

        if not port:
            if api in ['', None, '_unknown_']:
                truncated = truncate(data)
                LOG.info((
                    'Unable to find forwarding rule for host "%s", path "%s", '
                    'target header "%s", auth header "%s", data "%s"') %
                         (host, path, target, auth_header, truncated))
            else:
                LOG.info((
                    'Unable to determine forwarding port for API "%s" - please '
                    'make sure this API is enabled via the SERVICES configuration'
                ) % api)
            response = Response()
            response.status_code = 404
            response._content = '{"status": "running"}'
            return response

        connect_host = '%s:%s' % (config.HOSTNAME, port)
        url = '%s://%s%s' % (get_service_protocol(), connect_host, path)

        headers['Host'] = host
        function = getattr(requests, method.lower())
        if isinstance(data, dict):
            data = json.dumps(data)

        response = function(url,
                            data=data,
                            headers=headers,
                            verify=False,
                            stream=True)
        return response
Ejemplo n.º 2
0
    def forward_request(self, method, path, data, headers):

        if path.split('?')[0] == '/health':
            return serve_health_endpoint(method, path, data)
        if method == 'POST' and path == '/graph':
            return serve_resource_graph(data)

        # kill the process if we receive this header
        headers.get(HEADER_KILL_SIGNAL) and os._exit(0)

        target = headers.get('x-amz-target', '')
        auth_header = headers.get('authorization', '')
        host = headers.get('host', '')
        headers[HEADER_LOCALSTACK_EDGE_URL] = 'https://%s' % host

        # extract API details
        api, port, path, host = get_api_from_headers(headers, method=method, path=path, data=data)

        set_default_region_in_headers(headers)

        if port and int(port) < 0:
            return 404

        if not port:
            api, port = get_api_from_custom_rules(method, path, data, headers) or (api, port)

        if not port:
            if method == 'OPTIONS':
                return 200

            if api in ['', None, '_unknown_']:
                truncated = truncate(data)
                LOG.info(('Unable to find forwarding rule for host "%s", path "%s %s", '
                    'target header "%s", auth header "%s", data "%s"') % (
                        host, method, path, target, auth_header, truncated))
            else:
                LOG.info(('Unable to determine forwarding port for API "%s" - please '
                    'make sure this API is enabled via the SERVICES configuration') % api)
            response = Response()
            response.status_code = 404
            response._content = '{"status": "running"}'
            return response

        if api and not headers.get('Authorization'):
            headers['Authorization'] = aws_stack.mock_aws_request_headers(api)['Authorization']

        headers['Host'] = host
        if isinstance(data, dict):
            data = json.dumps(data)

        return do_forward_request(api, port, method, path, data, headers)
Ejemplo n.º 3
0
    def forward_request(self, method, path, data, headers):
        if method == 'OPTIONS':
            return 200

        # check region
        try:
            aws_stack.check_valid_region(headers)
            aws_stack.set_default_region_in_headers(headers)
        except Exception as e:
            return make_error(message=str(e), code=400)

        if method == 'POST' and path == '/':
            # parse payload and extract fields
            req_data = urlparse.parse_qs(to_str(data), keep_blank_values=True)
            req_action = req_data['Action'][0]
            topic_arn = req_data.get('TargetArn') or req_data.get('TopicArn') or req_data.get('ResourceArn')

            if topic_arn:
                topic_arn = topic_arn[0]
                topic_arn = aws_stack.fix_account_id_in_arns(topic_arn)

            if req_action == 'SetSubscriptionAttributes':
                sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
                if not sub:
                    return make_error(message='Unable to find subscription for given ARN', code=400)

                attr_name = req_data['AttributeName'][0]
                attr_value = req_data['AttributeValue'][0]
                sub[attr_name] = attr_value
                return make_response(req_action)

            elif req_action == 'GetSubscriptionAttributes':
                sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
                if not sub:
                    return make_error(message='Unable to find subscription for given ARN', code=400)

                content = '<Attributes>'
                for key, value in sub.items():
                    content += '<entry><key>%s</key><value>%s</value></entry>\n' % (key, value)
                content += '</Attributes>'
                return make_response(req_action, content=content)

            elif req_action == 'Subscribe':
                if 'Endpoint' not in req_data:
                    return make_error(message='Endpoint not specified in subscription', code=400)

            elif req_action == 'ConfirmSubscription':
                if 'TopicArn' not in req_data:
                    return make_error(message='TopicArn not specified in confirm subscription request', code=400)

                if 'Token' not in req_data:
                    return make_error(message='Token not specified in confirm subscription request', code=400)

                do_confirm_subscription(req_data.get('TopicArn')[0], req_data.get('Token')[0])

            elif req_action == 'Unsubscribe':
                if 'SubscriptionArn' not in req_data:
                    return make_error(message='SubscriptionArn not specified in unsubscribe request', code=400)

                do_unsubscribe(req_data.get('SubscriptionArn')[0])

            elif req_action == 'DeleteTopic':
                do_delete_topic(topic_arn)

            elif req_action == 'Publish':
                if req_data.get('Subject') == ['']:
                    return make_error(code=400, code_string='InvalidParameter', message='Subject')

                # No need to create a topic to send SMS or single push notifications with SNS
                # but we can't mock a sending so we only return that it went well
                if 'PhoneNumber' not in req_data and 'TargetArn' not in req_data:
                    if topic_arn not in SNS_SUBSCRIPTIONS.keys():
                        return make_error(code=404, code_string='NotFound', message='Topic does not exist')

                publish_message(topic_arn, req_data)

                # return response here because we do not want the request to be forwarded to SNS backend
                return make_response(req_action)

            elif req_action == 'ListTagsForResource':
                tags = do_list_tags_for_resource(topic_arn)
                content = '<Tags/>'
                if len(tags) > 0:
                    content = '<Tags>'
                    for tag in tags:
                        content += '<member>'
                        content += '<Key>%s</Key>' % tag['Key']
                        content += '<Value>%s</Value>' % tag['Value']
                        content += '</member>'
                    content += '</Tags>'
                return make_response(req_action, content=content)

            elif req_action == 'CreateTopic':
                topic_arn = aws_stack.sns_topic_arn(req_data['Name'][0])
                tag_resource_success = self._extract_tags(topic_arn, req_data, True)
                # in case if there is an error it returns an error , other wise it will continue as expected.
                if not tag_resource_success:
                    return make_error(code=400, code_string='InvalidParameter',
                                  message='Topic already exists with different tags')

            elif req_action == 'TagResource':
                self._extract_tags(topic_arn, req_data, False)
                return make_response(req_action)

            elif req_action == 'UntagResource':
                tags_to_remove = []
                req_tags = {k: v for k, v in req_data.items() if k.startswith('TagKeys.member.')}
                req_tags = req_tags.values()
                for tag in req_tags:
                    tags_to_remove.append(tag[0])
                do_untag_resource(topic_arn, tags_to_remove)
                return make_response(req_action)

            data = self._reset_account_id(data)
            return Request(data=data, headers=headers, method=method)

        return True
Ejemplo n.º 4
0
    def forward_request(self, method, path, data, headers):
        if method == "OPTIONS":
            return 200

        # check region
        try:
            aws_stack.check_valid_region(headers)
            aws_stack.set_default_region_in_headers(headers)
        except Exception as e:
            return make_error(message=str(e), code=400)

        if method == "POST":
            # parse payload and extract fields
            req_data = parse_qs(to_str(data), keep_blank_values=True)

            # parse data from query path
            if not req_data:
                parsed_path = urlparse(path)
                req_data = parse_qs(parsed_path.query, keep_blank_values=True)

            req_action = req_data["Action"][0]
            topic_arn = (req_data.get("TargetArn") or req_data.get("TopicArn")
                         or req_data.get("ResourceArn"))
            if topic_arn:
                topic_arn = topic_arn[0]
                topic_arn = aws_stack.fix_account_id_in_arns(topic_arn)
            if req_action == "SetSubscriptionAttributes":
                sub = get_subscription_by_arn(req_data["SubscriptionArn"][0])
                if not sub:
                    return make_error(
                        message="Unable to find subscription for given ARN",
                        code=400)

                attr_name = req_data["AttributeName"][0]
                attr_value = req_data["AttributeValue"][0]
                sub[attr_name] = attr_value
                return make_response(req_action)

            elif req_action == "GetSubscriptionAttributes":
                sub = get_subscription_by_arn(req_data["SubscriptionArn"][0])
                if not sub:
                    return make_error(
                        message="Subscription with arn {0} not found".format(
                            req_data["SubscriptionArn"][0]),
                        code=404,
                        code_string="NotFound",
                    )

                content = "<Attributes>"
                for key, value in sub.items():
                    if key in HTTP_SUBSCRIPTION_ATTRIBUTES:
                        continue
                    content += "<entry><key>%s</key><value>%s</value></entry>\n" % (
                        key,
                        value,
                    )
                content += "</Attributes>"
                return make_response(req_action, content=content)

            elif req_action == "Subscribe":
                if "Endpoint" not in req_data:
                    return make_error(
                        message="Endpoint not specified in subscription",
                        code=400)

                if req_data["Protocol"][0] not in SNS_PROTOCOLS:
                    return make_error(
                        message=
                        f"Invalid parameter: Amazon SNS does not support this protocol string: "
                        f"{req_data['Protocol'][0]}",
                        code=400,
                    )

                if ".fifo" in req_data["Endpoint"][
                        0] and ".fifo" not in topic_arn:
                    return make_error(
                        message=
                        "FIFO SQS Queues can not be subscribed to standard SNS topics",
                        code=400,
                        code_string="InvalidParameter",
                    )

            elif req_action == "ConfirmSubscription":
                if "TopicArn" not in req_data:
                    return make_error(
                        message=
                        "TopicArn not specified in confirm subscription request",
                        code=400,
                    )

                if "Token" not in req_data:
                    return make_error(
                        message=
                        "Token not specified in confirm subscription request",
                        code=400,
                    )

                do_confirm_subscription(
                    req_data.get("TopicArn")[0],
                    req_data.get("Token")[0])

            elif req_action == "Unsubscribe":
                if "SubscriptionArn" not in req_data:
                    return make_error(
                        message=
                        "SubscriptionArn not specified in unsubscribe request",
                        code=400,
                    )

                do_unsubscribe(req_data.get("SubscriptionArn")[0])

            elif req_action == "DeleteTopic":
                do_delete_topic(topic_arn)

            elif req_action == "Publish":
                if req_data.get("Subject") == [""]:
                    return make_error(code=400,
                                      code_string="InvalidParameter",
                                      message="Subject")
                if not req_data.get("Message") or all(
                        not message for message in req_data.get("Message")):
                    return make_error(code=400,
                                      code_string="InvalidParameter",
                                      message="Empty message")

                if topic_arn and ".fifo" in topic_arn and not req_data.get(
                        "MessageGroupId"):
                    return make_error(
                        code=400,
                        code_string="InvalidParameter",
                        message=
                        "The MessageGroupId parameter is required for FIFO topics",
                    )

                sns_backend = SNSBackend.get()
                # No need to create a topic to send SMS or single push notifications with SNS
                # but we can't mock a sending so we only return that it went well
                if "PhoneNumber" not in req_data and "TargetArn" not in req_data:
                    if topic_arn not in sns_backend.sns_subscriptions:
                        return make_error(
                            code=404,
                            code_string="NotFound",
                            message="Topic does not exist",
                        )

                message_id = publish_message(topic_arn, req_data, headers)

                # return response here because we do not want the request to be forwarded to SNS backend
                return make_response(req_action, message_id=message_id)

            elif req_action == "PublishBatch":
                entries = parse_urlencoded_data(
                    req_data, "PublishBatchRequestEntries.member",
                    "MessageAttributes.entry")

                if len(entries) > 10:
                    return make_error(
                        message=
                        "The batch request contains more entries than permissible",
                        code=400,
                        code_string="TooManyEntriesInBatchRequest",
                    )
                ids = [entry["Id"] for entry in entries]

                if len(set(ids)) != len(entries):
                    return make_error(
                        message=
                        "Two or more batch entries in the request have the same Id",
                        code=400,
                        code_string="BatchEntryIdsNotDistinct",
                    )

                if topic_arn and ".fifo" in topic_arn:
                    if not all(
                        ["MessageGroupId" in entry for entry in entries]):
                        return make_error(
                            message=
                            "The MessageGroupId parameter is required for FIFO topics",
                            code=400,
                            code_string="InvalidParameter",
                        )

                response = publish_batch(topic_arn, entries, headers)
                return requests_response_xml(
                    req_action,
                    response,
                    xmlns="http://sns.amazonaws.com/doc/2010-03-31/")

            elif req_action == "ListTagsForResource":
                tags = do_list_tags_for_resource(topic_arn)
                content = "<Tags/>"
                if len(tags) > 0:
                    content = "<Tags>"
                    for tag in tags:
                        content += "<member>"
                        content += "<Key>%s</Key>" % tag["Key"]
                        content += "<Value>%s</Value>" % tag["Value"]
                        content += "</member>"
                    content += "</Tags>"
                return make_response(req_action, content=content)

            elif req_action == "CreateTopic":
                sns_backend = SNSBackend.get()
                topic_arn = aws_stack.sns_topic_arn(req_data["Name"][0])
                tag_resource_success = self._extract_tags(
                    topic_arn, req_data, True, sns_backend)
                sns_backend.sns_subscriptions[topic_arn] = (
                    sns_backend.sns_subscriptions.get(topic_arn) or [])
                # in case if there is an error it returns an error , other wise it will continue as expected.
                if not tag_resource_success:
                    return make_error(
                        code=400,
                        code_string="InvalidParameter",
                        message="Topic already exists with different tags",
                    )

            elif req_action == "TagResource":
                sns_backend = SNSBackend.get()
                self._extract_tags(topic_arn, req_data, False, sns_backend)
                return make_response(req_action)

            elif req_action == "UntagResource":
                tags_to_remove = []
                req_tags = {
                    k: v
                    for k, v in req_data.items()
                    if k.startswith("TagKeys.member.")
                }
                req_tags = req_tags.values()
                for tag in req_tags:
                    tags_to_remove.append(tag[0])
                do_untag_resource(topic_arn, tags_to_remove)
                return make_response(req_action)

            data = self._reset_account_id(data)
            return Request(data=data, headers=headers, method=method)

        return True
Ejemplo n.º 5
0
    def forward_request(self, method, path, data, headers):

        if path.split('?')[0] == '/health':
            return serve_health_endpoint(method, path, data)
        if method == 'POST' and path == '/graph':
            return serve_resource_graph(data)

        # kill the process if we receive this header
        headers.get(HEADER_KILL_SIGNAL) and os._exit(0)

        target = headers.get('x-amz-target', '')
        auth_header = headers.get('authorization', '')
        host = headers.get('host', '')
        headers[HEADER_LOCALSTACK_EDGE_URL] = 'https://%s' % host

        # extract API details
        api, port, path, host = get_api_from_headers(headers, method=method, path=path, data=data)

        set_default_region_in_headers(headers)

        if port and int(port) < 0:
            return 404

        if not port:
            api, port = get_api_from_custom_rules(method, path, data, headers) or (api, port)

        if not port:
            if method == 'OPTIONS':
                return 200

            if api in ['', None, '_unknown_']:
                truncated = truncate(data)
                if auth_header or target or data or path not in ['/', '/favicon.ico']:
                    LOG.info(('Unable to find forwarding rule for host "%s", path "%s %s", '
                        'target header "%s", auth header "%s", data "%s"') % (
                            host, method, path, target, auth_header, truncated))
            else:
                LOG.info(('Unable to determine forwarding port for API "%s" - please '
                    'make sure this API is enabled via the SERVICES configuration') % api)
            response = Response()
            response.status_code = 404
            response._content = '{"status": "running"}'
            return response

        if api and not headers.get('Authorization'):
            headers['Authorization'] = aws_stack.mock_aws_request_headers(api)['Authorization']

        headers['Host'] = host
        if isinstance(data, dict):
            data = json.dumps(data)

        encoding_type = headers.get('content-encoding') or ''
        if encoding_type.upper() == GZIP_ENCODING and api is not S3:
            headers.set('content-encoding', IDENTITY_ENCODING)
            data = gzip.decompress(data)

        lock_ctx = BOOTSTRAP_LOCK
        if persistence.API_CALLS_RESTORED or is_internal_call_context(headers):
            lock_ctx = empty_context_manager()

        with lock_ctx:
            return do_forward_request(api, method, path, data, headers, port=port)
Ejemplo n.º 6
0
    def forward_request(self, method, path, data, headers):

        if common.INFRA_STOPPED:
            return 503

        if config.EDGE_FORWARD_URL:
            return do_forward_request_network(
                0,
                method,
                path,
                data,
                headers,
                target_url=config.EDGE_FORWARD_URL)

        # kill the process if we receive this header
        headers.get(HEADER_KILL_SIGNAL) and sys.exit(0)

        target = headers.get("x-amz-target", "")
        auth_header = get_auth_string(method, path, headers, data)
        if auth_header and not headers.get("authorization"):
            headers["authorization"] = auth_header
        host = headers.get("host", "")
        orig_req_url = headers.pop(HEADER_LOCALSTACK_REQUEST_URL, "")
        headers[HEADER_LOCALSTACK_EDGE_URL] = (re.sub(
            r"^([^:]+://[^/]+).*", r"\1", orig_req_url) or "http://%s" % host)

        # extract API details
        api, port, path, host = get_api_from_headers(headers,
                                                     method=method,
                                                     path=path,
                                                     data=data)

        set_default_region_in_headers(headers)

        if port and int(port) < 0:
            return 404

        if not port:
            api, port = get_api_from_custom_rules(method, path, data,
                                                  headers) or (
                                                      api,
                                                      port,
                                                  )

        should_log_trace = is_trace_logging_enabled(headers)
        if api and should_log_trace:
            # print request trace for debugging, if enabled
            LOG.debug('IN(%s): "%s %s" - headers: %s - data: %s', api, method,
                      path, dict(headers), data)

        if not port:
            if method == "OPTIONS":
                if api and should_log_trace:
                    # print request trace for debugging, if enabled
                    LOG.debug('IN(%s): "%s %s" - status: %s', api, method,
                              path, 200)
                return 200

            if api in ["", None, API_UNKNOWN]:
                truncated = truncate(data)
                if auth_header or target or data or path not in [
                        "/", "/favicon.ico"
                ]:
                    LOG.info(
                        ('Unable to find forwarding rule for host "%s", path "%s %s", '
                         'target header "%s", auth header "%s", data "%s"'),
                        host,
                        method,
                        path,
                        target,
                        auth_header,
                        truncated,
                    )
            else:
                LOG.info(
                    ('Unable to determine forwarding port for API "%s" - please '
                     "make sure this API is enabled via the SERVICES configuration"
                     ),
                    api,
                )
            response = Response()
            response.status_code = 404
            response._content = '{"status": "running"}'
            return response

        if api and not headers.get("Authorization"):
            headers["Authorization"] = aws_stack.mock_aws_request_headers(
                api)["Authorization"]
        headers[HEADER_TARGET_API] = str(api)

        headers["Host"] = host
        if isinstance(data, dict):
            data = json.dumps(data)

        encoding_type = headers.get("Content-Encoding") or ""
        if encoding_type.upper() == GZIP_ENCODING.upper(
        ) and api not in SKIP_GZIP_APIS:
            headers.set("Content-Encoding", IDENTITY_ENCODING)
            data = gzip.decompress(data)

        is_internal_call = is_internal_call_context(headers)

        self._require_service(api)

        lock_ctx = BOOTSTRAP_LOCK
        if is_internal_call or persistence.is_persistence_restored():
            lock_ctx = empty_context_manager()

        with lock_ctx:
            result = do_forward_request(api,
                                        method,
                                        path,
                                        data,
                                        headers,
                                        port=port)
            if should_log_trace and result not in [None, False, True]:
                result_status_code = getattr(result, "status_code", result)
                result_headers = getattr(result, "headers", {})
                result_content = getattr(result, "content", "")
                LOG.debug(
                    'OUT(%s): "%s %s" - status: %s - response headers: %s - response: %s',
                    api,
                    method,
                    path,
                    result_status_code,
                    dict(result_headers or {}),
                    result_content,
                )
            return result
Ejemplo n.º 7
0
    def forward_request(self, method, path, data, headers):

        if path.split('?')[0] == '/health':
            return serve_health_endpoint(method, path, data)
        if method == 'POST' and path == '/graph':
            return serve_resource_graph(data)

        # kill the process if we receive this header
        headers.get(HEADER_KILL_SIGNAL) and sys.exit(0)

        target = headers.get('x-amz-target', '')
        auth_header = get_auth_string(method, path, headers, data)
        if auth_header and not headers.get('authorization'):
            headers['authorization'] = auth_header
        host = headers.get('host', '')
        orig_req_url = headers.pop(HEADER_LOCALSTACK_REQUEST_URL, '')
        headers[HEADER_LOCALSTACK_EDGE_URL] = (re.sub(
            r'^([^:]+://[^/]+).*', r'\1', orig_req_url) or 'http://%s' % host)

        # extract API details
        api, port, path, host = get_api_from_headers(headers,
                                                     method=method,
                                                     path=path,
                                                     data=data)

        if api and config.LS_LOG:
            # print request trace for debugging, if enabled
            LOG.debug('IN(%s): "%s %s" - headers: %s - data: %s' %
                      (api, method, path, dict(headers), data))

        set_default_region_in_headers(headers)

        if port and int(port) < 0:
            return 404

        if not port:
            api, port = get_api_from_custom_rules(method, path, data,
                                                  headers) or (api, port)

        if not port:
            if method == 'OPTIONS':
                if api and config.LS_LOG:
                    # print request trace for debugging, if enabled
                    LOG.debug('OUT(%s): "%s %s" - status: %s' %
                              (api, method, path, 200))
                return 200

            if api in ['', None, API_UNKNOWN]:
                truncated = truncate(data)
                if auth_header or target or data or path not in [
                        '/', '/favicon.ico'
                ]:
                    LOG.info((
                        'Unable to find forwarding rule for host "%s", path "%s %s", '
                        'target header "%s", auth header "%s", data "%s"') %
                             (host, method, path, target, auth_header,
                              truncated))
            else:
                LOG.info((
                    'Unable to determine forwarding port for API "%s" - please '
                    'make sure this API is enabled via the SERVICES configuration'
                ) % api)
            response = Response()
            response.status_code = 404
            response._content = '{"status": "running"}'
            return response

        if api and not headers.get('Authorization'):
            headers['Authorization'] = aws_stack.mock_aws_request_headers(
                api)['Authorization']
        headers[HEADER_TARGET_API] = str(api)

        headers['Host'] = host
        if isinstance(data, dict):
            data = json.dumps(data)

        encoding_type = headers.get('Content-Encoding') or ''
        if encoding_type.upper() == GZIP_ENCODING.upper() and api not in [S3]:
            headers.set('Content-Encoding', IDENTITY_ENCODING)
            data = gzip.decompress(data)

        lock_ctx = BOOTSTRAP_LOCK
        if persistence.API_CALLS_RESTORED or is_internal_call_context(headers):
            lock_ctx = empty_context_manager()

        with lock_ctx:
            return do_forward_request(api,
                                      method,
                                      path,
                                      data,
                                      headers,
                                      port=port)
Ejemplo n.º 8
0
    def forward_request(self, method, path, data, headers):
        if method == "OPTIONS":
            return 200

        # check region
        try:
            aws_stack.check_valid_region(headers)
            aws_stack.set_default_region_in_headers(headers)
        except Exception as e:
            return make_error(message=str(e), code=400)

        if method == "POST":
            # parse payload and extract fields
            req_data = urlparse.parse_qs(to_str(data), keep_blank_values=True)

            # parse data from query path
            if not req_data:
                parsed_path = urlparse.urlparse(path)
                req_data = urlparse.parse_qs(parsed_path.query,
                                             keep_blank_values=True)

            req_action = req_data["Action"][0]
            topic_arn = (req_data.get("TargetArn") or req_data.get("TopicArn")
                         or req_data.get("ResourceArn"))
            if topic_arn:
                topic_arn = topic_arn[0]
                topic_arn = aws_stack.fix_account_id_in_arns(topic_arn)
            if req_action == "SetSubscriptionAttributes":
                sub = get_subscription_by_arn(req_data["SubscriptionArn"][0])
                if not sub:
                    return make_error(
                        message="Unable to find subscription for given ARN",
                        code=400)

                attr_name = req_data["AttributeName"][0]
                attr_value = req_data["AttributeValue"][0]
                sub[attr_name] = attr_value
                return make_response(req_action)

            elif req_action == "GetSubscriptionAttributes":
                sub = get_subscription_by_arn(req_data["SubscriptionArn"][0])
                if not sub:
                    return make_error(
                        message="Subscription with arn {0} not found".format(
                            req_data["SubscriptionArn"][0]),
                        code=404,
                        code_string="NotFound",
                    )

                content = "<Attributes>"
                for key, value in sub.items():
                    if key in HTTP_SUBSCRIPTION_ATTRIBUTES:
                        continue
                    content += "<entry><key>%s</key><value>%s</value></entry>\n" % (
                        key,
                        value,
                    )
                content += "</Attributes>"
                return make_response(req_action, content=content)

            elif req_action == "Subscribe":
                if "Endpoint" not in req_data:
                    return make_error(
                        message="Endpoint not specified in subscription",
                        code=400)

            elif req_action == "ConfirmSubscription":
                if "TopicArn" not in req_data:
                    return make_error(
                        message=
                        "TopicArn not specified in confirm subscription request",
                        code=400,
                    )

                if "Token" not in req_data:
                    return make_error(
                        message=
                        "Token not specified in confirm subscription request",
                        code=400,
                    )

                do_confirm_subscription(
                    req_data.get("TopicArn")[0],
                    req_data.get("Token")[0])

            elif req_action == "Unsubscribe":
                if "SubscriptionArn" not in req_data:
                    return make_error(
                        message=
                        "SubscriptionArn not specified in unsubscribe request",
                        code=400,
                    )

                do_unsubscribe(req_data.get("SubscriptionArn")[0])

            elif req_action == "DeleteTopic":
                do_delete_topic(topic_arn)

            elif req_action == "Publish":
                if req_data.get("Subject") == [""]:
                    return make_error(code=400,
                                      code_string="InvalidParameter",
                                      message="Subject")
                sns_backend = SNSBackend.get()
                # No need to create a topic to send SMS or single push notifications with SNS
                # but we can't mock a sending so we only return that it went well
                if "PhoneNumber" not in req_data and "TargetArn" not in req_data:
                    if topic_arn not in sns_backend.sns_subscriptions:
                        return make_error(
                            code=404,
                            code_string="NotFound",
                            message="Topic does not exist",
                        )

                message_id = publish_message(topic_arn, req_data, headers)

                # return response here because we do not want the request to be forwarded to SNS backend
                return make_response(req_action, message_id=message_id)

            elif req_action == "ListTagsForResource":
                tags = do_list_tags_for_resource(topic_arn)
                content = "<Tags/>"
                if len(tags) > 0:
                    content = "<Tags>"
                    for tag in tags:
                        content += "<member>"
                        content += "<Key>%s</Key>" % tag["Key"]
                        content += "<Value>%s</Value>" % tag["Value"]
                        content += "</member>"
                    content += "</Tags>"
                return make_response(req_action, content=content)

            elif req_action == "CreateTopic":
                sns_backend = SNSBackend.get()
                topic_arn = aws_stack.sns_topic_arn(req_data["Name"][0])
                tag_resource_success = self._extract_tags(
                    topic_arn, req_data, True, sns_backend)
                sns_backend.sns_subscriptions[topic_arn] = (
                    sns_backend.sns_subscriptions.get(topic_arn) or [])
                # in case if there is an error it returns an error , other wise it will continue as expected.
                if not tag_resource_success:
                    return make_error(
                        code=400,
                        code_string="InvalidParameter",
                        message="Topic already exists with different tags",
                    )

            elif req_action == "TagResource":
                sns_backend = SNSBackend.get()
                self._extract_tags(topic_arn, req_data, False, sns_backend)
                return make_response(req_action)

            elif req_action == "UntagResource":
                tags_to_remove = []
                req_tags = {
                    k: v
                    for k, v in req_data.items()
                    if k.startswith("TagKeys.member.")
                }
                req_tags = req_tags.values()
                for tag in req_tags:
                    tags_to_remove.append(tag[0])
                do_untag_resource(topic_arn, tags_to_remove)
                return make_response(req_action)

            data = self._reset_account_id(data)
            return Request(data=data, headers=headers, method=method)

        return True