Skip to content

Commit cdae1a2

Browse files
authored
Rework reading content for chunked responses (#294)
1 parent 8746eaa commit cdae1a2

9 files changed

Lines changed: 64 additions & 16 deletions

File tree

nanoFramework.System.Net.Http.Client/System.Net.Http.Client.nfproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@
111111
<Compile Include="..\nanoFramework.System.Net.Http\Http\System.Net.Http.Constants.cs">
112112
<Link>Http\System.Net.Http.Constants.cs</Link>
113113
</Compile>
114+
<Compile Include="..\nanoFramework.System.Net.Http\Http\System.Net.IKnowWhenDone.cs">
115+
<Link>Http\System.Net.IKnowWhenDone.cs</Link>
116+
</Compile>
114117
<Compile Include="..\nanoFramework.System.Net.Http\Http\System.Net.Internal.WebRequestPrefixElement.cs">
115118
<Link>Http\System.Net.Internal.WebRequestPrefixElement.cs</Link>
116119
</Compile>

nanoFramework.System.Net.Http.Server/System.Net.Http.Server.nfproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
<Compile Include="..\nanoFramework.System.Net.Http\Http\System.Net.HttpVersion.cs">
6565
<Link>Http\System.Net.HttpVersion.cs</Link>
6666
</Compile>
67+
<Compile Include="..\nanoFramework.System.Net.Http\Http\System.Net.IKnowWhenDone.cs">
68+
<Link>Http\System.Net.IKnowWhenDone.cs</Link>
69+
</Compile>
6770
<Compile Include="..\nanoFramework.System.Net.Http\Http\System.Net.Internal.cs">
6871
<Link>Http\System.Net.Internal.cs</Link>
6972
</Compile>

nanoFramework.System.Net.Http/Http/Headers/HttpContentHeaders.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@ public long ContentLength
3838
}
3939
}
4040

41-
if (_content.TryComputeLength(out long contentLength))
42-
{
43-
return contentLength;
44-
}
45-
4641
return -1;
4742
}
4843

nanoFramework.System.Net.Http/Http/HttpClientHandler.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,19 +330,15 @@ HttpResponseMessage CreateResponseMessage(HttpWebResponse wr, HttpRequestMessage
330330
ReasonPhrase = wr.StatusDescription
331331
};
332332

333-
// get response stream
334-
var responseStream = wr.GetResponseStream();
335-
336333
// set content
337-
response.Content = new StreamContent(
338-
responseStream,
339-
(int)responseStream.Length);
334+
response.Content = new StreamContent(wr.GetResponseStream());
340335

341336
var headers = wr.Headers;
342337

343338
foreach (var headerKey in headers.AllKeys)
344339
{
345340
response.Headers._headerStore.AddInternal(headerKey, headers[headerKey]);
341+
response.Content.Headers._headerStore.AddInternal(headerKey, headers[headerKey]);
346342
}
347343

348344
requestMessage.RequestUri = wr.ResponseUri;

nanoFramework.System.Net.Http/Http/HttpContent.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ public HttpContentHeaders Headers
4141
}
4242
}
4343

44+
/// <summary>
45+
/// Contains the actual bytes read into the buffer
46+
/// </summary>
47+
protected int TotalBytesRead { get; set; }
48+
4449
/// <summary>
4550
/// Initializes a new instance of the HttpContent class.
4651
/// </summary>
@@ -105,6 +110,11 @@ public Stream LoadIntoBuffer()
105110

106111
SerializeToStream(_buffer);
107112

113+
if (TotalBytesRead > 0)
114+
{
115+
_buffer.SetLength(TotalBytesRead);
116+
}
117+
108118
_buffer.Seek(0, SeekOrigin.Begin);
109119

110120
return _buffer;

nanoFramework.System.Net.Http/Http/StreamContent.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class StreamContent : HttpContent
2727
/// The <see cref="StreamContent"/> object calls <see cref="Dispose"/> on the provided Stream object when <see cref="StreamContent.Dispose"/> is called.
2828
/// </remarks>
2929
public StreamContent(Stream content)
30-
: this(content, 16 * 1024)
30+
: this(content, 4 * 1024)
3131
{
3232
}
3333

@@ -81,12 +81,24 @@ protected override void SerializeToStream(Stream stream)
8181
byte[] buffer = new byte[2048];
8282
int read;
8383
int totalRead = 0;
84+
int contentLength = (int)Headers.ContentLength;
8485

85-
while (totalRead < _bufferSize)
86+
// occurrs when there is not Content_Length header (i.e. chunked response)
87+
if (contentLength < 0)
88+
{
89+
contentLength = int.MaxValue;
90+
}
91+
92+
while (totalRead < contentLength)
8693
{
8794
read = _content.Read(buffer, 0, buffer.Length);
8895

89-
if (read == 0)
96+
if (_content is IKnowWhenDone knowWhenDone && knowWhenDone.IsDone)
97+
{
98+
//happens when a chunked response is at the end
99+
break;
100+
}
101+
else if (read == 0)
90102
{
91103
// need to let the native layer get more data
92104
Thread.Sleep(10);
@@ -97,6 +109,8 @@ protected override void SerializeToStream(Stream stream)
97109
stream.Write(buffer, 0, read);
98110
}
99111
}
112+
113+
TotalBytesRead = totalRead;
100114
}
101115

102116
/// <inheritdoc/>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
4+
// See LICENSE file in the project root for full license information.
5+
//
6+
7+
8+
namespace System.Net
9+
{
10+
/// <summary>
11+
/// The interface to let its consumer know work is done
12+
/// </summary>
13+
internal interface IKnowWhenDone
14+
{
15+
/// <summary>
16+
/// The property reflects if work is done
17+
/// </summary>
18+
/// <returns>
19+
/// True when work is done.
20+
/// </returns>
21+
bool IsDone { get; }
22+
}
23+
}

nanoFramework.System.Net.Http/Http/System.Net._InputNetworkStreamWrapper.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ namespace System.Net
1616
/// The InputNetworkStreamWrapper is used to re-implement calls to NetworkStream.Read
1717
/// It has internal buffer and during initial read operation it places available data from socket into buffer.
1818
/// Later it releases data to Stream.Read calls.
19-
/// It also provides direct access to bufferet data for internal code.
19+
/// It also provides direct access to buffered data for internal code.
2020
/// It provides possibility to "unread" or probe data - meaning user can read byte of data and then return it back to stream.
2121
/// </summary>
22-
internal class InputNetworkStreamWrapper : Stream
22+
internal class InputNetworkStreamWrapper : Stream, IKnowWhenDone
2323
{
2424
static private Text.Decoder UTF8decoder = System.Text.Encoding.UTF8.GetDecoder();
2525
static private Text.Encoding UTF8Encoding = System.Text.Encoding.UTF8;
@@ -200,6 +200,7 @@ public override int Read(byte[] buffer, int offset, int size)
200200
if (m_chunk.m_Size == 0)
201201
{
202202
// Nothing to read and actually it is the end of the message body. It is "case 4".
203+
IsDone = true;
203204
return 0;
204205
}
205206

@@ -456,6 +457,8 @@ public override int WriteTimeout
456457
set { m_Stream.WriteTimeout = value; }
457458
}
458459

460+
public bool IsDone { get; private set; }
461+
459462
public Stream CloneStream()
460463
{
461464
InputNetworkStreamWrapper clone = this.MemberwiseClone() as InputNetworkStreamWrapper;

nanoFramework.System.Net.Http/System.Net.Http.nfproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<Compile Include="Http\HttpResponseMessage.cs" />
6464
<Compile Include="Http\StreamContent.cs" />
6565
<Compile Include="Http\StringContent.cs" />
66+
<Compile Include="Http\System.Net.IKnowWhenDone.cs" />
6667
<Compile Include="Http\System.Net.Http.Constants.cs" />
6768
<Compile Include="Http\System.Net.AuthenticationType.cs" />
6869
<Compile Include="Http\System.Net.HttpListener.cs" />

0 commit comments

Comments
 (0)