HttpClient not writing to stream while downloading - c#

Currently I am implementing a way to report Progress with the HttpClient, since we share code with a .NET4 WPF and a Windows Universal App we use the Microsoft HTTP Client Libraries from NuGet. The idea was to wrap the target file stream in a CountingInputStream and report progress there:
public override void Write(byte[] buffer, int offset, int count)
{
_stream.Write(buffer, offset, count);
_bytesRead += count;
_progress.Report(_bytesRead);
if (_cancellationToken.IsCancellationRequested)
{
_cancellationToken.ThrowIfCancellationRequested();
}
}
Then I send my request with: HttpResponseMessage httpResponseMessage = AsyncHelpers.RunSync(() => _httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken));
After that I open the file stream and then copy the content stream. The response has correct headers:
Content-Length: 213334
Content-Type: application/octet-stream; charset=UTF-8
Content-Disposition: attachment; filename="Bondi Beach.jpg"; filename*=UTF-8''Bondi%20Beach.jpg
using(Stream fileStream = new CountingInputStream(storage.Open(downloadRequest.TargetPath, FileMode.Create), downloadRequest.Progress, cancellationToken )) {
await HttpHeaderResponseMessage.Content.CopyToAsync(fileStream);
}
The problem is that the StreamContent only starts writing to the file stream after the download has finished. When it started writing progress reporting just works fine.
I already tried different approaches like:
ReadAsStreamAsync and then copy the response stream to file stream
ReadAsStreamAsync manually read to buffer and then write to file stream
_httpClient = new System.Net.Http.HttpClient(){MaxResponseContentBufferSize = 4096};to restrict the BufferSize
Any ideas how I could force the ContentStream to write to the file stream while it is still downloading?
UPDATE:
Following Luaans advice I tried to override the WriteAsync and implemented a StreamContent Extensions method:
//CountingInputStream
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
_bytesRead += count;
_progress.Report(_bytesRead);
if (_cancellationToken.IsCancellationRequested)
{
_cancellationToken.ThrowIfCancellationRequested();
}
return _stream.WriteAsync(buffer, offset, count, cancellationToken);
}
//static Extensions Class
public static async Task CopyToAs(this StreamContent source, Stream targetStream)
{
int read;
byte[] buffer = new byte[4096];
using(Stream responseStream = await source.ReadAsStreamAsync()) {
while ((read = await responseStream.ReadAsync(buffer,0,buffer.Length))>0) {
await targetStream.WriteAsync(buffer, 0, read);
}
}
}
It still waits till the download is finish until it calls ReadAsync the first time. Any hints what I have done wrong?

The fact that ReadAsStreamAsync is, well, async, makes this rather suspicious. Why would you asynchronously wait to get the stream? You're supposed to read it asynchronously, but you should have the stream itself ready right away.
Reading the documentation makes this blatantly obvious:
This operation will not block. The returned task object will complete after the whole response (including content) is read.
However, there's overloads you can use to have it return after the headers have been read. This still means you need to wait for the server to process the request, before starting to get progress, but for the download itself, you are in luck.
Sample code:
var response =
await
(
new HttpClient()
.GetAsync("http://www.microsoft.com/", HttpCompletionOption.ResponseHeadersRead)
);
var stream = await response.Content.ReadAsStreamAsync();
var buffer = new byte[2048];
while (await stream.ReadAsync(buffer, 0, buffer.Length) > 0)
{
// Report progress and write to a different stream
}
EDIT:
It sounds like you should be using Windows.Web.Http.HttpClient instead of System.Net.Http.HttpClient:
async Task DownloadWithProgress()
{
var awaitable = httpClient.GetAsync(yourUrl)
awaitable.Progress = (res, progress) =>
{
// Report progress
}
await awaitable;
}

Related

.NET Socket ReadAsync blocked during write loop Async / Await

I am writing a TCP server using Async / Await that needs to send a list of messages to connected clients, based on what it receives from each client. Between each message sent to the client, I need to:
wait for an acknowledgement/response then send the next messages
resend the command if no acknowledgement after 5 seconds
To do this, I am setting a ResponseReceived property on my ConnClient class when the expected response comes in. Then, in the ConnClient.SendListAsync routine, I am checking to see if the property has been changed after sending each command. However, incoming responses are not read until the SendListAsync sends all messages, as can be seen in my debug statements below:
Sending Initial Message.
Received response, generate list of 3 initial commands and send them.
SendListAsync 5 second timeout w/o response.
SendListAsync 5 second timeout w/o response.
SendListAsync 5 second timeout w/o response.
Received response.
Received response.
Received response.
Question: How can I properly prevent ConnClient.SendListAsync from blocking incoming reads?
public class Svr
{
TcpListener listener;
public async Task Listen(IPAddress iP, int port)
{
listener = new TcpListener(iP, port);
listener.Start();
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
ConnClient cc = new ConnClient(client);
await Receive(ConnClient);
}
}
async Task Receive(ConnClient cc)
{
var headerSize = sizeof(short);
byte[] buffer = new byte[4000];
//Send initial msg
await cc.socket.GetStream().WriteAsync(Strings.InitialMsg, 0, Strings.InitialMsg.Length);
while (true)
{
buffer = new byte[headerSize];
if (!await ReadToBuffer(cc.socket.GetStream(), buffer, headerSize))
return;
var length = BitConverter.ToUInt16(new byte[2] { buffer[1], buffer[0] }, 0 );
buffer = new byte[length];
if (!await ReadToBuffer(cc.socket.GetStream(), buffer, length))
return;
await DoSomethingBasedOnReceived(messageBuffer, cc);
}
}
async Task<Boolean> ReadToBuffer(NetworkStream stream, byte[] buffer, int bytesToRead)
{
int offset = 0;
while (offset < bytesToRead)
{
var length = await stream.ReadAsync(buffer, offset, bytesToRead - offset);
if (length == 0)
return false;
offset += length;
}
return true;
}
public async Task DoSomethingBasedOnReceived(byte[] messageBuffer, ConnClient cc)
{
await SomeLogicToSetTheRRFlagIfMessageApplicable(messageBuffer, cc);
List<byte[]> ListOfMessagesToSend = SomeLogicToDetermineListOfMessages(messageBuffer);
await cc.SendListAsync(ListOfMessagesToSend);
}
}
ConnClient Class, representing an individual connected client.
public class ConnClient
{
public TcpClient socket { get; set; }
public Boolean ResponseReceived { get; set; }
public ConnClient (TcpClient cc)
{socket = cc}
public async Task SendListAsync(List<byte[]> messageList)
{
foreach (byte[] msg in messageList)
{
this.ResponseReceived = false;
await stream.WriteAsync(msg, 0, msg.Length);
int waitedSoFar = 0;
while (waitedSoFar < 5000)
{
if (this.ResponseReceived == true)
{
break;
}
waitedSoFar += 100;
await Task.Delay(100);
}
}
}
}
Your first problem is you will not be able to accept new clients.
while (true)
{
// accept the next connection
TcpClient client = await listener.AcceptTcpClientAsync();
// receive and send list
ConnClient cc = new ConnClient(client);
await Receive(ConnClient);
// the loop cannot continue to receive the next connection
// until you have done with your receive
}
You will need to execute Receive independently so you can wait for the next connection, you can either call it without an await (which will run as an async void), or offload it to a new task .
Remove the await
Receive(ConnClient);
Offloading
Task.Run(() => Receive(ConnClient));
Your second problem is your client is held up while sending and will not be able to receive. Once again you will to either offload, or run without the await.
As #PeterDuniho mentions
Given that the OP is already using async/await, and given that
Receive() is already async, there's no reason to use Task.Run(). It's
fire-and-forget either way (unless they change their code to store the
returned task), so they might as well just fire-and-forget the call to
Receive() as wrap it in a call to Task.Run().
Note : Creating a scalable client/server socket solution is not trivial, and i am not attempting to show this. However, it will solve your current problem.
Either way, be very mindful of errors. Since both the proposed solutions will run unobserved, exceptions will need to be handled

Proxying WebSocket messages between two streams

I have a HTTP proxy server which acts as a middle-man. It basically does the following:
Listen for client-browser request
Forward the request to the server
Parse the server response
Forward the response back to client-browser
So basically there is one NetworkStream, or even more often a SslStream between a client-browser and the proxy, and another one between the proxy and a server.
A requirement has arisen to also forward WebSocket traffic between a client and a server.
So now when a client-browser requests a connection upgrade to websocket, and the remote server responds with HTTP code 101, the proxy server maintains these connections in order to forward further messages from client to server and vice versa.
So after the proxy has received a message from the remote server saying it's ready to switch protocols, it needs to enter a loop where both client and server streams are polled for data, and where any received data is forwarded to the other party.
The problem
WebSocket allows both sides to send messages at any time. This is especially a problem with control messages such as ping/pong, where any side could send a ping at any time and the other side is expected to reply with a pong in a timely manner. Now consider having two instances of SslStream which don't have DataAvailable property, where the only way to read data is to call Read/ReadAsync which might not return until some data is available. Consider the following pseudo-code:
public async Task GetMessage()
{
// All these methods that we await read from the source stream
byte[] firstByte = await GetFirstByte(); // 1-byte buffer
byte[] messageLengthBytes = await GetMessageLengthBytes();
uint messageLength = GetMessageLength(messageLengthBytes);
bool isMessageMasked = DetermineIfMessageMasked(messageLengthBytes);
byte[] maskBytes;
if (isMessageMasked)
{
maskBytes = await GetMaskBytes();
}
byte[] messagePayload = await GetMessagePayload(messageLength);
// This method writes to the destination stream
await ComposeAndForwardMessageToOtherParty(firstByte, messageLengthBytes, maskBytes, messagePayload);
}
The above pseudo code reads from one stream and writes to the other. The problem is that the above procedure needs to be run for both streams simultaneously, because we don't know which side would send a message to the other at any given point in time. However, it is impossible to perform a write operation while there is a read operation active. And because we don't have the means necessary to poll for incoming data, read operations have to be blocking. That means if we start read operations for both streams at the same time, we can forget about writing to them. One stream will eventually return some data, but we won't be able to send that data to the other stream as it will still be busy trying to read. And that might take a while, at least until the side that owns that stream sends a ping request.
Thanks to comments from #MarcGravell we've learned that independent read/write operations are supported with network streams, i.e. NetworkStream acts as two independent pipes - one read, one write - it is fully duplex.
Therefore, proxying WebSocket messages can be as easy as just starting two independent tasks, one to read from client stream and write to server stream, and another to read from server stream and write to client stream.
If it can be of any help to anyone searching for it, here is how I implemented that:
public class WebSocketRequestHandler
{
private const int MaxMessageLength = 0x7FFFFFFF;
private const byte LengthBitMask = 0x7F;
private const byte MaskBitMask = 0x80;
private delegate Task WriteStreamAsyncDelegate(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
private delegate Task<byte[]> BufferStreamAsyncDelegate(int count, CancellationToken cancellationToken);
public async Task HandleWebSocketMessagesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var clientListener = ListenForClientMessages(cancellationToken);
var serverListener = ListenForServerMessages(cancellationToken);
await Task.WhenAll(clientListener, serverListener);
}
private async Task ListenForClientMessages(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
await ListenForMessages(YOUR_CLIENT_STREAM_BUFFER_METHOD_DELEGATE, YOUR_SERVER_STREAM_WRITE_METHOD_DELEGATE, cancellationToken);
}
}
private async Task ListenForServerMessages(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
await ListenForMessages(YOUR_SERVER_STREAM_BUFFER_METHOD_DELEGATE, YOUR_CLIENT_STREAM_WRITE_METHOD_DELEGATE, cancellationToken);
}
}
private static async Task ListenForMessages(BufferStreamAsyncDelegate sourceStreamReader,
WriteStreamAsyncDelegate destinationStreamWriter,
CancellationToken cancellationToken)
{
var messageBuilder = new List<byte>();
var firstByte = await sourceStreamReader(1, cancellationToken);
messageBuilder.AddRange(firstByte);
var lengthBytes = await GetLengthBytes(sourceStreamReader, cancellationToken);
messageBuilder.AddRange(lengthBytes);
var isMaskBitSet = (lengthBytes[0] & MaskBitMask) != 0;
var length = GetMessageLength(lengthBytes);
if (isMaskBitSet)
{
var maskBytes = await sourceStreamReader(4, cancellationToken);
messageBuilder.AddRange(maskBytes);
}
var messagePayloadBytes = await sourceStreamReader(length, cancellationToken);
messageBuilder.AddRange(messagePayloadBytes);
await destinationStreamWriter(messageBuilder.ToArray(), 0, messageBuilder.Count, cancellationToken);
}
private static async Task<byte[]> GetLengthBytes(BufferStreamAsyncDelegate sourceStreamReader, CancellationToken cancellationToken)
{
var lengthBytes = new List<byte>();
var firstLengthByte = await sourceStreamReader(1, cancellationToken);
lengthBytes.AddRange(firstLengthByte);
var lengthByteValue = firstLengthByte[0] & LengthBitMask;
if (lengthByteValue <= 125)
{
return lengthBytes.ToArray();
}
switch (lengthByteValue)
{
case 126:
{
var secondLengthBytes = await sourceStreamReader(2, cancellationToken);
lengthBytes.AddRange(secondLengthBytes);
return lengthBytes.ToArray();
}
case 127:
{
var secondLengthBytes = await sourceStreamReader(8, cancellationToken);
lengthBytes.AddRange(secondLengthBytes);
return lengthBytes.ToArray();
}
default:
throw new Exception($"Unexpected first length byte value: {lengthByteValue}");
}
}
private static int GetMessageLength(byte[] lengthBytes)
{
byte[] subArray;
switch (lengthBytes.Length)
{
case 1:
return lengthBytes[0] & LengthBitMask;
case 3:
if (!BitConverter.IsLittleEndian)
{
return BitConverter.ToUInt16(lengthBytes, 1);
}
subArray = lengthBytes.SubArray(1, 2);
Array.Reverse(subArray);
return BitConverter.ToUInt16(subArray, 0);
case 9:
subArray = lengthBytes.SubArray(1, 8);
Array.Reverse(subArray);
var retVal = BitConverter.ToUInt64(subArray, 0);
if (retVal > MaxMessageLength)
{
throw new Exception($"Unexpected payload length: {retVal}");
}
return (int) retVal;
default:
throw new Exception($"Impossibru!!1 The length of lengthBytes array was: '{lengthBytes.Length}'");
}
}
}
It can be used by just calling await handler.HandleWebSocketMessagesAsync(cancellationToken) after the initial handshake has been performed.
The SubArray method is taken from here: https://stackoverflow.com/a/943650/828023 (also from #Marc haha)

C# await event and timeout in serial port communication

Hi I have a simple communication on serial port well all is according to book and documentation so open port method looks like this:
public SerialPort OpenPort(string portName)
{
Port = new SerialPort(portName, BaudRate);
try
{
Port.Open();
Port.DtrEnable = true;
Port.RtsEnable = true;
Port.DataReceived += DataReceivedEvent;
}
catch (Exception e)
{
Console.WriteLine($"ERRROR: {e.Message}");
}
return Port;
}
Here we have an event on data read:
private async void DataReceivedEvent(object sender, SerialDataReceivedEventArgs e)
{
var data = new byte[Port.BytesToRead];
await Port.BaseStream.ReadAsync(data, 0, data.Length);
Response = data;
isFinished = true;
}
Well all is fine and dandy, but now i want to send a message on demand and store response in a property, also i want to add cancellation token on that task timeout. So i came up with this method:
public async Task SendMessenge(byte[] messange)
{
var cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
cancellationTokenSource.CancelAfter(5000);
token.ThrowIfCancellationRequested();
isFinished = false;
try
{
Task worker = Task.Run(() =>
{
while (!isFinished)
{
}
}, token);
await Port.BaseStream.WriteAsync(messange, 0, messange.Length, token);
await worker;
}
catch (OperationCanceledException e)
{
throw new OperationCanceledException(e.Message, e, token);
}
}
Problem is with this while loop, if it is task it goes into endless loop, and it does not capture timeout token, if i put it outside a task and remove worker it works but im loosing cancellation token. I guess i could do some manual countdown like:
double WaitTimeout = Timeout + DateAndTime.Now.TimeOfDay.TotalMilliseconds;
while (!(DateAndTime.Now.TimeOfDay.TotalMilliseconds >= WaitTimeout)|| !isFalse)
But it looks ugly.
So i think my basic question is how to effectively await for event to response and get a timeout?
Read data in a loop after write operation until get a full response. But you need to use synchronous API and Task.Run() as current version of the asynchronous API ignores SerialPort timeout properties completely and CancellationToken in Task based API almost completely.
Excerpt from the SerialPort.ReadTimeout Microsoft Docs that is relevant to SerialPort.BaseStream.ReadAsync() because it uses default implementation Stream.ReadAsync():
This property does not affect the BeginRead method of the stream returned by the BaseStream property.
Example implementation using synchronous API and dynamic timeout properties update:
static byte[] SendMessage(byte[] message, TimeSpan timeout)
{
// Use stopwatch to update SerialPort.ReadTimeout and SerialPort.WriteTimeout
// as we go.
var stopwatch = Stopwatch.StartNew();
// Organize critical section for logical operations using some standard .NET tool.
lock (_syncRoot)
{
var originalWriteTimeout = _serialPort.WriteTimeout;
var originalReadTimeout = _serialPort.ReadTimeout;
try
{
// Start logical request.
_serialPort.WriteTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0);
_serialPort.Write(message, 0, message.Length);
// Expected response length. Look for the constant value from
// the device communication protocol specification or extract
// from the response header (first response bytes) if there is
// any specified in the protocol.
int count = ...;
byte[] buffer = new byte[count];
int offset = 0;
// Loop until we recieve a full response.
while (count > 0)
{
_serialPort.ReadTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0);
var readCount = _serialPort.Read(buffer, offset, count);
offset += readCount;
count -= readCount;
}
return buffer;
}
finally
{
// Restore SerialPort state.
_serialPort.ReadTimeout = originalReadTimeout;
_serialPort.WriteTimeout = originalWriteTimeout;
}
}
}
And example usage:
byte[] request = ...;
TimeSpan timeout = ...;
var sendTask = Task.Run(() => SendMessage(request, timeout));
try
{
await await Task.WhenAny(sendTask, Task.Delay(timeout));
}
catch (TaskCanceledException)
{
throw new TimeoutException();
}
byte[] response = await sendTask;
You can do similar thing with CancellationToken instance and use CancellationToken.ThrowIfCancellationRequested() between read and write operations but you have to make sure that proper timeouts are set on SerialPort or otherwise Thread pool thread will hang forever possible holding a lock. As far as I know you can't utilize CancellationToken.Register() because there is no SerialPort method to call to cancel an operation.
For more information check:
Top 5 SerialPort Tips article by Kim Hamilton
Recommended asynchronous usage pattern of SerialPort, Document that CancellationToken in Stream.ReadAsync() is advisory and NetworkStream.ReadAsync/WriteAsync ignores CancellationToken related issues on .NET GitHub
Should I expose asynchronous wrappers for synchronous methods? article by Stephen Toub

How to redirect Request.Content stream

I trying to copy an incoming HTTP stream into another stream and use that instead.
When I use Request.Content.ReadAsStreamAsync().Result directly, everything works fine.
The problem that I am facing is how to "channel" the original stream thru another one.
I wrote the flowing method, but the problem is that it either blocks until all the stream data has arrived (when I use Wait()), or it returns immediately and exists without capturing any data.
private Task<Stream> GetAudioStream(RecordingSession recordingSession)
{
Task<Stream> task = Task<Stream>.Factory.StartNew(() =>
{
Request.Content.ReadAsStreamAsync().Result.CopyToAsync(recordingSession.AudioStream).Wait();
return recordingSession.AudioStream;
});
return task;
}
Perhaps, you tackle the problem from the wrong side. CopyToAsync gives you a complete copy of the stream, the Task returned by CopyToAsync is completed when all data has been copied.
If you don't need the whole complete copy of the stream, or don't want to wait for all data, then copy it manually yourself and process each chunk:
static async Task<Stream> CopyAndProcessAsync(RecordingSession recordingSession, CancellationToken token)
{
var srcStream = await Request.Content.ReadAsStreamAsync();
var dstStream = recordingSession.AudioStream;
var buffer = new byte[4096];
int bytesRead;
while ((bytesRead = await srcStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false)) != 0)
{
await dstStream.WriteAsync(buffer, 0, bytesRead, token).ConfigureAwait(false);
// do whatever you want with the chunk
PlaybackChunk(buffer);
}
return dstStream;
}
Do this wherever you'd otherwise be consuming the result of your GetAudioStream. It well may turn out that you don't need a copy at all, when you've processed each chunk.
I think you mean something like this:
private async Task<Stream> GetAudioStream(RecordingSession recordingSession)
{
var result = await Request.Content.ReadAsStreamAsync();
await result.CopyToAsync(recordingSession.AudioStream);
return recordingSession.AudioStream;
}
async methods return Tasks. Very rarely should a non-async method return a Task.

Working with System.Threading.Tasks.Task<Stream> instead of Stream

I was using a method like below on the previous versions of WCF Web API:
// grab the posted stream
Stream stream = request.Content.ContentReadStream;
// write it to
using (FileStream fileStream = File.Create(fullFileName, (int)stream.Length)) {
byte[] bytesInStream = new byte[stream.Length];
stream.Read(bytesInStream, 0, (int)bytesInStream.Length);
fileStream.Write(bytesInStream, 0, bytesInStream.Length);
}
But on the preview 6, HttpRequestMessage.Content.ContentReadStream property is gone. I believe that it now should look like this one:
// grab the posted stream
System.Threading.Tasks.Task<Stream> stream = request.Content.ReadAsStreamAsync();
But I couldn't figure out what the rest of the code should be like inside the using statement. Can anyone provide me a way of doing it?
You might have to adjust this depending on what code is happening before/after, and there's no error handling, but something like this:
Task task = request.Content.ReadAsStreamAsync().ContinueWith(t =>
{
var stream = t.Result;
using (FileStream fileStream = File.Create(fullFileName, (int) stream.Length))
{
byte[] bytesInStream = new byte[stream.Length];
stream.Read(bytesInStream, 0, (int) bytesInStream.Length);
fileStream.Write(bytesInStream, 0, bytesInStream.Length);
}
});
If, later in your code, you need to ensure that this has completed, you can call task.Wait() and it will block until this has completed (or thrown an exception).
I highly recommend Stephen Toub's Patterns of Parallel Programming to get up to speed on some of the new async patterns (tasks, data parallelism etc) in .NET 4.
Quick and dirty fix:
// grab the posted stream
Task<Stream> streamTask = request.Content.ReadAsStreamAsync();
Stream stream = streamTask.Result; //blocks until Task is completed
Be aware that the fact that the sync version has been removed from the API suggests that you should really be attempting to learn the new async paradigms to avoid gobbling up many threads under high load.
You could for instance:
streamTask.ContinueWith( _ => {
var stream = streamTask.Result; //result already available, so no blocking
//work with stream here
} )
or with new async await features:
//async wait until task is complete
var stream = await request.Content.ReadAsStreamAsync();
Take time to learn async/await. It's pretty handy.
Here is how you can do this better with async and await:
private async void WhatEverMethod()
{
var stream = await response.Content.ReadAsStreamAsync();
using (FileStream fileStream = File.Create(fullFileName, (int)stream.Length))
{
byte[] bytesInStream = new byte[stream.Length];
stream.Read(bytesInStream, 0, (int)bytesInStream.Length);
fileStream.Write(bytesInStream, 0, bytesInStream.Length);
}
});

Categories

Resources