// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Net; using Amazon.Runtime; using Amazon.S3; using Amazon.S3.Model; using Amazon.SQS; using Amazon.SQS.Model; using Amazon.S3.IO; namespace Tools.CrashReporter.CrashReportCommon { /// /// Helper class for accessing AWS /// public class AmazonClient : IDisposable { /// /// Is the AmazonClient initialized and ready to use with S3? /// public bool IsS3Valid { get { return S3Client != null; } } /// /// Is the AmazonClient initialized and ready to use with SQS? /// public bool IsSQSValid { get { return SqsClient != null; } } /// /// Create a new AmazonClient /// /// Amazon Credentials for the connection to AWS /// SQS connection details /// S3 connection details /// Out error, receives the error string if an error occurs public AmazonClient(AWSCredentials Credentials, AmazonSQSConfig SqsConfig, AmazonS3Config S3Config, out string OutError) { OutError = string.Empty; if (SqsConfig != null) { try { SqsClient = new AmazonSQSClient(Credentials, SqsConfig); } catch (Exception Ex) { OutError += Ex.Message; } } if (S3Config != null) { try { S3Client = new AmazonS3Client(Credentials, S3Config); } catch (Exception Ex) { OutError += Ex.Message; } } } /// /// Dispose of this object /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Dispose of this object /// /// Dispose of managed resources? protected virtual void Dispose(bool disposing) { if (disposing) { if (SqsClient != null) SqsClient.Dispose(); if (S3Client != null) S3Client.Dispose(); } } /// /// Get the number of items in an SQS queue /// /// The SQS queue URL /// Number of messages in the queue. Returns zero in the event of an error. public int GetSQSQueueCount(string QueueUrl) { if (!IsSQSValid) return 0; GetQueueAttributesRequest AttribRequest = new GetQueueAttributesRequest { QueueUrl = QueueUrl, AttributeNames = new List { "ApproximateNumberOfMessages" } }; GetQueueAttributesResponse AttribResponse = SqsClient.GetQueueAttributes(AttribRequest); return AttribResponse.ApproximateNumberOfMessages; } /// /// Gets a message from an SQS queue and if successful, deletes the message from the queue. /// Safe method for multiple concurrent consumers of a single SQS because the initial message read makes the message unavailable to other callers. /// /// The SQS queue URL /// Debug option. Skips the message delete when testing reading messages from a live queue /// A dequeued SQS message or null if it fails. public Message SQSDequeueMessage(string QueueUrl, bool bForceNoDelete) { if (!IsSQSValid) return null; ReceiveMessageRequest ReceiveRequest = new ReceiveMessageRequest { QueueUrl = QueueUrl, MaxNumberOfMessages = 1 }; ReceiveMessageResponse ReceiveResponse = SqsClient.ReceiveMessage(ReceiveRequest); if (ReceiveResponse.Messages.Count == 1) { Message Message = ReceiveResponse.Messages[0]; if (Message != null && (bForceNoDelete || DeleteRecordSQS(QueueUrl, Message))) { return Message; } } return null; } /// /// Retrieves an object from S3. (Caller can use the response to read the response stream) /// /// S3 bucket name /// S3 object key /// S3 object response for the requested object or null if it fails. public GetObjectResponse GetS3Object(string BucketName, string Key) { if (!IsS3Valid) return null; GetObjectRequest ObjectRequest = new GetObjectRequest { BucketName = BucketName, Key = Key }; return S3Client.GetObject(ObjectRequest); } /// /// Retrieves an URL with the appropriate signature to access the file for a predetermined amount of time /// /// S3 bucket name /// S3 object key /// Expiration of link in minutes /// The URL that can be used to access the file public string GetS3SignedUrl(string BucketName, string Key, int expirationInMinutes) { if (!IsS3Valid) return null; string url = null; S3FileInfo s3FileInfo = new Amazon.S3.IO.S3FileInfo(S3Client, BucketName, Key); if (s3FileInfo.Exists) { url = S3Client.GetPreSignedURL(new GetPreSignedUrlRequest { BucketName = BucketName, Key = Key, Expires = DateTime.UtcNow.AddMinutes(expirationInMinutes) //ResponseHeaderOverrides = new ResponseHeaderOverrides() { ContentDisposition = "attachment; filename=\"test.txt\"", } }); } return url; } /// /// Uploads a file to S3 synchronously (blocks until complete). /// /// S3 bucket name /// S3 object key /// Full path of the file to upload /// S3 object response for the uploaded object or null if it fails. public PutObjectResponse PutS3ObjectFromFile(string BucketName, string Key, string Filepath) { if (!IsS3Valid) return null; PutObjectRequest ObjectRequest = new PutObjectRequest { BucketName = BucketName, Key = Key, FilePath = Filepath }; return S3Client.PutObject(ObjectRequest); } /// /// Uploads a stream to S3 synchronously (blocks until complete). /// /// S3 bucket name /// S3 object key /// Source stream containing data to upload /// S3 object response for the uploaded object or null if it fails. public PutObjectResponse PutS3ObjectFromStream(string BucketName, string Key, Stream InStream) { if (!IsS3Valid) return null; PutObjectRequest ObjectRequest = new PutObjectRequest { BucketName = BucketName, Key = Key, InputStream = InStream }; return S3Client.PutObject(ObjectRequest); } private bool DeleteRecordSQS(string QueueUrl, Message InMessage) { DeleteMessageRequest DeleteRequest = new DeleteMessageRequest { QueueUrl = QueueUrl, ReceiptHandle = InMessage.ReceiptHandle }; var DeleteResponse = SqsClient.DeleteMessage(DeleteRequest); return DeleteResponse.HttpStatusCode == HttpStatusCode.OK; } private readonly AmazonSQSClient SqsClient; private readonly AmazonS3Client S3Client; } }