REQUEST_NOTIFICATION_STATUS CCacheHelperModule::OnSendResponse(
    IN IHttpContext* pHttpContext, IN ISendResponseProvider* pProvider)
{
    UNREFERENCED_PARAMETER(pProvider);

    IHttpResponse* pHttpResponse = pHttpContext->GetResponse();
    IHttpRequest* pHttpRequest = pHttpContext->GetRequest();

    if ((pHttpResponse != NULL) && (pHttpRequest != NULL))
    {
        HTTP_COOKED_URL url = pHttpRequest->GetRawHttpRequest()->CookedUrl;

        if (!m_pConfig->UrlHasSitePrefix(url.pAbsPath))
            return RQ_NOTIFICATION_CONTINUE;

        CachingBehavior behavior = CachingBehaviorIgnore;

        USHORT cchXCache;
        PCSTR szXCache = pHttpResponse->GetHeader((PCSTR)"X-Cache", &cchXCache);
        if (cchXCache > 0)
        {
            behavior = (CachingBehavior)StrToIntA(szXCache);
            pHttpResponse->DeleteHeader((PCSTR)"X-Cache");
        }
        else
        {
            behavior = m_pConfig->GetCachePolicy(url.pAbsPath);
        }
        
        switch (behavior)
        {
        case CachingBehaviorPrivate:
            MakeUncacheable(pHttpResponse);
            break;
        case CachingBehaviorConditional:
            MakeConditionallyCacheable(pHttpResponse);
            break;
        case CachingBehaviorPublic:
            MakeCacheable(pHttpResponse);
            break;
        }
    }

    return RQ_NOTIFICATION_CONTINUE;
}
Exemple #2
0
REQUEST_NOTIFICATION_STATUS
CMyHttpModule::OnBeginRequest(
    IN IHttpContext * pHttpContext,
    IN IHttpEventProvider * pProvider
)
{
    HRESULT                         hr                  = S_OK;
    IHttpRequest*                   pRequest            = NULL;
	MODSECURITY_STORED_CONTEXT*		pConfig = NULL;
    
    UNREFERENCED_PARAMETER ( pProvider );

	EnterCriticalSection(&m_csLock);

    if ( pHttpContext == NULL ) 
    {
        hr = E_UNEXPECTED;
        goto Finished;
    }

    pRequest = pHttpContext->GetRequest();

    if ( pRequest == NULL )
    {
        hr = E_UNEXPECTED;
        goto Finished;
    }

    hr = MODSECURITY_STORED_CONTEXT::GetConfig(pHttpContext, &pConfig );
    
    if ( FAILED( hr ) )
    {
        //hr = E_UNEXPECTED;
		hr = S_OK;
        goto Finished;
    }

	// If module is disabled, dont go any further
	//
	if( pConfig->GetIsEnabled() == false )
	{
        goto Finished;
	}

	// every 3 seconds we check for changes in config file
	//
	DWORD ctime = GetTickCount();

	if(pConfig->m_Config == NULL || (ctime - pConfig->m_dwLastCheck) > 3000)
	{
		char *path;
		USHORT pathlen;

		hr = pConfig->GlobalWideCharToMultiByte(pConfig->GetPath(), wcslen(pConfig->GetPath()), &path, &pathlen);

		if ( FAILED( hr ) )
		{
			hr = E_UNEXPECTED;
			goto Finished;
		}

		WIN32_FILE_ATTRIBUTE_DATA fdata;
		BOOL ret;

		ret = GetFileAttributesEx(path, GetFileExInfoStandard, &fdata);

		pConfig->m_dwLastCheck = ctime;

		if(pConfig->m_Config == NULL || (ret != 0 && (pConfig->m_LastChange.dwLowDateTime != fdata.ftLastWriteTime.dwLowDateTime ||
			pConfig->m_LastChange.dwHighDateTime != fdata.ftLastWriteTime.dwHighDateTime)))
		{
			pConfig->m_LastChange.dwLowDateTime = fdata.ftLastWriteTime.dwLowDateTime;
			pConfig->m_LastChange.dwHighDateTime = fdata.ftLastWriteTime.dwHighDateTime;

			pConfig->m_Config = modsecGetDefaultConfig();

			PCWSTR servpath = pHttpContext->GetApplication()->GetApplicationPhysicalPath();
			char *apppath;
			USHORT apppathlen;

			hr = pConfig->GlobalWideCharToMultiByte((WCHAR *)servpath, wcslen(servpath), &apppath, &apppathlen);

			if ( FAILED( hr ) )
			{
				delete path;
				hr = E_UNEXPECTED;
				goto Finished;
			}

			if(path[0] != 0)
			{
				const char * err = modsecProcessConfig((directory_config *)pConfig->m_Config, path, apppath);

				if(err != NULL)
				{
					WriteEventViewerLog(err, EVENTLOG_ERROR_TYPE);
					delete apppath;
					delete path;
					goto Finished;
				}
			}
			delete apppath;
		}
		delete path;
	}

	conn_rec *c;
	request_rec *r;

	c = modsecNewConnection();

	modsecProcessConnection(c);

	r = modsecNewRequest(c, (directory_config *)pConfig->m_Config);

	// on IIS we force input stream inspection flag, because its absence does not add any performance gain
	// it's because on IIS request body must be restored each time it was read
	//
	modsecSetConfigForIISRequestBody(r);

	REQUEST_STORED_CONTEXT *rsc = new REQUEST_STORED_CONTEXT();

	rsc->m_pConnRec = c;
	rsc->m_pRequestRec = r;
	rsc->m_pHttpContext = pHttpContext;
	rsc->m_pProvider = pProvider;

	pHttpContext->GetModuleContextContainer()->SetModuleContext(rsc, g_pModuleContext);

	StoreIISContext(r, rsc);

	HTTP_REQUEST *req = pRequest->GetRawHttpRequest();

	r->hostname = ConvertUTF16ToUTF8(req->CookedUrl.pHost, req->CookedUrl.HostLength / sizeof(WCHAR), r->pool);
	r->path_info = ConvertUTF16ToUTF8(req->CookedUrl.pAbsPath, req->CookedUrl.AbsPathLength / sizeof(WCHAR), r->pool);

	if(r->hostname == NULL)
	{
		if(req->Headers.KnownHeaders[HttpHeaderHost].pRawValue != NULL)
			r->hostname = ZeroTerminate(req->Headers.KnownHeaders[HttpHeaderHost].pRawValue,
										req->Headers.KnownHeaders[HttpHeaderHost].RawValueLength, r->pool);
	}

	int port = 0;
	char *port_str = NULL;

	if(r->hostname != NULL)
	{
		int k = 0;
		char *ptr = (char *)r->hostname;

		while(*ptr != 0 && *ptr != ':')
			ptr++;

		if(*ptr == ':')
		{
			*ptr = 0;
			port_str = ptr + 1;
			port = atoi(port_str);
		}
	}

	if(req->CookedUrl.pQueryString != NULL && req->CookedUrl.QueryStringLength > 0)
		r->args = ConvertUTF16ToUTF8(req->CookedUrl.pQueryString + 1, (req->CookedUrl.QueryStringLength / sizeof(WCHAR)) - 1, r->pool);

#define _TRANSHEADER(id,str) if(req->Headers.KnownHeaders[id].pRawValue != NULL) \
	{\
		apr_table_setn(r->headers_in, str, \
			ZeroTerminate(req->Headers.KnownHeaders[id].pRawValue, req->Headers.KnownHeaders[id].RawValueLength, r->pool)); \
	}

    _TRANSHEADER(HttpHeaderCacheControl, "Cache-Control");
    _TRANSHEADER(HttpHeaderConnection, "Connection");
    _TRANSHEADER(HttpHeaderDate, "Date");
    _TRANSHEADER(HttpHeaderKeepAlive, "Keep-Alive");             
    _TRANSHEADER(HttpHeaderPragma, "Pragma");                
    _TRANSHEADER(HttpHeaderTrailer, "Trailer");               
    _TRANSHEADER(HttpHeaderTransferEncoding, "Transfer-Encoding");      
    _TRANSHEADER(HttpHeaderUpgrade, "Upgrade");               
    _TRANSHEADER(HttpHeaderVia, "Via");                   
    _TRANSHEADER(HttpHeaderWarning, "Warning");               
    _TRANSHEADER(HttpHeaderAllow, "Allow");                 
    _TRANSHEADER(HttpHeaderContentLength, "Content-Length");         
    _TRANSHEADER(HttpHeaderContentType, "Content-Type");           
    _TRANSHEADER(HttpHeaderContentEncoding, "Content-Encoding");       
    _TRANSHEADER(HttpHeaderContentLanguage, "Content-Language");       
    _TRANSHEADER(HttpHeaderContentLocation, "Content-Location");       
    _TRANSHEADER(HttpHeaderContentMd5, "Content-Md5");            
    _TRANSHEADER(HttpHeaderContentRange, "Content-Range");          
    _TRANSHEADER(HttpHeaderExpires, "Expires");               
    _TRANSHEADER(HttpHeaderLastModified, "Last-Modified");          
    _TRANSHEADER(HttpHeaderAccept, "Accept");                
    _TRANSHEADER(HttpHeaderAcceptCharset, "Accept-Charset");        
    _TRANSHEADER(HttpHeaderAcceptEncoding, "Accept-Encoding");        
    _TRANSHEADER(HttpHeaderAcceptLanguage, "Accept-Language");        
    _TRANSHEADER(HttpHeaderAuthorization, "Authorization");         
    _TRANSHEADER(HttpHeaderCookie, "Cookie");                
    _TRANSHEADER(HttpHeaderExpect, "Expect");                
    _TRANSHEADER(HttpHeaderFrom, "From");                  
    _TRANSHEADER(HttpHeaderHost, "Host");                  
    _TRANSHEADER(HttpHeaderIfMatch, "If-Match");               
    _TRANSHEADER(HttpHeaderIfModifiedSince, "If-Modified-Since");       
    _TRANSHEADER(HttpHeaderIfNoneMatch, "If-None-Match");           
    _TRANSHEADER(HttpHeaderIfRange, "If-Range");               
    _TRANSHEADER(HttpHeaderIfUnmodifiedSince, "If-Unmodified-Since");     
    _TRANSHEADER(HttpHeaderMaxForwards, "Max-Forwards");           
    _TRANSHEADER(HttpHeaderProxyAuthorization, "Proxy-Authorization");    
    _TRANSHEADER(HttpHeaderReferer, "Referer");               
    _TRANSHEADER(HttpHeaderRange, "Range");                 
    _TRANSHEADER(HttpHeaderTe, "TE");                    
    _TRANSHEADER(HttpHeaderTranslate, "Translate");             
    _TRANSHEADER(HttpHeaderUserAgent, "User-Agent");

#undef _TRANSHEADER

	for(int i = 0; i < req->Headers.UnknownHeaderCount; i++)
	{
		apr_table_setn(r->headers_in, 
			ZeroTerminate(req->Headers.pUnknownHeaders[i].pName, req->Headers.pUnknownHeaders[i].NameLength, r->pool), 
			ZeroTerminate(req->Headers.pUnknownHeaders[i].pRawValue, req->Headers.pUnknownHeaders[i].RawValueLength, r->pool));
	}

	r->content_encoding = apr_table_get(r->headers_in, "Content-Encoding");
	r->content_type = apr_table_get(r->headers_in, "Content-Type");

	const char *lng = apr_table_get(r->headers_in, "Content-Languages");

	if(lng != NULL)
	{
		r->content_languages = apr_array_make(r->pool, 1, sizeof(const char *));

		*(const char **)apr_array_push(r->content_languages) = lng;
	}

	switch(req->Verb)
	{
	case HttpVerbUnparsed:
	case HttpVerbUnknown:
	case HttpVerbInvalid:
    case HttpVerbTRACK:  // used by Microsoft Cluster Server for a non-logged trace
    case HttpVerbSEARCH:
	default:
		r->method = "INVALID";
		r->method_number = M_INVALID;
		break;
	case HttpVerbOPTIONS:
		r->method = "OPTIONS";
		r->method_number = M_OPTIONS;
		break;
	case HttpVerbGET:
	case HttpVerbHEAD:
		r->method = "GET";
		r->method_number = M_GET;
		break;
	case HttpVerbPOST:
		r->method = "POST";
		r->method_number = M_POST;
		break;
    case HttpVerbPUT:
		r->method = "PUT";
		r->method_number = M_PUT;
		break;
    case HttpVerbDELETE:
		r->method = "DELETE";
		r->method_number = M_DELETE;
		break;
    case HttpVerbTRACE:
		r->method = "TRACE";
		r->method_number = M_TRACE;
		break;
    case HttpVerbCONNECT:
		r->method = "CONNECT";
		r->method_number = M_CONNECT;
		break;
    case HttpVerbMOVE:
		r->method = "MOVE";
		r->method_number = M_MOVE;
		break;
    case HttpVerbCOPY:
		r->method = "COPY";
		r->method_number = M_COPY;
		break;
    case HttpVerbPROPFIND:
		r->method = "PROPFIND";
		r->method_number = M_PROPFIND;
		break;
    case HttpVerbPROPPATCH:
		r->method = "PROPPATCH";
		r->method_number = M_PROPPATCH;
		break;
    case HttpVerbMKCOL:
		r->method = "MKCOL";
		r->method_number = M_MKCOL;
		break;
    case HttpVerbLOCK:
		r->method = "LOCK";
		r->method_number = M_LOCK;
		break;
    case HttpVerbUNLOCK:
		r->method = "UNLOCK";
		r->method_number = M_UNLOCK;
		break;
	}

	if(HTTP_EQUAL_VERSION(req->Version, 0, 9))
		r->protocol = "HTTP/0.9";
	else if(HTTP_EQUAL_VERSION(req->Version, 1, 0))
		r->protocol = "HTTP/1.0";
	else
		r->protocol = "HTTP/1.1";

	r->request_time = apr_time_now();

	r->parsed_uri.scheme = "http";
	r->parsed_uri.path = r->path_info;
	r->parsed_uri.hostname = (char *)r->hostname;
	r->parsed_uri.is_initialized = 1;
	r->parsed_uri.port = port;
	r->parsed_uri.port_str = port_str;
	r->parsed_uri.query = r->args;
	r->parsed_uri.dns_looked_up = 0;
	r->parsed_uri.dns_resolved = 0;
	r->parsed_uri.password = NULL;
	r->parsed_uri.user = NULL;
	r->parsed_uri.fragment = NULL;

	r->unparsed_uri = ZeroTerminate(req->pRawUrl, req->RawUrlLength, r->pool);
	r->uri = r->unparsed_uri;

	r->the_request = (char *)apr_palloc(r->pool, strlen(r->method) + 1 + req->RawUrlLength + 1 + strlen(r->protocol) + 1);

	strcpy(r->the_request, r->method);
	strcat(r->the_request, " ");
	strcat(r->the_request, r->uri);
	strcat(r->the_request, " ");
	strcat(r->the_request, r->protocol);

	HTTP_REQUEST_ID httpRequestID;
	char *pszValue = (char *)apr_palloc(r->pool, 24);

	httpRequestID = pRequest->GetRawHttpRequest()->RequestId;

	_ui64toa(httpRequestID, pszValue, 10);

	apr_table_setn(r->subprocess_env, "UNIQUE_ID", pszValue);

	PSOCKADDR pAddr = pRequest->GetRemoteAddress();

#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3
    c->remote_addr = CopySockAddr(r->pool, pAddr);
    c->remote_ip = GetIpAddr(r->pool, pAddr);
#else
    c->client_addr = CopySockAddr(r->pool, pAddr);
	c->client_ip = GetIpAddr(r->pool, pAddr);
#endif
	c->remote_host = NULL;

	int status = modsecProcessRequest(r);

	if(status != DECLINED)
	{
		pHttpContext->GetResponse()->SetStatus(status, "ModSecurity Action");
		pHttpContext->SetRequestHandled();

		hr = E_FAIL;
		goto Finished;
	}

Finished:
	LeaveCriticalSection(&m_csLock);

    if ( FAILED( hr )  )
    {
        return RQ_NOTIFICATION_FINISH_REQUEST;
    }
	return RQ_NOTIFICATION_CONTINUE;
}
REQUEST_NOTIFICATION_STATUS CIISxpressHttpModule::OnSendResponse(IN IHttpContext* pHttpContext, IN ISendResponseProvider* pProvider)
{
	const TCHAR* const pszMethodName = __FUNCTIONT__;	

	// only proceed if filter is enabled
	if (m_Config.GetEnabled() == false)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("module is disabled\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::FilterDisabled);
		return RQ_NOTIFICATION_CONTINUE;
	}

	IHttpRequest* pHttpRequest = pHttpContext->GetRequest();
	if (pHttpRequest == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetRequest() returned NULL\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}	

	IHttpResponse* pHttpResponse = pHttpContext->GetResponse();
	if (pHttpResponse == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetResponse() returned NULL\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}	

	const HTTP_REQUEST* pRawRequest = pHttpRequest->GetRawHttpRequest();
	if (pRawRequest == NULL)
	{		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetRawHttpRequest() returned NULL\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}		

	const HTTP_RESPONSE* pRawResponse = pHttpResponse->GetRawHttpResponse();
	if (pRawResponse == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetRawHttpResponse() returned NULL\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}		

	// we only handle GET or POST (if POST handling is enabled)
	if (pRawRequest->Verb != HttpVerbGET && !(pRawRequest->Verb == HttpVerbPOST && m_Config.HandlePOSTResponses()))
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("request was not GET or POST\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::RequestMethod);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// only handle status code 200
	if (pRawResponse->StatusCode != 200)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("response code is not 200 (%u)\n"),  pRawResponse->StatusCode);
		PerfCountersAddRejectedResponse(IISxpressNativePerf::ResponseCode);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// determine if the request came from localhost
	if (m_Config.GetCompressLocalhost() == false && IsUserLocalhost(pRawRequest) == true)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext,_T("localhost is disabled\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::LocalhostDisabled);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// ***********************************************************************************************************************************

	const char* pszUserAgent = EnsureNotNull(pHttpRequest->GetHeader(HttpHeaderUserAgent));	

	bool excludedUserAgent = false;
	if (m_Config.GetUserAgentExclusionEnabled())
	{
		DWORD dwUserAgentCacheCookie = m_Config.GetLoadCookie();
		
		if (!m_UserAgentCache.GetUserAgentState(dwUserAgentCacheCookie, pszUserAgent, excludedUserAgent))
		{
			HttpUserAgent::UserAgentProducts<std::string> agent;
			if (agent.ParseUserAgentString(pszUserAgent) == S_OK)
			{
				const HttpUserAgent::RuleUserAgents<std::string>& ruleAgents = m_Config.GetExcludedUserAgents();
				if (ruleAgents.Compare(agent))
				{
					excludedUserAgent = true;
				}

				m_UserAgentCache.AddUserAgentState(dwUserAgentCacheCookie, pszUserAgent, excludedUserAgent);
			}
		}
	}

	if (excludedUserAgent)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("the user agent '%s' has been excluded\n"),  pszUserAgent);		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::NeverRuleMatch);
		return RQ_NOTIFICATION_CONTINUE;
	}	

	// ***********************************************************************************************************************************

	// we must have only one chunk and data from memory or file
	if (pRawResponse->EntityChunkCount == 0)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext,_T("can't convert multi-entity buffer, incoming buffer is zero length\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::InvalidContentLength);
		return RQ_NOTIFICATION_CONTINUE;
	}
	else if (pRawResponse->EntityChunkCount != 1)
	{		
		// turn the multi-chunk response into a single chunk - NB. the response
		// must all be in memory
		if (MakeResponseSingleEntityBlock(pHttpContext, pHttpResponse, pRawResponse) == false)
		{
			AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext,_T("can't convert multi-entity buffer\n"));
			PerfCountersAddRejectedResponse(IISxpressNativePerf::MemoryAllocationFailed);
			return RQ_NOTIFICATION_CONTINUE;
		}				
	}	
	else if (pRawResponse->pEntityChunks->DataChunkType != HttpDataChunkFromMemory &&
			pRawResponse->pEntityChunks->DataChunkType != HttpDataChunkFromFileHandle)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext,_T("response isn't HttpDataChunkFromMemory or HttpDataChunkFromFileHandle\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	PHTTP_DATA_CHUNK pEntityChunk = pRawResponse->pEntityChunks;

	if (pEntityChunk == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("the response does not contain any data\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;	
	}

	// TODO: review max size here
	// we don't handle non-zero starting offsets or very large files (>250MB)
	if (pEntityChunk->DataChunkType == HttpDataChunkFromFileHandle &&
		(pEntityChunk->FromFileHandle.ByteRange.StartingOffset.QuadPart > 0 ||
		pEntityChunk->FromFileHandle.ByteRange.Length.QuadPart > (250 * 1024 * 1024)))
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("response has offset > 0 or is too big\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	PCSTR pszContentEncoding = pHttpResponse->GetHeader(HttpHeaderContentEncoding);
	if (pszContentEncoding != NULL && pszContentEncoding[0] != '\0')
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("response already has content encoding\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::AlreadyEncoded);
		return RQ_NOTIFICATION_CONTINUE;
	}

	PCSTR pszTransferEncoding = pHttpResponse->GetHeader(HttpHeaderTransferEncoding);
	if (pszTransferEncoding != NULL && pszTransferEncoding[0] != '\0')
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("response already has transfer encoding\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::AlreadyEncoded);
		return RQ_NOTIFICATION_CONTINUE;
	}

	USHORT nContentTypeLength = 0;
	PCSTR pszContentType = pHttpResponse->GetHeader(HttpHeaderContentType, &nContentTypeLength);			
	if (pszContentType == NULL || nContentTypeLength == 0)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetHeader(HttpHeaderContentType) returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::MissingContentType);
		return RQ_NOTIFICATION_CONTINUE;
	}

	USHORT nLastModifiedLength = 0;
	PCSTR pszLastModified = pHttpResponse->GetHeader(HttpHeaderLastModified, &nLastModifiedLength);			

	// ***********************************************************************
	
	USHORT nAcceptEncodingLength = 0;
	PCSTR pszAcceptEncoding = pHttpRequest->GetHeader(HttpHeaderAcceptEncoding, &nAcceptEncodingLength);	
	if (pszAcceptEncoding == NULL || nAcceptEncodingLength == 0 ||
		(strstr(pszAcceptEncoding, "deflate") == NULL && strstr(pszAcceptEncoding, "gzip") == NULL && strstr(pszAcceptEncoding, "bzip2") == NULL))
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("the client does not accept compressed responses\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::IncompatibleClient);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// ***********************************************************************

	const WCHAR* pszScriptTranslated = pHttpContext->GetScriptTranslated();
	if (pszScriptTranslated == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetScriptTranslated() returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}
		
	// get the url
	CAtlStringA sScriptTranslated(pszScriptTranslated);

	// get the site id
	CAtlStringA sInstanceId;
	sInstanceId.Format("%u", pHttpRequest->GetSiteId());
	
	// get the server name
	PCSTR pszServerName = NULL;		
	DWORD dwServerNameLength = 0;
	pHttpContext->GetServerVariable("SERVER_NAME", &pszServerName, &dwServerNameLength);
	if (pszServerName == NULL || dwServerNameLength == 0)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetServerVariable(\"SERVER_NAME\") returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// get the server port 		
	PCSTR pszServerPort = NULL;		
	DWORD dwServerPortLength = 0;
	pHttpContext->GetServerVariable("SERVER_PORT", &pszServerPort, &dwServerPortLength);
	if (pszServerPort == NULL || dwServerPortLength == 0)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetServerVariable(\"SERVER_PORT\") returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	DWORD dwContentLength = 0;
	if (pEntityChunk->DataChunkType == HttpDataChunkFromMemory)
	{
		dwContentLength = pRawResponse->pEntityChunks->FromMemory.BufferLength;
	}
	else if (pEntityChunk->DataChunkType == HttpDataChunkFromFileHandle)
	{
		dwContentLength = pEntityChunk->FromFileHandle.ByteRange.Length.LowPart;
	}
	else
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("the DataChunkType (%u) is not supported\n"), pEntityChunk->DataChunkType);		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}
	
	IISInfo iisinfo;
	iisinfo.pszInstanceId = sInstanceId;		
	iisinfo.pszServerName = pszServerName;
	iisinfo.pszServerPort = pszServerPort;
	iisinfo.pszURLMapPath = sScriptTranslated;		

	if (m_Config.GetDebugEnabled() || m_Config.GetLoggingLevel() >= IISXPRESS_LOGGINGLEVEL_ENH)	
	{				
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("IIS: server name='%hs'\n"), pszServerName);		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("IIS: server port='%hs'\n"), pszServerPort);
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("IIS: instance id='%hs'\n"), sInstanceId);
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("IIS: URL map path='%hs'\n"), sScriptTranslated);
	}			

	// ***********************************************************************

	const WCHAR* pszURI = pHttpContext->GetScriptName();
	if (pszURI == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetScriptName() returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// get the uri
	CAtlStringA sURI(pszURI);

	// get the query string
	CAtlStringA sQueryString;
	if (pRawRequest->CookedUrl.pQueryString != NULL)
	{
		sQueryString = pRawRequest->CookedUrl.pQueryString + 1;
	}

	// get the remote address
	// TODO: consider using a cache lookup here 
	CAtlStringA sRemoteAddress;
	if (pRawRequest != NULL && pRawRequest->Address.pRemoteAddress != NULL)			
	{
		if (pRawRequest->Address.pRemoteAddress->sa_family == AF_INET)
		{
			struct sockaddr_in* paddrin = (struct sockaddr_in*) pRawRequest->Address.pRemoteAddress;

			// TODO: make this a bit more efficient
			sRemoteAddress.Format("%u.%u.%u.%u", 
				(unsigned) paddrin->sin_addr.S_un.S_un_b.s_b1,
				(unsigned) paddrin->sin_addr.S_un.S_un_b.s_b2,
				(unsigned) paddrin->sin_addr.S_un.S_un_b.s_b3,
				(unsigned) paddrin->sin_addr.S_un.S_un_b.s_b4);
		}
		else if (pRawRequest->Address.pRemoteAddress->sa_family == AF_INET6)
		{
			SOCKADDR_IN6* paddrin = (SOCKADDR_IN6*) pRawRequest->Address.pRemoteAddress;

			// TODO: make this a bit more efficient
			sRemoteAddress.Format("%0x:%0x:%0x:%0x:%0x:%0x:%0x:%0x",				
				::htons(paddrin->sin6_addr.u.Word[0]), ::htons(paddrin->sin6_addr.u.Word[1]),
				::htons(paddrin->sin6_addr.u.Word[2]), ::htons(paddrin->sin6_addr.u.Word[3]),
				::htons(paddrin->sin6_addr.u.Word[4]), ::htons(paddrin->sin6_addr.u.Word[5]),
				::htons(paddrin->sin6_addr.u.Word[6]), ::htons(paddrin->sin6_addr.u.Word[7]));
		}
	}	
		
	RequestInfo requestinfo;				
	requestinfo.pszAcceptEncoding = pszAcceptEncoding;
	requestinfo.pszHostname = EnsureNotNull(pHttpRequest->GetHeader(HttpHeaderHost));
	requestinfo.pszQueryString = sQueryString;
	requestinfo.pszRemoteAddress = sRemoteAddress;
	requestinfo.pszURI = sURI;
	requestinfo.pszUserAgent = pszUserAgent;

	if (m_Config.GetDebugEnabled() || m_Config.GetLoggingLevel() >= IISXPRESS_LOGGINGLEVEL_BASIC)	
	{				
		char* pszMethod = "Unknown";		
		switch (pRawRequest->Verb)
		{
		case HttpVerbGET: pszMethod = "GET"; break;
		case HttpVerbPOST: pszMethod = "POST"; break;
		}

		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: method='%hs'\n"), pszMethod);		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: hostname='%hs'\n"), requestinfo.pszHostname);		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: user agent='%hs'\n"), requestinfo.pszUserAgent);		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: URI='%hs'\n"), requestinfo.pszURI);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: accept-encoding='%hs'\n"), requestinfo.pszAcceptEncoding);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: client address='%hs'\n"), requestinfo.pszRemoteAddress);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: query string='%hs'\n"), requestinfo.pszQueryString);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: content-type='%hs'\n"), pszContentType);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: content-length=%u\n"), dwContentLength);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: response code=%u\n"), pRawResponse->StatusCode);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: last-modified='%hs'\n"), pszLastModified != NULL ? pszLastModified : "null");	
	}			

	// ***********************************************************************	
	
	ResponseInfo responseinfo;		
	responseinfo.dwContentLength = dwContentLength;
	responseinfo.dwResponseCode = pRawResponse->StatusCode;	
	responseinfo.pszContentType = pszContentType;
	responseinfo.pszLastModified = pszLastModified;

	// get a connection to the server from the GIT (if it is connected)
	CComPtr<IIISxpressHTTPRequest> pIISxpressHTTPRequest;
	if (m_pGlobalIISxpressHTTPRequest.GetCookie() != 0)	
	{
		m_pGlobalIISxpressHTTPRequest.CopyTo(&pIISxpressHTTPRequest);
	}

	if (pIISxpressHTTPRequest == NULL)
	{
		// revoke the pointer in the GIT
		m_pGlobalIISxpressHTTPRequest.Revoke();

		// try to connect to the server
		if (GetHTTPRequestObject(pHttpContext, &pIISxpressHTTPRequest) == false || pIISxpressHTTPRequest == NULL)	
		{
			AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("unable to connect to server\n"));					
			PerfCountersAddRejectedResponse(IISxpressNativePerf::NoCompressionServer);
			return RQ_NOTIFICATION_CONTINUE;
		}		

		// store the resulting pointer in the GIT
		m_pGlobalIISxpressHTTPRequest = pIISxpressHTTPRequest;
	}			

	const DWORD dwTimerLoggingLevel = IISXPRESS_LOGGINGLEVEL_FULL;
	bool useTimer = (m_Config.GetDebugEnabled() || m_Config.GetLoggingLevel() >= dwTimerLoggingLevel);

	// start timer
	__int64 nStartTimer = useTimer ? g_Timer.GetMicroSecTimerCount() : 0;

	CAtlStringA sCacheKey;
	if (m_nCacheEnabled != 0)
	{	
		// update the cache perf counters based on the cache cookie
		PerfCountersUpdateCacheStatus(false);	

		if (pszLastModified != NULL && sQueryString.GetLength() == 0)
		{
			ATLASSERT(pszAcceptEncoding != NULL);

			// create the unique key for the response
			ResponseCache::CreateResponseCacheKey(
				sInstanceId, sInstanceId.GetLength(),
				sURI, sURI.GetLength(),
				responseinfo.pszContentType, nContentTypeLength,
				dwContentLength, 
				pszLastModified, nLastModifiedLength,
				pszAcceptEncoding, nAcceptEncodingLength,
				sCacheKey);

			// try to get a compressed response from the cache
			HCACHEITEM hCacheItem = NULL;
			HRESULT hr = m_ResponseCache.LookupEntry(sCacheKey, &hCacheItem);
			if (hr == S_OK)
			{
				// get the item from the cache
				ResponseCacheItem* pCacheItem;
				DWORD dwCacheItemSize = 0;
				hr = m_ResponseCache.GetData(hCacheItem, (void**) &pCacheItem, &dwCacheItemSize);	

				void* pBuffer = pHttpContext->AllocateRequestMemory(pCacheItem->dwContentLength);
				if (pBuffer != NULL)
				{
					pHttpResponse->SetHeader(HttpHeaderContentLength, pCacheItem->sContentLength, 
						(USHORT) pCacheItem->sContentLength.GetLength(), TRUE);

					pHttpResponse->SetHeader(HttpHeaderContentEncoding, pCacheItem->sContentEncoding, 
						(USHORT) pCacheItem->sContentEncoding.GetLength(), TRUE);

					pEntityChunk->DataChunkType = HttpDataChunkFromMemory;
					pEntityChunk->FromMemory.pBuffer = pBuffer;
					pEntityChunk->FromMemory.BufferLength = pCacheItem->dwContentLength;
					memcpy(pEntityChunk->FromMemory.pBuffer, pCacheItem->pbyData, pCacheItem->dwContentLength);

					hr = m_ResponseCache.ReleaseEntry(hCacheItem);

					pIISxpressHTTPRequest->NotifyCacheHit(&iisinfo, &requestinfo, &responseinfo, pCacheItem->dwContentLength);

					PerfCountersAddCachedResponse(dwContentLength, pCacheItem->dwContentLength);

					// calculate timer duration
					__int64 nEndTimer = useTimer ? g_Timer.GetMicroSecTimerCount() : 0;
					__int64 nInterval = nEndTimer - nStartTimer;

					AppendLogMessage(dwTimerLoggingLevel, pszMethodName, pHttpContext, _T("response resolved from cache, call took %I64d us\n"), nInterval);

					return RQ_NOTIFICATION_CONTINUE;
				}
				
				// we failed to handle the cached response, so just proceed as normal (we need to free the
				// cache entry tho)
				hr = m_ResponseCache.ReleaseEntry(hCacheItem);
			}		
		}
	}

	DWORD dwFilterContext = 0;
	HRESULT hr = pIISxpressHTTPRequest->OnSendResponse(&iisinfo, &requestinfo, &responseinfo, &dwFilterContext);

	// calculate timer duration
	__int64 nEndTimer = useTimer ? g_Timer.GetMicroSecTimerCount() : 0;
	__int64 nInterval = nEndTimer - nStartTimer;

	if (FAILED(hr) == TRUE)
	{
		// get the facility code of the failure
		DWORD dwFacility = HRESULT_FACILITY(hr);

		// if it isn't NULL or ITF then assume the server is now invalid
		if (dwFacility != FACILITY_NULL && dwFacility != FACILITY_ITF)
		{
			// the GIT is invalid, dump it
			m_pGlobalIISxpressHTTPRequest.Revoke();

			// the server connection is invalid, dump it
			pIISxpressHTTPRequest = NULL;		
		}		
	}

	AppendLogMessage(dwTimerLoggingLevel, pszMethodName, pHttpContext, _T("OnSendResponse() returns 0x%08x, call took %I64d us\n"), hr, nInterval);	

	if (FAILED(hr) == TRUE)
	{			
		// TODO: need to handle generic HRs
		PerfCountersAddRejectedResponse(hr);

		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("won't handle response (0x%08x)\n"), hr);					

		return RQ_NOTIFICATION_CONTINUE;
	}

	AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("will handle response (0x%08x)\n"), hr);		

	if (pEntityChunk->DataChunkType == HttpDataChunkFromFileHandle)
	{
		DWORD dwAllocatedBlockSize = 0;
		BYTE* pbyData = AllocateMemoryBlockForOverlappedIO(pHttpContext, dwContentLength, dwAllocatedBlockSize);
		if (pbyData != NULL)
		{		
			// we are about to perform overlapped IO, so setup the OVERLAPPED struct into a default state
			OVERLAPPED overlapped;
			memset(&overlapped, 0, sizeof(overlapped));

			HandleObject FileEvent;
			bool bHandleFromPool = m_hEventHandlePool.GetHandleObject(FileEvent);									

			// if we failed to get a handle from the pool then we need to make one
			if (bHandleFromPool == false)
			{
				HANDLE hReadOK = ::CreateEvent(NULL, TRUE, FALSE, NULL);
				FileEvent = HandleObject(hReadOK);
			}

			// use the event handle so we can track the IO completion
			overlapped.hEvent = FileEvent;

			// ask for a file read (asynchronous)		
			DWORD dwStatus = ::ReadFile(pEntityChunk->FromFileHandle.FileHandle, pbyData, dwAllocatedBlockSize, NULL, &overlapped);

			// wait for the IO to complete (NB. don't really use this since INFINITE can block forever)			
			::WaitForSingleObject(FileEvent, INFINITE);

			// we must return the handle (if it came from the pool)
			if (bHandleFromPool == true)
			{
				m_hEventHandlePool.ReturnHandleObject(FileEvent);
			}

			pEntityChunk->DataChunkType = HttpDataChunkFromMemory;
			pEntityChunk->FromMemory.BufferLength = dwContentLength;
			pEntityChunk->FromMemory.pBuffer = pbyData;
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
	}
	else if (pEntityChunk->DataChunkType == HttpDataChunkFromMemory)
	{
		// allocate the memory block
		BYTE* pbyData = (BYTE*) pHttpContext->AllocateRequestMemory(dwContentLength);			
		if (pbyData != NULL)
		{
			// copy the response data
			memcpy(pbyData, pEntityChunk->FromMemory.pBuffer, pEntityChunk->FromMemory.BufferLength);

			// change the buffer pointer
			pEntityChunk->FromMemory.pBuffer = pbyData;
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
	}		

	// allocate the response stream
	CComPtr<IStream> pStream;
	CComObject<CResponseStream>* pResponseStream = NULL;	
	if (hr == S_OK)
	{
		hr = CComObject<CResponseStream>::CreateInstance(&pResponseStream);
		if (hr == S_OK)
		{		
			pStream = pResponseStream;
		}
	}

	// catch any memory issues
	if (FAILED(hr) == TRUE)
	{			
		// abort the compression
		pIISxpressHTTPRequest->AbortRequest(dwFilterContext);

		// TODO: need to handle generic HRs
		PerfCountersAddRejectedResponse(hr);

		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("won't handle response (0x%08x)\n"), hr);					

		return RQ_NOTIFICATION_CONTINUE;
	}

	pResponseStream->AttachBuffer(pEntityChunk->FromMemory.pBuffer, dwContentLength, 0);

	char szContentEncoding[32] = "";
	hr = pIISxpressHTTPRequest->OnSendRawData(dwFilterContext, pStream, dwContentLength, FALSE, (signed char*) szContentEncoding, _countof(szContentEncoding));
	if (hr == S_OK)
	{		
		DWORD dwOriginalSize = dwContentLength;
		DWORD dwCompressedSize = pResponseStream->GetOffset();				

		// set the new content length into the header
		CAtlStringA sContentLength;
		sContentLength.Format("%u", dwCompressedSize);
		pHttpResponse->SetHeader(HttpHeaderContentLength, sContentLength, (USHORT) sContentLength.GetLength(), TRUE);

		// set the new size into the buffer
		pRawResponse->pEntityChunks->FromMemory.BufferLength = dwCompressedSize;
		
		pHttpResponse->SetHeader(HttpHeaderContentEncoding, szContentEncoding, (USHORT) strlen(szContentEncoding), TRUE);

		// add the item to the cache if we have a key (it means the data is cachable)
		if (dwCompressedSize < dwOriginalSize &&
			m_nCacheEnabled != 0 && sCacheKey.GetLength() > 0)
		{
			ResponseCacheItem* pCacheItem = new ResponseCacheItem();
			if (pCacheItem != NULL)
			{
				pCacheItem->sContentEncoding = szContentEncoding;
				pCacheItem->sContentLength = sContentLength;
				pCacheItem->dwContentLength = dwCompressedSize;
				pCacheItem->pbyData = new BYTE[dwCompressedSize];
				if (pCacheItem->pbyData != NULL)
				{
					// copy the compressed data and add it to the cache
					memcpy(pCacheItem->pbyData, pEntityChunk->FromMemory.pBuffer, dwCompressedSize);
					m_ResponseCache.Add(sCacheKey, pCacheItem, pCacheItem->dwContentLength, NULL, NULL, NULL, m_ResponseCacheItemDeallocator);

					PerfCountersUpdateCacheStatus(true);
				}
			}
		}

		PerfCountersAddCompressedResponse(dwContentLength, dwCompressedSize);

		// make sure the context is released
		hr = pIISxpressHTTPRequest->OnEndOfRequest(dwFilterContext, NULL, 0, FALSE, NULL, 0);		
	}
	else
	{
		// abort the compression
		pIISxpressHTTPRequest->AbortRequest(dwFilterContext);

		// TODO: need to handle generic HRs
		PerfCountersAddRejectedResponse(hr);

		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("won't handle response (0x%08x)\n"), hr);							
	}

	return RQ_NOTIFICATION_CONTINUE;
}
Exemple #4
0
HRESULT CHttpProtocol::SerializeRequestHeaders(CNodeHttpStoredContext* ctx, void** result, DWORD* resultSize, DWORD* resultLength)
{
	HRESULT hr;
	PCSTR originalUrl = NULL;
	USHORT originalUrlLength;
	DWORD remoteHostSize = INET6_ADDRSTRLEN + 1;
	char remoteHost[INET6_ADDRSTRLEN + 1];
	BOOL addXFF;
	char** serverVars;
	int serverVarCount;

	CheckNull(ctx);
	CheckNull(result);
	CheckNull(resultSize);
	CheckNull(resultLength);

	IHttpContext* context = ctx->GetHttpContext();

	DWORD bufferLength = CModuleConfiguration::GetInitialRequestBufferSize(context);
	DWORD offset = 0;
	IHttpRequest* request = context->GetRequest();
	HTTP_REQUEST* raw = request->GetRawHttpRequest();
	USHORT major, minor;
	const int tmpSize = 256;
	char tmp[tmpSize];
	PCSTR method = request->GetHttpMethod();
	
	ErrorIf(NULL == (*result = context->AllocateRequestMemory(bufferLength)), ERROR_NOT_ENOUGH_MEMORY);	

	// Determine whether response entity body is to be expected

	if (method && 0 == strcmpi("HEAD", method))
	{
		// HEAD requests do not have response entity body
		// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4

		ctx->SetExpectResponseBody(FALSE);
	}
	
	// Request-Line

	CheckError(CHttpProtocol::Append(context, method, 0, result, &bufferLength, &offset));
	CheckError(CHttpProtocol::Append(context, " ", 1, result, &bufferLength, &offset));
	CheckError(CHttpProtocol::Append(context, ctx->GetTargetUrl(), ctx->GetTargetUrlLength(), result, &bufferLength, &offset));
	request->GetHttpVersion(&major, &minor);
	if (1 == major && 1 == minor)
	{
		CheckError(CHttpProtocol::Append(context, " HTTP/1.1\r\n", 11, result, &bufferLength, &offset));
	}
	else if (1 == major && 0 == minor)
	{
		CheckError(CHttpProtocol::Append(context, " HTTP/1.0\r\n", 11, result, &bufferLength, &offset));
	}
	else
	{
		sprintf(tmp, " HTTP/%d.%d\r\n", major, minor);
		CheckError(CHttpProtocol::Append(context, tmp, 0, result, &bufferLength, &offset));
	}

	// Known headers

	for (int i = 0; i < HttpHeaderRequestMaximum; i++)
	{
		if (raw->Headers.KnownHeaders[i].RawValueLength > 0)
		{
			CheckError(CHttpProtocol::Append(context, CHttpProtocol::httpRequestHeaders[i], 0, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, raw->Headers.KnownHeaders[i].pRawValue, raw->Headers.KnownHeaders[i].RawValueLength, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));
		}
	}

	// Unknown headers

	if (TRUE == (addXFF = CModuleConfiguration::GetEnableXFF(context)))
	{
		PSOCKADDR addr = request->GetRemoteAddress();
		DWORD addrSize = addr->sa_family == AF_INET ? sizeof SOCKADDR_IN : sizeof SOCKADDR_IN6;
		ErrorIf(0 != WSAAddressToString(addr, addrSize, NULL, remoteHost, &remoteHostSize), GetLastError());
	}

	for (int i = 0; i < raw->Headers.UnknownHeaderCount; i++)
	{
		CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pName, raw->Headers.pUnknownHeaders[i].NameLength, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pRawValue, raw->Headers.pUnknownHeaders[i].RawValueLength, result, &bufferLength, &offset));

		if (addXFF && 15 == raw->Headers.pUnknownHeaders[i].NameLength && 0 == strcmpi("X-Forwarded-For", raw->Headers.pUnknownHeaders[i].pName))
		{
			// augment existing X-Forwarded-For header

			CheckError(CHttpProtocol::Append(context, ", ", 2, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, remoteHost, remoteHostSize - 1, result, &bufferLength, &offset));

			addXFF = FALSE;
		}

		CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));		
	}

	if (addXFF)
	{
		// add a new X-Forwarded-For header

		CheckError(CHttpProtocol::Append(context, "X-Forwarded-For: ", 17, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, remoteHost, remoteHostSize - 1, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));		
	}

	// promote server variables

	CheckError(CModuleConfiguration::GetPromoteServerVars(context, &serverVars, &serverVarCount));
	while (serverVarCount)
	{
		serverVarCount--;
		PCSTR varValue;
		DWORD varValueLength;
		if (S_OK == context->GetServerVariable(serverVars[serverVarCount], &varValue, &varValueLength))
		{
			CheckError(CHttpProtocol::Append(context, "X-iisnode-", 10, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, serverVars[serverVarCount], 0, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, varValue, varValueLength, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));		
		}
	}

	// CRLF

	CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));

	*resultSize = bufferLength;
	*resultLength = offset;

	return S_OK;
Error:
	
	if (NULL != result)
	{
		*result = NULL;
	}
	if (NULL != resultLength)
	{
		*resultLength = 0;
	}
	if (NULL != resultSize)
	{
		*resultSize = 0;
	}

	return hr;
}
Exemple #5
0
HRESULT CHttpProtocol::SerializeRequestHeaders(IHttpContext* context, void** result, DWORD* resultSize, DWORD* resultLength)
{
	HRESULT hr;

	CheckNull(context);
	CheckNull(result);
	CheckNull(resultSize);
	CheckNull(resultLength);

	DWORD bufferLength = CModuleConfiguration::GetInitialRequestBufferSize(context);
	DWORD offset = 0;
	IHttpRequest* request = context->GetRequest();
	HTTP_REQUEST* raw = request->GetRawHttpRequest();
	USHORT major, minor;
	char tmp[256];
	
	ErrorIf(NULL == (*result = context->AllocateRequestMemory(bufferLength)), ERROR_NOT_ENOUGH_MEMORY);	
	
	// Request-Line

	CheckError(CHttpProtocol::Append(context, request->GetHttpMethod(), 0, result, &bufferLength, &offset));
	CheckError(CHttpProtocol::Append(context, " ", 1, result, &bufferLength, &offset));
	CheckError(CHttpProtocol::Append(context, raw->pRawUrl, raw->RawUrlLength, result, &bufferLength, &offset));
	request->GetHttpVersion(&major, &minor);
	sprintf(tmp, " HTTP/%d.%d\r\n", major, minor);
	CheckError(CHttpProtocol::Append(context, tmp, 0, result, &bufferLength, &offset));

	// Known headers

	for (int i = 0; i < HttpHeaderRequestMaximum; i++)
	{
		if (raw->Headers.KnownHeaders[i].RawValueLength > 0)
		{
			CheckError(CHttpProtocol::Append(context, CHttpProtocol::httpRequestHeaders[i], 0, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, raw->Headers.KnownHeaders[i].pRawValue, raw->Headers.KnownHeaders[i].RawValueLength, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));
		}
	}

	// Unknown headers

	for (int i = 0; i < raw->Headers.UnknownHeaderCount; i++)
	{
		CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pName, raw->Headers.pUnknownHeaders[i].NameLength, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pRawValue, raw->Headers.pUnknownHeaders[i].RawValueLength, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));		
	}

	// CRLF

	CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));

	*resultSize = bufferLength;
	*resultLength = offset;

	return S_OK;
Error:
	
	if (NULL != result)
	{
		*result = NULL;
	}
	if (NULL != resultLength)
	{
		*resultLength = 0;
	}
	if (NULL != resultSize)
	{
		*resultSize = 0;
	}

	return hr;
}
Exemple #6
0
HRESULT CHttpProtocol::SerializeRequestHeaders(CNodeHttpStoredContext* ctx, void** result, DWORD* resultSize, DWORD* resultLength)
{
	HRESULT hr;
	PCSTR originalUrl = NULL;
	USHORT originalUrlLength;	

	CheckNull(ctx);
	CheckNull(result);
	CheckNull(resultSize);
	CheckNull(resultLength);

	IHttpContext* context = ctx->GetHttpContext();

	DWORD bufferLength = CModuleConfiguration::GetInitialRequestBufferSize(context);
	DWORD offset = 0;
	IHttpRequest* request = context->GetRequest();
	HTTP_REQUEST* raw = request->GetRawHttpRequest();
	USHORT major, minor;
	char tmp[256];
	PCSTR method = request->GetHttpMethod();
	
	ErrorIf(NULL == (*result = context->AllocateRequestMemory(bufferLength)), ERROR_NOT_ENOUGH_MEMORY);	

	// Determine whether response entity body is to be expected

	if (method && 0 == strcmpi("HEAD", method))
	{
		// HEAD requests do not have response entity body
		// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4

		ctx->SetExpectResponseBody(FALSE);
	}
	
	// Request-Line

	CheckError(CHttpProtocol::Append(context, method, 0, result, &bufferLength, &offset));
	CheckError(CHttpProtocol::Append(context, " ", 1, result, &bufferLength, &offset));
	CheckError(CHttpProtocol::Append(context, ctx->GetTargetUrl(), ctx->GetTargetUrlLength(), result, &bufferLength, &offset));
	request->GetHttpVersion(&major, &minor);
	if (1 == major && 1 == minor)
	{
		CheckError(CHttpProtocol::Append(context, " HTTP/1.1\r\n", 11, result, &bufferLength, &offset));
	}
	else if (1 == major && 0 == minor)
	{
		CheckError(CHttpProtocol::Append(context, " HTTP/1.0\r\n", 11, result, &bufferLength, &offset));
	}
	else
	{
		sprintf(tmp, " HTTP/%d.%d\r\n", major, minor);
		CheckError(CHttpProtocol::Append(context, tmp, 0, result, &bufferLength, &offset));
	}

	// Known headers

	for (int i = 0; i < HttpHeaderRequestMaximum; i++)
	{
		if (raw->Headers.KnownHeaders[i].RawValueLength > 0)
		{
			CheckError(CHttpProtocol::Append(context, CHttpProtocol::httpRequestHeaders[i], 0, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, raw->Headers.KnownHeaders[i].pRawValue, raw->Headers.KnownHeaders[i].RawValueLength, result, &bufferLength, &offset));
			CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));
		}
	}

	// Unknown headers

	for (int i = 0; i < raw->Headers.UnknownHeaderCount; i++)
	{
		CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pName, raw->Headers.pUnknownHeaders[i].NameLength, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, ": ", 2, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, raw->Headers.pUnknownHeaders[i].pRawValue, raw->Headers.pUnknownHeaders[i].RawValueLength, result, &bufferLength, &offset));
		CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));		
	}

	// CRLF

	CheckError(CHttpProtocol::Append(context, "\r\n", 2, result, &bufferLength, &offset));

	*resultSize = bufferLength;
	*resultLength = offset;

	return S_OK;
Error:
	
	if (NULL != result)
	{
		*result = NULL;
	}
	if (NULL != resultLength)
	{
		*resultLength = 0;
	}
	if (NULL != resultSize)
	{
		*resultSize = 0;
	}

	return hr;
}