Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

343 lines
8.7 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name :
  4. cachevalidation.cxx
  5. Abstract:
  6. Handle Cache Validation (If-* headers)
  7. Author:
  8. Anil Ruia (AnilR) 3-Apr-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. UlW3.dll
  13. --*/
  14. #include "precomp.hxx"
  15. #include "staticfile.hxx"
  16. dllexp BOOL FindInETagList(LPCSTR pLocalETag,
  17. LPCSTR pETagList,
  18. BOOL fWeakCompare)
  19. /*++
  20. Routine Description:
  21. Search input list of ETags for one that matches our local ETag.
  22. Arguments:
  23. pLocalETag - The local ETag we're using.
  24. pETagList - The ETag list we've received from the client.
  25. bWeakCompare - Whether using Weak Comparison is ok
  26. Returns:
  27. TRUE if we found a matching ETag, FALSE otherwise.
  28. --*/
  29. {
  30. UINT QuoteCount;
  31. LPCSTR pFileETag;
  32. BOOL Matched;
  33. // We'll loop through the ETag string, looking for ETag to
  34. // compare, as long as we have an ETag to look at.
  35. do
  36. {
  37. while (SAFEIsSpace(*pETagList))
  38. {
  39. pETagList++;
  40. }
  41. if (!*pETagList)
  42. {
  43. // Ran out of ETag.
  44. return FALSE;
  45. }
  46. // If this ETag is *, it's a match.
  47. if (*pETagList == '*')
  48. {
  49. return TRUE;
  50. }
  51. // See if this ETag is weak.
  52. if (pETagList[0] == 'W' && pETagList[1] == '/')
  53. {
  54. // This is a weak validator. If we're not doing the weak
  55. // comparison, fail.
  56. if (!fWeakCompare)
  57. {
  58. return FALSE;
  59. }
  60. // Skip over the 'W/', and any intervening whitespace.
  61. pETagList += 2;
  62. while (SAFEIsSpace(*pETagList))
  63. {
  64. pETagList++;
  65. }
  66. if (!*pETagList)
  67. {
  68. // Ran out of ETag.
  69. return FALSE;
  70. }
  71. }
  72. if (*pETagList != '"')
  73. {
  74. // This isn't a quoted string, so fail.
  75. return FALSE;
  76. }
  77. // OK, right now we should be at the start of a quoted string that
  78. // we can compare against our current ETag.
  79. QuoteCount = 0;
  80. Matched = TRUE;
  81. pFileETag = pLocalETag;
  82. // Do the actual compare. We do this by scanning the current ETag,
  83. // which is a quoted string. We look for two quotation marks, the
  84. // the delimiters if the quoted string. If after we find two quotes
  85. // in the ETag everything has matched, then we've matched this ETag.
  86. // Otherwise we'll try the next one.
  87. do
  88. {
  89. CHAR Temp;
  90. Temp = *pETagList;
  91. if (Temp == '"')
  92. {
  93. QuoteCount++;
  94. }
  95. if (*pFileETag != Temp)
  96. {
  97. Matched = FALSE;
  98. }
  99. if (!Temp)
  100. {
  101. return FALSE;
  102. }
  103. pETagList++;
  104. if (*pFileETag == '\0')
  105. {
  106. break;
  107. }
  108. pFileETag++;
  109. }
  110. while (QuoteCount != 2);
  111. if (Matched)
  112. {
  113. return TRUE;
  114. }
  115. // Otherwise, at this point we need to look at the next ETag.
  116. while (QuoteCount != 2)
  117. {
  118. if (*pETagList == '"')
  119. {
  120. QuoteCount++;
  121. }
  122. else
  123. {
  124. if (*pETagList == '\0')
  125. {
  126. return FALSE;
  127. }
  128. }
  129. pETagList++;
  130. }
  131. while (SAFEIsSpace(*pETagList))
  132. {
  133. pETagList++;
  134. }
  135. if (*pETagList == ',')
  136. {
  137. pETagList++;
  138. }
  139. else
  140. {
  141. return FALSE;
  142. }
  143. }
  144. while ( *pETagList );
  145. return FALSE;
  146. }
  147. HRESULT W3_STATIC_FILE_HANDLER::CacheValidationDoWork(
  148. W3_CONTEXT *pW3Context,
  149. W3_FILE_INFO *pOpenFile,
  150. BOOL *pfHandled)
  151. /*++
  152. Synopsis
  153. Handle the Cache Related If-* headers
  154. Input
  155. pW3Context : W3_CONTEXT for the request
  156. pOpenFile : The file's cache entry
  157. pfHandled : On return indicates whether, we have handled the request
  158. or further processing needs to be done
  159. Returns
  160. HRESULT
  161. --*/
  162. {
  163. W3_RESPONSE *pResponse = pW3Context->QueryResponse();
  164. W3_REQUEST *pRequest = pW3Context->QueryRequest();
  165. //
  166. // There are currently 4 possible Cache Related If-* modifiers:
  167. // If-Match, If-Unmodified-Since, If-Non-Match, If-Modified-Since.
  168. // We handle them in that order if all are present, and as soon as
  169. // one condition fails we stop processing
  170. //
  171. //
  172. // Now handle the If-Match header, if we have one.
  173. //
  174. LPCSTR pszIfMatch = pRequest->GetHeader(HttpHeaderIfMatch);
  175. if (pszIfMatch != NULL)
  176. {
  177. if (pOpenFile->QueryIsWeakETag() ||
  178. !FindInETagList(pOpenFile->QueryETag(), pszIfMatch, FALSE))
  179. {
  180. pResponse->ClearHeaders();
  181. pResponse->SetStatus(HttpStatusPreconditionFailed);
  182. *pfHandled = TRUE;
  183. return S_OK;
  184. }
  185. }
  186. //
  187. // Now see if we have an If-None-Match, and if so handle that.
  188. //
  189. LPCSTR pszIfNoneMatch = pRequest->GetHeader(HttpHeaderIfNoneMatch);
  190. BOOL fIsNoneMatchPassed = TRUE;
  191. BOOL fSkipIfModifiedSince = FALSE;
  192. if (pszIfNoneMatch != NULL)
  193. {
  194. if (FindInETagList(pOpenFile->QueryETag(),
  195. pszIfNoneMatch,
  196. TRUE))
  197. {
  198. fIsNoneMatchPassed = FALSE;
  199. }
  200. else
  201. {
  202. // If none of the tags match, we should skip If-Modified-Since
  203. fSkipIfModifiedSince = TRUE;
  204. }
  205. }
  206. //
  207. // Made it through that, handle If-Modified-Since if we have that.
  208. //
  209. LPCSTR pszIfModifiedSince = pRequest->GetHeader(HttpHeaderIfModifiedSince);
  210. if (!fSkipIfModifiedSince && pszIfModifiedSince != NULL)
  211. {
  212. LARGE_INTEGER liModifiedSince;
  213. if (StringTimeToFileTime(pszIfModifiedSince,
  214. &liModifiedSince))
  215. {
  216. FILETIME tm;
  217. pOpenFile->QueryLastWriteTime(&tm);
  218. // Check if our last write time is greater than their
  219. // ModifiedSince time
  220. if (*(LONGLONG*)&tm <= liModifiedSince.QuadPart)
  221. {
  222. // Need to check and see if the Modified-Since time is greater
  223. // than our current time. If it is, we ignore it.
  224. GetSystemTimeAsFileTime(&tm);
  225. if (*(LONGLONG *)&tm >= liModifiedSince.QuadPart)
  226. {
  227. //
  228. // Clear out any content-encoding set since we are not
  229. // sending the actual body
  230. //
  231. pResponse->SetHeaderByReference(HttpHeaderContentEncoding,
  232. NULL, 0);
  233. pResponse->SetStatus(HttpStatusNotModified);
  234. *pfHandled = TRUE;
  235. return S_OK;
  236. }
  237. }
  238. }
  239. fIsNoneMatchPassed = TRUE;
  240. }
  241. if (!fIsNoneMatchPassed)
  242. {
  243. //
  244. // Clear out any content-encoding set since we are not
  245. // sending the actual body
  246. //
  247. pResponse->SetHeaderByReference(HttpHeaderContentEncoding,
  248. NULL, 0);
  249. pResponse->SetStatus(HttpStatusNotModified);
  250. *pfHandled = TRUE;
  251. return S_OK;
  252. }
  253. //
  254. // Made it through that, handle If-Unmodified-Since if we have that.
  255. //
  256. LPCSTR pszIfUnmodifiedSince = pRequest->GetHeader(
  257. HttpHeaderIfUnmodifiedSince);
  258. if (pszIfUnmodifiedSince != NULL)
  259. {
  260. LARGE_INTEGER liUnmodifiedSince;
  261. if (StringTimeToFileTime(pszIfUnmodifiedSince,
  262. &liUnmodifiedSince))
  263. {
  264. FILETIME tm;
  265. pOpenFile->QueryLastWriteTime(&tm);
  266. // If our last write time is greater than their UnmodifiedSince
  267. // time, the precondition fails.
  268. if (*(LONGLONG*)&tm > liUnmodifiedSince.QuadPart)
  269. {
  270. pResponse->ClearHeaders();
  271. pResponse->SetStatus(HttpStatusPreconditionFailed);
  272. *pfHandled = TRUE;
  273. return S_OK;
  274. }
  275. }
  276. }
  277. *pfHandled = FALSE;
  278. return S_OK;
  279. }