home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / activedocument / range / callback.cpp < prev    next >
C/C++ Source or Header  |  1996-07-31  |  20KB  |  624 lines

  1. // ===========================================================================
  2. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  3. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  4. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  5. // PARTICULAR PURPOSE.
  6. //
  7. // Copyright 1996 Microsoft Corporation.  All Rights Reserved.
  8. // ===========================================================================
  9. #include <urlmon.h>
  10. #include <wininet.h>
  11. #include "callback.hpp"
  12.  
  13. #define BOUNDARY_MAXSIZE 500 
  14. #define RECV_BUF_SIZE   8192
  15.  
  16. static char szCRLF[] = "\r\n";
  17. static char szLF[]   = "\n";
  18.  
  19. // ---------------------------------------------------------------------------
  20. // CUrlmonCallback::CUrlmonCallback
  21. // ---------------------------------------------------------------------------
  22. CUrlmonCallback::CUrlmonCallback (PHTTP_REQUEST_PARAM pParam)
  23. {
  24.     m_pBinding = NULL;
  25.     m_pstm = NULL;
  26.     m_cRef = 0;
  27.  
  28.     m_pParam = pParam;
  29.  
  30.     if (m_pParam->punkOuter)
  31.         ((IUnknown *) m_pParam->punkOuter)->AddRef();
  32.     
  33.     m_CBParam.cbStruct = sizeof(m_CBParam);
  34.     m_CBParam.dwRequestCtx = m_pParam->dwRequestCtx;
  35.  
  36.     m_dwOffset = 0;
  37.     m_dwResponseCode = 0;
  38.     m_pszRangeDelimiter = NULL;
  39. }
  40.  
  41. // ---------------------------------------------------------------------------
  42. // CUrlmonCallback::~CUrlmonCallback
  43. // ---------------------------------------------------------------------------
  44. CUrlmonCallback::~CUrlmonCallback()
  45. {
  46.     if (m_pParam->punkOuter)
  47.         ((IUnknown *) m_pParam->punkOuter)->Release();
  48.     if (m_pstm)
  49.         m_pstm->Release();
  50.     if (m_pszRangeDelimiter)
  51.         LocalFree ((HLOCAL) m_pszRangeDelimiter);
  52. }
  53.  
  54. // ---------------------------------------------------------------------------
  55. // CUrlmonCallback::QueryInterface
  56. // ---------------------------------------------------------------------------
  57. STDMETHODIMP CUrlmonCallback::QueryInterface(REFIID riid, void** ppv)
  58.     if(IsEqualGUID(riid,IID_IUnknown))
  59.         *ppv = (IUnknown *) (IBindStatusCallback *) this;
  60.     else if (IsEqualGUID(riid,IID_IBindStatusCallback))
  61.         *ppv = (IBindStatusCallback *) this;
  62.     else if (IsEqualGUID(riid, IID_IHttpNegotiate))
  63.         *ppv = (IHttpNegotiate *) this;
  64.     else
  65.         *ppv = NULL;
  66.  
  67.     if (!*ppv)
  68.         return E_NOINTERFACE;
  69.     
  70.     // Increment our reference count before we hand out our interface
  71.     ((LPUNKNOWN)*ppv)->AddRef();
  72.     return S_OK;
  73.  
  74. }
  75.  
  76.  
  77. // ---------------------------------------------------------------------------
  78. // CUrlmonCallback::AddRef
  79. // ---------------------------------------------------------------------------
  80. STDMETHODIMP_(ULONG) CUrlmonCallback::AddRef(void)
  81. {
  82.     if (m_pParam->punkOuter)
  83.         ((IUnknown *) m_pParam->punkOuter)->AddRef();
  84.     return m_cRef++;
  85. }
  86.  
  87.  
  88. // ---------------------------------------------------------------------------
  89. // CUrlmonCallback::Release
  90. // ---------------------------------------------------------------------------
  91. STDMETHODIMP_(ULONG) CUrlmonCallback::Release(void)
  92. {
  93.     if (m_pParam->punkOuter)
  94.         ((IUnknown *) m_pParam->punkOuter)->Release();
  95.  
  96.     if (--m_cRef == 0)
  97.     {
  98.         delete this;
  99.         return 0;
  100.     }
  101.     return m_cRef;
  102. }
  103.  
  104. // ---------------------------------------------------------------------------
  105. // CUrlmonCallback::BeginningTransaction
  106. // ---------------------------------------------------------------------------
  107. STDMETHODIMP
  108. CUrlmonCallback::BeginningTransaction (LPCWSTR szURL,
  109.     LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *ppszAdditionalHeaders)
  110. {
  111.     static char szRangeHeader[] = "Range: bytes=";
  112.     static char szUnlessHeader[] = "Unless-Modified-Since: ";
  113.  
  114.     HRESULT hr = S_OK;
  115.     PSTR pszHeader = NULL;
  116.     
  117.     // Don't add any headers if not a range request.
  118.     DWORD cRanges = m_pParam->cRanges;
  119.     if (!cRanges)
  120.     {
  121.         *ppszAdditionalHeaders = NULL;
  122.         goto done;
  123.     }
  124.  
  125.     PHTTP_REQUEST_RANGE pRanges;
  126.     pRanges = m_pParam->pRanges;
  127.  
  128.     // Allocate ANSI buffer.
  129.     DWORD cbHeader;
  130.     cbHeader = sizeof(szRangeHeader) + 20 * cRanges + 2;
  131.     if (m_pParam->pstUnlessModifiedSince)
  132.        cbHeader += sizeof(szUnlessHeader) + INTERNET_RFC1123_BUFSIZE + 2;
  133.     pszHeader = (PSTR) LocalAlloc (LMEM_FIXED, cbHeader);
  134.     if (!pszHeader)
  135.     {
  136.         hr = E_OUTOFMEMORY;
  137.         goto done;
  138.     }
  139.  
  140.     // Format the read range request header.
  141.     UINT cchHeader;
  142.     cchHeader = wsprintf (pszHeader, "%s", szRangeHeader);
  143.  
  144.     // Add the ranges.
  145.     while (cRanges--)
  146.     {
  147.         if (pRanges->dwSize)
  148.         {
  149.             // Format range, end value is inclusive.
  150.             cchHeader += wsprintf (pszHeader + cchHeader, "%d-%d",
  151.                 pRanges->dwOffset, pRanges->dwOffset + pRanges->dwSize - 1);
  152.         }
  153.         else
  154.         {
  155.             // Format range to end of file.
  156.             cchHeader += wsprintf (pszHeader + cchHeader, "%d-",
  157.                 pRanges->dwOffset);
  158.         }
  159.             
  160.         pRanges++;
  161.         if (cRanges)
  162.            cchHeader += wsprintf (pszHeader + cchHeader, ", ");
  163.         else
  164.            cchHeader += wsprintf (pszHeader + cchHeader, szCRLF);
  165.     }
  166.  
  167.     if (m_pParam->pstUnlessModifiedSince)
  168.     {
  169.         // Add unless-modified-since qualifier.
  170.         cchHeader += wsprintf (pszHeader + cchHeader, szUnlessHeader);
  171.         if (!InternetTimeFromSystemTime
  172.         (
  173.             m_pParam->pstUnlessModifiedSince,
  174.             INTERNET_RFC1123_FORMAT,
  175.             pszHeader + cchHeader,
  176.             INTERNET_RFC1123_BUFSIZE
  177.         ))
  178.         {
  179.             hr = E_FAIL;
  180.             goto done;
  181.         }
  182.         
  183.         cchHeader += lstrlen (pszHeader + cchHeader);
  184.         cchHeader += wsprintf (pszHeader + cchHeader, szCRLF);
  185.     }
  186.  
  187.     cchHeader++; // for NULL termination
  188.  
  189.     // Allocate Unicode buffer.
  190.     *ppszAdditionalHeaders = (WCHAR *) CoTaskMemAlloc (sizeof(WCHAR) * cchHeader);
  191.     if (*ppszAdditionalHeaders)
  192.     {
  193.         MultiByteToWideChar (CP_ACP, 0, pszHeader, -1, *ppszAdditionalHeaders,
  194.             sizeof(WCHAR) * cchHeader);
  195.     }
  196.  
  197.     hr = *ppszAdditionalHeaders? S_OK : E_OUTOFMEMORY;
  198.  
  199. done:
  200.     if (pszHeader)
  201.         LocalFree (pszHeader);
  202.     return hr;
  203. }
  204.  
  205. // ---------------------------------------------------------------------------
  206. // CUrlmonCallback::OnResponse
  207. // ---------------------------------------------------------------------------
  208. STDMETHODIMP CUrlmonCallback::OnResponse
  209. (
  210.     DWORD   dwResponseCode, 
  211.     LPCWSTR szResponseHeaders,
  212.     LPCWSTR szRequestHeaders,
  213.     LPWSTR  *pszAdditionalRequestHeaders
  214. )
  215. {
  216.     // Get the HttpQueryInfo wrapper object.
  217.     IWinInetHttpInfo *pHttpInfo = NULL;
  218.     HRESULT hr = m_pBinding->QueryInterface
  219.         (IID_IWinInetHttpInfo, (void **) &pHttpInfo);
  220.     if (FAILED(hr))
  221.         goto done;
  222.  
  223.     // Save the response code.
  224.     m_dwResponseCode = dwResponseCode;
  225.     m_CBParam.dwResponseCode = dwResponseCode;
  226.  
  227.     DWORD cbBuf;
  228.  
  229.     // Check for partial response.
  230.     if (dwResponseCode == 206)
  231.     {
  232.        // Server responded with byte ranges, ergo must support them.
  233.        m_CBParam.fdwRequestFlags = HTTP_REQUEST_ACCEPT_RANGES;
  234.  
  235.        // Look for multi-part delimiter embedded in content type.
  236.        const static char szMultiPart[] = "multipart/x-byteranges; boundary";
  237.        const static DWORD cbMultiPart = sizeof(szMultiPart);
  238.  
  239.        // Get length of buffer to hold content type.
  240.        DWORD cbContentType = 0;
  241.        pHttpInfo->QueryInfo
  242.           (HTTP_QUERY_CONTENT_TYPE, NULL, &cbContentType, NULL, 0);
  243.  
  244.        if (cbContentType > cbMultiPart + 1)
  245.        {
  246.             // Content type is big enough to embed a delimiter.
  247.             m_pszRangeDelimiter = (PSTR) LocalAlloc (LMEM_FIXED, cbContentType);
  248.             if (!m_pszRangeDelimiter)
  249.             {
  250.                 hr = E_OUTOFMEMORY;
  251.                 goto done;
  252.             }
  253.  
  254.             if (S_OK != pHttpInfo->QueryInfo (HTTP_QUERY_CONTENT_TYPE,
  255.                  m_pszRangeDelimiter, &cbContentType, NULL, 0))
  256.             {
  257.                 hr = E_FAIL;
  258.                 goto done;
  259.             } 
  260.             
  261.             // Split the string at the '='
  262.             m_pszRangeDelimiter[cbMultiPart - 1] = 0;
  263.             if (lstrcmpi (m_pszRangeDelimiter, szMultiPart))
  264.             {
  265.                 // Response must not be multi-part.
  266.                 LocalFree ((HLOCAL) m_pszRangeDelimiter);
  267.                 m_pszRangeDelimiter = NULL;
  268.             }
  269.             else
  270.             {
  271.                 m_cchRangeDelimiter =
  272.                     lstrlen (m_pszRangeDelimiter + cbMultiPart);
  273.  
  274.                 // Shift the delimiter to offset 2 of string.
  275.                 MoveMemory
  276.                 (
  277.                   m_pszRangeDelimiter + 2, // +2 for prefix
  278.                   m_pszRangeDelimiter + cbMultiPart,
  279.                   m_cchRangeDelimiter + 1  // +1 for null
  280.                 );    
  281.  
  282.                 // Prefix delimiter with "--"
  283.                 m_pszRangeDelimiter[0] = '-';
  284.                 m_pszRangeDelimiter[1] = '-';
  285.                 m_cchRangeDelimiter += 2;
  286.  
  287.                 // Initialize range boundaries.
  288.                 m_dwRangeBeg = 0;
  289.                 m_dwRangeEnd = 0;
  290.             }
  291.             
  292.         } // end if (cbContentType > cbMultiPart + 1)
  293.  
  294.         // Adjust the offset if we have a single range
  295.         if (!m_pszRangeDelimiter)
  296.             m_dwOffset = m_pParam->pRanges[0].dwOffset;
  297.     }
  298.     
  299.     else // if (dwResponseCode != 206)
  300.     {
  301.         // Check if ranges are supported.
  302.         static char szBytes[] = "bytes";
  303.         char szBuf[sizeof(szBytes)];
  304.         cbBuf = sizeof(szBytes);
  305.         hr = pHttpInfo->QueryInfo
  306.             (HTTP_QUERY_ACCEPT_RANGES, szBuf, &cbBuf, NULL, 0);
  307.         if (SUCCEEDED(hr) && !lstrcmpi (szBytes, szBuf))
  308.             m_CBParam.fdwRequestFlags = HTTP_REQUEST_ACCEPT_RANGES;
  309.         else
  310.             m_CBParam.fdwRequestFlags = 0;
  311.     }
  312.  
  313.     // Get last modified time.
  314.     SYSTEMTIME stLastModified;
  315.     cbBuf = sizeof(stLastModified);
  316.     hr = pHttpInfo->QueryInfo
  317.     (
  318.         HTTP_QUERY_FLAG_SYSTEMTIME | HTTP_QUERY_LAST_MODIFIED,
  319.         &stLastModified, &cbBuf, NULL, 0
  320.     );
  321.     if (hr != S_OK)
  322.     {
  323.         memset (&stLastModified, 0, sizeof(stLastModified));
  324.         hr = S_OK;
  325.     }
  326.     m_CBParam.pstLastModified = &stLastModified;
  327.                 
  328.     // Get content length.
  329.     m_CBParam.dwContentLength = 0;
  330.     if (dwResponseCode != 206)
  331.     {   
  332.         cbBuf = sizeof(m_CBParam.dwContentLength);
  333.         hr = pHttpInfo->QueryInfo
  334.         (
  335.             HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_CONTENT_LENGTH,
  336.             &m_CBParam.dwContentLength, &cbBuf, NULL, 0
  337.         );
  338.         if (hr != S_OK)
  339.         {
  340.             m_CBParam.dwContentLength = 0;
  341.             hr = S_OK;
  342.         }
  343.     }
  344.  
  345.  
  346.     // Inform the client the request is started.
  347.     m_CBParam.CallbackType = REQUESTCB_STARTED;
  348.     if (!(*(m_pParam->pfnRequestCB)) (&m_CBParam))
  349.          m_pBinding->Abort();
  350.     hr = S_OK;
  351.  
  352. done:
  353.  
  354.     if (pHttpInfo)
  355.         pHttpInfo->Release();
  356.     
  357.     return hr;
  358. }
  359.  
  360. // ---------------------------------------------------------------------------
  361. // CUrlmonCallback::OnStartBinding
  362. // ---------------------------------------------------------------------------
  363. STDMETHODIMP CUrlmonCallback::OnStartBinding
  364.     (DWORD grfBSCOption, IBinding* pBinding)
  365. {
  366.     if (pBinding != NULL)
  367.     {
  368.          m_pBinding = pBinding;
  369.          m_pBinding->AddRef();
  370.     }
  371.     
  372.     // Initialize receive buffer.
  373.     if (!BufAlloc(RECV_BUF_SIZE))
  374.         return E_OUTOFMEMORY;
  375.         
  376.     return S_OK;
  377. }
  378.  
  379. // ---------------------------------------------------------------------------
  380. // CUrlmonCallback::GetPriority
  381. // ---------------------------------------------------------------------------
  382. STDMETHODIMP CUrlmonCallback::GetPriority(LONG* pnPriority) 
  383. {
  384.     return E_NOTIMPL;
  385. }
  386.  
  387. // ---------------------------------------------------------------------------
  388. // CUrlmonCallback::OnLowResource
  389. // ---------------------------------------------------------------------------
  390. STDMETHODIMP CUrlmonCallback::OnLowResource(DWORD dwReserved)
  391. {
  392.     return E_NOTIMPL;
  393. }
  394.  
  395. // ---------------------------------------------------------------------------
  396. // CUrlmonCallback::OnProgress
  397. // ---------------------------------------------------------------------------
  398. STDMETHODIMP CUrlmonCallback::OnProgress
  399.     (ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
  400. {
  401.     return S_OK;
  402. }
  403.  
  404. // ---------------------------------------------------------------------------
  405. // CUrlmonCallback::OnStopBinding
  406. // ---------------------------------------------------------------------------
  407. STDMETHODIMP CUrlmonCallback::OnStopBinding
  408.     (HRESULT hrStatus, LPCWSTR pszError) 
  409. {
  410.     // Release the binding, which will eventually release the callback object.
  411.     m_pBinding->Release();
  412.     m_pBinding = NULL;
  413.  
  414.     // Notify the client that we are done.
  415.     m_CBParam.CallbackType = REQUESTCB_DONE;
  416.     m_CBParam.hrRequest = hrStatus;
  417.     (*(m_pParam->pfnRequestCB)) (&m_CBParam);
  418.     
  419.     return S_OK;
  420. }
  421.  
  422. // ---------------------------------------------------------------------------
  423. // CUrlmonCallback::GetBindInfo
  424. // ---------------------------------------------------------------------------
  425. STDMETHODIMP CUrlmonCallback::GetBindInfo
  426.     (DWORD* pgrfBINDF, BINDINFO* pbindInfo)
  427. {
  428.     *pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
  429.     *pgrfBINDF |= BINDF_GETNEWESTVERSION;
  430.     pbindInfo->cbSize = sizeof(BINDINFO);
  431.     pbindInfo->szExtraInfo = NULL;
  432.     ZeroMemory (&pbindInfo->stgmedData, sizeof(STGMEDIUM));
  433.     pbindInfo->grfBindInfoF = 0;
  434.     pbindInfo->dwBindVerb = BINDVERB_GET;
  435.     pbindInfo->szCustomVerb = NULL;
  436.     return S_OK;
  437. }
  438.  
  439. // ---------------------------------------------------------------------------
  440. // CUrlmonCallback::OnDataAvailable
  441. // ---------------------------------------------------------------------------
  442. STDMETHODIMP CUrlmonCallback::OnDataAvailable
  443.     (DWORD grfBSCF, DWORD dwSize, FORMATETC* pfmtetc, STGMEDIUM* pstgmed) 
  444. {
  445.     if (!m_pstm && pstgmed->tymed == TYMED_ISTREAM)
  446.     {
  447.         m_pstm = pstgmed->pstm;
  448.         m_pstm->AddRef();
  449.         m_CBParam.CallbackType = REQUESTCB_DATA;
  450.     }
  451.  
  452.     // Check for multi-part response.
  453.     if (m_pszRangeDelimiter)
  454.        return ParseMultiPartBuffer (grfBSCF & BSCF_LASTDATANOTIFICATION);
  455.  
  456.     HRESULT hrRead;
  457.     DWORD cbActual;
  458.  
  459.     do // until hrRead != S_OK
  460.     {
  461.         // Read some more data.
  462.         hrRead = m_pstm->Read (BufBeg(), BufSize(), &cbActual);
  463.  
  464.         // Notify the client we got some data.
  465.         if (cbActual)
  466.         {
  467.             m_CBParam.dwDataOffset = m_dwOffset;
  468.             m_CBParam.lpDataBuffer = BufBeg();
  469.             m_CBParam.cbDataLength = cbActual;
  470.             if (!(*(m_pParam->pfnRequestCB)) (&m_CBParam))
  471.             {
  472.                  // Client wants to stop.
  473.                  m_pBinding->Abort();
  474.                  return S_OK;
  475.             }
  476.         }   
  477.  
  478.         m_dwOffset += cbActual;
  479.         
  480.     } while (hrRead == S_OK);
  481.  
  482.     return (hrRead == S_FALSE || hrRead == E_PENDING)? S_OK : hrRead;
  483. }
  484.  
  485. // ---------------------------------------------------------------------------
  486. // CUrlmonCallback::OnObjectAvailable
  487. // ---------------------------------------------------------------------------
  488. STDMETHODIMP CUrlmonCallback::OnObjectAvailable(REFIID riid, IUnknown* punk) 
  489. {
  490.     return E_NOTIMPL;
  491. }
  492.  
  493. // ---------------------------------------------------------------------------
  494. // CUrlmonCallback::ParseMultiPartHeader
  495. // ---------------------------------------------------------------------------
  496. BOOL CUrlmonCallback::ParseMultiPartHeader (void)
  497. {
  498.     // multi-part boundary string literals.
  499.     static char szContentType[]  = "Content-Type: ";
  500.     static char szContentRange[] = "Content-Range: bytes ";
  501.  
  502. // Some macros for prettiness...
  503. #define GrokStr(str) if (!BufScanStr (str, sizeof(str)-1)) return FALSE;
  504. #define GrokInt(pint, delim) if (!BufScanInt (pint,delim)) return FALSE;
  505.  
  506.     // Check for presence of boundary.
  507.     if (m_dwRangeBeg > 0) // must not be first range
  508.         GrokStr (szLF);   // will also detect a CR-LF
  509.     if (!BufScanStr (m_pszRangeDelimiter, m_cchRangeDelimiter))
  510.         return FALSE;
  511.  
  512.     // Check for end boundary.
  513.     if
  514.     (    m_pbDataBeg + 2 <= m_pbDataEnd
  515.         && m_pbDataBeg[0] == '-'
  516.         && m_pbDataBeg[1] == '-'
  517.     )
  518.     {
  519.         m_dwRangeBeg = 0;
  520.         m_dwRangeEnd = 0;
  521.         return TRUE;
  522.     }
  523.     
  524.     // Scan content type and data range.
  525.     GrokStr (szLF);
  526.     GrokStr (szContentType);
  527.     GrokStr (szLF);
  528.     GrokStr (szContentRange);
  529.     GrokInt (&m_dwRangeBeg, '-');
  530.     GrokInt (&m_dwRangeEnd, '/');
  531.     GrokStr (szLF);
  532.     GrokStr (szLF);
  533.     
  534.     // Range end is inclusive; make it exclusive.
  535.     m_dwRangeEnd++; 
  536.     return TRUE;
  537.  
  538. #undef GrokStr
  539. #undef GrokInt
  540. }
  541.  
  542. // ---------------------------------------------------------------------------
  543. // CUrlmonCallback::ParseMultiPartBuffer
  544. // ---------------------------------------------------------------------------
  545. HRESULT CUrlmonCallback::ParseMultiPartBuffer (BOOL fLastCall)
  546. {
  547.     HRESULT hrRead;
  548.     DWORD cbActual;
  549.  
  550.     do // until hrRead != S_OK
  551.     {
  552.         // Read data and append to buffer.
  553.         hrRead = m_pstm->Read(m_pbDataEnd, BufSpace(), &cbActual);
  554.         m_pbDataEnd += cbActual;
  555.  
  556.         // Dispatch to current state handler.
  557.         if (m_dwRangeBeg != m_dwRangeEnd)
  558.             goto process_data;
  559.         else
  560.             goto parse_header;
  561.             
  562. parse_header:
  563.  
  564.         // If download not at EOF, don't try to parse multi-part
  565.         // boundary if any chance it would be split across buffers.
  566.         if (hrRead != S_FALSE && m_pbDataBeg > m_pbDataEnd - BOUNDARY_MAXSIZE)
  567.         {
  568.             BufShift();
  569.             continue;
  570.         }
  571.  
  572.         // Attempt to parse the multi-part boundary.
  573.         if (!ParseMultiPartHeader())
  574.             break;
  575.                
  576.         // Check if we got termination boundary.
  577.         if (m_dwRangeBeg == m_dwRangeEnd)
  578.             break;
  579.  
  580.         goto process_data;
  581.  
  582. process_data:
  583.  
  584.         // Calculate amount of data to report.
  585.         DWORD cbData   = m_pbDataEnd  - m_pbDataBeg;
  586.         DWORD cbRange  = m_dwRangeEnd - m_dwRangeBeg;
  587.         DWORD cbLength = min (cbData, cbRange);
  588.  
  589.         if (cbLength)
  590.         {
  591.             // Call back the client with more data.
  592.             m_CBParam.dwDataOffset = m_dwRangeBeg;
  593.             m_CBParam.lpDataBuffer = m_pbDataBeg;
  594.             m_CBParam.cbDataLength = cbLength;
  595.             if (!(*(m_pParam->pfnRequestCB)) (&m_CBParam))
  596.             {
  597.                  // Client wants to stop.
  598.                  m_pBinding->Abort();
  599.                  break;
  600.             }
  601.             m_dwRangeBeg += cbLength;
  602.         }
  603.  
  604.         // Adjust the buffer depending on next state.
  605.         if (m_dwRangeBeg == m_dwRangeEnd) // (cbLength == cbRange)
  606.         {
  607.             // Consume the data and look for next header.
  608.             m_pbDataBeg += cbLength;
  609.             goto parse_header;
  610.         }
  611.         else
  612.         {
  613.             // Reset buffer and get some more data.
  614.             BufReset();
  615.             continue;
  616.         }
  617.  
  618.     } while (hrRead == S_OK);
  619.  
  620.     return (hrRead == S_FALSE || hrRead == E_PENDING)? S_OK : hrRead;
  621. }
  622.  
  623.