def set_api_id_stage_invocation_path( invocation_context: ApiInvocationContext, ) -> ApiInvocationContext: # skip if all details are already available values = ( invocation_context.api_id, invocation_context.stage, invocation_context.path_with_query_string, ) if all(values): return invocation_context # skip if this is a websocket request if invocation_context.is_websocket_request(): return invocation_context path = invocation_context.path headers = invocation_context.headers path_match = re.search(PATH_REGEX_USER_REQUEST, path) host_header = headers.get(HEADER_LOCALSTACK_EDGE_URL, "") or headers.get("Host") or "" host_match = re.search(HOST_REGEX_EXECUTE_API, host_header) test_invoke_match = re.search(PATH_REGEX_TEST_INVOKE_API, path) if path_match: api_id = path_match.group(1) stage = path_match.group(2) relative_path_w_query_params = "/%s" % path_match.group(3) elif host_match: api_id = extract_api_id_from_hostname_in_url(host_header) stage = path.strip("/").split("/")[0] relative_path_w_query_params = "/%s" % path.lstrip("/").partition( "/")[2] elif test_invoke_match: # special case: fetch the resource details for TestInvokeApi invocations stage = None region_name = invocation_context.region_name api_id = test_invoke_match.group(1) resource_id = test_invoke_match.group(2) query_string = test_invoke_match.group(4) or "" apigateway = aws_stack.connect_to_service(service_name="apigateway", region_name=region_name) resource = apigateway.get_resource(restApiId=api_id, resourceId=resource_id) resource_path = resource.get("path") relative_path_w_query_params = f"{resource_path}{query_string}" else: raise Exception( f"Unable to extract API Gateway details from request: {path} {dict(headers)}" ) if api_id: # set current region in request thread local, to ensure aws_stack.get_region() works properly if getattr(THREAD_LOCAL, "request_context", None) is not None: THREAD_LOCAL.request_context.headers[ MARKER_APIGW_REQUEST_REGION] = API_REGIONS.get(api_id, "") # set details in invocation context invocation_context.api_id = api_id invocation_context.stage = stage invocation_context.path_with_query_string = relative_path_w_query_params return invocation_context
def forward_request(self, method, path, data, headers): invocation_context = ApiInvocationContext(method, path, data, headers) forwarded_for = headers.get(HEADER_LOCALSTACK_EDGE_URL, "") if re.match(PATH_REGEX_USER_REQUEST, path) or "execute-api" in forwarded_for: result = invoke_rest_api_from_request(invocation_context) if result is not None: return result data = data and json.loads(to_str(data)) if re.match(PATH_REGEX_AUTHORIZERS, path): return handle_authorizers(method, path, data, headers) if re.match(PATH_REGEX_DOC_PARTS, path): return handle_documentation_parts(method, path, data, headers) if re.match(PATH_REGEX_VALIDATORS, path): return handle_validators(method, path, data, headers) if re.match(PATH_REGEX_RESPONSES, path): return handle_gateway_responses(method, path, data, headers) if re.match(PATH_REGEX_PATH_MAPPINGS, path): return handle_base_path_mappings(method, path, data, headers) if helpers.is_test_invoke_method(method, path): # if call is from test_invoke_api then use http_method to find the integration, # as test_invoke_api makes a POST call to request the test invocation match = re.match(PATH_REGEX_TEST_INVOKE_API, path) invocation_context.method = match[3] if data: orig_data = data path_with_query_string = orig_data.get("pathWithQueryString", None) if path_with_query_string: invocation_context.path_with_query_string = path_with_query_string invocation_context.data = data.get("body") invocation_context.headers = orig_data.get("headers", {}) result = invoke_rest_api_from_request(invocation_context) result = { "status": result.status_code, "body": to_str(result.content), "headers": dict(result.headers), } return result return True