{ $HDR$} {**********************************************************************} { Unit archived using Team Coherence } { Team Coherence is Copyright 2002 by Quality Software Components } { } { For further information / comments, visit our WEB site at } { http://www.TeamCoherence.com } {**********************************************************************} {} { $Log: 13890: IdHTTPProxyServer.pas { { Rev 1.4 10/14/2004 2:07:40 PM BGooijen { changed WriteHeader to WriteStrings } { { Rev 1.3 10/14/2004 1:44:04 PM BGooijen { reverted back to 1.1 } { { Rev 1.1 10/14/2004 1:42:00 PM BGooijen { Ported back from I10 } { { Rev 1.0 2002.11.22 8:37:16 PM czhower } unit IdHTTPProxyServer; interface { Indy HTTP proxy Server Original Programmer: Bas Gooijen (bas_gooijen@yahoo.com) Current Maintainer: Bas Gooijen Code is given to the Indy Pit Crew. Modifications by Chad Z. Hower (Kudzu) Quick Notes: Revision History: 10-May-2002: Created Unit. } uses Classes, IdAssignedNumbers, IdGlobal, IdTCPConnection, IdTCPServer, IdHeaderList; const IdPORT_HTTPProxy = 8080; type { not needed (yet) TIdHTTPProxyServerThread = class( TIdPeerThread ) protected // what needs to be stored... fUser: string; fPassword: string; public constructor Create( ACreateSuspended: Boolean = True ) ; override; destructor Destroy; override; // Any functions for vars property Username: string read fUser write fUser; property Password: string read fPassword write fPassword; end; } TIdHTTPProxyServer = class; TOnHTTPDocument = procedure(ASender: TIdHTTPProxyServer; const ADocument: string; var VStream: TStream; const AHeaders: TIdHeaderList) of object; TIdHTTPProxyServer = class(TIdTcpServer) protected FOnHTTPDocument: TOnHTTPDocument; // CommandHandlers procedure CommandGET(ASender: TIdCommand); procedure CommandPOST(ASender: TIdCommand); procedure CommandHEAD(ASender: TIdCommand); procedure CommandConnect(ASender: TIdCommand); // for ssl procedure DoHTTPDocument(const ADocument: string; var VStream: TStream; const AHeaders: TIdHeaderList); procedure InitializeCommandHandlers; override; procedure TransferData(ASrc: TIdTCPConnection; ADest: TIdTCPConnection; const ADocument: string; const ASize: Integer; const AHeaders: TIdHeaderList); public constructor Create( AOwner: TComponent ) ; override; published property DefaultPort default IdPORT_HTTPProxy; property OnHTTPDocument: TOnHTTPDocument read FOnHTTPDocument write FOnHTTPDocument; end; // Procs procedure Register; implementation uses IdResourceStrings, IdRFCReply, IdTCPClient, IdURI, SysUtils; procedure Register; begin RegisterComponents('Indy 10', [TIdHTTPProxyServer]); end; procedure TIdHTTPProxyServer.InitializeCommandHandlers; begin inherited; with CommandHandlers.Add do begin Command := 'GET'; {do not localize} OnCommand := CommandGet; ParseParams := True; Disconnect := true; end; with CommandHandlers.Add do begin Command := 'POST'; {do not localize} OnCommand := CommandPOST; ParseParams := True; Disconnect := true; end; with CommandHandlers.Add do begin Command := 'HEAD'; {do not localize} OnCommand := CommandHEAD; ParseParams := True; Disconnect := true; end; with CommandHandlers.Add do begin Command := 'CONNECT'; {do not localize} OnCommand := Commandconnect; ParseParams := True; Disconnect := true; end; //HTTP Servers/Proxies do not send a greeting Greeting.Clear; end; procedure TIdHTTPProxyServer.TransferData( ASrc: TIdTCPConnection; ADest: TIdTCPConnection; const ADocument: string; const ASize: Integer; const AHeaders: TIdHeaderList ); //TODO: This captures then sends. This is great and we need this as an option for proxies that // modify data. However we also need another option that writes as it captures. // Two modes? Intercept and not? var LStream: TStream; begin //TODO: Have an event to let the user perform stream creation LStream := TMemoryStream.Create; try ASrc.ReadStream(LStream, ASize, ASize = -1); LStream.Position := 0; DoHTTPDocument(ADocument, LStream, AHeaders); // Need to recreate IdStream, DoHTTPDocument passes it as a var and user can change the // stream that is returned ADest.WriteStream(LStream); finally FreeAndNil(LStream); end; end; procedure TIdHTTPProxyServer.CommandGET( ASender: TIdCommand ) ; var LClient: TIdTCPClient; LDocument: string; LHeaders: TIdHeaderList; LRemoteHeaders: TIdHeaderList; LURI: TIdURI; LPageSize: Integer; begin ASender.PerformReply := false; LHeaders := TIdHeaderList.Create; try ASender.Thread.Connection.Capture(LHeaders, ''); LClient := TIdTCPClient.Create(nil); try LURI := TIdURI.Create(ASender.Params.Strings[0]); try LClient.Port := StrToIntDef(LURI.Port, 80); LClient.Host := LURI.Host; //We have to remove the host and port from the request LDocument := LURI.Path + LURI.Document + LURI.Params; finally FreeAndNil(LURI); end; LClient.Connect; try LClient.WriteLn('GET ' + LDocument + ' HTTP/1.0'); {Do not Localize} LClient.WriteStrings(LHeaders); LClient.WriteLn(''); LRemoteHeaders := TIdHeaderList.Create; try LClient.Capture(LRemoteHeaders, ''); ASender.Thread.Connection.WriteStrings(LRemoteHeaders); ASender.Thread.Connection.WriteLn(''); LPageSize := StrToIntDef(LRemoteHeaders.Values['Content-Length'], -1) ; {Do not Localize} TransferData(LClient, ASender.Thread.Connection, LDocument, LPageSize, LRemoteHeaders); finally FreeAndNil(LRemoteHeaders); end; finally LClient.Disconnect; end; finally FreeAndNil(LClient); end; finally FreeAndNil(LHeaders); end; end; procedure TIdHTTPProxyServer.CommandPOST( ASender: TIdCommand ) ; var LClient: TIdTCPClient; LDocument: string; LHeaders: TIdHeaderList; LRemoteHeaders: TIdHeaderList; LURI: TIdURI; LPageSize: Integer; LPostStream: TMemoryStream; begin ASender.PerformReply := false; LHeaders := TIdHeaderList.Create; try ASender.Thread.Connection.Capture(LHeaders, ''); LPostStream:=tmemorystream.Create; try LPostStream.size:=StrToIntDef( LHeaders.Values['Content-Length'], 0 ); {Do not Localize} ASender.Thread.Connection.ReadStream(LPostStream,LPostStream.Size,false); LClient := TIdTCPClient.Create(nil); try LURI := TIdURI.Create(ASender.Params.Strings[0]); try LClient.Port := StrToIntDef(LURI.Port, 80); LClient.Host := LURI.Host; //We have to remove the host and port from the request LDocument := LURI.Path + LURI.Document + LURI.Params; finally FreeAndNil(LURI); end; LClient.Connect; try LClient.WriteLn('POST ' + LDocument + ' HTTP/1.0'); {Do not Localize} LClient.WriteStrings(LHeaders); LClient.WriteLn(''); LClient.WriteStream(LPostStream); LRemoteHeaders := TIdHeaderList.Create; try LClient.Capture(LRemoteHeaders, ''); ASender.Thread.Connection.WriteStrings(LRemoteHeaders); ASender.Thread.Connection.Writeln(''); LPageSize := StrToIntDef(LRemoteHeaders.Values['Content-Length'], -1) ; {Do not Localize} TransferData(LClient, ASender.Thread.Connection, LDocument, LPageSize, LRemoteHeaders); finally FreeAndNil(LRemoteHeaders); end; finally LClient.Disconnect; end; finally FreeAndNil(LClient); end; finally FreeAndNil(LPostStream); end; finally FreeAndNil(LHeaders); end; end; procedure TIdHTTPProxyServer.CommandConnect( ASender: TIdCommand ) ; begin end; procedure TIdHTTPProxyServer.CommandHEAD( ASender: TIdCommand ) ; begin end; constructor TIdHTTPProxyServer.Create( AOwner: TComponent ) ; begin inherited; DefaultPort := IdPORT_HTTPProxy; Greeting.Text.Text := ''; // RS ReplyUnknownCommand.Text.Text := ''; // RS end; procedure TIdHTTPProxyServer.DoHTTPDocument(const ADocument: string; var VStream: TStream; const AHeaders: TIdHeaderList); begin if Assigned(OnHTTPDocument) then begin OnHTTPDocument(Self, ADocument, VStream, AHeaders); end; end; end.