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; }
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; }
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; }
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; }
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; }