{
  $Project$
  $Workfile$
  $Revision$
  $DateUTC$
  $Id$

  This file is part of the Indy (Internet Direct) project, and is offered
  under the dual-licensing agreement described on the Indy website.
  (http://www.indyproject.org/)

  Copyright:
   (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
  $Log$
}
{
  Rev 1.21    3/10/05 3:24:30 PM  RLebeau
  Updated ReadFromSource() and WriteDirect() to access the Intercept property
  directly.

  Rev 1.20    10/21/2004 11:07:30 PM  BGooijen
  works in win32 now too

  Rev 1.19    10/21/2004 1:52:56 PM  BGooijen
  Raid 214235

  Rev 1.18    7/23/04 6:20:52 PM  RLebeau
  Removed memory leaks in Send/ReceiveStream property setters

  Rev 1.17    2004.05.20 11:39:08 AM  czhower
  IdStreamVCL

  Rev 1.16    23/04/2004 20:29:36  CCostelloe
  Minor change to support IdMessageClient's new TIdIOHandlerStreamMsg

  Rev 1.15    2004.04.16 11:30:32 PM  czhower
  Size fix to IdBuffer, optimizations, and memory leaks

  Rev 1.14    2004.04.08 3:56:36 PM  czhower
  Fixed bug with Intercept byte count. Also removed Bytes from Buffer.

  Rev 1.13    2004.03.07 11:48:46 AM  czhower
  Flushbuffer fix + other minor ones found

  Rev 1.12    2004.03.03 11:55:04 AM  czhower
  IdStream change

  Rev 1.11    2004.02.03 4:17:16 PM  czhower
  For unit name changes.

  Rev 1.10    11/01/2004 19:52:44  CCostelloe
  Revisions for TIdMessage SaveToFile & LoadFromFile for D7 & D8

  Rev 1.8    08/01/2004 23:37:16  CCostelloe
  Minor changes

  Rev 1.7    1/8/2004 1:01:22 PM  BGooijen
  Cleaned up

  Rev 1.6    1/8/2004 4:23:06 AM  BGooijen
  temp fixed TIdIOHandlerStream.WriteToDestination

  Rev 1.5    08/01/2004 00:25:22  CCostelloe
  Start of reimplementing LoadFrom/SaveToFile

  Rev 1.4    2003.12.31 7:44:54 PM  czhower
  Matched constructors visibility to ancestor.

  Rev 1.3    2003.10.24 10:44:54 AM  czhower
  IdStream implementation, bug fixes.

  Rev 1.2    2003.10.14 11:19:14 PM  czhower
  Updated for better functionality.

  Rev 1.1    2003.10.14 1:27:14 PM  czhower
  Uupdates + Intercept support

  Rev 1.0    2003.10.13 6:40:40 PM  czhower
  Moved from root

  Rev 1.9    2003.10.11 10:00:36 PM  czhower
  Compiles again.

  Rev 1.8    10/10/2003 10:53:42 PM  BGooijen
  Changed const-ness of some methods to reflect base class changes

  Rev 1.7    7/10/2003 6:07:58 PM  SGrobety
  .net

  Rev 1.6    17/07/2003 00:01:24  CCostelloe
  Added (empty) procedures for the base classes' abstract CheckForDataOnSource
  and CheckForDisconnect

    Rev 1.5    7/1/2003 12:45:56 PM  BGooijen
  changed FInputBuffer.Size := 0 to FInputBuffer.Clear

  Rev 1.4    12-8-2002 21:05:28  BGooijen
  Removed call to Close in .Destroy, this is already done in
  TIdIOHandler.Destroy

  Rev 1.3    12/7/2002 06:42:44 PM  JPMugaas
  These should now compile except for Socks server.  IPVersion has to be a
  property someplace for that.

  Rev 1.2    12/5/2002 02:53:52 PM  JPMugaas
  Updated for new API definitions.

  Rev 1.1    05/12/2002 15:29:16  AO'Neill

  Rev 1.0    11/13/2002 07:55:08 AM  JPMugaas
}

unit IdIOHandlerStream;

interface

uses
  IdBaseComponent,
  IdGlobal,
  IdIOHandler,
  IdObjs,
  IdStream,
  IdSys;

type
  TIdIOHandlerStream = class;
  TIdIOHandlerStreamType = (stRead, stWrite, stReadWrite);
  TIdOnGetStreams = procedure(
    ASender: TIdIOHandlerStream;
    var VReceiveStream: TIdStream;
    var VSendStream: TIdStream
    ) of object;

  TIdIOHandlerStream = class(TIdIOHandler)
  protected
    FFreeStreams: Boolean;
    FOnGetStreams: TIdOnGetStreams;
    FReceiveStream: TIdStream;
    FSendStream: TIdStream;
    FStreamType: TIdIOHandlerStreamType;
    //
    function GetReceiveStream: TIdStream;
    function GetSendStream: TIdStream;
    procedure SetReceiveStream(AStream: TIdStream);
    procedure SetSendStream(AStream: TIdStream);
    function ReadFromSource(
      ARaiseExceptionIfDisconnected: Boolean = True;
      ATimeout: Integer = IdTimeoutDefault;
      ARaiseExceptionOnTimeout: Boolean = True
      ): Integer; override;
  public
    procedure CheckForDataOnSource(
      ATimeout: Integer = 0
      ); override;
    procedure CheckForDisconnect(
      ARaiseExceptionIfDisconnected: Boolean = True;
      AIgnoreBuffer: Boolean = False
      ); override;

    constructor Create(
      AOwner: TIdNativeComponent;
      AReceiveStream: TIdStream;
      ASendStream: TIdStream = nil
      ); reintroduce; overload; virtual;
    constructor Create(
      AOwner: TIdNativeComponent
      ); reintroduce; overload;
    function Connected
      : Boolean;
      override;
    procedure Close;
      override;
    procedure Open;
      override;
    function Readable(AMSec: integer = IdTimeoutDefault): boolean; override;
    procedure WriteDirect(var ABuffer: TIdBytes); override;
    //
    property ReceiveStream: TIdStream read GetReceiveStream {write SetReceiveStream};
    property SendStream: TIdStream read GetSendStream {write SetSendStream};
  published
    property FreeStreams: Boolean read FFreeStreams write FFreeStreams;
    property StreamType: TIdIOHandlerStreamType read FStreamType
     write FStreamType;
    //
    property OnGetStreams: TIdOnGetStreams read FOnGetStreams
     write FOnGetStreams;
  end;

implementation

{ TIdIOHandlerStream }

procedure TIdIOHandlerStream.CheckForDataOnSource(ATimeout: Integer = 0);
begin
  if Connected then begin
    ReadFromSource(False, ATimeout, False);
  end;
end;

procedure TIdIOHandlerStream.CheckForDisconnect(
  ARaiseExceptionIfDisconnected: Boolean = True;
  AIgnoreBuffer: Boolean = False);
begin
  FClosedGracefully := (FSendStream = nil) and (FReceiveStream = nil);
  if FClosedGracefully and ARaiseExceptionIfDisconnected then begin
    RaiseConnClosedGracefully;
  end;
end;

procedure TIdIOHandlerStream.Close;
begin
  inherited Close;
  if FreeStreams then begin
    Sys.FreeAndNil(FReceiveStream);
    Sys.FreeAndNil(FSendStream);
  end;
end;

function TIdIOHandlerStream.Connected: Boolean;
begin
  Result := False;  // Just to avoid warning message
  case FStreamType of
    stRead: Result := ReceiveStream <> nil;
    stWrite: Result := SendStream <> nil;
    stReadWrite: Result := (ReceiveStream <> nil) and (SendStream <> nil);
  end;
end;

constructor TIdIOHandlerStream.Create( AOwner: TIdNativeComponent );
begin
  inherited Create(AOwner);
  FFreeStreams := True;
  //
  FStreamType := stReadWrite;
end;

constructor TIdIOHandlerStream.Create(
  AOwner: TIdNativeComponent;
  AReceiveStream: TIdStream;
  ASendStream: TIdStream = nil
  );
begin
  inherited Create(AOwner);
  FFreeStreams := True;
  //
  FStreamType := stReadWrite;
  if (AReceiveStream <> nil) and (ASendStream = nil) then begin
    FStreamType := stWrite;
  end else if (AReceiveStream = nil) and (ASendStream <> nil) then begin
    FStreamType := stRead;
  end;
  SetReceiveStream(AReceiveStream);
  SetSendStream(ASendStream);
end;

procedure TIdIOHandlerStream.Open;
begin
  inherited Open;
  if Assigned(OnGetStreams) then begin
    OnGetStreams(Self, FReceiveStream, FSendStream);
  end;
end;

function TIdIOHandlerStream.Readable(AMSec: Integer): Boolean;
begin
  Result := ReceiveStream <> nil;
  if Result then begin
    Result := ReceiveStream.Position < ReceiveStream.Size;
  end;
end;

function TIdIOHandlerStream.ReadFromSource(
  ARaiseExceptionIfDisconnected: Boolean; ATimeout: Integer;
  ARaiseExceptionOnTimeout: Boolean): Integer;
var
  LBuffer: TIdBytes;
begin
  Result := 0;
  if ReceiveStream <> nil then begin
    // We dont want to read the whole stream in at a time. If its a big file will consume way too
    // much memory by loading it all at once. So lets read it in chunks.
    Result := Min(32 * 1024, FReceiveStream.Size - FReceiveStream.Position);
    if Result > 0 then begin
      SetLength(LBuffer, Result);
      TIdStreamHelper.ReadBytes(FReceiveStream, LBuffer, Result);
      if Intercept <> nil then begin
        Intercept.Receive(LBuffer);
        Result := Length(LBuffer);
      end;
      if Result > 0 then begin
        FInputBuffer.Write(LBuffer);
      end;
    end;
  end else begin
    FInputBuffer.Clear;
  end;
end;

procedure TIdIOHandlerStream.WriteDirect(var ABuffer: TIdBytes);
begin
  inherited WriteDirect(ABuffer);
  if FSendStream <> nil then begin
    TIdStreamHelper.Write(FSendStream, ABuffer, Length(ABuffer));
  end;
end;

function TIdIOHandlerStream.GetReceiveStream: TIdStream;
begin
  Result := FReceiveStream;
end;

function TIdIOHandlerStream.GetSendStream: TIdStream;
begin
  Result := FSendStream;
end;

procedure TIdIOHandlerStream.SetReceiveStream(AStream: TIdStream);
begin
  if AStream <> nil then begin
    FReceiveStream := AStream;
  end;
end;

procedure TIdIOHandlerStream.SetSendStream(AStream: TIdStream);
begin
  if AStream <> nil then begin
    FSendStream := AStream;
  end;
end;

end.

