Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- type
- {
- Default TIdIOHandler implementation hangs in ReadBytes until it reads at least
- RecvBufferSize, see:
- http://stackoverflow.com/questions/15615347/http-continuous-packeted-stream-with-indy
- This is unacceptable with event streams as events have to be handled timely
- as they received.
- }
- TIdStreamIoHandler = class(TIdIOHandlerStack)
- public
- function TryReadBytes(var VBuffer: TIdBytes; AByteCount: Integer;
- AAppend: Boolean = True): integer; virtual;
- procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1;
- AReadUntilDisconnect: Boolean = False); override;
- end;
- //Reads up to AByteCount bytes in one go. Returns the number of bytes read, >0.
- //Copied from ReadBytes with some modifications.
- function TIdStreamIOHandler.TryReadBytes(var VBuffer: TIdBytes; AByteCount: Integer;
- AAppend: Boolean = True): integer;
- begin
- Assert(FInputBuffer<>nil);
- if AByteCount > 0 then begin
- // Read from stack if we have not enough data
- if FInputBuffer.Size < AByteCount then begin
- ReadFromSource(False); //whatever we can
- CheckForDisconnect(True, True);
- end;
- Result := IndyMin(AByteCount, FInputBuffer.Size);
- FInputBuffer.ExtractToBytes(VBuffer, Result, AAppend);
- end else if AByteCount < 0 then begin
- ReadFromSource(False, ReadTimeout, False);
- CheckForDisconnect(True, True);
- Result := FInputBuffer.Size;
- FInputBuffer.ExtractToBytes(VBuffer, Result, AAppend);
- end else
- Result := 0;
- end;
- { Copied from TIdIOHandler.ReadStream with adjustments and simplifications.
- ReadUntilDisconnect: Read all data until the connection closes.
- Data is processed in RecvBufferSize chunks, or in how much is available.
- AByteCount>0 + !ReadUntilDisconnect: Read up to AByteCount in one go.
- AByteCount<0 + !ReadUntilDisconnect: Read all available data in one go.
- }
- procedure TIdStreamIOHandler.ReadStream(AStream: TStream; AByteCount: TIdStreamSize;
- AReadUntilDisconnect: Boolean);
- var
- i: Integer;
- LBuf: TIdBytes;
- LByteCount: TIdStreamSize;
- begin
- Assert(AStream<>nil);
- LByteCount := AByteCount;
- if AReadUntilDisconnect and (AByteCount>0) then
- AByteCount := -1;
- if AReadUntilDisconnect then begin
- BeginWork(wmRead);
- end else begin
- BeginWork(wmRead, LByteCount);
- end;
- try
- // If data already exists in the buffer, write it out first.
- // should this loop for all data in buffer up to workcount? not just one block?
- if FInputBuffer.Size > 0 then begin
- if AByteCount<0 then begin
- i := FInputBuffer.Size;
- end else begin
- i := IndyMin(FInputBuffer.Size, LByteCount);
- Dec(LByteCount, i);
- end;
- FInputBuffer.ExtractToStream(AStream, i);
- end;
- // RLebeau - don't call Connected() here! ReadBytes() already
- // does that internally. Calling Connected() here can cause an
- // EIdConnClosedGracefully exception that breaks the loop
- // prematurely and thus leave unread bytes in the InputBuffer.
- // Let the loop catch the exception before exiting...
- SetLength(LBuf, RecvBufferSize); // preallocate the buffer
- repeat
- if AByteCount<0 then begin
- i := -1;
- end else begin
- i := IndyMin(LByteCount, RecvBufferSize);
- if i < 1 then break;
- end;
- try
- try
- i := TryReadBytes(LBuf, i, False);
- except
- on E: Exception do begin
- // RLebeau - ReadFromSource() inside of ReadBytes()
- // could have filled the InputBuffer with more bytes
- // than actually requested, so don't extract too
- // many bytes here...
- if i>0 then //else it's already -1 or 0
- i := IndyMin(i, FInputBuffer.Size);
- FInputBuffer.ExtractToBytes(LBuf, i, False);
- if (E is EIdConnClosedGracefully) and AReadUntilDisconnect then
- break
- else
- raise;
- end;
- end;
- TIdAntiFreezeBase.DoProcess;
- finally
- if i > 0 then begin //how much was read
- TIdStreamHelper.Write(AStream, LBuf, i);
- if not AReadUntilDisconnect then
- Dec(LByteCount, i);
- end;
- end;
- until false;
- finally
- EndWork(wmRead);
- LBuf := nil;
- end;
- end;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement