/*
* Copyright 2019 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 Amazon.Lambda.RuntimeSupport.Helpers;
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Amazon.Lambda.RuntimeSupport
{
///
/// Client to call the AWS Lambda Runtime API.
///
public class RuntimeApiClient : IRuntimeApiClient
{
private readonly HttpClient _httpClient;
private readonly IInternalRuntimeApiClient _internalClient;
#if NET6_0_OR_GREATER
private readonly IConsoleLoggerWriter _consoleLoggerRedirector = new LogLevelLoggerWriter();
#else
private readonly IConsoleLoggerWriter _consoleLoggerRedirector = new SimpleLoggerWriter();
#endif
internal Func ExceptionConverter { get; set; }
internal LambdaEnvironment LambdaEnvironment { get; set; }
///
/// Create a new RuntimeApiClient
///
/// The HttpClient to use to communicate with the Runtime API.
public RuntimeApiClient(HttpClient httpClient)
: this(new SystemEnvironmentVariables(), httpClient)
{
}
internal RuntimeApiClient(IEnvironmentVariables environmentVariables, HttpClient httpClient)
{
ExceptionConverter = ExceptionInfo.GetExceptionInfo;
_httpClient = httpClient;
LambdaEnvironment = new LambdaEnvironment(environmentVariables);
var internalClient = new InternalRuntimeApiClient(httpClient);
internalClient.BaseUrl = "http://" + LambdaEnvironment.RuntimeServerHostAndPort + internalClient.BaseUrl;
_internalClient = internalClient;
}
internal RuntimeApiClient(IEnvironmentVariables environmentVariables, IInternalRuntimeApiClient internalClient)
{
LambdaEnvironment = new LambdaEnvironment(environmentVariables);
_internalClient = internalClient;
ExceptionConverter = ExceptionInfo.GetExceptionInfo;
}
///
/// Report an initialization error as an asynchronous operation.
///
/// The exception to report.
/// The optional cancellation token to use.
/// A Task representing the asynchronous operation.
public Task ReportInitializationErrorAsync(Exception exception, CancellationToken cancellationToken = default)
{
if (exception == null)
throw new ArgumentNullException(nameof(exception));
return _internalClient.ErrorAsync(null, LambdaJsonExceptionWriter.WriteJson(ExceptionInfo.GetExceptionInfo(exception)), cancellationToken);
}
///
/// Send an initialization error with a type string but no other information as an asynchronous operation.
/// This can be used to directly control flow in Step Functions without creating an Exception class and throwing it.
///
/// The type of the error to report to Lambda. This does not need to be a .NET type name.
/// The optional cancellation token to use.
/// A Task representing the asynchronous operation.
public Task ReportInitializationErrorAsync(string errorType, CancellationToken cancellationToken = default)
{
if (errorType == null)
throw new ArgumentNullException(nameof(errorType));
return _internalClient.ErrorAsync(errorType, null, cancellationToken);
}
///
/// Get the next function invocation from the Runtime API as an asynchronous operation.
/// Completes when the next invocation is received.
///
/// The optional cancellation token to use to stop listening for the next invocation.
/// A Task representing the asynchronous operation.
public async Task GetNextInvocationAsync(CancellationToken cancellationToken = default)
{
SwaggerResponse response = await _internalClient.NextAsync(cancellationToken);
var headers = new RuntimeApiHeaders(response.Headers);
_consoleLoggerRedirector.SetCurrentAwsRequestId(headers.AwsRequestId);
var lambdaContext = new LambdaContext(headers, LambdaEnvironment, _consoleLoggerRedirector);
return new InvocationRequest
{
InputStream = response.Result,
LambdaContext = lambdaContext,
};
}
///
/// Report an invocation error as an asynchronous operation.
///
/// The ID of the function request that caused the error.
/// The exception to report.
/// The optional cancellation token to use.
/// A Task representing the asynchronous operation.
public Task ReportInvocationErrorAsync(string awsRequestId, Exception exception, CancellationToken cancellationToken = default)
{
if (awsRequestId == null)
throw new ArgumentNullException(nameof(awsRequestId));
if (exception == null)
throw new ArgumentNullException(nameof(exception));
var exceptionInfo = ExceptionInfo.GetExceptionInfo(exception);
var exceptionInfoJson = LambdaJsonExceptionWriter.WriteJson(exceptionInfo);
var exceptionInfoXRayJson = LambdaXRayExceptionWriter.WriteJson(exceptionInfo);
return _internalClient.ErrorWithXRayCauseAsync(awsRequestId, exceptionInfo.ErrorType, exceptionInfoJson, exceptionInfoXRayJson, cancellationToken);
}
///
/// Send a response to a function invocation to the Runtime API as an asynchronous operation.
///
/// The ID of the function request being responded to.
/// The content of the response to the function invocation.
/// The optional cancellation token to use.
///
public async Task SendResponseAsync(string awsRequestId, Stream outputStream, CancellationToken cancellationToken = default)
{
await _internalClient.ResponseAsync(awsRequestId, outputStream, cancellationToken);
}
}
}