View difference between Paste ID: 5uyuuL1V and SFHU3hYR
SHOW: | | - or go back to the newest paste.
1
type
2
 {
3
  Default TIdIOHandler implementation hangs in ReadBytes until it reads at least
4
  RecvBufferSize, see:
5
    http://stackoverflow.com/questions/15615347/http-continuous-packeted-stream-with-indy
6
  This is unacceptable with event streams as events have to be handled timely
7
  as they received.
8
 }
9
  TIdStreamIoHandler = class(TIdIOHandlerStack)
10
  public
11
    function TryReadBytes(var VBuffer: TIdBytes; AByteCount: Integer;
12
      AAppend: Boolean = True): integer; virtual;
13
    procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1;
14
     AReadUntilDisconnect: Boolean = False); override;
15
  end;
16
17
18
//Reads up to AByteCount bytes in one go. Returns the number of bytes read, >0.
19
//Copied from ReadBytes with some modifications.
20
function TIdStreamIOHandler.TryReadBytes(var VBuffer: TIdBytes; AByteCount: Integer;
21
  AAppend: Boolean = True): integer;
22
begin
23
  Assert(FInputBuffer<>nil);
24
  if AByteCount > 0 then begin
25
    // Read from stack if we have not enough data
26
    if FInputBuffer.Size < AByteCount then begin
27
      ReadFromSource(False); //whatever we can
28
      CheckForDisconnect(True, True);
29
    end;
30
    Result := IndyMin(AByteCount, FInputBuffer.Size);
31
    FInputBuffer.ExtractToBytes(VBuffer, Result, AAppend);
32
  end else if AByteCount < 0 then begin
33
    ReadFromSource(False, ReadTimeout, False);
34
    CheckForDisconnect(True, True);
35
    Result := FInputBuffer.Size;
36
    FInputBuffer.ExtractToBytes(VBuffer, Result, AAppend);
37
  end else
38
    Result := 0;
39
end;
40
41
{ Copied from TIdIOHandler.ReadStream with adjustments and simplifications.
42
ReadUntilDisconnect: Read all data until the connection closes.
43
  Data is processed in RecvBufferSize chunks, or in how much is available.
44
AByteCount>0 + !ReadUntilDisconnect: Read up to AByteCount in one go.
45
AByteCount<0 + !ReadUntilDisconnect: Read all available data in one go.
46
}
47
procedure TIdStreamIOHandler.ReadStream(AStream: TStream; AByteCount: TIdStreamSize;
48
  AReadUntilDisconnect: Boolean);
49
var
50
  i: Integer;
51
  LBuf: TIdBytes;
52
  LByteCount: TIdStreamSize;
53
begin
54
  Assert(AStream<>nil);
55
  LByteCount := AByteCount;
56
57
  if AReadUntilDisconnect and (AByteCount>0) then
58
    AByteCount := -1;
59
60
  if AReadUntilDisconnect then begin
61
    BeginWork(wmRead);
62
  end else begin
63
    BeginWork(wmRead, LByteCount);
64
  end;
65
66
  try
67
    // If data already exists in the buffer, write it out first.
68
    // should this loop for all data in buffer up to workcount? not just one block?
69
    if FInputBuffer.Size > 0 then begin
70
      if AByteCount<0 then begin
71
        i := FInputBuffer.Size;
72
      end else begin
73
        i := IndyMin(FInputBuffer.Size, LByteCount);
74
        Dec(LByteCount, i);
75
      end;
76
      FInputBuffer.ExtractToStream(AStream, i);
77
    end;
78
79
    // RLebeau - don't call Connected() here!  ReadBytes() already
80
    // does that internally. Calling Connected() here can cause an
81
    // EIdConnClosedGracefully exception that breaks the loop
82
    // prematurely and thus leave unread bytes in the InputBuffer.
83
    // Let the loop catch the exception before exiting...
84
85
    SetLength(LBuf, RecvBufferSize); // preallocate the buffer
86
    repeat
87
88
      if AByteCount<0 then begin
89
        i := -1;
90
      end else begin
91
        i := IndyMin(LByteCount, RecvBufferSize);
92
        if i < 1 then break;
93
      end;
94
95
      try
96
        try
97
          i := TryReadBytes(LBuf, i, False);
98
        except
99
          on E: Exception do begin
100
            // RLebeau - ReadFromSource() inside of ReadBytes()
101
            // could have filled the InputBuffer with more bytes
102
            // than actually requested, so don't extract too
103
            // many bytes here...
104
            if i>0 then //else it's already -1 or 0
105
              i := IndyMin(i, FInputBuffer.Size);
106
            FInputBuffer.ExtractToBytes(LBuf, i, False);
107
            if (E is EIdConnClosedGracefully) and AReadUntilDisconnect then
108
              break
109
            else
110
              raise;
111
          end;
112
        end;
113
        TIdAntiFreezeBase.DoProcess;
114
      finally
115
        if i > 0 then begin //how much was read
116
          TIdStreamHelper.Write(AStream, LBuf, i);
117
          if not AReadUntilDisconnect then
118
            Dec(LByteCount, i);
119
        end;
120
      end;
121
122
    until false;
123
124
  finally
125
    EndWork(wmRead);
126
    LBuf := nil;
127
  end;
128
end;