diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.sln b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.sln
new file mode 100644
index 0000000..7e2a480
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.40629.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elasticsearch.Net.Aws", "Elasticsearch.Net.Aws\Elasticsearch.Net.Aws.csproj", "{17DB4A76-0442-4020-8659-13645AEEE722}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{4C55D18B-FD05-424C-8E22-C37F511FD5DA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {17DB4A76-0442-4020-8659-13645AEEE722}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {17DB4A76-0442-4020-8659-13645AEEE722}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {17DB4A76-0442-4020-8659-13645AEEE722}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {17DB4A76-0442-4020-8659-13645AEEE722}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4C55D18B-FD05-424C-8E22-C37F511FD5DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4C55D18B-FD05-424C-8E22-C37F511FD5DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4C55D18B-FD05-424C-8E22-C37F511FD5DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4C55D18B-FD05-424C-8E22-C37F511FD5DA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsHttpConnection.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsHttpConnection.cs
new file mode 100644
index 0000000..14f0462
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsHttpConnection.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Linq;
+using System.Net;
+using Elasticsearch.Net.Connection;
+using Elasticsearch.Net.Connection.Configuration;
+
+namespace Elasticsearch.Net.Aws
+{
+ ///
+ /// Encapsulates an IConnection that works with AWS's Elasticsearch service.
+ ///
+ public class AwsHttpConnection : HttpConnection
+ {
+ readonly string _accessKey;
+ readonly string _secretKey;
+ readonly string _region;
+
+ ///
+ /// Initializes a new instance of the AwsHttpConnection class.
+ ///
+ /// The NEST/Elasticsearch.Net settings.
+ /// AWS specific settings required for signing requests.
+ public AwsHttpConnection(IConnectionConfigurationValues settings, AwsSettings awsSettings) : base(settings)
+ {
+ if (awsSettings == null) throw new ArgumentNullException("awsSettings");
+ if (string.IsNullOrWhiteSpace(awsSettings.SecretKey)) throw new ArgumentException("awsSettings.SecretKey is invalid.", "awsSettings");
+ if (string.IsNullOrWhiteSpace(awsSettings.Region)) throw new ArgumentException("awsSettings.Region is invalid.", "awsSettings");
+ _accessKey = awsSettings.AccessKey;
+ _secretKey = awsSettings.SecretKey;
+ _region = awsSettings.Region.ToLowerInvariant();
+ }
+
+ protected override HttpWebRequest CreateHttpWebRequest(Uri uri, string method, byte[] data, IRequestConfiguration requestSpecificConfig)
+ {
+ var request = base.CreateHttpWebRequest(uri, method, data, requestSpecificConfig);
+ SignV4Util.SignRequest(request, data, _accessKey, _secretKey, _region, "es");
+ return request;
+ }
+ }
+}
diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsSettings.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsSettings.cs
new file mode 100644
index 0000000..0bfb9cc
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsSettings.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+
+namespace Elasticsearch.Net.Aws
+{
+ ///
+ /// Encapsulates
+ ///
+ public class AwsSettings
+ {
+ ///
+ /// Gets or sets the region. e.g. us-east-1. Required.
+ ///
+ public string Region { get; set; }
+
+ ///
+ /// Gets or sets the AWS access key. Required.
+ ///
+ public string AccessKey { get; set; }
+
+ ///
+ /// Gets or sets the AWS secret key. e.g. wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY
+ /// Required.
+ ///
+ public string SecretKey { get; set; }
+ }
+}
diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.csproj b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.csproj
new file mode 100644
index 0000000..0759dac
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws.csproj
@@ -0,0 +1,71 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {17DB4A76-0442-4020-8659-13645AEEE722}
+ Library
+ Properties
+ Elasticsearch.Net.Aws
+ Elasticsearch.Net.Aws
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Elasticsearch.Net.1.7.1\lib\net45\Elasticsearch.Net.dll
+ True
+
+
+ ..\packages\NEST.1.7.1\lib\net45\Nest.dll
+ True
+
+
+ ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Properties/AssemblyInfo.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..f19947e
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Elasticsearch.Net.Aws")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Elasticsearch.Net.Aws")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("4571fc2e-a85c-49ce-b8bb-b509f78829b4")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: InternalsVisibleTo("Tests")]
diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/SignV4Util.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/SignV4Util.cs
new file mode 100644
index 0000000..2efa69d
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/SignV4Util.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Net;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Elasticsearch.Net.Aws
+{
+ internal static class SignV4Util
+ {
+ static readonly char[] _datePartSplitChars = { 'T' };
+
+ public static void SignRequest(HttpWebRequest request, byte[] body, string accessKey, string secretKey, string region, string service)
+ {
+ var date = DateTime.UtcNow;
+ var dateStamp = date.ToString("yyyyMMdd");
+ var amzDate = date.ToString("yyyyMMddTHHmmssZ");
+ request.Headers["X-Amz-Date"] = amzDate;
+ var signingKey = GetSigningKey(secretKey, dateStamp, region, service);
+ var signature = signingKey.GetHmacSha256Hash(GetStringToSign(request, body, region, service)).ToLowercaseHex();
+ var auth = string.Format(
+ "AWS4-HMAC-SHA256 Credential={0}/{1}, SignedHeaders={2}, Signature={3}",
+ accessKey,
+ GetCredentialScope(dateStamp, region, service),
+ GetSignedHeaders(request),
+ signature);
+ request.Headers[HttpRequestHeader.Authorization] = auth;
+ }
+
+ public static byte[] GetSigningKey(string secretKey, string dateStamp, string region, string service)
+ {
+ return _encoding.GetBytes("AWS4" + secretKey)
+ .GetHmacSha256Hash(dateStamp)
+ .GetHmacSha256Hash(region)
+ .GetHmacSha256Hash(service)
+ .GetHmacSha256Hash("aws4_request");
+ }
+
+ private static byte[] GetHmacSha256Hash(this byte[] key, string data)
+ {
+ using (var kha = KeyedHashAlgorithm.Create("HmacSHA256"))
+ {
+ kha.Key = key;
+ return kha.ComputeHash(_encoding.GetBytes(data));
+ }
+ }
+
+ public static string GetStringToSign(HttpWebRequest request, byte[] data, string region, string service)
+ {
+ var canonicalRequest = GetCanonicalRequest(request, data);
+ var awsDate = request.Headers["x-amz-date"];
+ Debug.Assert(Regex.IsMatch(awsDate, @"\d{8}T\d{6}Z"));
+ var datePart = awsDate.Split(_datePartSplitChars, 2)[0];
+ return string.Join("\n",
+ "AWS4-HMAC-SHA256",
+ awsDate,
+ GetCredentialScope(datePart, region, service),
+ GetHash(canonicalRequest).ToLowercaseHex()
+ );
+ }
+
+ private static string GetCredentialScope(string date, string region, string service)
+ {
+ return string.Format("{0}/{1}/{2}/aws4_request", date, region, service);
+ }
+
+ public static string GetCanonicalRequest(HttpWebRequest request, byte[] data)
+ {
+ var canonicalHeaders = request.GetCanonicalHeaders();
+ var result = new StringBuilder();
+ result.Append(request.Method);
+ result.Append('\n');
+ result.Append(request.RequestUri.AbsolutePath);
+ result.Append('\n');
+ result.Append(request.RequestUri.Query);
+ result.Append('\n');
+ WriteCanonicalHeaders(canonicalHeaders, result);
+ result.Append('\n');
+ WriteSignedHeaders(canonicalHeaders, result);
+ result.Append('\n');
+ WriteRequestPayloadHash(data, result);
+ return result.ToString();
+ }
+
+ private static Dictionary GetCanonicalHeaders(this HttpWebRequest request)
+ {
+ var q = from string key in request.Headers
+ let headerName = key.ToLowerInvariant()
+ let headerValues = string.Join(",",
+ request.Headers
+ .GetValues(key) ?? Enumerable.Empty()
+ .Select(v => v.Trimall())
+ )
+ select new { headerName, headerValues };
+ var result = q.ToDictionary(v => v.headerName, v => v.headerValues);
+ result["host"] = request.RequestUri.Host.ToLowerInvariant();
+ return result;
+ }
+
+ private static void WriteCanonicalHeaders(Dictionary canonicalHeaders, StringBuilder output)
+ {
+ var q = from pair in canonicalHeaders
+ orderby pair.Key ascending
+ select string.Format("{0}:{1}\n", pair.Key, pair.Value);
+ foreach (var line in q)
+ {
+ output.Append(line);
+ }
+ }
+
+ private static string GetSignedHeaders(HttpWebRequest request)
+ {
+ var canonicalHeaders = request.GetCanonicalHeaders();
+ var result = new StringBuilder();
+ WriteSignedHeaders(canonicalHeaders, result);
+ return result.ToString();
+ }
+
+ private static void WriteSignedHeaders(Dictionary canonicalHeaders, StringBuilder output)
+ {
+ bool started = false;
+ foreach (var pair in canonicalHeaders.OrderBy(v => v.Key))
+ {
+ if (started) output.Append(';');
+ output.Append(pair.Key.ToLowerInvariant());
+ started = true;
+ }
+ }
+
+ static readonly byte[] _emptyBytes = new byte[0];
+
+ private static void WriteRequestPayloadHash(byte[] data, StringBuilder output)
+ {
+ data = data ?? _emptyBytes;
+ var hash = GetHash(data);
+ foreach (var b in hash)
+ {
+ output.AppendFormat("{0:x2}", b);
+ }
+ }
+
+ private static string ToLowercaseHex(this byte[] data)
+ {
+ var result = new StringBuilder();
+ foreach (var b in data)
+ {
+ result.AppendFormat("{0:x2}", b);
+ }
+ return result.ToString();
+ }
+
+ static readonly UTF8Encoding _encoding = new UTF8Encoding(false);
+
+ private static byte[] GetHash(string data)
+ {
+ return GetHash(_encoding.GetBytes(data));
+ }
+
+ private static byte[] GetHash(this byte[] data)
+ {
+ using (var algo = HashAlgorithm.Create("SHA256"))
+ {
+ return algo.ComputeHash(data);
+ }
+ }
+ }
+}
diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/StringUtil.cs b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/StringUtil.cs
new file mode 100644
index 0000000..baa5245
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/StringUtil.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Elasticsearch.Net.Aws
+{
+ internal static class StringUtil
+ {
+ public static string Trimall(this string value)
+ {
+ value = value.Trim();
+ var output = new StringBuilder();
+ using (var reader = new StringReader(value))
+ {
+ while (true)
+ {
+ var next = reader.Peek();
+ if (next < 0) break;
+ var c = (char)next;
+ if (c == '"')
+ {
+ ReadQuotedString(output, reader);
+ continue;
+ }
+ if (char.IsWhiteSpace(c))
+ {
+ ReadWhitespace(output, reader);
+ continue;
+ }
+ output.Append((char)reader.Read());
+ }
+ }
+ return output.ToString();
+ }
+
+ private static void ReadQuotedString(StringBuilder output, StringReader reader)
+ {
+ var start = reader.Read();
+ Debug.Assert(start == '"');
+ output.Append('"');
+ bool escape = false;
+ while (true)
+ {
+ var next = reader.Read();
+ if (next < 0) break;
+ var c = (char)next;
+ output.Append(c);
+ if (escape)
+ {
+ escape = false;
+ }
+ else
+ {
+ if (c == '"') break;
+ if (c == '\\') escape = true;
+ }
+ }
+ }
+
+ private static void ReadWhitespace(StringBuilder output, StringReader reader)
+ {
+ var lastWhitespace = (char)reader.Read();
+ Debug.Assert(char.IsWhiteSpace(lastWhitespace));
+ while (true)
+ {
+ var next = reader.Peek();
+ if (next < 0) break;
+ var c = (char)next;
+ if (!char.IsWhiteSpace(c)) break;
+ lastWhitespace = c;
+ reader.Read();
+ }
+ output.Append(lastWhitespace);
+ }
+
+ private static string GetCanonicalRequest(HttpWebRequest request, byte[] data)
+ {
+ var result = new StringBuilder();
+ result.Append(request.Method);
+ result.Append('\n');
+ result.Append(request.RequestUri.AbsolutePath);
+ result.Append('\n');
+ result.Append(request.RequestUri.Query);
+ result.Append('\n');
+ WriteCanonicalHeaders(request, result);
+ result.Append('\n');
+ WriteSignedHeaders(request, result);
+ result.Append('\n');
+ WriteRequestPayloadHash(data, result);
+ return result.ToString();
+ }
+
+ private static void WriteCanonicalHeaders(HttpWebRequest request, StringBuilder output)
+ {
+ var q = from string key in request.Headers
+ let headerName = key.ToLowerInvariant()
+ let headerValues = string.Join(",",
+ request.Headers
+ .GetValues(key) ?? Enumerable.Empty()
+ .Select(v => v.Trimall())
+ )
+ orderby headerName ascending
+ select string.Format("{0}:{1}\n", headerName, headerValues);
+ foreach (var line in q)
+ {
+ output.Append(line);
+ }
+ }
+
+ private static void WriteSignedHeaders(HttpWebRequest request, StringBuilder output)
+ {
+ bool started = false;
+ foreach (string headerName in request.Headers)
+ {
+ if (started) output.Append(';');
+ output.Append(headerName.ToLowerInvariant());
+ started = true;
+ }
+ }
+
+ static readonly byte[] _emptyBytes = new byte[0];
+
+ private static void WriteRequestPayloadHash(byte[] data, StringBuilder output)
+ {
+ data = data ?? _emptyBytes;
+ byte[] hash;
+ using (var algo = HashAlgorithm.Create("SHA256"))
+ {
+ hash = algo.ComputeHash(data);
+ }
+ foreach (var b in hash)
+ {
+ output.AppendFormat("{0:x2}", b);
+ }
+ }
+ }
+}
diff --git a/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/packages.config b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/packages.config
new file mode 100644
index 0000000..273a18c
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Elasticsearch.Net.Aws/Tests/Properties/AssemblyInfo.cs b/src/Elasticsearch.Net.Aws/Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5ed36c2
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Tests")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3eec6e0d-be36-4993-b4ab-9f302a797e75")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Elasticsearch.Net.Aws/Tests/SignUtilTests.cs b/src/Elasticsearch.Net.Aws/Tests/SignUtilTests.cs
new file mode 100644
index 0000000..bb30fab
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Tests/SignUtilTests.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Net;
+using System.Text;
+using Elasticsearch.Net.Aws;
+using NUnit.Framework;
+
+namespace Tests
+{
+ [TestFixture]
+ public class SignUtilTests
+ {
+ HttpWebRequest _sampleRequest;
+ byte[] _sampleBody;
+
+ [SetUp]
+ public void SetUp()
+ {
+ _sampleRequest = WebRequest.CreateHttp("https://iam.amazonaws.com/");
+ _sampleRequest.Method = "POST";
+ _sampleRequest.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
+ _sampleRequest.Headers["X-Amz-Date"] = "20110909T233600Z";
+ var encoding = new UTF8Encoding(false);
+ _sampleBody = encoding.GetBytes("Action=ListUsers&Version=2010-05-08");
+ }
+
+ [Test]
+ public void GetCanonicalRequest_should_match_sample()
+ {
+ var canonicalRequest = SignV4Util.GetCanonicalRequest(_sampleRequest, _sampleBody);
+ Trace.WriteLine("=== Actual ===");
+ Trace.Write(canonicalRequest);
+
+ const string expected = "POST\n/\n\ncontent-type:application/x-www-form-urlencoded; charset=utf-8\nhost:iam.amazonaws.com\nx-amz-date:20110909T233600Z\n\ncontent-type;host;x-amz-date\nb6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2";
+ Trace.WriteLine("=== Expected ===");
+ Trace.Write(expected);
+
+ Assert.AreEqual(expected, canonicalRequest);
+ }
+
+ [Test]
+ public void GetStringToSign_should_match_sample()
+ {
+ var stringToSign = SignV4Util.GetStringToSign(_sampleRequest, _sampleBody, "us-east-1", "iam");
+ Trace.WriteLine("=== Actual ===");
+ Trace.Write(stringToSign);
+
+ const string expected = "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/iam/aws4_request\n3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2";
+ Trace.WriteLine("=== Expected ===");
+ Trace.Write(expected);
+
+ Assert.AreEqual(expected, stringToSign);
+ }
+
+ [Test]
+ public void GetSigningKey_should_match_sample()
+ {
+ // sample comes from - http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html
+ var secretKey = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
+ var date = "20120215";
+ var region = "us-east-1";
+ var service = "iam";
+ var expectedSigningKey = "f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d";
+ var actualSigningKeyBytes = SignV4Util.GetSigningKey(secretKey, date, region, service);
+ var actualSigningKey = BitConverter.ToString(actualSigningKeyBytes).Replace("-", "").ToLowerInvariant();
+ Trace.WriteLine("Expected: " + expectedSigningKey);
+ Trace.WriteLine("Actual: " + actualSigningKey);
+ Assert.AreEqual(expectedSigningKey, actualSigningKey);
+ }
+
+ [Test]
+ public void SignRequest_should_apply_signature_to_request()
+ {
+ var secretKey = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
+ SignV4Util.SignRequest(_sampleRequest, _sampleBody, "ExampleKey", secretKey, "us-east-1", "iam");
+ var amzDate = _sampleRequest.Headers["X-Amz-Date"];
+
+ Assert.IsNotNullOrEmpty(amzDate);
+ Trace.WriteLine("X-Amz-Date: " + amzDate);
+
+ var auth = _sampleRequest.Headers[HttpRequestHeader.Authorization];
+ Assert.IsNotNullOrEmpty(auth);
+ Trace.WriteLine("Authorize: " + auth);
+ }
+ }
+}
diff --git a/src/Elasticsearch.Net.Aws/Tests/StringUtilTests.cs b/src/Elasticsearch.Net.Aws/Tests/StringUtilTests.cs
new file mode 100644
index 0000000..b8f1431
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Tests/StringUtilTests.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Linq;
+using Elasticsearch.Net.Aws;
+using NUnit.Framework;
+
+namespace Tests
+{
+ [TestFixture]
+ public class StringUtilTests
+ {
+ [TestCase(" leading whitespace", "leading whitespace")]
+ [TestCase("trailing whitespace ", "trailing whitespace")]
+ [TestCase(" whitespace\t\r \tin the middle \r\n", "whitespace\tin the middle")]
+ public void Trimall_should_follow_rfc_2616(string original, string expected)
+ {
+ Assert.AreEqual(original.Trimall(), expected.Trimall());
+ }
+ }
+}
diff --git a/src/Elasticsearch.Net.Aws/Tests/Tests.csproj b/src/Elasticsearch.Net.Aws/Tests/Tests.csproj
new file mode 100644
index 0000000..271b7fb
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Tests/Tests.csproj
@@ -0,0 +1,67 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {4C55D18B-FD05-424C-8E22-C37F511FD5DA}
+ Library
+ Properties
+ Tests
+ Tests
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {17db4a76-0442-4020-8659-13645aeee722}
+ Elasticsearch.Net.Aws
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Elasticsearch.Net.Aws/Tests/packages.config b/src/Elasticsearch.Net.Aws/Tests/packages.config
new file mode 100644
index 0000000..c714ef3
--- /dev/null
+++ b/src/Elasticsearch.Net.Aws/Tests/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file