3535using System . Threading ;
3636using System . Threading . Tasks ;
3737
38+ using MonoTorrent . Logging ;
3839using MonoTorrent . Messages ;
3940using MonoTorrent . Messages . Peer ;
4041
@@ -44,6 +45,8 @@ namespace MonoTorrent.Connections.Peer
4445{
4546 sealed class HttpPeerConnection : IPeerConnection
4647 {
48+ static readonly Logger Log = Logger . Create ( nameof ( HttpPeerConnection ) ) ;
49+
4750 static readonly Uri PaddingFileUri = new Uri ( "http://__paddingfile__" ) ;
4851
4952 class HttpRequestData
@@ -305,17 +308,20 @@ public async ReusableTask<int> SendAsync (Memory<byte> socketBuffer)
305308 {
306309 SendResult = new TaskCompletionSource < object ? > ( ) ;
307310
311+ var info = TorrentData . TorrentInfo ;
308312 List < BlockInfo > bundle = DecodeMessages ( socketBuffer . Span ) ;
309- if ( bundle . Count > 0 ) {
310- RequestMessages . AddRange ( bundle ) ;
311- // The RequestMessages are always sequential
312- BlockInfo start = RequestMessages [ 0 ] ;
313- BlockInfo end = RequestMessages [ RequestMessages . Count - 1 ] ;
314- CreateWebRequests ( start , end ) ;
315- } else {
313+
314+ // If the messages in the send buffer are anything *other* than piece request messages,
315+ // just pretend the bytes were sent and everything was fine.
316+ if ( bundle . Count == 0 || info == null )
316317 return socketBuffer . Length ;
317- }
318318
319+ // Otherwise, if there were 1 or more piece request messages, convert these to HTTP requests.
320+ // and only mark the bytes as successfully sent after all webrequests have run to completion
321+ // and the data has been received.
322+ RequestMessages . AddRange ( bundle ) ;
323+ ValidateWebRequests ( info , RequestMessages . ToArray ( ) ) ;
324+ CreateWebRequestsForSequentialRange ( RequestMessages [ 0 ] , RequestMessages [ RequestMessages . Count - 1 ] ) ;
319325 ReceiveWaiter . Set ( ) ;
320326 await SendResult . Task ;
321327 return socketBuffer . Length ;
@@ -334,8 +340,26 @@ static List<BlockInfo> DecodeMessages (ReadOnlySpan<byte> buffer)
334340 return messages ;
335341 }
336342
343+ static void ValidateWebRequests ( ITorrentInfo torrentInfo , BlockInfo [ ] blocks )
344+ {
345+ if ( blocks . Length > 0 ) {
346+ BlockInfo startBlock = blocks [ 0 ] ;
347+ BlockInfo currentBlock = startBlock ;
348+ for ( int i = 1 ; i < blocks . Length ; i ++ ) {
349+ BlockInfo previousBlock = blocks [ i - 1 ] ;
350+ currentBlock = blocks [ i ] ;
351+ long endOffsetOfPreviousEndBlock = torrentInfo . PieceIndexToByteOffset ( previousBlock . PieceIndex ) + previousBlock . StartOffset + previousBlock . RequestLength ;
352+ long startOffsetOfCurrentBlock = torrentInfo . PieceIndexToByteOffset ( currentBlock . PieceIndex ) + currentBlock . StartOffset ;
353+ if ( endOffsetOfPreviousEndBlock != startOffsetOfCurrentBlock ) {
354+ var msg = "Piece requests made from HTTP peers must be a strictly sequential range of blocks. The range must be 1 or more blocks long." ;
355+ Log . Error ( msg ) ;
356+ throw new InvalidOperationException ( msg ) ;
357+ }
358+ }
359+ }
360+ }
337361
338- void CreateWebRequests ( BlockInfo start , BlockInfo end )
362+ void CreateWebRequestsForSequentialRange ( BlockInfo start , BlockInfo end )
339363 {
340364 Uri uri = Uri ;
341365
@@ -354,11 +378,10 @@ void CreateWebRequests (BlockInfo start, BlockInfo end)
354378 if ( count == 0 )
355379 break ;
356380
357- var lengthWithPadding = file . Length + file . Padding ;
358381 // If the first byte of data is from the next file, move to the next file immediately
359382 // and adjust start offset to be relative to that file.
360- if ( startOffset >= lengthWithPadding ) {
361- startOffset -= lengthWithPadding ;
383+ if ( startOffset >= file . Length + file . Padding ) {
384+ startOffset -= file . Length + file . Padding ;
362385 continue ;
363386 }
364387
0 commit comments