httpfileobj.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:54k
源码类别:

Symbian

开发平台:

C/C++

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: RCSL 1.0/RPSL 1.0
  3.  *
  4.  * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
  5.  *
  6.  * The contents of this file, and the files included with this file, are
  7.  * subject to the current version of the RealNetworks Public Source License
  8.  * Version 1.0 (the "RPSL") available at
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed
  10.  * the file under the RealNetworks Community Source License Version 1.0
  11.  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
  12.  * in which case the RCSL will apply. You may also obtain the license terms
  13.  * directly from RealNetworks.  You may not use this file except in
  14.  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks
  15.  * applicable to this file, the RCSL.  Please see the applicable RPSL or
  16.  * RCSL for the rights, obligations and limitations governing use of the
  17.  * contents of the file.
  18.  *
  19.  * This file is part of the Helix DNA Technology. RealNetworks is the
  20.  * developer of the Original Code and owns the copyrights in the portions
  21.  * it created.
  22.  *
  23.  * This file, and the files included with this file, is distributed and made
  24.  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  25.  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  26.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
  27.  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  28.  *
  29.  * Technology Compatibility Kit Test Suite(s) Location:
  30.  *    http://www.helixcommunity.org/content/tck
  31.  *
  32.  * Contributor(s):
  33.  *
  34.  * ***** END LICENSE BLOCK ***** */
  35. /****************************************************************************
  36.  * Includes
  37.  */
  38. #include "hlxclib/stdio.h"              /* FILE                     */
  39. #include "hlxclib/string.h"             /* strcpy, etc.             */
  40. #include "hlxclib/sys/stat.h"           /* stat, etc.               */
  41. #include "hxtypes.h"                    /* UINT32, BOOL, etc        */
  42. #include "hxcom.h"                      /* IUnknown                 */
  43. #include "hxcomm.h"                     /* IHXCommonClassFactory    */
  44. #include "ihxpckts.h"                   /* IHXValues, IHXBuffers    */
  45. #include "hxbuffer.h"                   /* CHXBuffer                */
  46. #include "hxurl.h"                      /* CHXURL                   */
  47. #include "chxpckts.h"                   /* CHXHeader                */
  48. #include "hxcache2.h"                   /* IHXCache2, IHXCacheObject,
  49.                                            IHXCacheObjectResponse   */
  50. #include "httpfilesys.h"                /* FILE_SYS_PROTOCOL        */
  51. #include "httpfileobj.h"                /* CHXHTTPFileObject        */
  52. #include "debug.h"                      /* DPRINTF                  */
  53. #include "chxcache2.h"                  /* CHXCache2                */
  54. #include "hxprefs.h"                    /* IHXPreferences           */
  55. #include "multilog.h"                   /* INIT_MULTILOG_GROUP_NO_COREDEBUG(...) */
  56. #include "mlog_http.h"                  /* MLOG_HTTP(...)           */
  57. #ifdef _SYMBIAN
  58.     #include "symbhxdir.h"              /*  OS_SEPARATOE_CHAR       */
  59. #else
  60.     #include "hxdir.h"
  61. #endif
  62. #define D_HTTP_FO 0x1000000
  63. // The capacity of the Cache in bytes. IMPORTANT >> See the comment for CHUNK_SIZE.
  64. #define CACHE_CAPACITY (12 * 1024)
  65. // The amount of data requested through the TCP socket read operation.
  66. // IMPORTANT !!! The chunk_size should be <= (1 - THRESHOLD/100)*CACHE_CAPACITY
  67. // Otherwise we may get into a position wherein the cache has less than
  68. // THRESHOLD read-data but it won't ever be able to accept any new data as the
  69. // new data buffer size (CHUNK_SIZE) will overflow the cache. Also, since
  70. // read-data is below threshold, the cache will never discard any more data.
  71. #define CHUNK_SIZE (2 * 1024)
  72. // 70%  -> The percentage of read data we would like the cache
  73. // to hold. It is made high because when a request for old data can't
  74. // be satisfied, then the whole connection has to be teared down
  75. // everything has to be restrated.  So, it's better to have this value high.
  76. // Note that some fileformat objects start seeking from start whatever be
  77. // the direction of the User Interface seek. In such cases, threshold doesn't
  78. // matter.
  79. #define THRESHOLD 70
  80. #define DEFAULT_HTTP_PORT 80
  81. #define DEFAULT_CALLBACK_INTERVAL 10 // ms
  82. // A utility function which does searches for a *string* in a
  83. // *character buffer* of size haystacksize. strnstr() wouldn't work since
  84. // it requires both the parameters to be strings.
  85. char* bufnstr(char *haystackBuffer, char* needleString, int haystackSize);
  86. // CHXHTTPFileObject Class Methods
  87. /****************************************************************************
  88.  *  CHXHTTPFileObject::CHXHTTPFileObject
  89.  *
  90.  *  Constructor
  91.  */
  92. CHXHTTPFileObject::CHXHTTPFileObject(IUnknown* pContext, IHXValues* pOptions)
  93.     : m_RefCount                (0),        // See header file for the
  94.       m_pContext                (NULL),     // purpose of these variables.
  95.       m_pOptions                (NULL),
  96.       m_pClassFactory           (NULL),
  97.       m_pFileResponse           (NULL),
  98.       m_pRequest                (NULL),
  99.       m_pCache                  (NULL),
  100.       m_pSocket                 (NULL),
  101.       m_pCHXURL                 (NULL),
  102.       m_ulCurrentReadOffset     (0),
  103.       m_lNewReadOffset          (-1),
  104.       m_bInSeekDone             (FALSE),
  105.       m_bInReadDone             (FALSE),
  106.       m_bReadPending            (FALSE),
  107.       m_bIncompleteReadPending  (FALSE),
  108.       m_ulFileLength            (0),
  109.       m_pHdrListRoot            (NULL),
  110.       m_bHeaderCompletelyRead   (FALSE),
  111.       m_pHeader                 (NULL),
  112.       m_bStartAllOverAgain      (FALSE),
  113.       m_pScheduler              (NULL),
  114.       m_ulCallbackHandle        (0),
  115.       m_ulCallBackInterval      (DEFAULT_CALLBACK_INTERVAL),  // 10 ms
  116.       m_bFirstChunk             (TRUE),
  117.       m_bInitResponsePending    (FALSE),
  118.       m_bInitialized            (FALSE),
  119.       m_ulFileDataRead          (0),
  120.       m_bAddBlockPending        (FALSE),
  121.       m_pPendingAddBlock        (NULL),
  122.       m_bDisconnected           (FALSE)
  123. {
  124.     MLOG_HTTP("CHXHTTPFileObject::CHXHTTPFileObject()n");
  125.     m_pContext = pContext;
  126.     m_pOptions = pOptions;
  127.     // Signify that we need to keep a reference to this object
  128.     if (m_pOptions != NULL)
  129.     {
  130. m_pOptions->AddRef();
  131.     }
  132.     if (m_pContext != NULL)
  133.     {
  134. m_pContext->AddRef();
  135.         m_pContext->QueryInterface(IID_IHXScheduler,
  136.                                  (void**) &m_pScheduler);
  137.         m_pContext->QueryInterface(IID_IHXCommonClassFactory,
  138.  (void**)&m_pClassFactory);
  139.     }
  140. } // CHXHTTPFileObject()
  141. /****************************************************************************
  142.  *  CHXHTTPFileObject::~CHXHTTPFileObject
  143.  *
  144.  *  Destructor.
  145.  */
  146. CHXHTTPFileObject::~CHXHTTPFileObject(void)
  147. {
  148.     MLOG_HTTP("CHXHTTPFileObject::~CHXHTTPFileObject()n");
  149.     _CleanUp();
  150. } // ~CHXHTTPFileObject()
  151. /****************************************************************************
  152. * IHXFileObject::_CleanUp()
  153. *
  154. * Cleans up the object by relesing all member objects. This is a helper
  155. * function used by the Close() and the destructor.
  156. *
  157. */
  158. STDMETHODIMP
  159. CHXHTTPFileObject::_CleanUp(void)
  160. {
  161.     MLOG_HTTP("CHXHTTPFileObject::_CleanUp()n");
  162.     m_bAddBlockPending = FALSE;
  163.     HX_RELEASE(m_pPendingAddBlock);
  164.     
  165.     if (m_ulCallbackHandle)
  166.     {
  167. m_pScheduler->Remove(m_ulCallbackHandle);
  168. m_ulCallbackHandle = 0;
  169.     }
  170.     m_bInitialized = FALSE;
  171.     
  172.     HX_RELEASE(m_pContext);
  173.     HX_RELEASE(m_pOptions);
  174.     HX_RELEASE(m_pClassFactory);
  175.     HX_RELEASE(m_pFileResponse);
  176.     HX_RELEASE(m_pRequest);
  177.     HX_RELEASE(m_pCache);
  178.     HX_RELEASE(m_pSocket);
  179.     HX_DELETE(m_pCHXURL);
  180.     HX_RELEASE(m_pPendingReadInfo.pPendingReadBuff);
  181.     HX_RELEASE(m_pHeader);
  182.     HX_RELEASE(m_pScheduler);
  183.     HX_RELEASE(m_pPendingAddBlock);
  184.     return HXR_OK;
  185. } // _CleanUp()
  186. // IHXFileObject Interface Methods
  187. /****************************************************************************
  188.  *  IHXFileObject::Init
  189.  *
  190.  *  This routine associates this File Object with a File Response object
  191.  *  which is notified when file operations (read, write, seek, etc.) are
  192.  *  complete. This method also checks the validity of the file by actually
  193.  *  opening it.
  194.  */
  195. STDMETHODIMP
  196. CHXHTTPFileObject::Init(UINT32            fileAccessMode, // We ignore access mode here
  197. IHXFileResponse*  pFileResponse)
  198. {
  199.     MLOG_HTTP("CHXHTTPFileObject::Init()n");
  200.     HX_RESULT res = HXR_OK;
  201.     if (pFileResponse != NULL)
  202.     {
  203. // Release any previous File Response objects
  204. if (m_pFileResponse != NULL)
  205. {
  206.     m_pFileResponse->Release();
  207. }
  208. m_pFileResponse = pFileResponse;
  209. m_pFileResponse->AddRef();
  210.     }
  211.     else
  212.     {
  213. return HXR_INVALID_PARAMETER;
  214.     }
  215.     if(m_bInitialized) // Init() was previously called atleast once
  216.     {
  217.         // There is no point continuing with a pending read as
  218.         // the caller has changed.
  219.         m_bReadPending = FALSE;
  220.         m_bIncompleteReadPending = FALSE;
  221.         HX_RELEASE(m_pPendingReadInfo.pPendingReadBuff);
  222.         /* If we have already opened a file, then seek back
  223.          * to zero during re-initialization
  224.          */
  225.         m_ulCurrentReadOffset = 0;
  226.         m_pFileResponse->InitDone(HXR_OK);
  227.         return HXR_OK;
  228.      }
  229.     m_pPendingReadInfo.pPendingReadBuff = NULL;
  230.     // Else, this is the first time Init() is being called
  231.     if(m_pCHXURL == NULL) // Init() being called before calling SetRequest()
  232.     {
  233.         return HXR_INVALID_OPERATION;
  234.     }
  235.     /*
  236.      * We have to check the validity of the file. What we do is we
  237.      * contact the HTTP server and request for the HTTP file header.
  238.      * This lets us do two things:
  239.      *      (1) It lets us know if the file really exists on the server.
  240.      *      (2) If file exists, the response contains the stats of the file.
  241.      */
  242.     res = _Start(); // Commence the action - connect to server, send GET req, etc
  243.     return res;
  244. } // Init()
  245. // Assumes the state of the network related state variables is
  246. // as it would be if this method is called for the first time
  247. // (for eg., from the constructor). So, m_pSocket, etc should all
  248. // be NULL
  249. STDMETHODIMP
  250. CHXHTTPFileObject::_Start()
  251. {
  252.     MLOG_HTTP("CHXHTTPFileObject::_Start()n");
  253.     IHXNetworkServices* pNetworkServices = NULL;
  254.     if (HXR_OK != m_pContext->QueryInterface( IID_IHXNetworkServices,
  255.                                             (void **)&pNetworkServices))
  256.     {
  257.         return HXR_FAIL;
  258.     }
  259.     HX_RESULT res = HXR_OK;
  260.     res = pNetworkServices->CreateTCPSocket(&m_pSocket);
  261.     pNetworkServices->Release();
  262.     if( (res != HXR_OK) || (m_pSocket == NULL) )
  263.     {
  264.         return HXR_FAIL;
  265.     }
  266.     // Identify yourself as the object to whom the results of all
  267.     // socket related operations should be reported.
  268.     res = m_pSocket->Init( (IHXTCPResponse *)this );
  269.     if( ( res != HXR_OK) || (m_pCHXURL == NULL) )
  270.     {
  271.         HX_RELEASE(m_pSocket);
  272.         return HXR_FAIL;
  273.     }
  274.     // Parse the URL to get server address and port
  275.     IHXValues* pHeader = m_pCHXURL->GetProperties();
  276.     IHXBuffer* pBuffer = NULL;
  277.     char *serverAddress = NULL;
  278.     pHeader->GetPropertyBuffer(PROPERTY_HOST, pBuffer);
  279.     if(pBuffer != NULL)
  280.     {
  281.         serverAddress = (char*)(pBuffer->GetBuffer());
  282.     }
  283.     ULONG32 serverPort = 0;
  284.     pHeader->GetPropertyULONG32(PROPERTY_PORT, serverPort);
  285.     // Connect to the HTTP server
  286.     res = m_pSocket->Connect(serverAddress, (UINT16)serverPort);
  287.     if(res != HXR_OK)
  288.     {
  289.         HX_RELEASE(m_pSocket);
  290.         return HXR_FAIL;
  291.     }
  292.     return HXR_OK;
  293. } // _Start()
  294. /****************************************************************************
  295.  *  IHXFileObject::GetFilename
  296.  *
  297.  *  This routine returns the name of the requested file (without any path
  298.  *  information). This method may be called by the File Format plug-in if the
  299.  *  short name of the file is required.
  300.  */
  301. STDMETHODIMP
  302. CHXHTTPFileObject::GetFilename(REF(const char*) pFileName)
  303. {
  304.     MLOG_HTTP("CHXHTTPFileObject::GetFilename()n");
  305.     pFileName = NULL;
  306.     HX_RESULT res = HXR_OK;
  307.     //  From the File path, extract the file name and return it.
  308.     if(m_pCHXURL != NULL)
  309.     {
  310.         IHXBuffer* pBuffer = NULL;
  311.         IHXValues* pHeader = m_pCHXURL->GetProperties();
  312.         pHeader->GetPropertyBuffer(PROPERTY_RESOURCE, pBuffer);
  313.         if(pBuffer != NULL)
  314.         {
  315.             pFileName = (char*)(pBuffer->GetBuffer());
  316.         }
  317.     }
  318.     else
  319.     {
  320.         res = HXR_FAIL;
  321.     }
  322.     return res;
  323. }
  324. /****************************************************************************
  325.  *  IHXFileObject::Read
  326.  *
  327.  *  This routine reads a block of data of the specified length from the file.
  328.  *  When reading has completed, the caller is asynchronously notified via the
  329.  *  File Response object associated with this File Object. This method is
  330.  *  called by the File Format plug-in when it needs to read from the file.
  331.  */
  332. STDMETHODIMP
  333. CHXHTTPFileObject::Read(UINT32 byteCount)
  334. {
  335.     MLOG_HTTP("CHXHTTPFileObject::Read(%u)n", byteCount);
  336.     // The whole file has already been read
  337.     if(m_ulCurrentReadOffset == m_ulFileLength)
  338.     {
  339.         HX_RESULT res = HXR_FAIL;
  340.         if (m_pFileResponse)
  341.         {
  342.             m_pFileResponse->ReadDone(HXR_FAIL, 0);
  343.             res = HXR_OK;
  344.         }
  345.         return res;
  346.     }
  347.     // Can't have a read when a previous read is outstanding
  348.     if(m_bReadPending)
  349.     {
  350.         return HXR_INVALID_OPERATION;
  351.     }
  352.     m_bIncompleteReadPending = FALSE;
  353.     // Can't read more than the (remaining) file length
  354.     int actualLen = m_ulFileLength - m_ulCurrentReadOffset;
  355.     if(actualLen > byteCount)
  356.         actualLen = byteCount;
  357.     m_bReadPending = TRUE;
  358.     m_pPendingReadInfo.pPendingReadBuff = NULL;
  359.     m_pPendingReadInfo.ulWriteOffset = 0;
  360.     m_pPendingReadInfo.ulReadOffset = m_ulCurrentReadOffset;
  361.     m_pPendingReadInfo.ulSize = (UINT32)actualLen;
  362.     HX_RESULT r = m_pCache->ReadBlock(m_ulCurrentReadOffset, (UINT32)actualLen);
  363.     return r;
  364. } // Read()
  365. /****************************************************************************
  366.  *  IHXFileObject::Write
  367.  *
  368.  *  This routine writes a block of data to the file. When writing has
  369.  *  completed, the caller is asynchronously notified via the File Response
  370.  *  object associated with this File Object. This method called by the File
  371.  *  Format plug-in when it needs to write to the file.
  372.  */
  373. STDMETHODIMP
  374. CHXHTTPFileObject::Write(IHXBuffer* pDataToWrite)
  375. {
  376.     MLOG_HTTP("CHXHTTPFileObject::Write()n");
  377.     return HXR_NOTIMPL;
  378. }
  379. /****************************************************************************
  380.  *  IHXFileObject::Seek
  381.  *
  382.  *  This routine moves to a given position in the file. The move can be
  383.  *  either from the beginning of the file (absolute), or relative to the
  384.  *  current file position. When seeking has completed, the caller is
  385.  *  asynchronously notified via the File Response object associated with this
  386.  *  File Object. This method called by the File Format plug-in when it needs
  387.  *  to seek to a location within the file.
  388.  */
  389. STDMETHODIMP
  390. CHXHTTPFileObject::Seek(UINT32 offset, BOOL   bIsRelative)
  391. {
  392.     const char *seekType = NULL;
  393.     seekType = (bIsRelative == TRUE)? "REL": "ABS";
  394.     MLOG_HTTP("CHXHTTPFileObject::Seek(%u, %s)n", offset, seekType);
  395.     m_lNewReadOffset = offset;
  396.     if(bIsRelative)
  397.     {
  398.         m_lNewReadOffset += m_ulCurrentReadOffset;
  399.     }
  400.     // Can't seek to outside the file
  401.     if( (m_lNewReadOffset < 0) || (m_lNewReadOffset > m_ulFileLength) )
  402.     {
  403.         m_lNewReadOffset = -1;
  404.         return HXR_FAIL;
  405.     }
  406.     if(!m_bInSeekDone)
  407.     {
  408.         while(m_lNewReadOffset >= 0)
  409.         {
  410.             // Cancel any pending reads
  411.             m_bReadPending = FALSE;
  412.             m_bIncompleteReadPending = FALSE;
  413.             HX_RELEASE(m_pPendingReadInfo.pPendingReadBuff);
  414.             m_ulCurrentReadOffset = m_lNewReadOffset;
  415.             m_lNewReadOffset = -1;
  416.             m_bInSeekDone = TRUE;
  417.             if (m_pFileResponse)
  418.             {
  419.                 m_pFileResponse->SeekDone(HXR_OK);
  420.             }
  421.             m_bInSeekDone = FALSE;
  422.         }
  423.     }
  424.     return HXR_OK;
  425. }
  426. /****************************************************************************
  427.  *  IHXFileObject::Advise
  428.  *
  429.  *  This routine is passed information about the intended usage of this
  430.  *  object. The useage will indicate whether sequential or random requests
  431.  *  for information will be made. This may be useful, for example, in
  432.  *  developing a caching scheme.
  433.  */
  434. STDMETHODIMP
  435. CHXHTTPFileObject::Advise(UINT32  usage)
  436. {
  437.     MLOG_HTTP("CHXHTTPFileObject::Advice(%u)n", usage);
  438.     HX_RESULT res = HXR_FAILED;
  439.     // Because of the simple cache, only
  440.     // linear useage is allowed.
  441.     if(HX_FILEADVISE_RANDOMACCESS == usage)
  442.     {
  443. res = HXR_ADVISE_PREFER_LINEAR;
  444.     }
  445.     return res;
  446. } // Advise()
  447. /****************************************************************************
  448.  *  IHXFileObject::Close
  449.  *
  450.  *  This routine closes the file resource and releases all resources
  451.  *  associated with the object. This routine is crucial and must be called
  452.  *  before the File Object is destroyed.
  453.  */
  454. STDMETHODIMP
  455. CHXHTTPFileObject::Close(void)
  456. {
  457.     MLOG_HTTP("CHXHTTPFileObject::Close()n");
  458.     /*
  459.      * Store this in temp since calling _CleanUp()
  460.      * causes m_pFileResponse to get released. We will
  461.      * have pCallCloseDone on the stack to call
  462.      * CloseDone() and then to safely release.
  463.      */
  464.     IHXFileResponse* pCallCloseDone = m_pFileResponse;
  465.     if(pCallCloseDone)
  466.         pCallCloseDone->AddRef();
  467.     HX_RELEASE(m_pFileResponse);
  468.     if(pCallCloseDone)
  469.     {
  470.         pCallCloseDone->CloseDone(HXR_OK);
  471.         pCallCloseDone->Release();
  472.     }
  473.     
  474.     return HXR_OK;
  475. } // Close()
  476. // IHXRequestHandler Interface Methods
  477. /****************************************************************************
  478.  *  IHXRequestHandler::SetRequest
  479.  *
  480.  *  This routine associates this File Object with the file Request object
  481.  *  passed to it from the Helix core. This Request object is primarily used to
  482.  *  obtain the requested URL. This method is called just after the File
  483.  *  Object is created.
  484.  */
  485. STDMETHODIMP
  486. CHXHTTPFileObject::SetRequest(IHXRequest* pRequest)
  487. {
  488.     MLOG_HTTP("CHXHTTPFileObject::SetRequest()n");
  489.     HX_RESULT res = HXR_OK;
  490.     if(pRequest == NULL)
  491.     {
  492.         res = HXR_INVALID_PARAMETER;
  493.     }
  494.     else
  495.     {
  496.         // Release any previous request object, URL
  497.         HX_RELEASE(m_pRequest);
  498.         if(m_pCHXURL != NULL)
  499.         {
  500.             delete m_pCHXURL;
  501.             m_pCHXURL = NULL;
  502.         }
  503.         // Store a reference to the object
  504.         m_pRequest = pRequest;
  505.         m_pRequest->AddRef();
  506.         const char *pURL = NULL;
  507.         if(m_pRequest->GetURL(pURL) != HXR_OK)
  508.         {
  509.             HX_RELEASE(m_pRequest);
  510.             return HXR_FAIL;
  511.         }
  512.         // Store the URL in a CHXURL object for parsing.
  513.         m_pCHXURL = new CHXURL(pURL);
  514.         if(m_pCHXURL == NULL)
  515.         {
  516.             HX_RELEASE(m_pRequest);
  517.             return HXR_FAIL;
  518.             //return HXR_OUTOFMEMORY;
  519.         }
  520.     }
  521.     return res;
  522. } // SetRequest()
  523. /****************************************************************************
  524.  *  IHXRequestHandler::GetRequest
  525.  *
  526.  *  This routine retrieves the Request object associated with this File
  527.  *  Object. It is called just after the SetRequest() method.
  528.  */
  529. STDMETHODIMP
  530. CHXHTTPFileObject::GetRequest(REF(IHXRequest*) pRequest)
  531. {
  532.     MLOG_HTTP("CHXHTTPFileObject::GetRequest()n");
  533.     pRequest = m_pRequest;
  534.     if (pRequest != NULL)
  535.     {
  536. pRequest->AddRef();
  537.     }
  538.     return HXR_OK;
  539. }
  540. // IUnknown COM Interface Methods
  541. /****************************************************************************
  542.  *  IUnknown::AddRef
  543.  *
  544.  *  This routine increases the object reference count in a thread safe
  545.  *  manner. The reference count is used to manage the lifetime of an object.
  546.  *  This method must be explicitly called by the user whenever a new
  547.  *  reference to an object is used.
  548.  */
  549. STDMETHODIMP_(UINT32) CHXHTTPFileObject::AddRef(void)
  550. {
  551.     MLOG_HTTP("CHXHTTPFileObject::AddRef()n");
  552.     return InterlockedIncrement(&m_RefCount);
  553. }
  554. /****************************************************************************
  555.  *  IUnknown::Release
  556.  *
  557.  *  This routine decreases the object reference count in a thread safe
  558.  *  manner, and deletes the object if no more references to it exist. It must
  559.  *  be called explicitly by the user whenever an object is no longer needed.
  560.  */
  561. STDMETHODIMP_(UINT32) CHXHTTPFileObject::Release(void)
  562. {
  563.     MLOG_HTTP("CHXHTTPFileObject::Release()n");
  564.     if (InterlockedDecrement(&m_RefCount) > 0)
  565.     {
  566. return m_RefCount;
  567.     }
  568.     delete this;
  569.     return 0;
  570. }
  571. /****************************************************************************
  572.  *  IUnknown::QueryInterface
  573.  *
  574.  *  This routine indicates which interfaces this object supports. If a given
  575.  *  interface is supported, the object's reference count is incremented, and
  576.  *  a reference to that interface is returned. Otherwise a NULL object and
  577.  *  error code are returned. This method is called by other objects to
  578.  *  discover the functionality of this object.
  579.  */
  580. STDMETHODIMP CHXHTTPFileObject::QueryInterface(REFIID interfaceID,
  581.        void** ppInterfaceObj)
  582. {
  583.     MLOG_HTTP("CHXHTTPFileObject::QueryInterface()n");
  584.     // Initially assume no interfaces are supported
  585.     HX_RESULT res = HXR_NOINTERFACE;
  586.     *ppInterfaceObj = NULL;
  587.     // By definition all COM objects support the IUnknown interface
  588.     if (IsEqualIID(interfaceID, IID_IUnknown))
  589.     {
  590. *ppInterfaceObj = (IUnknown*)(IHXFileObject*)this;
  591.     }
  592.     // IHXFileObject interface is supported
  593.     else if (IsEqualIID(interfaceID, IID_IHXFileObject))
  594.     {
  595. *ppInterfaceObj = (IHXFileObject*)this;
  596.     }
  597.     // IHXRequestHandler interface is supported
  598.     else if (IsEqualIID(interfaceID, IID_IHXRequestHandler))
  599.     {
  600. *ppInterfaceObj = (IHXRequestHandler*)this;
  601.     }
  602.     // IHXFileStat interface is supported
  603.     else if (IsEqualIID(interfaceID, IID_IHXFileStat))
  604.     {
  605. *ppInterfaceObj = (IHXFileStat*)this;
  606.     }
  607.     // IHXCacheObjectResponse interface is supported
  608.     else if(IsEqualIID(interfaceID, IID_IHXCacheObjectResponse))
  609.     {
  610. *ppInterfaceObj = (IHXCacheObjectResponse*)this;
  611.     }
  612.     // IHXTCPResponse interface is supported
  613.     else if (IsEqualIID(interfaceID, IID_IHXTCPResponse))
  614.     {
  615. *ppInterfaceObj = (IHXTCPResponse*)this;
  616.     }
  617.     // IHXCallback interface is supported
  618.     else if (IsEqualIID(interfaceID, IID_IHXCallback))
  619.     {
  620. *ppInterfaceObj = (IHXCallback*)this;
  621.     }
  622.     if (*ppInterfaceObj)
  623.     {
  624. AddRef();
  625. res = HXR_OK;
  626.     }
  627.     return res;
  628. }
  629. /************************************************************************
  630.  * Method:
  631.  * IHXFileStat::Stat
  632.  * Purpose:
  633.  * Collects information about the file that is returned to the
  634.  * caller in an IHXStat object
  635.  */
  636. STDMETHODIMP
  637. CHXHTTPFileObject::Stat(IHXFileStatResponse* pFileStatResponse)
  638. {
  639.     MLOG_HTTP("CHXHTTPFileObject::Stat()n");
  640.     if(pFileStatResponse == NULL)
  641.     {
  642.         return HXR_INVALID_PARAMETER;
  643.     }
  644.     // It shouldn't call this function before it gets a InitDone() (by which time
  645.     // header would have been completely read).
  646.     // >>> Later, give proper values for last four parameters too...
  647.     if(m_ulFileLength > 0)
  648.     {
  649.         pFileStatResponse->StatDone(HXR_OK, m_ulFileLength, 0, 0, 0, HX_S_IFREG);
  650.     }
  651.     else
  652.     {
  653.         return HXR_FAIL;
  654.     }
  655.     return HXR_OK;
  656. }
  657. /*
  658.  * IHXCacheObjectResponse methods
  659.  */
  660. /************************************************************************
  661.  * Method:
  662.  *
  663.  *     IHXCacheObjectResponse::InitDone
  664.  *
  665.  * Purpose:
  666.  *
  667.  *     Notification that IHXCacheObject::Init call has completed.
  668.  */
  669. STDMETHODIMP
  670. CHXHTTPFileObject::InitDone(HX_RESULT   /*IN*/ status)
  671. {
  672.     MLOG_HTTP("CHXHTTPFileObject::InitDone(%s)n", StrRep(status));
  673.     return HXR_OK;
  674. } // InitDone()
  675. /************************************************************************
  676.  * Method:
  677.  *
  678.  *     IHXCacheObjectResponse::ChangeCapacityDone
  679.  *
  680.  * Purpose:
  681.  *
  682.  *     Notification that IHXCacheObject::ChangeCapacity call has completed.
  683.  */
  684. STDMETHODIMP
  685. CHXHTTPFileObject::ChangeCapacityDone(HX_RESULT /*IN*/ status)
  686. {
  687.     MLOG_HTTP("CHXHTTPFileObject::ChangeCapacityDone(%s)n", StrRep(status));
  688.     return HXR_NOTIMPL;
  689. } // ChangeCapacityDone()
  690. /************************************************************************
  691.  * Method:
  692.  *
  693.  *     IHXCacheObjectResponse::AddBlockDone
  694.  *
  695.  * Purpose:
  696.  *
  697.  *     Notification that IHXCacheObject::AddBlock operation has completed.
  698.  */
  699. STDMETHODIMP
  700. CHXHTTPFileObject::AddBlockDone(HX_RESULT /*IN*/ status)
  701. {
  702.     MLOG_HTTP("CHXHTTPFileObject::AddBlockDone(%s)n", StrRep(status));
  703.     // Can the Cache accomodate more data that might arrive
  704.     // should we request the network for some.
  705.     UINT32 uReadSize = m_ulFileLength - m_ulFileDataRead;
  706.     if(uReadSize > CHUNK_SIZE)
  707.         uReadSize = CHUNK_SIZE;
  708.     if(status == HXR_OUTOFMEMORY)
  709.     {
  710.         // Try to add this block again when you read
  711.         // something of the cache (i.e., in ReadBlockDone()
  712.         m_bAddBlockPending = TRUE;
  713.     }
  714.     else
  715.     {
  716.         HX_RELEASE(m_pPendingAddBlock);
  717.         // Ask the network services to get more data.
  718.         m_pSocket->Read((UINT16)uReadSize);
  719.     }
  720.     return HXR_OK;
  721. } // AddBlockDone()
  722. /************************************************************************
  723.  * Method:
  724.  *
  725.  *     IHXCacheObjectResponse::VerifyBlockDone
  726.  *
  727.  * Purpose:
  728.  *
  729.  *     Notification that IHXCacheObject::VerifyBlock operation has
  730.  *     completed.
  731.  */
  732. STDMETHODIMP
  733. CHXHTTPFileObject::VerifyBlockDone(BOOL /*IN*/ bExist)
  734. {
  735.     MLOG_HTTP("CHXHTTPFileObject::VerifyBlockDone(%d)n", bExist);
  736.     return HXR_NOTIMPL;
  737. } // VerifyBlockDone()
  738. /************************************************************************
  739.  * Method:
  740.  *
  741.  *     IHXCacheObjectResponse::ReadBlockDone
  742.  *
  743.  * Purpose:
  744.  *
  745.  *     Notification that IHXCacheObject::ReadBlock operation has
  746.  *     completed.
  747.  */
  748. STDMETHODIMP
  749. CHXHTTPFileObject::ReadBlockDone(HX_RESULT /*IN*/ status,
  750.  IHXBuffer* /*IN*/ pBuffer)
  751. {
  752.     MLOG_HTTP("CHXHTTPFileObject::ReadBlockDone(%s)n", StrRep(status));
  753.     // If the cache has failed to give us anydata and it will not do
  754.     // so in the future and at the same time we notice that the server
  755.     // has disconnected, then raise alarm. Note that we need to check
  756.     // for all these conditions because if the server disconnected after
  757.     // serving all data and if it is stored in the cache, then we shouldn't
  758.     // be worried.
  759.     if(
  760.             (status == HXR_INCOMPLETE) && (pBuffer == NULL) &&
  761.             (m_bDisconnected == TRUE)  && (m_pCache->IsEmpty())
  762.         )
  763.     {
  764.         if (m_pFileResponse)
  765.         {
  766.             m_pFileResponse->ReadDone(HXR_SERVER_DISCONNECTED, NULL);
  767.         }
  768.         return HXR_OK;
  769.     }
  770.     if(pBuffer != NULL)
  771.         pBuffer->AddRef();
  772.     HX_RESULT res = HXR_OK;
  773.     // This is a response to a ReadBlock() called from Func(). Func() does this
  774.     // only when there is an incomplete Read.
  775.     if(m_bIncompleteReadPending)
  776.     {
  777.         if( ((status == HXR_OK) || (status == HXR_INCOMPLETE)) && (pBuffer != NULL) )
  778.         {
  779.             // Copy data into the buffer set aside for the incomplete read.
  780.             memcpy((void*)(m_pPendingReadInfo.pPendingReadBuff->GetBuffer() + m_pPendingReadInfo.ulWriteOffset),
  781.                            (void*)(pBuffer->GetBuffer()), pBuffer->GetSize());
  782.             // Update offsets and sizes for the next read installment
  783.             m_pPendingReadInfo.ulWriteOffset += pBuffer->GetSize(); // Next installment buffer-write offset
  784.             m_pPendingReadInfo.ulReadOffset += pBuffer->GetSize();  // Next installment cache-read offset
  785.             m_pPendingReadInfo.ulSize -= pBuffer->GetSize();        // Next installment max-size
  786.         }
  787.         if(HXR_OK == status)
  788.         {
  789.             // This installment finally fills up the incomplete read buffer.
  790.             m_ulCurrentReadOffset = m_pPendingReadInfo.ulReadOffset;
  791.             m_bIncompleteReadPending = FALSE;
  792.             m_bReadPending = FALSE;
  793.             // Get a handle since there is a possibilily of m_pPendingReadInfo
  794.             // being modified in some method called from ReadDone().
  795.             IHXBuffer* pBuff = m_pPendingReadInfo.pPendingReadBuff;
  796.             m_pPendingReadInfo.pPendingReadBuff = NULL;
  797.             if (m_pFileResponse)
  798.             {
  799.                 m_pFileResponse->ReadDone(HXR_OK, pBuff);
  800.             }
  801.             HX_RELEASE(pBuff);
  802.             pBuff = NULL;
  803.         }
  804.         else if(status == HXR_INCOMPLETE) // Need to get more installments to fill up ReadBuffer
  805.         {
  806.             m_ulCallbackHandle = m_pScheduler->RelativeEnter(this, m_ulCallBackInterval);
  807.         }
  808.         else
  809.         {
  810.             // Can't happen
  811.         }
  812.     }
  813.     else // !m_bIncompleteReadPending
  814.     {
  815.         if(status == HXR_OK) // Cool, hand data over to the FF object
  816.         {
  817.             m_ulCurrentReadOffset = m_pPendingReadInfo.ulReadOffset + m_pPendingReadInfo.ulSize;
  818.             m_bReadPending = FALSE;
  819.             if (m_pFileResponse)
  820.             {
  821.                 m_pFileResponse->ReadDone(HXR_OK, pBuffer);
  822.             }
  823.         }
  824.         // This means that the Cache has only partial data. Create a buffer to store this partial data.
  825.         // Mark this read as incomplete and try to read from the cache in installments.
  826.         // Request scheduler to signal you after some time. When the scheduler signals
  827.         // you (via Func()), check to see if Cache has recieved anymore data.
  828.         else if(status == HXR_INCOMPLETE)
  829.         {
  830.             if(m_pClassFactory != NULL)
  831.             {
  832.                 // Create a buffer to fill it in installments.
  833.                 m_pClassFactory->CreateInstance(CLSID_IHXBuffer,
  834.                 (void**)&m_pPendingReadInfo.pPendingReadBuff);
  835.                 if (m_pPendingReadInfo.pPendingReadBuff != NULL)
  836.                 {
  837.                     res =  m_pPendingReadInfo.pPendingReadBuff->SetSize(m_pPendingReadInfo.ulSize);
  838.                     if(HXR_OK == res)
  839.                     {
  840.                         if(pBuffer != NULL)
  841.                         {
  842.                             // Copy the partial data into the pending read buffer.
  843.                             memcpy((void*)(m_pPendingReadInfo.pPendingReadBuff->GetBuffer()),
  844.                                    (void*)(pBuffer->GetBuffer()), pBuffer->GetSize());
  845.                             m_pPendingReadInfo.ulWriteOffset = pBuffer->GetSize();  // Next installment buffer-write offset
  846.                             m_pPendingReadInfo.ulReadOffset += pBuffer->GetSize();  // Next installment cache-read offset
  847.                             m_pPendingReadInfo.ulSize -= pBuffer->GetSize();        // Next installment max-size
  848.                         }
  849.                     }
  850.                     else
  851.                     {
  852.                         HX_RELEASE(m_pPendingReadInfo.pPendingReadBuff);
  853.                         res = HXR_OUTOFMEMORY;
  854.                     }
  855.                 }
  856.                 else
  857.                 {
  858.                     res = HXR_OUTOFMEMORY;
  859.                 }
  860.             }
  861.             else
  862.             {
  863.                 res = HXR_UNEXPECTED;
  864.             }
  865.             // Not enough memory to create buffers.
  866.             // Let's do the best we can do! Return the incomplete data.
  867.             if(HXR_OK != res)
  868.             {
  869.                 m_bReadPending = FALSE;
  870.                 m_ulCurrentReadOffset = m_pPendingReadInfo.ulReadOffset;
  871.                 if (m_pFileResponse)
  872.                 {
  873.                     m_pFileResponse->ReadDone(HXR_INCOMPLETE, pBuffer);
  874.                 }
  875.             }
  876.             else
  877.             {
  878.                 // We will try to fill the remaining data in the buffer after waiting for some
  879.                 // time. We ask the scheduler to call us back after some time and then we check if the
  880.                 // Cache has recieved any new data.
  881.                 m_bIncompleteReadPending = TRUE;
  882.                 m_ulCallbackHandle = m_pScheduler->RelativeEnter(this, m_ulCallBackInterval);
  883.             }
  884.         }
  885.         // status = HXR_FAIL, i.e, the Cache had discarded some part of the data
  886.         // we requeted. The cache has already discarded the data and so there is no hope
  887.         // of getting that data. The only way to satisfy the Read request
  888.         // is to tear down the connection and start-all-over-again.
  889.         else //status = HXR_FAIL and the reason is request for past data
  890.         {
  891.             // Set this flag so that other activities are all paralysed
  892.             m_bStartAllOverAgain = TRUE;
  893.             m_bAddBlockPending = FALSE;
  894.             HX_RELEASE(m_pPendingAddBlock);
  895.             m_pPendingAddBlock = NULL;
  896.             // Close the socket
  897.             HX_RELEASE(m_pSocket);
  898.             m_bIncompleteReadPending = TRUE;
  899.             if(m_pClassFactory != NULL)
  900.             {
  901.                 // Create a buffer and fill it in installments.
  902.                 m_pClassFactory->CreateInstance(CLSID_IHXBuffer,
  903.                 (void**)&m_pPendingReadInfo.pPendingReadBuff);
  904.                 m_pPendingReadInfo.pPendingReadBuff->SetSize(m_pPendingReadInfo.ulSize);
  905.             }
  906.             // Flush the cache so that we can freshly start
  907.             // storing the new info into it.
  908.             m_pCache->Flush();
  909.             // Rest of the the "start-all-over-again" continues in FlushDone()
  910.         }
  911.     } // !m_bIncompleteReadPending
  912.     if(m_bAddBlockPending)
  913.     {
  914.         // Now that we have read some data out of cache, maybe the cache would accept
  915.         // the previously rejected data.
  916.         if( (pBuffer != NULL) && (pBuffer->GetSize() > 0) )
  917.         {
  918.             if(m_pCache->GetUnusedCapacity() >= m_pPendingAddBlock->GetSize())
  919.             {
  920.                 m_ulCallbackHandle = m_pScheduler->RelativeEnter(this, 0);
  921.             }
  922.         }
  923.     }
  924.     HX_RELEASE(pBuffer);
  925.     pBuffer = NULL;
  926.     return HXR_OK;
  927. } // ReadBlockDone()
  928. /************************************************************************
  929.  * Method:
  930.  *
  931.  *     IHXCacheObjectResponse::FlushDone
  932.  *
  933.  * Purpose:
  934.  *
  935.  *     Notification that IHXCacheObject::Flush operation has completed.
  936.  */
  937. STDMETHODIMP
  938. CHXHTTPFileObject::FlushDone(HX_RESULT  /*IN*/ status)
  939. {
  940.     MLOG_HTTP("CHXHTTPFileObject::FlushDone(%s)n", StrRep(status));
  941.     // Discard the old header so that the incoming new
  942.     // hdr is not confused for data.
  943.     HX_RELEASE(m_pHeader);
  944.     m_pHeader = NULL;
  945.     m_bHeaderCompletelyRead = FALSE;
  946.     m_ulFileDataRead = 0;
  947.     m_bFirstChunk = TRUE;   // The TCP chunk that will be recived later
  948.                             // will be the first one from the new connection.
  949.     m_bStartAllOverAgain = FALSE;
  950.     // Start the whole process (viz., connecting, GET request, etc) all over again.
  951.     _Start();
  952.     return HXR_OK;
  953. } // FlushDone()
  954. /*
  955.  * IHXTCPResponse methods
  956.  */
  957. /************************************************************************
  958.  * Method:
  959.  *     IHXTCPResponse::ConnectDone
  960.  * Purpose:
  961.  *     A Connect operation has been completed or an error has occurred.
  962.  */
  963. STDMETHODIMP
  964. CHXHTTPFileObject::ConnectDone(HX_RESULT status)
  965. {
  966.     MLOG_HTTP("CHXHTTPFileObject::ConnectDone(%s)n", StrRep(status));
  967.     if(status != HXR_OK)
  968.     {
  969.         if(!m_bInitialized && m_pFileResponse)
  970.         {
  971.            m_pFileResponse->InitDone(status);
  972.         }
  973.         return HXR_OK;
  974.     }
  975.     // OK, the connection has been established. Now send a GET request to
  976.     // start getting the file.
  977.     char* HttpGetReq = NULL;
  978.     UINT32 ulReqLen = 0;
  979.     HX_RESULT res = HXR_OK;
  980.     res = _PrepareHTTP10GetMessage(HttpGetReq, ulReqLen);
  981.     if(res != HXR_OK)
  982.     {
  983.         if(!m_bInitialized && m_pFileResponse)
  984.         {
  985.             m_pFileResponse->InitDone(HXR_OUTOFMEMORY);
  986.         }
  987.         return HXR_OK;
  988.     }
  989.     CHXBuffer* pSendBuffer = new CHXBuffer((UCHAR*)HttpGetReq, ulReqLen);
  990.     if(pSendBuffer == NULL)
  991.     {
  992.         if(!m_bInitialized && m_pFileResponse)
  993.         {
  994.             m_pFileResponse->InitDone(HXR_OUTOFMEMORY);
  995.         }
  996.         return HXR_OK;
  997.     }
  998.     pSendBuffer->AddRef();
  999.     // Now write the HTTP/1.0 GET request into the socket
  1000.     res = m_pSocket->Write( (IHXBuffer *)pSendBuffer );
  1001.     // Ask for the response
  1002.     res = m_pSocket->Read((UINT16)CHUNK_SIZE);
  1003.     HX_RELEASE(pSendBuffer);
  1004.     return res;
  1005. } //  ConnectDone()
  1006. /************************************************************************
  1007.  * Method:
  1008.  *     IHXTCPResponse::ReadDone
  1009.  * Purpose:
  1010.  *     A Read operation has been completed or an error has occurred.
  1011.  *     The data is returned in the IHXBuffer.
  1012.  */
  1013. STDMETHODIMP
  1014. CHXHTTPFileObject::ReadDone(HX_RESULT status,
  1015.     IHXBuffer* pBuffer)
  1016. {
  1017.     MLOG_HTTP("CHXHTTPFileObject::ReadDone(%s)n", StrRep(status));
  1018.     if(pBuffer != NULL)
  1019.         pBuffer->AddRef();
  1020.     // A backward seek had failed. Which means the Cache is going
  1021.     // to be flushed, connection tore down and everythings going to
  1022.     // start all over again. So, no point storing this in the cache.
  1023.     if(m_bStartAllOverAgain)
  1024.     {
  1025.         HX_RELEASE(pBuffer);
  1026.         return HXR_OK;
  1027.     }
  1028.     if(!m_bHeaderCompletelyRead) // The TCP data we recvd is a chunk of HTTP header
  1029.     {
  1030.         if( (status != HXR_OK) || (pBuffer == NULL) )
  1031.         {
  1032.             if(!m_bInitialized && m_pFileResponse)
  1033.             {
  1034.                 m_pFileResponse->InitDone(HXR_FAIL);
  1035.             }
  1036.             return HXR_OK;
  1037.         }
  1038.         // Check if response indicates success
  1039.         if(m_bFirstChunk)  // If it is the first chunk from the server
  1040.         {
  1041.             m_bFirstChunk = FALSE;
  1042.             const char pSuccess[] = "200 OKrn"; // HTTP success code
  1043.             // Is the starting line "HTTP/1.x 200 OKrn" ?
  1044.             if( memcmp((void*)(pBuffer->GetBuffer() + 9), (void*)pSuccess, strlen(pSuccess)) != 0 )
  1045.             {
  1046.                 if(!m_bInitialized)
  1047.                 {
  1048.                     m_bFirstChunk = TRUE;
  1049.                     // The requested file is not present on the web server
  1050.                     if (m_pFileResponse)
  1051.                     {
  1052.                         m_pFileResponse->InitDone(HXR_DOC_MISSING);
  1053.                     }
  1054.                 }
  1055.                 return HXR_OK;
  1056.             }
  1057.         }
  1058.         // Append this header chunk to the list of such chunks being maintained.
  1059.         HdrChunk *hi = new HdrChunk;
  1060.         hi->pPartOfHdr = pBuffer;
  1061.         hi->next = NULL;
  1062.         if(m_pHdrListRoot == NULL)
  1063.         {
  1064.             m_pHdrListRoot = hi;
  1065.         }
  1066.         else
  1067.         {
  1068.             HdrChunk *temp = NULL;
  1069.             for(temp = m_pHdrListRoot; temp->next != NULL; temp = temp->next);
  1070.             temp->next = hi;
  1071.         }
  1072.         // Is this the last header chunk? If so, does this chunk
  1073.         // also contain some non-header data? If true, store this
  1074.         // non-hdr data in the Cache and consolidate all the previous
  1075.         // header chunks into a single buffer. Then destroy the
  1076.         // header-chunks list.
  1077.         // Note!!!! GetBuffer() doesn't return a NULL terminated string. So,
  1078.         // shouldn't use strstr(). That's why I used my utility function bufnstr()!
  1079.         char *marker = bufnstr((char*)(hi->pPartOfHdr->GetBuffer()), "rnrn",
  1080.                                 hi->pPartOfHdr->GetSize());
  1081.         if(marker == NULL) // End of hdr not found in this chunk
  1082.         {
  1083.             return m_pSocket->Read((UINT16)CHUNK_SIZE); // Read more data
  1084.         }
  1085.         else
  1086.         {
  1087.             m_bHeaderCompletelyRead = TRUE;
  1088.         }
  1089.         // This chunk has the end-of-header marker.
  1090.         // Consolidate all the header chunks
  1091.         marker += 4; // Move the pointer just after the header end-marker.
  1092.         int hdrLen = 0;
  1093.         int dataLen = 0;
  1094.         char *hdr = NULL;
  1095.         char *data = NULL;
  1096.         CHXBuffer* dataBuff = NULL;
  1097.         int len = 0;
  1098.         for(HdrChunk *temp = m_pHdrListRoot; temp != hi; temp = temp->next)
  1099.             len += temp->pPartOfHdr->GetSize();
  1100.         int lastPktHdrLen = marker - (char *)(hi->pPartOfHdr->GetBuffer());
  1101.         hdrLen = len + lastPktHdrLen;
  1102.         dataLen = hi->pPartOfHdr->GetSize() - lastPktHdrLen;
  1103.         hdr = new char[hdrLen];
  1104.         data = new char[dataLen];
  1105.         if( (hdr == NULL) || (data == NULL) )
  1106.         {
  1107.             if(!m_bInitialized && m_pFileResponse)
  1108.             {
  1109.                 m_pFileResponse->InitDone(HXR_OUTOFMEMORY);
  1110.             }
  1111.             return HXR_OK;
  1112.         }
  1113.         int i = 0;
  1114.         for(HdrChunk *temp2 = m_pHdrListRoot; temp2 != hi; temp2 = temp2->next)
  1115.         {
  1116.             int buffLen = temp2->pPartOfHdr->GetSize();
  1117.             memcpy((void*)(hdr + i), (void*)(temp2->pPartOfHdr->GetBuffer()),buffLen);
  1118.             i += buffLen;
  1119.         }
  1120.         // m_ulFileDataRead is the amount of file data that has been read
  1121.         // so far.
  1122.         m_ulFileDataRead = dataLen;
  1123.         memcpy((void*)hdr, (void*)(hi->pPartOfHdr->GetBuffer()), lastPktHdrLen);
  1124.         memcpy((void*)data, (void*)(hi->pPartOfHdr->GetBuffer() + lastPktHdrLen), dataLen);
  1125.         m_pHeader = new CHXBuffer((UCHAR*)hdr, hdrLen);
  1126.         m_pHeader->AddRef();
  1127.         dataBuff = new CHXBuffer((UCHAR*)data, dataLen);
  1128.         dataBuff->AddRef();
  1129.         if( (!m_pHeader) || (!dataBuff) )
  1130.         {
  1131.             delete hdr;
  1132.             delete data;
  1133.             if(!m_bInitialized && m_pFileResponse)
  1134.             {
  1135.                 m_pFileResponse->InitDone(HXR_OUTOFMEMORY);
  1136.             }
  1137.             return HXR_OK;
  1138.         }
  1139.         // Destroy the header-chunks list
  1140.         HdrChunk *temp3 = m_pHdrListRoot;
  1141.         HdrChunk *temp4 = NULL;
  1142.         while(temp3 != NULL)
  1143.         {
  1144.             temp4 = temp3;
  1145.             temp3 = temp3->next;
  1146.             temp4->pPartOfHdr->Release();
  1147.             delete temp4;
  1148.         }
  1149.         m_pHdrListRoot = NULL;
  1150.         // Parse the header for file statistics
  1151.         marker = bufnstr((char*)(m_pHeader->GetBuffer()), "rnContent-Length: ",
  1152.                             m_pHeader->GetSize());
  1153.         if(marker == NULL)
  1154.         {
  1155.             m_ulFileLength = 0;
  1156.             HX_RELEASE(m_pHeader);
  1157.             m_pHeader = NULL;
  1158.             HX_RELEASE(dataBuff);
  1159.             if(!m_bInitialized && m_pFileResponse)
  1160.             {
  1161.                 m_pFileResponse->InitDone(HXR_INVALID_FILE);
  1162.             }
  1163.             return HXR_OK;
  1164.         }
  1165.         marker += 18;
  1166.         int remLen = m_pHeader->GetSize() - (marker - (char*)(m_pHeader->GetBuffer()));
  1167.         char *marker2 = bufnstr(marker, "rn", remLen);
  1168.         if(marker2 == NULL)
  1169.         {
  1170.             m_ulFileLength = 0;
  1171.             HX_RELEASE(m_pHeader);
  1172.             m_pHeader = NULL;
  1173.             HX_RELEASE(dataBuff);
  1174.             if(!m_bInitialized && m_pFileResponse)
  1175.             {
  1176.                 m_pFileResponse->InitDone(HXR_INVALID_FILE);
  1177.             }
  1178.             return HXR_OK;
  1179.         }
  1180.         int l = marker2 - marker;
  1181.         char *flen = new char[l + 1];
  1182.         memcpy((void*)flen, (void*)marker, l);
  1183.         flen[l] = '';
  1184.         m_ulFileLength = atol(flen);
  1185.         delete flen;
  1186.         if(m_pCache == NULL)
  1187.         {
  1188.             CHXCache2 *pObj = new CHXCache2();
  1189.             pObj->AddRef();
  1190.             #ifdef HELIX_FEATURE_HTTP_FILECACHE
  1191.                 BOOL bCacheEnabled = FALSE;
  1192.                 char *pCacheFile = NULL;
  1193.                 _GetCachePreferences(bCacheEnabled, pCacheFile);
  1194.                 if(bCacheEnabled)
  1195.                 {
  1196.                     pObj->CreateFileCacheObject(&m_pCache, m_pClassFactory,
  1197.                                         m_ulFileLength, pCacheFile);
  1198.                 }
  1199.             #endif
  1200.             if(m_pCache == NULL)
  1201.             {
  1202.                 pObj->CreateMemCacheObject(&m_pCache, m_pClassFactory);
  1203.             }
  1204.             pObj->Release();
  1205.             m_pCache->Init((IHXCacheObjectResponse*)this, CACHE_CAPACITY,
  1206.                                         THRESHOLD);
  1207.         }
  1208.         // You are on the network thread, so you can't call InitDone(). Let the
  1209.         // scheduler do it.
  1210.         if(!m_bInitialized)
  1211.         {
  1212.             m_bInitResponsePending = TRUE;
  1213.             m_ulCallbackHandle = m_pScheduler->RelativeEnter(this, 0);
  1214.         }
  1215.         // Store the non-header portion of the latest chunk in cache.
  1216.         m_pPendingAddBlock = (IHXBuffer*)dataBuff;
  1217.         m_pCache->AddBlock((IHXBuffer*)dataBuff);
  1218.         if(m_bIncompleteReadPending)
  1219.         {
  1220.             // Now that we have got the data for which we had to "startAllOverAgain",
  1221.             // let Func() take over and satisfy the "failed" Read().
  1222.             m_ulCallbackHandle = m_pScheduler->RelativeEnter(this, 0);
  1223.         }
  1224.     }
  1225.     else // The TCP data we recvd is applications data (i.e., not part of HTTP header)
  1226.     {
  1227.         if( (status == HXR_OK) && (pBuffer != NULL) )
  1228.         {
  1229.             m_ulFileDataRead += pBuffer->GetSize();
  1230.             m_pPendingAddBlock = pBuffer;
  1231.             m_pCache->AddBlock(pBuffer);
  1232.         }
  1233.     }
  1234.     return HXR_OK;
  1235. } //  ReadDone()
  1236. /************************************************************************
  1237.  * Method:
  1238.  *     IHXTCPResponse::WriteReady
  1239.  * Purpose:
  1240.  *     This is the response method for WantWrite.
  1241.  *     If HX_RESULT is ok, then the TCP channel is ok to Write to.
  1242.  */
  1243. STDMETHODIMP
  1244. CHXHTTPFileObject::WriteReady(HX_RESULT status)
  1245. {
  1246.     MLOG_HTTP("CHXHTTPFileObject::WriteReady(%s)n", StrRep(status));
  1247.     return HXR_NOTIMPL;
  1248. } //  WriteReady()
  1249. /************************************************************************
  1250.  * Method:
  1251.  *     IHXTCPResponse::Closed
  1252.  * Purpose:
  1253.  *     This method is called to inform you that the TCP channel has
  1254.  *     been closed by the peer or closed due to error.
  1255.  */
  1256. STDMETHODIMP
  1257. CHXHTTPFileObject::Closed(HX_RESULT status)
  1258. {
  1259.     MLOG_HTTP("CHXHTTPFileObject::Closed(%s)n", StrRep(status));
  1260.     m_bDisconnected = TRUE;
  1261.     return HXR_OK;
  1262. } //  Closed()
  1263. /************************************************************************
  1264.  * IHXCallback::Func()
  1265.  */
  1266. STDMETHODIMP
  1267. CHXHTTPFileObject::Func()
  1268. {
  1269.     MLOG_HTTP("CHXHTTPFileObject::Func()n");
  1270.     if(m_bInitResponsePending  && m_pFileResponse)
  1271.     {
  1272.         m_bInitResponsePending = FALSE;
  1273.         m_bInitialized = TRUE;
  1274.         m_pFileResponse->InitDone(HXR_OK);
  1275.     }
  1276.     // Is there any incomplete read pending?
  1277.     if(m_bIncompleteReadPending && m_pCache && m_pFileResponse)
  1278.     {
  1279.         // Ask the cache if it can supply the next installment for the
  1280.         // incomplete read.
  1281.         HX_RESULT res = m_pCache->ReadBlock(m_pPendingReadInfo.ulReadOffset,
  1282.                                             m_pPendingReadInfo.ulSize);
  1283.         if(res != HXR_OK)
  1284.         {
  1285.             m_bIncompleteReadPending = FALSE;
  1286.             IHXBuffer* pBuff = m_pPendingReadInfo.pPendingReadBuff;
  1287.             m_pPendingReadInfo.pPendingReadBuff = NULL;
  1288.             if (m_pFileResponse)
  1289.             {
  1290.                 m_pFileResponse->ReadDone(HXR_INCOMPLETE, pBuff);
  1291.             }
  1292.             HX_RELEASE(pBuff);
  1293.             pBuff = NULL;
  1294.         }
  1295.     }
  1296.     // Is there any block which was rejected by the cache earlier because it
  1297.     // was full? If so, let's try inserting into the cache again. Maybe, the
  1298.     // cache has since freed some space.
  1299.     if(m_bAddBlockPending && m_pCache)
  1300.     {
  1301.         m_bAddBlockPending = FALSE;
  1302.         m_pCache->AddBlock(m_pPendingAddBlock);
  1303.     }
  1304.     return HXR_OK;
  1305. } // Func()
  1306. // A utility function which does searches for a *string* in a
  1307. // *character buffer* of size haystacksize. strnstr() wouldn't work since
  1308. // it requires both the parameters to be strings.
  1309. char* bufnstr(char *haystackBuffer, char* needleString, int haystackSize)
  1310. {
  1311.     char lastchar = haystackBuffer[haystackSize];
  1312.     haystackBuffer[haystackSize] = ''; // Now, it's a string
  1313.     char* marker = strstr(haystackBuffer, needleString);
  1314.     if(marker != NULL)
  1315.     {
  1316.         haystackBuffer[haystackSize] = lastchar;
  1317.         return marker;
  1318.     }
  1319.     // Perhaps the pattern occurs at the very end?
  1320.     haystackBuffer[haystackSize] = lastchar;
  1321.     int needleSize = strlen(needleString);
  1322.     marker = haystackBuffer + haystackSize - needleSize;
  1323.     char *tailPiece = new char[needleSize + 1];
  1324.     if(tailPiece == NULL)
  1325.         return NULL;
  1326.     memcpy((void*)tailPiece, (void*)marker, needleSize);
  1327.     tailPiece[needleSize] = '';
  1328.     if( strcmp(tailPiece, needleString) != 0 )
  1329.         marker = NULL;
  1330.     delete tailPiece;
  1331.     return marker;
  1332. } // bufnstr()
  1333. // From the preferences, get the directory and file where the
  1334. // contents should be cached.
  1335. STDMETHODIMP
  1336. CHXHTTPFileObject::_GetCachePreferences(BOOL& bCacheEnabled, char* &pCacheFile )
  1337. {
  1338.     IHXPreferences* pPreferences;
  1339.     m_pContext->QueryInterface(IID_IHXPreferences, (void **)&pPreferences);
  1340.     HX_ASSERT(pPreferences);
  1341.     // Get cache directory, if not set and available
  1342.     IHXBuffer* pBuffer  = NULL;
  1343.     // Check to see if the cache is enabled
  1344.     bCacheEnabled = TRUE;
  1345.     if (pPreferences->ReadPref("CacheEnabled", pBuffer) == HXR_OK)
  1346.     {
  1347.         // Cache enabled entry exists
  1348.         if (atoi((const char*)pBuffer->GetBuffer()))
  1349.         {
  1350.             // Cache enabled
  1351.             bCacheEnabled = TRUE;
  1352.         }
  1353.         else
  1354.         {
  1355.             // Cache disabled
  1356.             bCacheEnabled = FALSE;
  1357.         }
  1358.     }
  1359.     else
  1360.     {
  1361.         bCacheEnabled = FALSE;
  1362.     }
  1363.     if(bCacheEnabled == FALSE)
  1364.     {
  1365.         HX_RELEASE(pBuffer);
  1366.         return HXR_OK;
  1367.     }
  1368.     char separator[2] = {OS_SEPARATOR_CHAR, ''};
  1369.     BOOL bCacheDirSpecified = FALSE;
  1370.     if (pPreferences->ReadPref("CacheFolder", pBuffer) == HXR_OK)
  1371.     {
  1372.         // Make sure that the given filename belongs to a directory
  1373.         struct stat statbuf = { 0 };
  1374.         // If cache database filename does not refer to a file, drop the filename part
  1375.         if (!stat ((const char*)pBuffer->GetBuffer(), &statbuf))
  1376.         {
  1377.           if (statbuf.st_mode & S_IFDIR)
  1378.           {
  1379.               // Valid cache directory specified
  1380.               bCacheDirSpecified = TRUE;
  1381.           }
  1382.         }
  1383.     }
  1384.     if( bCacheDirSpecified == FALSE )
  1385.     {
  1386.         return HXR_FAIL;
  1387.     }
  1388.     char *cacheDir = (char*)(pBuffer->GetBuffer());
  1389.     // Now append the file name
  1390.     HX_ASSERT(m_pCHXURL);
  1391.     IHXValues* pHeader = m_pCHXURL->GetProperties();
  1392.     HX_ASSERT(pHeader);
  1393.     IHXBuffer* pPath = NULL;
  1394.     char *path = NULL;
  1395.     IHXBuffer* pFullPath = NULL;
  1396.     char* fullPath = NULL;
  1397.     pHeader->GetPropertyBuffer(PROPERTY_PATH, pPath);
  1398.     if(pPath != NULL)
  1399.     {
  1400.         path = (char*)(pPath->GetBuffer());
  1401.     }
  1402.     pHeader->GetPropertyBuffer(PROPERTY_FULLPATH, pFullPath);
  1403.     if(pFullPath!= NULL)
  1404.     {
  1405.         fullPath = (char*)(pFullPath->GetBuffer());
  1406.     }
  1407.     char *justFileName = &fullPath[strlen(path)];
  1408.     UINT32 l1 = strlen(cacheDir);
  1409.     UINT32 l2 = strlen(separator);
  1410.     UINT32 l3 = strlen(justFileName);
  1411.     pCacheFile = new char[l1 + l2 + l3 + 1];
  1412.     HX_ASSERT(pCacheFile);
  1413.     memcpy( (void*)pCacheFile, (void*)cacheDir, l1);
  1414.     memcpy( (void*)(pCacheFile + l1), (void*)separator, l2);
  1415.     memcpy( (void*)(pCacheFile + l1 + l2), (void*)justFileName, l3);
  1416.     pCacheFile[l1 + l2 + l3] = '';
  1417.     HX_RELEASE(pBuffer);
  1418.     HX_RELEASE(pPath);
  1419.     HX_RELEASE(pFullPath);
  1420.     return HXR_OK;
  1421. } // _GetCachePreferences()
  1422. // Prepare a HTTP/1.0 GET Request Message with the appropriate headers.
  1423. STDMETHODIMP
  1424. CHXHTTPFileObject::_PrepareHTTP10GetMessage(char* &pGetMsg,
  1425.                                             UINT32 &ulMsgLen)
  1426. {
  1427.     char* SP = " ";     // space
  1428.     char* CRLF = "rn";
  1429.     char* CRLFCRLF = "rnrn";
  1430.     char* resource = NULL;
  1431.     IHXBuffer* pBuffer = NULL;
  1432.     IHXValues* pHeader = m_pCHXURL->GetProperties();
  1433.     pHeader->GetPropertyBuffer(PROPERTY_RESOURCE, pBuffer);
  1434.     if(pBuffer != NULL)
  1435.     {
  1436.         resource = (char*)(pBuffer->GetBuffer());
  1437.     }
  1438.     if(resource == NULL)
  1439.     {
  1440.         return HXR_UNEXPECTED;
  1441.     }
  1442.     CHXString ReqStr;
  1443.     ReqStr += (const char*)"GET /";
  1444.     ReqStr += (const char*)resource;
  1445.     ReqStr += (const char*)SP;
  1446.     ReqStr += (const char*)"HTTP/1.0";
  1447.     IHXValues* pHeaders = NULL;
  1448.     m_pRequest->GetRequestHeaders(pHeaders);
  1449.     const char* pKey = NULL;
  1450.     IHXBuffer* pStr = NULL;
  1451.     IHXKeyValueList* pKeyVals = NULL;
  1452.     m_pClassFactory->CreateInstance(CLSID_IHXKeyValueList,
  1453.     (void**)&pKeyVals);
  1454.     pKeyVals->ImportValues(pHeaders);
  1455.     IHXKeyValueListIter* pIter;
  1456.     pKeyVals->GetIter(pIter);
  1457.     // Attach the appropriate HTTP Request headers
  1458.     HX_RESULT retVal = pIter->GetNextPair(pKey, pStr);
  1459.     while(retVal == HXR_OK)
  1460.     {
  1461.         ReqStr += (const char*)CRLF;
  1462.         ReqStr += (const char*)pKey;
  1463.         ReqStr += (const char*)": ";
  1464.         ReqStr += (const char*)pStr->GetBuffer();
  1465.         retVal = pIter->GetNextPair(pKey, pStr);
  1466.     }
  1467.     ReqStr += (const char*)CRLFCRLF;
  1468.     // Set the function's out parameters
  1469.     ulMsgLen = ReqStr.GetLength();
  1470.     pGetMsg = NULL;
  1471.     pGetMsg = new char[ulMsgLen];
  1472.     if(pGetMsg == NULL)
  1473.     {
  1474.         return HXR_OUTOFMEMORY;
  1475.     }
  1476.     memcpy(pGetMsg, ReqStr.GetBuffer(ulMsgLen), ulMsgLen);
  1477.     return HXR_OK;
  1478. } // _PrepareHTTP10GetMessage()