随着网络环境的多样化,越来越多的用户通过HTTP代理上网。
传统HTTP代理服务器的主要工作过程如下:
1)在代理服务器的某个固定端口,监听客户端的连接,并接收客户端的HTTP请求。
2)对客户请求的HTTP包,进行解析和处理,并产生新的请求包。根据请求中的域名(或者IP地址)和Web服务器端口号,建立与Web服务器的连接,将新的请求发送给Web服务器。
3)等待并接收Web服务器返回的HTTP响应。
4)将HTTP响应转发给客户端Web浏览器。
从以上的工作过程可以看出,传统的HTTP代理服务一般需要用户在浏览器中进行相应的设置,对非专业用户来说增加了使用难度。除此以外,在服务器端打开相应的端口供客户端进行连接,增加了安全隐患,并且增加了维护成本。但是在服务器上存在基于J2EE架构的网站时,可以利用本文提出的设计方法在网站中集成HTTP代理服务,从而避免以上问题。最后本文给出了这种设计方案的典型应用场景,供读者参考。
1总体设计
HTTP代理的主要功能是针对Web客户端提出的浏览请求,访问指定的Web服务器,并在取得相应内容后,返回给
Web客户端。
根据HTTP代理的工作模型,结合J2EE架构网站的特点,分成如下几个模块来设计。
收稿日期:2010-12-17。
前端页面接受用户想要访问的网站地址,并将该网址作为参数传递给Servlet进行处理。
ProxyServlet首先调用鉴权模块,判断客户端是否有对该网址访问的权限,然后调用下载模块获得指定网址的HTML代码,并将其传入HTML解析模块。最后将处理完的数据利用response中的PrintWriter写入客户端浏览器。
FormServlet用来处理表单数据的Servlet,将表单中的数据提交到指定的网站,获取数据后返回给客户端。
HTML解析模块使用HTMLParser对网页进行解析,得到HTML中的标签,调用相应的处理模块,最后将处理完的
HTML代码返回给相应的Servlet。
鉴权模块通过单例模式加载预先设置好的配置文件,用来判断用户是否具有访问某网址的权限。配置文件可选择记录禁止用户访问的网址或允许用户访问的网址。
下载模块工具类。利用JDK提供的HttpURLConnection连接用户请求的Web服务器后,下载指定的URL。
多媒体处理模块调用下载模块下载HTML代码中存在的多媒体信息,并调用本地保存模块,保存到本地。
脚本处理模块处理HTML代码中存在的引用脚本,将其下载到本地并保存。
超链接处理模块处理代码中存在的超链接,使用户点击超链接后可继续调用代理服务。
样式表处理模块样式表控制网页样式,正确的处理样式表有助于提高用户体验。此模块用来处理代码中存在的样式表。
2HTML解析模块的设计
对HTML代码进行解析是本系统的重要部分,主要包括
作者简介:史承毅(1984-),男,山东青岛人,硕士,主要研究方向:计算机软件与理论。
28计算机应用2011年
对多媒体、脚本、超链接、表单、CSS样式表的处理。HTML代码为半结构化的文本,其中有许多标签和嵌套结构。针对HTML代码的处理可以自己开发一套单独的网页解析器,也可以使用现有的开源类库,例如HTMLParser。开发单独的网页解析器可以利用正则表达式来进行标签匹配,相对于使用现有的开源类库来说开发周期较长,因此本文选用
HTMLParser来进行网页解析。HTMLParser是http://sourceforge.net上活跃的一个开源项目,它提供了线性和嵌套两种方式来解析网页,主要用于HTML网页的转换
(Transformation)以及网页内容的抽取(Extraction)。HTMLParser有如下一些易于使用的特性:过滤器(Filters),访问者模式(Visitors),处理自定义标签以及易于使用的
JavaBeans,目前最新版本为1.6。
HTML解析模块的代码如下:
//解析HTML代码,并将得到的标签传入处理模块后,返回处理
//后的HTML代码
//参数HTML是由下载模块返回的HTML代码,targetUrl从
//Servlet中取得
public static String parserHTML(String HTML,String targetUrl)
{
//生成处理模块的责任链
Handler imageHandler=new ImageHandler();
Handler linkHandler=new LinkHandler();
Handler formHandler=new FormHandler();
Handler scriptHandler=new ScriptHandler();Handler styleHandler=new StyleHandler();imageHandler.SetSuccessor(linkHandler);linkHandler.SetSuccessor(formHandler);formHandler.SetSuccessor(scriptHandler);scriptHandler.SetSuccessor(styleHandler);
//获取HTML标签内的所有内容
NodeList list=Parser.createParser(HTML,"gb2312").parse
(new TagNameFilter("HTML"));
Node HTMLNode=list.elementAt(0);
//对HTML内容进行访问
HTMLNode.accept(new NodeVisitor()
{
public void visitTag(Tag tag)
{
imageHandler.handlerRequest(tag,targetUrl);
}
};
//返回处理完的HTML代码
return HTMLNode.toHTML;
}
3对各个标签的处理
各标签处理模块采用责任链模式进行设计。定义一个抽
象处理者,具体针对各个标签的处理模块继承此类。
public abstract class Handler
{
protected Handler successor;
//设置后续的处理模块
public void setSuccessor(Handler successor)
{
this.successor=successor;
}
//处理请求的方法,返回处理后的路径
public abstract String handleRequest(Tag tag,String targetUrl);
}
整条责任链模式的UML类图,如图1所示。
图1责任链的UML类图
3.1对多媒体标签的处理以图片为例。根据解析模块传入的地址调用下载模块将图片下载到本地进行保存,并将图片的src属性设置为本地路径。代码如下:
public class ImageHandler extends Handler
{
Override
public void handleRequest(Tag tag,String targetUrl)
{Image.setImageURL(localSrc);
}
else if(successor!=null)
{
//非ImageTag,调用后继模块进行处理
successor.handleRequest(tag,targetUrl)
}
}
}
3.2对脚本的处理if(tag instanceof ImageTag)
{只需要处理引用方式的脚本。取得src属性后通过下载
ImageTag image=(ImageTag)tag;模块下载到本地保存,并将src属性替换为相应的路径。代
//获得图片的URL地址码与ImageHandler基本相同。
String targetSrc=targetUrl+image.getImageURL();
3.3对超链接的处理
//下载图片并保存到本地,返回图片本地路径
String localSrc=DownloadUtil.downImg(targetSrc);对超链接的href属性进行替换将原来的href属性作为访
//设置新的路径,使客户端可以访问问ProxyServlet的参数。例如访问ProxyServlet的URL为:
增刊1史承毅:在基于J2EE架构的网站中集成HTTP代理的设计与实现29
http://127.0.0.1/ProxyServlet,则将href属性替换为http://127.0.0.1/ProxyServlet?Url=bbs.xxxxx.com,在ProxyServlet中可以取得URL参数进行进一步的代理服务。访问
ProxyServlet的具体形式可在Web.xml中进行配置。代码如下:
public class LinkHandler extends Handler
{
Override public void handlerRequest(Tag tag,String targetUrl)
{
if(tag instanceof LinkTag)
{
LinkTag link=(LinkTag)tag;
String oldLink=link.getLink();
//处理相对路径
If(oldLink.startsWith("/"))
{
oldLink+=targetUrl;
}
//设置新的访问路径,确保用户点击超链接时可以调用到
//代理服务
Link.setLink(localAddr+"?url="+oldLink);
}
else if(successor!=null)
{
//非LinkTag,调用后继模块进行处理
successor.handleRequest(tag,targetUrl);
}
}
/*本地访问ProxyServlet的路径,可在程序中动态获取,此处为了保持代码的简洁设置成final变量*/
private static final String localAddr="http://127.0.0.1/ProxyServlet/";
}
3.4对表单的处理
将从parserHTML得到的action属性加上targetUrl,补齐后作为访问FormServlet的参数,以便用户在提交表单时可以调用到代理服务。例如:对<form action="login.php"method="post">进行替换,若Web.xml中的配置如下:
<servlet-mapping>
<servlet-name>FormServlet</servlet-name>
<url-pattern>/FormServlet</url-pattern>
</servlet-mapping>
则将action属性替换为/FormServlet?submit=bbs.
xxxxx.com/login.php。此部分代码与LinkHandler基本相同。当用户提交表单时,在FormServlet中可以取得submit参数作为提交地址,并将表单中存在的参数全部取出拼接后提交。
FormServlet的关键代码如下:
//构造准备提交的数据,其中formMap中存储从form中取得的键
//值对
String post="";for(Iterator iter=formMap.entrySet().iterator();iter.hasNext();)
{
Map.Entry entry=(Map.Entry)iter.next();
If("".equals(post))
{
post=""+entry.getKey()+'="'+entry.getValue();
}
post+='"&'+entry.getKey()+'="'+entry.getValue();
}
//其中urlStr为submit参数的值.代表表单提交到的目标服务器
//的地址
URL url=new URL(urlStr);
//打开一个到目标服务器的连接
HttpURLConnection httpConn=(HttpURLConnection)url.openConnection();
//将doOutput标志设置为true,指示应用程序要从URL连接读
//取数据
httpConn.setDoOutput(true);
//将doInput标志设置为true,指示应用程序要从URL连接读取
//数据
httpConn.setDoInput(true);
//设置Http请求头
httpConn.setRequestProperty("User-Agent",
"Mozilla/5.0(compatible;MSIE 6.0;Windows NT)");httpConn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
//以下进行post操作
PrintStream send=new PrintStream(httpConn.getOutputStream());send.print(post);
//post完成
send.close();httpConn.disconnect();
3.5对CSS样式表的处理样式表的处理与脚本和多媒体的处理基本相同。只需要对引用形式的样式表文件进行处理。下载后将其保存到本地,并修改路径为本地路径即可。
4典型应用
在许多网络设备上都存在一套基于Web的管理软件,例如路由器、网络存储设备等。当对这些设备进行统一管理的时候,经常使用一套基于J2EE的网管软件。此时若用户与这些设备不在同一网段,就可以在浏览器中直接使用网管软件提供的代理服务来访问设备自身的管理软件,如图2所示。