注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Simon

 
 
 

日志

 
 

Symbian 使用CEComFilter捕获系统HTTP请求  

2010-11-11 13:06:15|  分类: Symbian C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、前言

  在IE浏览器中,如果想捕获浏览器的HTTP请求,可以通过BHO或者“异步可插协议”技术实现,类似于“迅雷”之类的下载软件,当用户点击一个下载链接会自动弹出。在Symbian OS中有一种技术与“异步可插协议”非常类似,Symbian OS会在HTTP请求中每一步处理过程都通知用户代码,本文将介绍这种技术的实现,并在文章的最后提供在手机上实现下载软件的具体思路。

  二、核心ECOM接口类CEComFilter

  从类名可以看出,CEComFilter是一个ECOM接口类(关于ECOM方面的编程总结,我会在后面的章节陆续放出),以下列出CEComFilter类的关键代码(SDK中文件cecomfilter.h内):

  1 const TUid KUidFilterPluginInterface = {0x101F446D};

  2 class CEComFilter : public CBase

  3 {

  4 public:

  5 static CEComFilter* InstallFilterL(RHTTPSession aSession, const TUid aEComFilterUid)

  6 {

  7 TAny* filterPlugin = REComSession::CreateImplementationL(aEComFilterUid, _FOFF(CEComFilter,iEcomDtorID), &aSession);

  8 return REINTERPRET_CAST(CEComFilter*, filterPlugin);

  9 }

  10 11 static CEComFilter* InstallFilterL(RHTTPSession aSession, const TDesC8& aEComFilterDataType)

  12 {

  13 TEComResolverParams resolverParams;

  14 resolverParams.SetDataType(aEComFilterDataType);

  15 16 // This will Leave if the Plugin is not found17 TAny* filterPlugin = REComSession::CreateImplementationL(KUidFilterPluginInterface,

  18 _FOFF(CEComFilter,iEcomDtorID),

  19 &aSession, resolverParams);

  20 return REINTERPRET_CAST(CEComFilter*, filterPlugin);

  21 }

  22 23 ~CEComFilter()

  24 {

  25 REComSession::DestroyedImplementation(iEcomDtorID);

  26 }

  27 28 private:

  29 TUid iEcomDtorID;

  30 };

  通过代码我们可以分析出以下结论:

  第1行,CEComFilter这个ECOM接口类的接口UID(interface_uid)为0x101F446D;

  第5行、第11行,该接口提供了能够获得“ECOM实现(指实现了ECOM接口的类)”的两个工厂方法:InstallFilterL的两个重载函数。第一个函数将“ECOM实现”的“实现UID(implementation_uid)”传给默认ECOM决议者,第二个函数将提示参数传给默认ECOM决议者。

注意:两个工厂方法都会将RHTTPSession http会话的指针通过ECOM决议者创建“ECOM实现”时传入“ECOM实现”的构造函数。

  系统创建了HTTP会话后,会遍历所有实现了CEComFilter接口的ECOM实现UID(implementation_uid),使用工厂方法将ECOM实现实例化,同时传入HTTP会话(RHTTPSession);这样,我们的CEComFilter的实现就能够针对此HTTP会话进行相关监控。

  在此,系统只知道interface_uid,如何通过interface_uid知道有哪些implementation_uid的实现呢?很简单,使用如下函数即可获得:

  class REComSession : public RSessionBase

  {

  ...

  IMPORT_C static void ListImplementationsL(TUid aInterfaceUid, RImplInfoPtrArray& aImplInfoArray);

  ...

  }

  具体流程如下(模拟实现):


  三、如何实现CEComFilter接口

  实现CEComFilter,其实就是实现一个普通的ECOM接口,步骤非常简单,在此不再说明具体步骤。重点在第5步:如何对传入的RHTTPSession进行监控。

  SDK中关于RHTTPSession::FilterCollection()方法的解释:

  Accessor for the filter collection. Note that the filter collection can't be edited after the first transaction has been created.

  注意:如果已经在RHTTPSession上创建了RHTTPTransaction,则不能对Filter Collection进行修改。

  此方法返回类型RHTTPFilterCollection,使用RHTTPFilterCollection::AddFilterL的重载函数向RHTTPSession的监视器队列中添加“监视器”,用于捕获HTTP请求信息:

  IMPORT_C void AddFilterL(MHTTPFilter& aFilter, THTTPEvent aEvent, RStringF aHeader, TInt aStatusCode,TInt aPosition, RStringF aName);

  参数说明:

  aFilter:类型为MHTTPFilter的引用,MHTTPFilter的定义为:

  1 class MHTTPFilter

  2 {

  3 public:

  4 IMPORT_C virtual void MHFUnload(RHTTPSession aSession, THTTPFilterHandle aHandle);

  5 IMPORT_C virtual void MHFLoad(RHTTPSession aSession, THTTPFilterHandle

aHandle);

  6 IMPORT_C virtual void MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent);

  7 IMPORT_C virtual void MHFSessionRunL(const THTTPSessionEvent& aEvent);

  8 IMPORT_C virtual TInt MHFRunError(TInt aError, RHTTPTransaction aTransaction,const THTTPEvent& aEvent);

  9 IMPORT_C virtual TInt MHFSessionRunError(TInt aError, const THTTPSessionEvent& aEvent);

  10 };

  看到这里大家肯定会想起MHTTPTransactionCallback类,此类用在 RHTTPSession::OpenTransactionL中,为HTTP请求的回调,我们使用 RHTTPSession::OpenTransactionL打开一个连接后,往往会在MHFRunL方法中获取HTTP返回的字节、检查 StatusCode、检查HTTP头信息等。而MHTTPFilter也提供MHFRunL方法,我们可以再此方法中对系统任意一个HTTP请求进行捕获,做相应的检查和处理;

  另外,MHTTPFilter类中的方法都不是纯虚函数,我们的类中可以有选择的进行继承;

  aEvent:监视器所监视的HTTP请求事件类型,可以监视所有时间或者只针对Transaction Event、Session Event进行监视,具体请参看SDK中“THTTPEvent”类的文档;

  aHeaders:HTTP接收到指定的头将触发事件;

  aStatusCode:HTTP接收到指定的状态码将触发事件;

  aPosition:监视器所在“位置”,在RHTTPSession完成一个HTTP请求时,会分为几个步骤:处理协议、处理缓存、处理状态码、处理UA等,aPosition所代表的“位置”指的就是把监视器插入哪个步骤中进行监视,具体数值及详细解释请参看 MHTTPFilter::TPositions枚举值的说明;

  aName:自定义的监视器的名字。

  根据以上分析,我们可以通过实现CEComFilter接口完成一个Demo:只要系统提交了HTTP进行Url的请求,就弹出一个全局消息框,显示HTTP所请求的Url地址。

  四、实现CEComFilter接口

  完整源代码请点击此处下载

  1、实现接口的类名叫CFelixHttpFilter,定义如下:

  1 class CFelixHttpFilter: public CEComFilter,private MHTTPFilter

  2 {

  3 public:

  4 ~CFelixHttpFilter();

  5 static CFelixHttpFilter* NewL(TAny* aHttpSession);

  6 static CFelixHttpFilter* NewLC(TAny* aHttpSession);

7 8 public:

  9 // MHTTPFilter10 void MHFUnload(RHTTPSession aSession, THTTPFilterHandle aHandle);

  11 void MHFLoad(RHTTPSession aSession, THTTPFilterHandle aHandle);

  12 void MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent);

  13 14 private:

  15 CFelixHttpFilter();

  16 void ConstructL(TAny* aHttpSession);

  17

  18 private:

  19 RStringF iFilterName;

  20 };

  21

  2、关键类代码实现如下:

  1 _LIT8(KFilterName, "FelixFilter");

  2 3 CFelixHttpFilter::~CFelixHttpFilter()

  4 {

  5 iFilterName.Close();

  6 }

  7 8 CFelixHttpFilter* CFelixHttpFilter::NewLC(TAny* aHttpSession)

  9 {

  10 CFelixHttpFilter* self = new (ELeave) CFelixHttpFilter();

  11 CleanupStack::PushL(self);

  12 self->ConstructL(aHttpSession);

  13 return self;

  14 }

  15 16 CFelixHttpFilter* CFelixHttpFilter::NewL(TAny* aHttpSession)

  17 {

  18 CFelixHttpFilter* self = CFelixHttpFilter::NewLC(aHttpSession);

  19 CleanupStack::Pop(); // self;20 return self;

  21 }

  22 23 void CFelixHttpFilter::ConstructL(TAny* aHttpSession)

  24 {

  25 RHTTPSession& httpSession = *(reinterpret_cast(aHttpSession));

  26

  27 iFilterName = httpSession.StringPool().OpenFStringL(KFilterName);

  28 CleanupClosePushL(iFilterName);

  29

  30 httpSession.FilterCollection().AddFilterL(*this, THTTPEvent::EAnyTransactionEvent, RStringF(), KAnyStatusCode, EClientFilters, iFilterName);

  31 CleanupStack::Pop(&iFilterName);

  32 }

  33 34 void CFelixHttpFilter::MHFUnload(RHTTPSession aSession, THTTPFilterHandle aHandle)

  35 {

  36 delete this;

37 }

  38 39 void CFelixHttpFilter::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)

  40 {

  41 if (aEvent == THTTPEvent::ESubmit)

  42 {

  43 // 在此处拦截到请求的Url44 const TDesC8& url = aTransaction.Request().URI().UriDes();

  45 TBuf url16;

  46 url16.Copy(url);

  47

  48 // 效果:将url打印到屏幕上49 CEikonEnv::Static()->InfoWinL(url16, KNullDesC16);

  50 }

  51 }

  52 53 const TImplementationProxy kImplementationTable[]=54 {

  55 IMPLEMENTATION_PROXY_ENTRY(0xA00133E9,CFelixHttpFilter::NewL)

  56 };

  57 58 EXPORT_C const TImplementationProxy*ImplementationGroupProxy(TInt& aTableCount)

  59 {

  60 aTableCount = sizeof(kImplementationTable) /sizeof(TImplementationProxy);

  61 return kImplementationTable;

  62 }

  63

  注意,代码第55行中的0xA00133E9是我们ECOM实现的implementation_uid。

  3、我们的程序UID3为0xA00133E8,ECOM资源文件名:A00133E8.rss,内容如下:

  1 RESOURCE REGISTRY_INFO filterinfo

  2 {

  3 dll_uid = _UID3;

  4 interfaces= 5 {

  6 INTERFACE_INFO

  7 {

  8 interface_uid = 0x101F446D;

  9 implementations =

  10 {

  11 IMPLEMENTATION_INFO

  12 {

  13 implementation_uid = 0xA00133E9;

  14 version_no = 1;

  15 display_name = "Felix Filter";

  16 default_data = "HTTP/+FELIXFILTER";

  17 opaque_data = "";

  18 }

  19 };

  20 }

  21 };

  22 }

  23

  以上资源文件第16行,default_data的作用:

  HTTP监视器能够“主动的”加载或者“被动的”加载,这取决于你资源文件中IMPLEMENTATION_INFO小节中的“default_data”字段(以上代码第16行)所设定的值,有如下三个加载选项被支持:

HTTP/+FELIXFILTER:监视器总是能自动加载

  HTTP/-FELIXFILTER:监视器不会加载,用户只能手动的加载它

  HTTP/FELIXFILTER:除非用户从监视器集合中移除,监视器都会加载

  五、如何实现手机下载软件

  基本概念都已经清楚,现在我们来想想如何实现手机上的迅雷。根据以下流程可以容易的做到:

  按照以上步骤,实现CEComFilter接口,生成基于多态dll的plugin;

  重写MHTTPFilter::MHFRunL方法;

  因为要做下载软件不可能在MHFRunL方法中简单地通过判断Url来判断文件类型,必须通过判断服务器返回的MIME类型是否为所支持下载文件类型的MIME,因此得判断aEvent是否为“服务器端Headers返回”的状态,来获取返回头的“Content-Type”字段;

  判断MHFRunL方法中的aEvent是否为EGotResponseHeaders;

  通过aTransaction.Response().GetHeaderCollection()获取字段“Content-Type”的值,通过“自定义字符串池(将在以后章节介绍)”的技术加快MIME的匹配对比;

  如果服务器返回的MIME类型满足下载工具支持的类型,则弹出全局询问框,询问用户是否通过下载工具下载;

  用户选择“是”,则将aTransaction.Cancel(),获取请求的Url,启动下载程序并将Url发送过去;

  用户选择“否”,什么事都不做。

  评论这张
 
阅读(621)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018