基于Vc++开发IIS7以及IIS6的万能筛选器
BOOL CTestFilter::GetFilterVersion(PHTTP_FILTER_VERSION pVer) { // Call default implementation for initialization CTestFilter::GetFilterVersion(pVer); // Clear the flags set by base class pVer->dwFlags &= ~SF_NOTIFY_ORDER_MASK; // Set the flags we are interested in pVer->dwFlags |= SF_NOTIFY_ORDER_HIGH| SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_SEND_RAW_DATA; // Load description string TCHAR sz[SF_MAX_FILTER_DESC_LEN+1]; ISAPIVERIFY(::LoadString(AfxGetResourceHandle(), IDS_FILTER, sz, SF_MAX_FILTER_DESC_LEN)); _tcscpy(pVer->lpszFilterDesc, sz); return TRUE; }不要关注其他代码,只需要关注第10行,这几个系统常量是用来指定筛选器需要拦截哪些http内容的,我简单拦截类型有好几种,详细信息可以查询msdn的文档。
我只简单介绍下其中的SF_NOTIFY_PREPROC_HEADERS和 SF_NOTIFY_SEND_RAW_DATA:
SF_NOTIFY_PREPROC_HEADERS代表在iis处理http的header请求时触发一次拦截 SF_NOTIFY_SEND_RAW_DATA代表在iis向客户端回写http的response内容前触发拦截,在这个拦截当中,你可以接收到response的指针,修改response的内容。3)还是用wizard,去实现CHttpFilter基类的各个拦截方法(需要与前面的GetFilterVersion方法中指定的拦截事件对应,否则iis不会触发这些方法的),这里我还是介绍下与上一步向对应的OnSendRawData(CHttpFilterContext* pfc, PHTTP_FILTER_RAW_DATA pRawData)和OnPreprocHeaders(CHttpFilterContext* pfc,PHTTP_FILTER_PREPROC_HEADERS pHeaders)两个函数,大家可以很容易地看出这两个函数与前面两个拦截类型的对应关系。
OnPreprocHeaders是专门用来在iis处理http请求前,对客户端的http header进行处理,其中入参pHeaders是个结构型数据,其中包含了一个指向header对象的指针,我们可以拿到这个指针进行预处理,并可以修改headers的内容。 OnSendRawData可以说是这些方法中最强悍的了,它可以拿到iis的response内容,并可以修改response的内容,其中pRawData这个结构型的入参就包含了response内容的指针还是上代码给大家讲讲这个函数是如何去修改一切的吧:
DWORD CTestFilter::OnSendRawData(CHttpFilterContext* pfc, PHTTP_FILTER_RAW_DATA pRawData) { // TODO: Add your specialized code here and/or call the base class CString resp="Hello isapi!!"; void* mem=(void*)pfc->AllocMem(resp.GetLength());//必须使用isapi提供的申请内存方法,才能正常返回http内容,用isapi的方法申请的内存,iis会自动帮你回收 memset(mem,0,resp.GetLength()); memcpy(mem,resp.GetBuffer(0),resp.GetLength()); pRawData->cbInBuffer=resp.GetLength(); pRawData->cbInData=resp.GetLength(); pRawData->pvInData=mem; return SF_STATUS_REQ_NEXT_NOTIFICATION; }
#define _WINSOCKAPI_ #include <windows.h> #include <sal.h> #include <httpserv.h> class CMyHttpModule: public CHttpModule { public: REQUEST_NOTIFICATION_STATUS OnBeginRequest( IN IHttpContext * pHttpContext, IN IHttpEventProvider * pProvider ) { UNREFERENCED_PARAMETER( pProvider ); // 创建一个 HRESULT 来接收方法返回值. HRESULT hr; // 获取一个指向response对象的指针. IHttpResponse * pHttpResponse = pHttpContext->GetResponse(); if (pHttpResponse != NULL) { // 直接清理掉原来的response内容. pHttpResponse->Clear(); // 设置response的格式. pHttpResponse->SetHeader( HttpHeaderContentType,"text/plain", (USHORT)strlen("text/plain"),TRUE); PCSTR pszBuffer = "Hello HttpModule!!!"; // 创建一个数据块. HTTP_DATA_CHUNK dataChunk; // 把数据块类型设置成http类型的(后续的内存清理工作就会由iis容器自己完成). dataChunk.DataChunkType = HttpDataChunkFromMemory; DWORD cbSent; // 给数据块赋值. dataChunk.FromMemory.pBuffer = (PVOID) pszBuffer; dataChunk.FromMemory.BufferLength = (USHORT) strlen(pszBuffer); // 将数据块插入到response内容中. hr = pHttpResponse->WriteEntityChunks( &dataChunk,1,FALSE,TRUE,&cbSent); if (FAILED(hr)) { pHttpResponse->SetStatus(500,"Server Error",0,hr); } return RQ_NOTIFICATION_FINISH_REQUEST; } return RQ_NOTIFICATION_CONTINUE; } }
class CMyHttpModuleFactory : public IHttpModuleFactory { public: HRESULT GetHttpModule( OUT CHttpModule ** ppModule, IN IModuleAllocator * pAllocator ) { UNREFERENCED_PARAMETER( pAllocator ); // 实例化一个模块的指针. CMyHttpModule * pModule = new CMyHttpModule; if (!pModule) { return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); } else { *ppModule = pModule; pModule = NULL; return S_OK; } } void Terminate() { // 清理自己的内存. delete this; } }; // 用来注册模块工厂的方法. HRESULT __stdcall RegisterModule( DWORD dwServerVersion, IHttpModuleRegistrationInfo * pModuleInfo, IHttpServer * pGlobalInfo ) { UNREFERENCED_PARAMETER( dwServerVersion ); UNREFERENCED_PARAMETER( pGlobalInfo ); // 设置需要拦截的方式,这里设置的是给客户端返回response内容前,和我们之前iis6的示例类似,可以设置多个,但必须和你的httpmodule中对应. return pModuleInfo->SetRequestNotifications( new CMyHttpModuleFactory, RQ_BEGIN_REQUEST, 0 ); }