用Delphi7的Indy控件IdMappedPortTcp做HTTP代理程序

很多朋友一定象我一样需要一个可以自己定制的HTTP代理程序,受到论坛里面的LXM365朋友的启发(在此感谢一下!),发现利用DELPHI7 的INDY控件IdMappedPortTcp可以做成HTTP代理程序.于是我仔细查看了一下IdMappedPortTcp的源代码,发现Lxm365在OnExecute事件里面处理的方案并不完美,因为在触发OnExecute事件前代理程序必须已经连接到一个目标机器,所以他的代码中有: 

TidTcpClient(AThread.OutboundClient).Host:=RequestHost; 

TidTcpClient(AThread.OutboundClient).Port:=RequestPort; 

TidTcpClient(AThread.OutboundClient).Disconnect; 

TidTcpClient(AThread.OutboundClient).Connect(AThread.ConnectTimeOut); 

这就表示需要断开已有的连接,重新连接到另外一个目标.这么表叙吧.有A,B,C,D4个机器,A是客户机,B是代理服务器,C是B机上设置的默认访问的WEB服务器,D是需要访问的WEB服务器,当A连接到B时,B将连接到默认的C主机,然后B机根据A的请求,修改MAPPEDHOST和MAPPEDPORT为D机,然后断开B机与C机的连接,接着马上连接到D机.这样的话,C机必须存在,不然的话应该无法触发ONEXECUTE事件,那么也就无法继续代理服务了.那么我们怎么解决这个问题呢?我的想法是在A机连接到B机时暂时不影射到默认的C机上,而是等待A机的请求数据,根据HTTP请求提供的信息来修改MAPPEDHOST和MAPPEDPORT,然后由控件自动实现连接到需要的D机,这样A机到B机的连接就被影射到了D机上了,相当于A机直接访问了D机的WEB端口,那么我们就可以做一个函数ReceiveData()来获取来自A机的请求包,这时有个问题要注意,我们必须把从A机收到的请求内容转发给D机呀,什么时候发呢?当然是一连接到D机的时候就发呀,所以我们需要另外一个函数SendData()来发送请求,这可不能忘,嘿嘿,直接转发可不行咯,HTTP协议里面客户端访问WEB服务器时,通过代理和不通过代理的请求包内容可是不同的.所以我们必须要处理.那么我们就需要另外一个函数ProcessData()来分析和修改处理请求包.因此整个HTTP代理服务器的实现变成了这样: 

1.A机向代理机B产生连接并发送请求包 

2.B机接收A机连接并读取A机的请求(此时B机已经有专门的线程来控制该连接) 

3.B机在影射到目标机器前分析请求包,并根据请求修改该连接线程的MappedHost和MappedPort为D机的地址和端口 

4.B机的连接线程连接到预定的MappedHost,并将已经修改过的请求包发送给D机. 

5.D机返回的数据包直接由连接线程控制返回给A机,不用我们干预,完全自动. 

6.由于我没有更多资料,我所了解的HTTP协议就是"连接-->请求<--->应答-->断开"模式,因此我认为客户端基本上只向WEB服务器发送一次请求,比如GET,HEAD,POST方法,其他的我不太清楚,如果在没有断开时又有请求包,那么就需要在OnExecute里面处理请求包了.谁有详细的HTTP协议中文资料呀,特别是HTTP代理部分.请一定给我一份,我的EMAIL:qianlj@gtjas.com 

 

好了,说了一大堆想法,让我们实践一下了.我们新建一个程序,在FORM上放上以下控件: 

1.Checkbox1:TCheckBox (用于控制MappedPortTcp控件的ACTIVE属性的TURE和FALSE) 

2.LocalPort:TLabeledEdit(D7中才有,本地代理服务端口,一般设为80或8080) 

3.DefaultHost:TLabeledEdit(默认影射到的主机地址,域名或IP都可以) 

4.DefaultPort:TLabeledEdit(默认影射到的端口) 

5.Log:TCheckBox (控制是否显示传输的内容,可以监控你的用户访问了些什么内容哟) 

6.Memo:TMemo (用于记录传输的内容) 

7.MappedPortTcp:TIdMappedPortTcp (这可是核心的东西呀,全都靠他了!!!) 

8.IdAntiFreeze1:TIdAntiFreeze1 (这个可要可不要,好象INDY中介绍,用了他可避免界面不响应的问题) 

继续....写代码,在MappedPortTcp的事件中添加以下函数吧. 

1.在OnConnect中 

ReceiveData(Athread); 

if log.Checked then begin 

memo.Lines.BeginUpdate; 

memo.Lines.Add('--------连接请求数据包-------------'); 

memo.Lines.Add(athread.NetData); 

memo.Lines.EndUpdate; 

end; 

if Athread.Connection.Connected then 

ProcessData(Athread); 

if log.Checked then begin 

memo.Lines.BeginUpdate; 

memo.Lines.Add('------修改过的连接请求数据包------'); 

memo.Lines.Add(athread.NetData); 

memo.Lines.EndUpdate; 

end; 

2.在OnOutBoundConnect中: 

if not Assigned(AException) then 

if not SendData(Athread) then begin 

memo.Lines.Add('----------------发送数据出错--------------------') 

end; 

3.在OnOutBoundData中 

if log.Checked then begin 

memo.Lines.Add('------服务端返回数据包----------'); 

memo.Lines.Add(athread.NetData); 

end; 

4.在OnExecute中 

//目前我没管他,如果一次连接中,HTTP的请求不是一次而是多次,那么在这个事件中也要对来自A机的后续请求包进行处理,用ProcessData(Athread)就应该可以了. 

OK,完成了,不会吧就这么简单,对就这么简单,嘿嘿,别急,还没搞定呢,我们还需要看看关键的三个函数:ReceiveData(Athread),ProcessData(Athread),SendData(Athread). 

 

1.获取来自A机的请求数据.将数据直接保存到Athread.NetData. 

function TMain_Form.ReceiveData(Athread: TIdMappedPortThread):boolean; 

begin 

with Athread do begin 

NetData:=''; 

result:=false; 

try 

Connection.ReadFromStack(true,-1,true); 

NetData:=Connection.InputBuffer.Extract(Connection.InputBuffer.Size); 

result:=true; 

except 

end; 

end; 

end; 

2.当连接到目标机D时,将请求包Athread.NetData发送给目标机器D. 

function TMain_Form.SendData(Athread:TIdMappedPortThread):boolean; 

begin 

result:=false; 

if Assigned(Athread.OutboundClient) and Athread.OutboundClient.Connected then begin 

try 

if Athread.Connection.Tag=1 then begin 

Athread.NetData:=''; 

//对HTTP隧道技术的支持,如果连接目标机器成功则向客户端返回连接成功信息 

Athread.Connection.WriteLn('HTTP/1.0 200 OK'+EOL); 

end; 

if length(Athread.NetData)<>0 then 

Athread.OutboundClient.Write(Athread.NetData); 

result:=true; 

except 

memo.Lines.Add('------转发数据出错!!!------') 

end; 

end; 

end; 

3.处理来自A机的请求,并且根据请求改变将要影射到的目标机器地址和端口 

function TMain_Form.ProcessData(AThread: TIdMappedPortThread):boolean; 

var 

RemoteHost:TIdURI; 

InputLine,HttpCmd,HttpVer:string; 

HttpHeader:TIdHeaderList; 

TempHeader:TStringList; 

Proxy:boolean; 

begin 

HttpHeader:=TIdHeaderList.Create; 

TempHeader:=TStringList.Create; 

result:=true; 

try 

HttpHeader.UnfoldLines:=false; 

HttpHeader.FoldLines:=false; 

HttpHeader.Text:=Athread.NetData;//将请求包放取出来处理 

RemoteHost:=TIdURI.Create('HTTP://'+HttpHeader.Values['Host']); 

 

TempHeader.CommaText:=HttpHeader.Strings[0];//获取HTTP请求行 

Proxy:=length(HttpHeader.Values['Proxy-Connection'])>0; 

//是否代理请求包,我是通过Proxy-Connection标志判断的,但是我跟踪发现有些程序通过HTTP代理连接时没有这个标志,我不知道是他们的错,还是我对HTTP协议了解不够,但是IE是有这个标志段的. 

//请求行至少有三段,由空格分开:命令 URL HTTP版本,所以Count需要>2 

if TempHeader.Count>2 then begin 

HttpCmd:=UpperCase(TempHeader.Strings[0]);//HTTP命令 

HttpVer:=TempHeader.Strings[TempHeader.count-1];//HTTP协议版本 

InputLine:=TempHeader.Strings[1];//请求的URI 

//如果是代理请求包 

if Proxy then begin 

//分别处理不同的请求命令,并重建HTTP请求行,因为直接访问WEB服务器的请求包中的第一行中是不包含域名和端口信息的,而通过代理时有完整的URL,我们删除了域名信息就相当与在代理机上模拟了一个普通客户端请求. 

TempURI:=TIdURI.Create(InputLine); 

if (HttpCmd='HEAD') or (HttpCmd='GET') or (HttpCmd='POST') then begin 

 

HttpHeader.Strings[0]:=HttpCmd+' '+TempUri.Path+TempUri.Document+TempUri.Params; 

if TempUri.Bookmark<>'' then 

HttpHeader.Strings[0]:=HttpHeader.Strings[0]+'#'+TempUri.Bookmark; 

HttpHeader.Strings[0]:=HttpHeader.Strings[0]+' '+HttpVer; 

end; 

 

if HttpCmd='OPTIONS' then begin 

end; 

if HttPCmd='TRACE' then begin 

end; 

if HttpCmd='PUT' then begin 

end; 

if HttpCmd='DELETE' then begin 

end; 

//删除请求包中的代理标志 

HttpHeader.Delete(HttpHeader.IndexOfName('Proxy-Connection')); 

if HttpCmd='CONNECT'then begin 

RemoteHost.URI:='HTTP://'+InputLine; 

Athread.Connection.Tag:=1; 

//这里是对HTTP隧道技术的支持,代理程序连接到目标机器后需要向客户机返回一个连接成功信息,我偷懒就直接标记了一下用于判断.在OnOutBoundConnect中会用到 

end; 

TempURI.free; 

end; 

 

if Assigned(AThread.OutboundClient) and Athread.Connection.Connected then begin 

if not Athread.OutboundClient.Connected then begin 

TidTcpClient(AThread.OutboundClient).Host:=trim(RemoteHost.Host); 

TidTcpClient(AThread.OutboundClient).Port:=StrToIntDef(RemoteHost.Port,TidTcpClient(AThread.OutboundClient).Port);

end; 

end; 

end; 

 

Athread.NetData:=HttpHeader.Text; 

//将修改过的数据保存到Athread.NetData中,这里有一个小问题,一般情况下如果请求包中不包含特别数据,最后两个字节是#D#A,而如果包含数据则不需要添加#D#A,但是上面这句产生的ATHREAD.NETDATA的最后两个字节总是#D#A,本来是要判断一下的,但是我懒,没试,刚才看到这才想起来,应该可以这么判断一下,大家可以试试,不过我之前没判断好象也可以正常使用,嘿嘿 

//if StrToIntDef(Trim(HttpHeader.Values['Content-Length']), -1)>0 then begin 

// SetLength(Athread.NetData,Length(Athread.NetData)-2); 

// 删除末尾的#D#A(换行回车符) 

//end 

RemoteHost.Free; 

finally 

TempHeader.Free; 

HttpHeader.Free; 

end; 

end; 

 

 

OK,OK,这下应该了解了吧,是不是很简单?就想使用?哈哈,当心......还没完呢......别用手指着我,我可是"横眉冷对千夫指,俯首甘为孺子牛"呀,呵呵.我老实交代了: 

1.在程序的uses中有这些东西吗? 

IdTcpclient,IdMappedPortTCP,IdURI,IdHeaderList,IdGlobal;没有?那得加上. 

2.把以下内容放到 private与public之间 

function ReceiveData(Athread: TIdMappedPortThread):boolean; 

function ProcessData(AThread: TIdMappedPortThread):boolean; 

function SendData(Athread:TIdMappedPortThread):boolean; 

. 3.在Checkbox1的OnClick中添加 

begin 

try 

if not MappedPortTcp.Active then begin 

MappedPortTcp.DefaultPort:=strtointdef(LocalPort.Text,80); 

MappedPortTcp.MappedHost:=DefaultHost.Text; 

MappedPortTcp.MappedPort:=strtointdef(DefaultPort.Text,80); 

end; 

MappedPortTcp.Active:=checkbox1.Checked; 

except 

end; 

checkbox1.Checked:=MappedPortTcp.Active; 

end; 

 

哎呀,好累呀,手都酸了,不多写了,还有好多东西自己都没完全了解呢,我现在计划在这个里面多加点内容,比如限制可访问代理的IP地址,每连接的流量控制,还得加上代理的用户验证(在HTTP请求包分析函数里面处理),还可以添加域名的转向和站点的访问限制,还有不同类型文件的下载控制等等,......,不知道大家还需要什么功能,我试试看能否实现.大家都给点建议,呵呵,当然也需要大家多给评点分. 

最后想到一点.这个程序有个功能不知道大家发现了没有?......... 

当程序运行在一台跨公网和内网的机器上时,内部网的用户将IE中代理的IP地址设为该机的内网地址就可以很好的访问INTERNET了,废话...这是什么新功能!!!,别急,如果你的客户端是公网IP,他的IE中代理的IP地址设为该机器的公网IP的话,在地址栏中输入http://你的内部WEB服务器IP 的话,不是就可以访问你的内部网了吗.呵.有用吧,可怕吧.怎么控制呢?简单,在服务ACTIVE:=TRUE前只绑定内部IP就可以了(也就是只在内部网内提供服务),怎么绑定?累了,不说了,看DELPHI的DEMOS吧.俺就不罗嗦了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Delphi 7是一种集成开发环境(IDE),经常用于创建Windows应用程序。而Indy是一个Delphi中的开源组件库,用于网络编程。 HTTPS(Hypertext Transfer Protocol Secure)是一种安全的HTTP通信协议,它通过使用SSL/TLS协议来改进数据传输的安全性。Indy组件库提供了处理HTTPS通信的功能,使得在Delphi 7中实现HTTPS通信变得相对简单。 要在Delphi 7中使用Indy组件来实现HTTPS,首先需要将Indy组件库添加到Delphi项目中。这可以通过设置Delphi的搜索路径,或手动将相应的包文件添加到项目中来完成。添加完成后,在Delphi的组件面板中可以看到Indy相关的组件。 在项目中,我们可以使用TIdHTTP组件进行HTTPS通信。TIdHTTPIndy组件库提供的用于HTTPHTTPS通信的组件。我们需要设置TIdHTTP组件的一些属性,例如URL地址、如果需要的话设置代理服务器等。 接下来,我们可以使用TIdHTTP组件提供的方法来发送HTTP请求,例如GET或POST请求。对于HTTPS,我们需要设置一些额外的属性,例如SSL版本、证书等。 当我们发送HTTPS请求后,服务器将使用SSL/TLS协议来对数据进行加密和认证。在使用TIdHTTP组件时,Indy会自动处理SSL/TLS握手和证书验证等操作,使得我们无需关心这些细节。 最后,我们可以通过解析返回的HTTP响应来获取服务器返回的数据。可以使用TIdHTTP组件提供的方法来获取响应的内容。 总的来说,通过Delphi 7和Indy组件,我们可以方便地实现HTTPS通信功能。利用Indy提供的TIdHTTP组件,我们可以发送HTTPS请求,并获取服务器返回的数据。Delphi 7和Indy的组合为我们提供了一种简单而可靠的方式来开发安全的网络应用程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值