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