/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using Amazon.Runtime.Endpoints;
namespace Amazon.Runtime.Internal
{
///
/// Custom PipelineHandler responsible for resolving endpoint and setting authentication parameters for service requests.
/// Collects values for EndpointParameters and then resolves endpoint via global or service-specific EndpointProvider.
/// Responsible for setting authentication and http headers provided by resolved endpoint.
///
public class BaseEndpointResolver : PipelineHandler
{
public override void InvokeSync(IExecutionContext executionContext)
{
PreInvoke(executionContext);
base.InvokeSync(executionContext);
}
#if AWS_ASYNC_API
public override System.Threading.Tasks.Task InvokeAsync(IExecutionContext executionContext)
{
PreInvoke(executionContext);
return base.InvokeAsync(executionContext);
}
#elif AWS_APM_API
public override IAsyncResult InvokeAsync(IAsyncExecutionContext executionContext)
{
PreInvoke(ExecutionContext.CreateFromAsyncContext(executionContext));
return base.InvokeAsync(executionContext);
}
#endif
protected virtual void PreInvoke(IExecutionContext executionContext)
{
ProcessRequestHandlers(executionContext);
}
public virtual void ProcessRequestHandlers(IExecutionContext executionContext)
{
var requestContext = executionContext.RequestContext;
var parameters = MapEndpointsParameters(requestContext);
var config = requestContext.ClientConfig;
Endpoint endpoint = null;
if (GlobalEndpoints.Provider != null)
{
endpoint = GlobalEndpoints.Provider.ResolveEndpoint(requestContext.ServiceMetaData?.ServiceId, parameters);
}
else if (endpoint == null && config.EndpointProvider != null)
{
endpoint = config.EndpointProvider.ResolveEndpoint(parameters);
}
// Ensure url ends with "/" to avoid signature mismatch issues.
if (!endpoint.URL.EndsWith("/") && (string.IsNullOrEmpty(requestContext.Request.ResourcePath) || requestContext.Request.ResourcePath == "/"))
{
endpoint.URL += "/";
}
requestContext.Request.Endpoint = new Uri(endpoint.URL);
// If an explicit ServiceURL was provided, do not manipulate it based on UseHttp
// This preserves existing behavior prior to 3.7.100
if (config.UseHttp && string.IsNullOrEmpty(requestContext.ClientConfig.ServiceURL))
{
var uriBuilder = new UriBuilder(requestContext.Request.Endpoint)
{
Scheme = Uri.UriSchemeHttp,
Port = requestContext.Request.Endpoint.IsDefaultPort ? -1 : requestContext.Request.Endpoint.Port
};
requestContext.Request.Endpoint = uriBuilder.Uri;
}
// set authentication parameters and headers
SetAuthenticationAndHeaders(requestContext.Request, endpoint);
// service-specific handling, code-generated
ServiceSpecificHandler(executionContext, parameters);
// override AuthenticationRegion from ClientConfig if specified
if (!string.IsNullOrEmpty(config.AuthenticationRegion))
{
requestContext.Request.AuthenticationRegion = config.AuthenticationRegion;
}
}
///
/// Service-specific handling, we code-gen override per service.
///
protected virtual void ServiceSpecificHandler(IExecutionContext executionContext, EndpointParameters parameters)
{
}
private static readonly string[] SupportedAuthSchemas = { "sigv4", "sigv4a" };
private static void SetAuthenticationAndHeaders(IRequest request, Endpoint endpoint)
{
if (endpoint.Attributes != null)
{
var authSchemes = (IList)endpoint.Attributes["authSchemes"];
if (authSchemes != null)
{
var schemaFound = false;
foreach (PropertyBag schema in authSchemes)
{
var schemaName = (string)schema["name"];
if (SupportedAuthSchemas.Contains(schemaName))
{
switch (schemaName)
{
case "sigv4":
{
request.SignatureVersion = SignatureVersion.SigV4;
var signingRegion = (string)schema["signingRegion"];
if (!string.IsNullOrEmpty(signingRegion))
{
request.AuthenticationRegion = signingRegion;
}
ApplyCommonSchema(request, schema);
break;
}
case "sigv4a":
{
request.SignatureVersion = SignatureVersion.SigV4a;
var signingRegions = ((List