CACHE.CPP

// =========================================================================== 
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1996 Microsoft Corporation. All Rights Reserved.
// ===========================================================================
#include <windows.h>
#include <wininet.h>
#include "range.h"

//==============================================================================
BOOL QueryHeaders
(
HANDLE hRequest,
PHTTP_REQUESTCB_PARAM pCBParam,
SYSTEMTIME* pstLastModified
)
{
DWORD cbBuf;
BOOL fRet;

pCBParam->CallbackType = REQUESTCB_STARTED;
pCBParam->fdwRequestFlags = HTTP_REQUEST_FROM_CACHE;

// Get if the server supports ranges from the response headers.
static char szBytes[] ="bytes";
char szBuf[sizeof(szBytes)];
cbBuf = sizeof(szBytes);
fRet = HttpQueryInfo
(hRequest, HTTP_QUERY_ACCEPT_RANGES, szBuf, &cbBuf, 0);
if (fRet && !lstrcmpi (szBuf, szBytes))
pCBParam->fdwRequestFlags |= HTTP_REQUEST_ACCEPT_RANGES;

// Get the response code.
cbBuf = sizeof(pCBParam->dwResponseCode);
fRet = HttpQueryInfo
(
hRequest,
HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE,
&pCBParam->dwResponseCode,
&cbBuf,
NULL
);
if (!fRet)
pCBParam->dwResponseCode = 0;

// Get the last modified time from the response headers.
cbBuf = sizeof(*pstLastModified);
fRet = HttpQueryInfo
(
hRequest,
HTTP_QUERY_FLAG_SYSTEMTIME | HTTP_QUERY_LAST_MODIFIED,
pstLastModified,
&cbBuf,
NULL
);
pCBParam->pstLastModified = fRet? pstLastModified : NULL;

// Get the content length from the cache file size.
pCBParam->dwContentLength =
InternetSetFilePointer (hRequest, NULL, 0, FILE_END, 0);

return TRUE;
}

//==============================================================================
BOOL HttpReadFromCache (PHTTP_REQUEST_PARAM pParam)
{
// Locals that must be initialized before goto done.
BOOL fSuccess = FALSE;
HANDLE hInternet = NULL;
HANDLE hConnect = NULL;
HANDLE hRequest = NULL;
PVOID pRead = NULL;
HRESULT hrRequest = S_OK;

// Initialize wininet
hInternet = InternetOpen
("RR", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_OFFLINE);
if (!hInternet)
goto done;
hRequest = InternetOpenUrl
(hInternet, pParam->pszUrl, NULL, 0, INTERNET_FLAG_OFFLINE, 0);
if (!hRequest)
goto done;

// Initialize callback parameters.
HTTP_REQUESTCB_PARAM CBParam;
CBParam.cbStruct = sizeof(CBParam);
CBParam.dwRequestCtx = pParam->dwRequestCtx;

// Allocate read buffer
DWORD cbRead;
cbRead = 4096;
pRead = LocalAlloc (LMEM_FIXED, cbRead);
if (!pRead)
{
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
goto done;
}

SYSTEMTIME st;
if (!QueryHeaders (hRequest, &CBParam, &st))
goto done;

fSuccess = TRUE;

// Notify the client that request is started.
if (!(*pParam->pfnRequestCB) (&CBParam))
goto notify;

// Set up ranges to read.
HTTP_REQUEST_RANGE EntireRange;
PHTTP_REQUEST_RANGE pRanges;
DWORD cRanges;

if (pParam->cRanges)
{
pRanges = pParam->pRanges;
cRanges = pParam->cRanges;
}
else
{
EntireRange.dwOffset = 0;
EntireRange.dwSize = CBParam.dwContentLength;
pRanges = &EntireRange;
cRanges = 1;
}

// Loop over the ranges, reading into the buffer.
CBParam.CallbackType = REQUESTCB_DATA;
CBParam.lpDataBuffer = pRead;

while (cRanges--)
{
CBParam.dwDataOffset = pRanges->dwOffset;
DWORD cbRange = pRanges->dwSize;
if (!cbRange)
cbRange = InternetSetFilePointer (hRequest, 0, NULL, FILE_END, 0)
- pRanges->dwOffset;

while (cbRange) // Loop until we have all the data for the range.
{
InternetSetFilePointer
(hRequest, CBParam.dwDataOffset, NULL, FILE_BEGIN, 0);

if (!InternetReadFile
(hRequest, pRead, min(cbRead, cbRange), &CBParam.cbDataLength))
{
hrRequest = E_FAIL;
goto notify;
}

// Check for EOF
if (!CBParam.cbDataLength)
break;

// Call the client with data.
if (!(*pParam->pfnRequestCB) (&CBParam))
goto notify;

CBParam.dwDataOffset += CBParam.cbDataLength;
cbRange -= CBParam.cbDataLength;
}

// Advance to next range.
pRanges++;

} // end while (cRanges--)

notify:

// Notify the client we are done.
CBParam.CallbackType = REQUESTCB_DONE;
CBParam.hrRequest = hrRequest;
(*pParam->pfnRequestCB) (&CBParam);

done:
if (pRead)
LocalFree ((HLOCAL) pRead);
if (hRequest)
InternetCloseHandle (hRequest);
if (hConnect)
InternetCloseHandle (hConnect);
if (hInternet)
InternetCloseHandle (hInternet);
return fSuccess;
}

//==============================================================================
BOOL HttpReadRequest (PHTTP_REQUEST_PARAM pParam)
{
if (pParam->cbStruct != sizeof(HTTP_REQUEST_PARAM))
{
SetLastError (ERROR_INVALID_PARAMETER);
return FALSE;
}

if (HttpReadFromCache (pParam))
return TRUE;
return HttpReadFromServer (pParam);
}