public void Dispose() { if (Disposed) { return; } Disposed = true; SendResult?.TrySetCanceled(); DataStreamResponse?.Dispose(); DataStream?.Dispose(); ReceiveWaiter.Set(); DataStreamResponse = null; DataStream = null; }
public async ReusableTask <int> SendAsync(ByteBuffer buffer, int offset, int count) { SendResult = new TaskCompletionSource <object> (); List <RequestMessage> bundle = DecodeMessages(buffer, offset, count); if (bundle.Count > 0) { RequestMessages.AddRange(bundle); // The RequestMessages are always sequential RequestMessage start = bundle[0]; RequestMessage end = bundle[bundle.Count - 1]; CreateWebRequests(start, end); } else { return(count); } ReceiveWaiter.Set(); await SendResult.Task; return(count); }
public async ReusableTask <int> ReceiveAsync(ByteBuffer buffer, int offset, int count) { // This is a little tricky, so let's spell it out in comments... if (Disposed) { throw new OperationCanceledException(); } // If this is the first time ReceiveAsync is invoked, then we should get the first PieceMessage from the queue if (CurrentRequest == null) { // When we call 'SendAsync' with request piece messages, we add them to the list and then toggle the handle. // When this returns it means we have requests ready to go! await Task.Run(() => ReceiveWaiter.WaitOne()); if (Disposed) { throw new OperationCanceledException(); } // Grab the request. We know the 'SendAsync' call won't return until we process all the queued requests, so // this is threadsafe now. CurrentRequest = new HttpRequestData(RequestMessages[0]); RequestMessages.RemoveAt(0); } // If we have not sent the length header for this message, send it now if (!CurrentRequest.SentLength) { // The message length counts as the first four bytes CurrentRequest.SentLength = true; CurrentRequest.TotalReceived += 4; Message.Write(buffer.Data, offset, CurrentRequest.TotalToReceive - CurrentRequest.TotalReceived); return(4); } // Once we've sent the length header, the next thing we need to send is the metadata for the 'Piece' message int written = 0; if (!CurrentRequest.SentHeader) { CurrentRequest.SentHeader = true; // We have *only* written the messageLength to the stream // Now we need to write the rest of the PieceMessage header written += Message.Write(buffer.Data, offset + written, PieceMessage.MessageId); written += Message.Write(buffer.Data, offset + written, CurrentRequest.Request.PieceIndex); written += Message.Write(buffer.Data, offset + written, CurrentRequest.Request.StartOffset); count -= written; offset += written; CurrentRequest.TotalReceived += written; } // Once we have sent the message length, and the metadata, we now need to add the actual data from the HTTP server. // If we have already connected to the server then DataStream will be non-null and we can just read the next bunch // of data from it. if (DataStream != null) { int result = await DataStream.ReadAsync(buffer.Data, offset, count); DataStreamCount -= result; // If result is zero it means we've read the last data from the stream. if (result == 0) { using (DataStreamResponse) DataStream.Dispose(); DataStreamResponse = null; DataStream = null; // If we requested more data (via the range header) than we were actually given, then it's a truncated // stream and we can give up immediately. if (DataStreamCount > 0) { throw new WebException("Unexpected end of stream"); } } else { // Otherwise if we have received non-zero data we can accumulate that! CurrentRequest.TotalReceived += result; // If the request is complete we should dequeue the next RequestMessage so we can process // that the next ReceiveAsync is invoked. Otherwise, if we have processed all the queued // messages we can mark the 'SendAsync' as complete and we can wait for the piece picker // to add more requests for us to process. if (CurrentRequest.Complete) { if (RequestMessages.Count > 0) { CurrentRequest = new HttpRequestData(RequestMessages[0]); RequestMessages.RemoveAt(0); } else { using (DataStreamResponse) DataStream.Dispose(); DataStreamResponse = null; DataStream = null; CurrentRequest = null; SendResult.TrySetResult(null); } } return(result + written); } } // Finally, if we have had no datastream what we need to do is execute the next web request in our list, // and then begin reading data from that stream. while (WebRequests.Count > 0) { KeyValuePair <WebRequest, int> r = WebRequests.Dequeue(); using var cts = new CancellationTokenSource(ConnectionTimeout); using (cts.Token.Register(() => r.Key.Abort())) { DataStreamResponse = await r.Key.GetResponseAsync(); DataStream = DataStreamResponse.GetResponseStream(); DataStreamCount = r.Value; return(await ReceiveAsync(buffer, offset, count) + written); } } // If we reach this point it means that we processed all webrequests and still ended up receiving *less* data than we required, // and we did not throw an unexpected end of stream exception. throw new WebException("Unable to download the required data from the server"); }