Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9facdcb

Browse files
committed
feat(signing): use new signing
Use new signing technique that allows for all http headers to be accounted for correctly. This may fix signing when http compression is turned on.
1 parent bf8208c commit 9facdcb

File tree

11 files changed

+250
-50
lines changed

11 files changed

+250
-50
lines changed

‎src/Elasticsearch.Net.Aws/ElasticSearch.Net.Aws.IntegrationTests/PostTests.cs‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,35 @@
88

99
namespace IntegrationTests
1010
{
11-
[TestFixture]
11+
[TestFixture(true)]
12+
[TestFixture(false)]
1213
public class PostTests
1314
{
1415
private static string Region => TestConfig.Region;
1516

17+
bool _withCompression;
1618
string _indexName;
1719
ElasticLowLevelClient _client;
1820

21+
public PostTests(bool withCompression)
22+
{
23+
_withCompression = withCompression;
24+
}
25+
1926
[SetUp]
2027
public void Setup()
2128
{
2229
var httpConnection = new AwsHttpConnection(Region);
2330
var pool = new SingleNodeConnectionPool(new Uri(TestConfig.Endpoint));
2431
var config = new ConnectionConfiguration(pool, httpConnection);
2532
config.DisableDirectStreaming();
33+
config.EnableHttpCompression(_withCompression);
34+
config.EnableDebugMode(details =>
35+
{
36+
Console.WriteLine(details.DebugInformation);
37+
});
2638
_client = new ElasticLowLevelClient(config);
27-
_indexName = $"unittest_{Guid.NewGuid().ToString("n")}";
39+
_indexName = $"unittest{Guid.NewGuid().ToString("n")}";
2840
}
2941

3042
[TearDown]
@@ -36,11 +48,11 @@ public void TearDown()
3648
[Test]
3749
public void SimplePost_should_work()
3850
{
39-
var id = Guid.NewGuid().ToString("n");
51+
var id = "1";
4052
var response = _client.Create<VoidResponse>(
4153
_indexName,
4254
id,
43-
PostData.Serializable(new {message = "Hello, World!" }));
55+
PostData.Serializable(new TestDocument{Message = "Hello, World!" }));
4456
Assert.AreEqual(true, response.Success, response.DebugInformation);
4557
var getResponse = _client.Get<StringResponse>(_indexName, id);
4658
Assert.AreEqual(true, getResponse.Success, getResponse.DebugInformation);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace IntegrationTests
6+
{
7+
public class TestDocument
8+
{
9+
public string Message { get; set; }
10+
}
11+
}

‎src/Elasticsearch.Net.Aws/ElasticSearch.Net.Aws.Tests/ElasticSearch.Net.Aws.Tests.csproj‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
4+
<TargetFrameworks>netcoreapp3.1;net461</TargetFrameworks>
55
<AssemblyName>ElasticSearch.Net.Aws.Tests</AssemblyName>
66
<PackageId>ElasticSearch.Net.Aws.Tests</PackageId>
77
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>

‎src/Elasticsearch.Net.Aws/ElasticSearch.Net.Aws.Tests/SignUtilTests.cs‎

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,55 @@
11
using System;
22
using System.Diagnostics;
33
using System.Globalization;
4+
using System.Net.Http;
45
using System.Text;
56
using System.Threading;
7+
using System.Threading.Tasks;
68
using Amazon.Runtime;
79
using Elasticsearch.Net.Aws;
810
using NUnit.Framework;
11+
using Elasticsearch.Net;
12+
using System.IO;
913
#if NETFRAMEWORK
1014
using System.Net;
1115
#endif
1216

1317
namespace Tests
1418
{
19+
#if NETCOREAPP
20+
[TestFixture(true)]
21+
[TestFixture(false)]
22+
#else
1523
[TestFixture]
24+
#endif
1625
public class SignUtilTests
1726
{
27+
bool _useByteArrayContent;
1828
IRequest _sampleRequest;
1929
byte[] _sampleBody;
2030

31+
#if NETCOREAPP
32+
public SignUtilTests(bool useByteArrayContent)
33+
{
34+
_useByteArrayContent = useByteArrayContent;
35+
}
36+
#endif
37+
2138
[SetUp]
2239
public void SetUp()
2340
{
2441
var encoding = new UTF8Encoding(false);
2542
_sampleBody = encoding.GetBytes("Action=ListUsers&Version=2010年05月08日");
2643
#if NETCOREAPP
27-
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Post, "https://iam.amazonaws.com/");
28-
request.Content = new System.Net.Http.ByteArrayContent(_sampleBody);
44+
var request = new HttpRequestMessage(System.Net.Http.HttpMethod.Post, "https://iam.amazonaws.com/");
45+
if (_useByteArrayContent)
46+
{
47+
request.Content = new ByteArrayContent(_sampleBody);
48+
}
49+
else
50+
{
51+
request.Content = new StreamContent(new MemoryStream(_sampleBody));
52+
}
2953
request.Content.Headers.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
3054
request.Headers.TryAddWithoutValidation("X-Amz-Date", "20110909T233600Z");
3155
_sampleRequest = new HttpRequestMessageAdapter(request);
@@ -35,14 +59,22 @@ public void SetUp()
3559
//request.Content = new System.Net.Http.ByteArrayContent(_sampleBody);
3660
request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
3761
request.Headers["X-Amz-Date"] = "20110909T233600Z";
38-
_sampleRequest = new HttpWebRequestAdapter(request);
62+
var data = new RequestData(
63+
Elasticsearch.Net.HttpMethod.POST,
64+
"/",
65+
PostData.Bytes(_sampleBody),
66+
new ConnectionConfiguration(),
67+
new CreateRequestParameters(),
68+
new MemoryStreamFactory());
69+
_sampleRequest = new HttpWebRequestAdapter(request, data);
3970
#endif
4071
}
4172

4273
[Test]
43-
public void GetCanonicalRequest_should_match_sample()
74+
public asyncTask GetCanonicalRequest_should_match_sample()
4475
{
45-
var canonicalRequest = SignV4Util.GetCanonicalRequest(_sampleRequest, _sampleBody);
76+
await _sampleRequest.PrepareForSigningAsync();
77+
var canonicalRequest = SignV4Util.GetCanonicalRequest(_sampleRequest);
4678
Trace.WriteLine("=== Actual ===");
4779
Trace.Write(canonicalRequest);
4880

@@ -54,9 +86,10 @@ public void GetCanonicalRequest_should_match_sample()
5486
}
5587

5688
[Test]
57-
public void GetStringToSign_should_match_sample()
89+
public asyncTask GetStringToSign_should_match_sample()
5890
{
59-
var stringToSign = SignV4Util.GetStringToSign(_sampleRequest, _sampleBody, "us-east-1", "iam");
91+
await _sampleRequest.PrepareForSigningAsync();
92+
var stringToSign = SignV4Util.GetStringToSign(_sampleRequest, "us-east-1", "iam");
6093
Trace.WriteLine("=== Actual ===");
6194
Trace.Write(stringToSign);
6295

@@ -84,11 +117,11 @@ public void GetSigningKey_should_match_sample()
84117
}
85118

86119
[Test]
87-
public void SignRequest_should_apply_signature_to_request()
120+
public asyncTask SignRequest_should_apply_signature_to_request()
88121
{
89122
var creds = new SessionAWSCredentials("ExampleKey", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", "token1")
90123
.GetCredentials();
91-
SignV4Util.SignRequest(_sampleRequest,_sampleBody, creds, "us-east-1", "iam");
124+
awaitSignV4Util.SignRequestAsync(_sampleRequest, creds, "us-east-1", "iam");
92125

93126
var amzDate = _sampleRequest.Headers.XAmzDate;
94127
Assert.False(String.IsNullOrEmpty(amzDate));
@@ -104,18 +137,22 @@ public void SignRequest_should_apply_signature_to_request()
104137
}
105138

106139
[Test]
107-
public void SignRequest_should_apply_signature_to_request_right_culture()
140+
public asyncTask SignRequest_should_apply_signature_to_request_right_culture()
108141
{
109142
Thread.CurrentThread.CurrentCulture = new CultureInfo("th");
110143

111144
var creds = new SessionAWSCredentials("ExampleKey", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", "token1")
112145
.GetCredentials();
113-
SignV4Util.SignRequest(_sampleRequest,_sampleBody, creds, "us-east-1", "iam");
146+
awaitSignV4Util.SignRequestAsync(_sampleRequest, creds, "us-east-1", "iam");
114147

115148
var amzDateValue = _sampleRequest.Headers.XAmzDate;
116149
Assert.False(String.IsNullOrEmpty(amzDateValue));
117150
var amzDates = amzDateValue.Split(',');
118-
Assert.AreEqual(2, amzDates.Length);
151+
if (amzDates.Length != 2)
152+
{
153+
Assert.Inconclusive("Thai culture not working so this test won't work on this platform.");
154+
return;
155+
}
119156
Assert.True(amzDates[1].StartsWith(DateTime.UtcNow.Year.ToString()));
120157
Trace.WriteLine("X-Amz-Date: " + amzDateValue);
121158

‎src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/AwsHttpConnection.cs‎

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,10 @@ public AwsHttpConnection() : this(
8181
}
8282

8383
#if NETSTANDARD
84-
protected override HttpRequestMessageCreateHttpRequestMessage(RequestData requestData)
84+
protected override HttpMessageHandlerCreateHttpClientHandler(RequestData requestData)
8585
{
86-
var request = base.CreateHttpRequestMessage(requestData);
87-
SignRequest(new HttpRequestMessageAdapter(request), requestData);
88-
return request;
86+
var innerHandler = base.CreateHttpClientHandler(requestData);
87+
return new SigningHttpMessageHandler(_credentials, _region, innerHandler);
8988
}
9089
#else
9190
[ThreadStatic]
@@ -105,7 +104,7 @@ HttpWebRequest CreateWebRequestInternal(RequestData requestData, Func<RequestDat
105104
}
106105
if (_createHttpRequestDepth == 0)
107106
{
108-
SignRequest(new HttpWebRequestAdapter(ret), requestData);
107+
SignRequest(new HttpWebRequestAdapter(ret, requestData));
109108
}
110109
return ret;
111110

@@ -116,23 +115,9 @@ protected override HttpWebRequest CreateHttpWebRequest(RequestData requestData)
116115

117116
protected override HttpWebRequest CreateWebRequest(RequestData requestData)
118117
=> CreateWebRequestInternal(requestData, base.CreateWebRequest);
119-
#endif
120118

121-
private void SignRequest(IRequest request,RequestDatarequestData)
119+
private void SignRequest(IRequest request)
122120
{
123-
byte[] data = null;
124-
if (requestData.PostData != null)
125-
{
126-
data = requestData.PostData.WrittenBytes;
127-
if (data == null)
128-
{
129-
using (var ms = new MemoryStream())
130-
{
131-
requestData.PostData.Write(ms, requestData.ConnectionSettings);
132-
data = ms.ToArray();
133-
}
134-
}
135-
}
136121
ImmutableCredentials credentials;
137122
try
138123
{
@@ -147,7 +132,8 @@ private void SignRequest(IRequest request, RequestData requestData)
147132
{
148133
throw new Exception("Unable to retrieve credentials required to sign the request.");
149134
}
150-
SignV4Util.SignRequest(request, data,credentials, _region.SystemName, "es");
135+
SignV4Util.SignRequestAsync(request, credentials, _region.SystemName, "es").Wait();
151136
}
137+
#endif
152138
}
153139
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#if NETSTANDARD
2+
using System;
3+
using System.Linq;
4+
using System.Linq.Expressions;
5+
using System.Net.Http;
6+
using System.Reflection;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace Elasticsearch.Net.Aws
11+
{
12+
internal static class HttpMessageHandlerExtensions
13+
{
14+
static readonly Func<HttpMessageHandler, HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _sendFunc = GetSendAsync();
15+
16+
static Func<HttpMessageHandler, HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> GetSendAsync()
17+
{
18+
var info = typeof(HttpMessageHandler).GetTypeInfo();
19+
var q = from method in info.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
20+
where method.Name == "SendAsync"
21+
let p = method.GetParameters()
22+
where p.Length == 2 && p[0].ParameterType == typeof(HttpRequestMessage) && p[1].ParameterType == typeof(CancellationToken)
23+
select method;
24+
var sendAsyncMethod = q.FirstOrDefault();
25+
if (sendAsyncMethod == null) throw new InvalidOperationException($"Unable to find SendAsync method on handler of type {nameof(HttpMessageHandler)}");
26+
var handler = Expression.Parameter(typeof(HttpMessageHandler), "handler");
27+
var request = Expression.Parameter(typeof(HttpRequestMessage), "request");
28+
var cancellationToken = Expression.Parameter(typeof(CancellationToken), "cancellationToken");
29+
return Expression.Lambda<Func<HttpMessageHandler, HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>>>(
30+
Expression.Call(handler, sendAsyncMethod, request, cancellationToken),
31+
"WrappedSendAsync",
32+
new[] { handler, request, cancellationToken }
33+
).Compile();
34+
}
35+
36+
public static Task<HttpResponseMessage> SendAsync(this HttpMessageHandler handler, HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken))
37+
{
38+
// this reflection is necessary to bypass the "protected internal" access modifiers
39+
// on HttpMessageHandler.SendAsync
40+
// unfortunately I haven't found a good way around doing this.
41+
return _sendFunc(handler, request, cancellationToken);
42+
}
43+
}
44+
}
45+
#endif

‎src/Elasticsearch.Net.Aws/Elasticsearch.Net.Aws/HttpRequestMessageAdapter.cs‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Net.Http;
44
using System.Collections.Generic;
55
using System.Linq;
6+
using System.Threading.Tasks;
67

78
namespace Elasticsearch.Net.Aws
89
{
@@ -43,6 +44,35 @@ public HttpRequestMessageAdapter(HttpRequestMessage message)
4344
public IHeaders Headers { get; private set; }
4445
public string Method => _message.Method.ToString();
4546
public Uri RequestUri => _message.RequestUri;
47+
48+
byte[] _content;
49+
public byte[] Content => _content ?? throw new InvalidOperationException("You must first call PrepareForSigningAsync");
50+
51+
public Task PrepareForSigningAsync()
52+
{
53+
if (_message.Content == null)
54+
{
55+
_content = Array.Empty<byte>();
56+
return Task.CompletedTask;
57+
}
58+
return PrepareForSigningWithContentReplacementAsync();
59+
}
60+
61+
async Task PrepareForSigningWithContentReplacementAsync()
62+
{
63+
_content = await _message.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
64+
if (_message.Content is ByteArrayContent) return;
65+
var newContent = new ByteArrayContent(_content);
66+
foreach (var h in _message.Content.Headers)
67+
{
68+
if (h.Key == "Content-Length") continue;
69+
foreach (var val in h.Value)
70+
{
71+
newContent.Headers.TryAddWithoutValidation(h.Key, val);
72+
}
73+
}
74+
_message.Content = newContent;
75+
}
4676
}
4777
}
4878
#endif

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /