📄 httpproxy.pas
字号:
{
Http proxy
Created By LLJ 2003-05-07
代理服务器工作:
1、客户机 Socket 连接上: 在连接链中增加一个连接纪录(TSessionRec)节点。
2、客户端发送连接请求数据: 根据请求数据取得客户端要连接的目标主机的地址
和端口,并连接目标主机。
3、连接到目标主机:
4、发送客户端数据到目标主机: 如果客户端有数据未发送,则发送
5、将目标主机返回的数据发送到客户端:
}
unit HttpProxy;
interface
uses Windows, SysUtils, Classes, ExtCtrls, ScktComp;
type
// 客户端与代理服务器的会话信息。
PSessionRec = ^TSessionRec;
TSessionRec = packed record
Used: Boolean; // 是否被使用。
Lookingup: Boolean; // 远程客户端是否正在连接
LookupTime: Integer; // 远程客户端已经在连接的时间
ClientConnected: Boolean; // 客户端是否连接
RemoteConnected: Boolean; // 远程客户端是否连接
ClientSocketHandle: Integer; // 客户端的 SocketHandle
csRemoteClient: TClientSocket; // 远程客户端,用来连接远程目标主机
// KeepConnection: Boolean;
IsClientRequesting: Boolean; // 客户端是否有请求消息要发往远程主机
RequestString: String; // 客户端的请求消息
end;
TProxy = class
private
FProxyServer: TServerSocket; // 代理服务器的服务器 Socket
FLookupTimer: TTimer; // 连接时间控制计时器
FOutPut: TStrings;
FPort: Integer;
FLookupTimeLimite: Integer;
FLookupTimeLimited: Boolean;
procedure AddMessage(const Msg: String);
procedure AddSession(const ASocket: TCustomWinSocket);
function GetClientFromHandle(const ASocketHandle: Integer): TCustomWinSocket;
function GetSessionRemote(
ASocketHandle: Integer): PSessionRec;
procedure EndSessionClient(const ASocket: TCustomWinSocket);
procedure EndSessionRemote(const ASocket: TCustomWinSocket);
function GetSessionClient(const ASocket: TCustomWinSocket): PSessionRec;
// 服务器(ServerSocket) 的事件,客户端连接,断开,读
procedure FProxyServerClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure FProxyServerClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure FProxyServerClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure FProxyServerListen(Sender: TObject;
Socket: TCustomWinSocket);
// 远程客户端(ClientSocket) 的事件
procedure FRemoteClientConnect(Sender: TObject; Socket: TCustomWinSocket);
procedure FRemoteClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure FRemoteClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
procedure FRemoteClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure FRemoteClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure FLookupTimerTimer(Sender: TObject);
procedure SetPort(const Value: Integer);
procedure SetLookupTimeLimit(const Value: Integer);
procedure SetLookupTimeLimited(const Value: Boolean);
{ Private declarations }
public
constructor Create;
destructor Destroy; override;
procedure StopServer;
procedure StartServer;
property Output: TStrings write FOutput;
property LookupTimeLimit: Integer read FLookupTimeLimite write SetLookupTimeLimit;
property LookupTimeLimited: Boolean read FLookupTimeLimited write SetLookupTimeLimited;
property Port: Integer read FPort write SetPort;
{ Public declarations }
end;
var
SessionRecs: array of TSessionRec;
SessionCount: Integer;
implementation
{
根据连接到代理服务器的客户端的信息,创建会话。
}
procedure TProxy.AddSession(const ASocket: TCustomWinSocket);
var
I: Integer;
{
根据连接到代理服务器的客户端的信息,创建会话。
}
procedure NewSession(ASessionRec: PSessionRec);
begin
with ASessionRec^ do
begin
Used := True;
ClientConnected := True;
Lookingup := False;
RemoteConnected := False;
ClientSocketHandle := ASocket.SocketHandle;
IsClientRequesting := False;
end;
end;
begin
// if SessionCount >= MAX_CLIENTS then Exit;
{
看看有没有空闲的会话可以使用。
}
for I := 0 to SessionCount - 1 do with SessionRecs[I] do
begin
if not Used then
begin
NewSession(@SessionRecs[I]);
Exit;
end;
end;
{
新增一个会话。
}
Inc(SessionCount);
SetLength(SessionRecs, SessionCount);
NewSession(@SessionRecs[SessionCount - 1]);
end;
{
当有客户端连接到代理服务器时,创建一个会话。
}
procedure TProxy.FProxyServerClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
AddMessage(Socket.RemoteHost + ' Connected.' + IntToStr(Socket.SocketHandle));
AddSession(Socket);
end;
{
远程客户端连接到目标主机时,设置已经连接到标志。
}
procedure TProxy.FRemoteClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
AddMessage('Connected to Remote Host.' + IntToStr(Socket.SocketHandle));
with GetSessionRemote(Socket.SocketHandle)^ do
begin
Lookingup := False;
csRemoteClient.Tag := Socket.SocketHandle;
end;
end;
{
处理客户端发送请求。
1、客户端的会话已经在客户端连接时建立,现在利用客户端发送过来的信息,
创建远程客户端连接远程目标主机。
2、如果已经发送消息的客户端已经连接到远程目标主机,则将客户端的消息通过
远程客户端与远程目标主机的连接,将客户端的消息转发到远程目标主机。
}
procedure TProxy.FProxyServerClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
strTemp: String;
idx: Integer;
begin
with GetSessionClient(Socket)^ do
begin
RequestString := Socket.ReceiveText;
IsClientRequesting := True;
if RemoteConnected then
csRemoteClient.Socket.SendText(RequestString)
else begin
if Assigned(csRemoteClient) then csRemoteClient.Free;
csRemoteClient := TClientSocket.Create(nil);
{ 从请求消息中获取主机字符串和端口,以此创建远程客户端,以建立到远程
主机的连接。}
strTemp := RequestString;
while strTemp <> '' do
begin
idx := Pos('Host', strTemp);
if idx = 1 then
begin
Delete(strTemp, 1, idx + 5);
idx := Pos(#13#10, strTemp);
strTemp := Copy(strTemp, 1, idx - 1);
idx := Pos(':', strTemp);
if idx > 0 then
begin
csRemoteClient.Host := Copy(strTemp, 1, idx - 1);
Delete(strTemp, 1, idx);
try
csRemoteClient.Port := StrToInt(strTemp);
except
csRemoteClient.Port := 80;
end;
end else
begin
csRemoteClient.Host := strTemp;
csRemoteClient.Port := 80;
end;
Break;
end;
Delete(strTemp, 1, Pos(#13#10, strTemp) + 1);
end;
{
设置远程客户端的事件处理
}
csRemoteClient.OnConnect := FRemoteClientConnect;
csRemoteClient.OnRead := FRemoteClientRead;
csRemoteClient.OnWrite := FRemoteClientWrite;
csRemoteClient.OnDisConnect := FRemoteClientDisconnect;
csRemoteClient.OnError := FRemoteClientError;
Lookingup := True;
csRemoteClient.Active := True;
end;
end;
end;
{
处理远程服务器返回的响应。将消息原样返回给客户端。
}
procedure TProxy.FRemoteClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
Buf: array[0..2047] of Byte;
RecvLen: Integer;
begin
AddMessage('Receive message from remote' + IntToStr(Socket.SocketHandle));
with GetSessionRemote(Socket.SocketHandle)^ do
begin
RecvLen := Socket.ReceiveBuf(Buf, 2048);
// LLJ 2003-05-13
// 判断客户端是否连接,否则断开远程客户端的连接。
if ClientConnected then
GetClientFromHandle(ClientSocketHandle).SendBuf(Buf, RecvLen)
else csRemoteClient.Active := False;
end;
end;
{
根据远程客户端的 Socket 句柄获得相应的会话。
}
function TProxy.GetSessionRemote(ASocketHandle: Integer): PSessionRec;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -