NSUrlSessionHandler: Rewind request content to the start before sending (#16341)
Fixes #16339 --------- Co-authored-by: Manuel de la Pena <mandel@microsoft.com> Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
This commit is contained in:
Родитель
ca562147e8
Коммит
6ef7d9c0f9
|
@ -491,6 +491,11 @@ namespace Foundation {
|
|||
};
|
||||
|
||||
if (stream != Stream.Null) {
|
||||
// Rewind the stream to the beginning in case the HttpContent implementation
|
||||
// will be accessed again (e.g. for retry/redirect) and it keeps its stream open behind the scenes.
|
||||
if (stream.CanSeek)
|
||||
stream.Seek (0, SeekOrigin.Begin);
|
||||
|
||||
// HttpContent.TryComputeLength is `protected internal` :-( but it's indirectly called by headers
|
||||
var length = request.Content?.Headers?.ContentLength;
|
||||
if (length.HasValue && (length <= MaxInputInMemory))
|
||||
|
|
|
@ -758,5 +758,79 @@ namespace MonoTests.System.Net.Http {
|
|||
Assert.AreEqual (HttpStatusCode.Unauthorized, httpStatus, "Second status not ok");
|
||||
}
|
||||
}
|
||||
|
||||
class TestDelegateHandler : DelegatingHandler {
|
||||
public int Iterations;
|
||||
public HttpResponseMessage [] Responses;
|
||||
|
||||
public TestDelegateHandler (int iterations)
|
||||
{
|
||||
Responses = new HttpResponseMessage [iterations];
|
||||
Iterations = iterations;
|
||||
}
|
||||
|
||||
public bool IsCompleted (int iteration)
|
||||
{
|
||||
return Responses [iteration] is not null;
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
// test that we can perform a retry with the same request
|
||||
for (var i = 0; i < Iterations; i++)
|
||||
Responses [i] = await base.SendAsync (request, cancellationToken);
|
||||
return Responses.Last ();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GHIssue16339 ()
|
||||
{
|
||||
// test that we can perform two diff requests with the same managed HttpRequestMessage
|
||||
var json = "{this:\"\", is:\"a\", test:2}";
|
||||
var iterations = 2;
|
||||
var bodies = new string [iterations];
|
||||
|
||||
var request = new HttpRequestMessage {
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new (NetworkResources.Httpbin.PostUrl),
|
||||
Content = new StringContent (json, Encoding.UTF8, "application/json")
|
||||
};
|
||||
|
||||
using var delegatingHandler = new TestDelegateHandler (iterations) {
|
||||
InnerHandler = new NSUrlSessionHandler (),
|
||||
};
|
||||
|
||||
var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => {
|
||||
using var client = new HttpClient (delegatingHandler);
|
||||
var _ = await client.SendAsync (request);
|
||||
for (var i = 0; i < iterations; i++) {
|
||||
if (delegatingHandler.IsCompleted (i))
|
||||
bodies [i] = await delegatingHandler.Responses [i].Content.ReadAsStringAsync ();
|
||||
}
|
||||
}, out var ex);
|
||||
|
||||
if (!done) { // timeouts happen in the bots due to dns issues, connection issues etc.. we do not want to fail
|
||||
Assert.Inconclusive ("Request timedout.");
|
||||
} else {
|
||||
Assert.IsNull (ex, "Exception");
|
||||
|
||||
for (var i = 0; i < iterations; i++) {
|
||||
var rsp = delegatingHandler.Responses [i];
|
||||
Assert.IsTrue (delegatingHandler.IsCompleted (i), $"Completed #{i}");
|
||||
Assert.IsTrue (rsp.IsSuccessStatusCode, $"IsSuccessStatusCode #{i}");
|
||||
Assert.AreEqual ("OK", rsp.ReasonPhrase, $"ReasonPhrase #{i}");
|
||||
Assert.AreEqual (HttpStatusCode.OK, rsp.StatusCode, $"StatusCode #{i}");
|
||||
|
||||
var body = bodies [i];
|
||||
// Poor-man's json parser
|
||||
var data = body.Split ('\n', '\r').Single (v => v.Contains ("\"data\": \""));
|
||||
data = data.Trim ().Replace ("\"data\": \"", "").TrimEnd ('"', ',');
|
||||
data = data.Replace ("\\\"", "\"");
|
||||
|
||||
Assert.AreEqual (json, data, $"Post data #{i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче