Clone
ixen <ixen@copyhandler.com>
committed
on 03 Jan 16
First batch of changes related to checking for updates (CH-209).
LoggerImprovements + 5 more
src/ch/AsyncHttpFile.cpp (+428)
  1 // ============================================================================
  2 //  Copyright (C) 2001-2015 by Jozef Starosczyk
  3 //  ixen@copyhandler.com
  4 //
  5 //  This program is free software; you can redistribute it and/or modify
  6 //  it under the terms of the GNU Library General Public License
  7 //  (version 2) as published by the Free Software Foundation;
  8 //
  9 //  This program is distributed in the hope that it will be useful,
  10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 //  GNU General Public License for more details.
  13 //
  14 //  You should have received a copy of the GNU Library General Public
  15 //  License along with this program; if not, write to the
  16 //  Free Software Foundation, Inc.,
  17 //  59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18 // ============================================================================
  19 #include "stdafx.h"
  20 #include "AsyncHttpFile.h"
  21
  22 // timeout used with waiting for events (avoiding hangs)
  23 #define FORCE_TIMEOUT 60000
  24
  25 using details::CONTEXT_REQUEST;
  26
  27 // ============================================================================
  28 /// CAsyncHttpFile::CAsyncHttpFile
  29 /// @date 2009/04/18
  30 ///
  31 /// @brief     Constructs the CAsyncHttpFile object.
  32 // ============================================================================
  33 CAsyncHttpFile::CAsyncHttpFile() :
  34         m_hInternet(NULL),
  35         m_hOpenUrl(NULL),
  36         m_dwExpectedState(0),
  37         m_hFinishedEvent(NULL),
  38         m_dwError(ERROR_SUCCESS)
  39 {
  40         memset(&m_internetBuffers, 0, sizeof(INTERNET_BUFFERS));
  41
  42         m_tOpenRequest.pHttpFile = this;
  43         m_tOpenRequest.eOperationType = CONTEXT_REQUEST::eInternetOpenUrl;
  44
  45         m_tReadRequest.pHttpFile = this;
  46         m_tReadRequest.eOperationType = CONTEXT_REQUEST::eInternetReadFileEx;
  47 }
  48
  49 // ============================================================================
  50 /// CAsyncHttpFile::~CAsyncHttpFile
  51 /// @date 2009/04/18
  52 ///
  53 /// @brief     Destructs the CASyncHttpFile object.
  54 // ============================================================================
  55 CAsyncHttpFile::~CAsyncHttpFile()
  56 {
  57         Close();
  58 }
  59
  60 // ============================================================================
  61 /// CAsyncHttpFile::Open
  62 /// @date 2009/04/18
  63 ///
  64 /// @brief     Opens the specified internet address (starts those operations).
  65 /// @param[in] pszPath          Url to be opened (full path to file).
  66 /// @return    S_OK if opened, S_FALSE if wait for result is needed, E_* for errors.
  67 // ============================================================================
  68 HRESULT CAsyncHttpFile::Open(const wchar_t* pszPath, const wchar_t* pszUserAgent, const wchar_t* pszHeaders)
  69 {
  70         if(!pszPath)
  71         {
  72                 SetErrorCode(ERROR_INTERNAL_ERROR);
  73                 return E_INVALIDARG;
  74         }
  75
  76         if(m_hInternet || m_hFinishedEvent)
  77         {
  78                 SetErrorCode(ERROR_INTERNAL_ERROR);
  79                 return E_FAIL;
  80         }
  81
  82         // reset error code
  83         SetErrorCode(ERROR_SUCCESS);
  84
  85         // create event
  86         m_hFinishedEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  87         if(!m_hFinishedEvent)
  88         {
  89                 SetErrorCode(ERROR_INTERNAL_ERROR);
  90                 return E_FAIL;
  91         }
  92
  93         m_hInternet = ::InternetOpen(pszUserAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC);
  94         if(!m_hInternet)
  95         {
  96                 SetErrorCode(GetLastError());
  97
  98                 ::CloseHandle(m_hFinishedEvent);
  99                 m_hFinishedEvent = NULL;
  100
  101                 return E_FAIL;
  102         }
  103
  104         if(::InternetSetStatusCallback(m_hInternet, (INTERNET_STATUS_CALLBACK)&CAsyncHttpFile::InternetStatusCallback) == INTERNET_INVALID_STATUS_CALLBACK)
  105         {
  106                 SetErrorCode(GetLastError());
  107
  108                 ::InternetCloseHandle(m_hInternet);
  109                 ::CloseHandle(m_hFinishedEvent);
  110
  111                 m_hFinishedEvent = NULL;
  112                 return E_FAIL;
  113         }
  114
  115         m_dwExpectedState = INTERNET_STATUS_REQUEST_COMPLETE;
  116
  117         HINTERNET hOpenUrl = ::InternetOpenUrl(m_hInternet, pszPath, pszHeaders, (DWORD)-1, INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD, (DWORD_PTR)&m_tOpenRequest);
  118         if(!hOpenUrl)
  119         {
  120                 SetErrorCode(::GetLastError());
  121                 if(GetErrorCode() != ERROR_IO_PENDING)
  122                 {
  123                         ::InternetSetStatusCallback(m_hInternet, NULL);
  124                         ::InternetCloseHandle(m_hInternet);
  125                         ::CloseHandle(m_hFinishedEvent);
  126
  127                         m_hInternet = NULL;
  128                         m_hFinishedEvent = NULL;
  129                         m_dwExpectedState = 0;
  130
  131                         return E_FAIL;
  132                 }
  133         }
  134         else
  135         {
  136                 m_dwExpectedState = 0;          // everything has been completed
  137                 ::SetEvent(m_hFinishedEvent);
  138         }
  139
  140         return hOpenUrl ? S_OK : S_FALSE;
  141 }
  142
  143 // ============================================================================
  144 /// CAsyncHttpFile::GetFileSize
  145 /// @date 2009/04/18
  146 ///
  147 /// @brief     Retrieves the size of file opened with CAsyncHttpFile::Open()
  148 //                              (if such information exists in http headers).
  149 /// @param[out] stSize  Receives the size of file (receives 65536 if http headers
  150 ///                     did not contain the information).
  151 /// @return             Result of the operation.
  152 // ============================================================================
  153 HRESULT CAsyncHttpFile::GetFileSize(size_t& stSize)
  154 {
  155         if(!m_hInternet || !m_hOpenUrl)
  156         {
  157                 SetErrorCode(ERROR_INTERNAL_ERROR);
  158                 return E_FAIL;
  159         }
  160
  161         DWORD dwContentLengthSize = sizeof(DWORD);
  162         if(!HttpQueryInfo(m_hOpenUrl, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &stSize, &dwContentLengthSize, NULL) || stSize == 0 || stSize > 1 * 1024UL * 1024UL)
  163         {
  164                 stSize = 65536;         // safe fallback
  165                 return S_FALSE;
  166         }
  167
  168         return S_OK;
  169 }
  170
  171 // ============================================================================
  172 /// CAsyncHttpFile::RequestData
  173 /// @date 2009/04/18
  174 ///
  175 /// @brief     Requests the data from already opened url.
  176 /// @param[in]  pBuffer  Buffer for the data.
  177 /// @param[in]  stSize   Buffer size.
  178 /// @return        S_OK if completed, S_FALSE if needs waiting, E_* on error.
  179 // ============================================================================
  180 HRESULT CAsyncHttpFile::RequestData(void* pBuffer, size_t stSize)
  181 {
  182         if(!pBuffer)
  183         {
  184                 SetErrorCode(ERROR_INTERNAL_ERROR);
  185                 return E_INVALIDARG;
  186         }
  187         if(!m_hInternet || !m_hOpenUrl || !m_hFinishedEvent)
  188         {
  189                 SetErrorCode(ERROR_INTERNAL_ERROR);
  190                 return E_FAIL;
  191         }
  192
  193         SetErrorCode(ERROR_SUCCESS);
  194
  195         if(!::ResetEvent(m_hFinishedEvent))
  196         {
  197                 SetErrorCode(ERROR_INTERNAL_ERROR);
  198                 return E_FAIL;
  199         }
  200
  201         memset(&m_internetBuffers, 0, sizeof(INTERNET_BUFFERS));
  202         m_internetBuffers.dwStructSize = sizeof(INTERNET_BUFFERS);
  203         m_internetBuffers.dwBufferLength = (DWORD)stSize;
  204         m_internetBuffers.dwBufferTotal = (DWORD)stSize;
  205         m_internetBuffers.lpvBuffer = pBuffer;
  206
  207         m_dwExpectedState = INTERNET_STATUS_REQUEST_COMPLETE;
  208         if(!::InternetReadFileEx(m_hOpenUrl, &m_internetBuffers, IRF_NO_WAIT, (DWORD_PTR)&m_tReadRequest))
  209         {
  210                 SetErrorCode(::GetLastError());
  211                 if(GetErrorCode() == ERROR_IO_PENDING)
  212                         return S_FALSE;
  213                 else
  214                         return E_FAIL;
  215         }
  216
  217         if(!::SetEvent(m_hFinishedEvent))
  218         {
  219                 SetErrorCode(ERROR_INTERNAL_ERROR);
  220                 return E_FAIL;
  221         }
  222
  223         return S_OK;
  224 }
  225
  226 // ============================================================================
  227 /// CAsyncHttpFile::RetrieveRequestedData
  228 /// @date 2009/04/18
  229 ///
  230 /// @brief     Retrieves the size of data retrieved.
  231 /// @param[out] stSize  Receives the size of data read from file.
  232 /// @return    Result of the operation.
  233 // ============================================================================
  234 HRESULT CAsyncHttpFile::GetRetrievedDataSize(size_t& stSize)
  235 {
  236         if(!m_hInternet)
  237         {
  238                 SetErrorCode(ERROR_INTERNAL_ERROR);
  239                 return E_FAIL;
  240         }
  241
  242         stSize = m_internetBuffers.dwBufferLength;
  243         return S_OK;
  244 }
  245
  246 // ============================================================================
  247 /// CAsyncHttpFile::Close
  248 /// @date 2009/04/18
  249 ///
  250 /// @brief     Closes the file.
  251 /// @return    Result of the operation.
  252 // ============================================================================
  253 HRESULT CAsyncHttpFile::Close()
  254 {
  255         SetErrorCode(ERROR_SUCCESS);
  256         if(m_hOpenUrl)
  257         {
  258                 m_dwExpectedState = INTERNET_STATUS_CLOSING_CONNECTION;
  259                 if(!::InternetCloseHandle(m_hOpenUrl))
  260                 {
  261                         SetErrorCode(::GetLastError());
  262                         if(GetErrorCode() == ERROR_IO_PENDING)
  263                                 return S_FALSE;
  264                         else
  265                         {
  266                                 SetErrorCode(ERROR_INTERNAL_ERROR);
  267                                 return E_FAIL;
  268                         }
  269                 }
  270
  271                 // if closing url handle succeeded, we close internet here, if not
  272                 // then a separate call to close need to be performed.
  273                 m_dwExpectedState = 0;
  274                 SetUrlHandle(NULL);
  275                 ::InternetCloseHandle(m_hInternet);
  276         }
  277
  278         if(m_hFinishedEvent)
  279         {
  280                 ::CloseHandle(m_hFinishedEvent);
  281                 m_hFinishedEvent = NULL;
  282         }
  283
  284         return S_OK;
  285 }
  286
  287 // ============================================================================
  288 /// CAsyncHttpFile::GetResult
  289 /// @date 2009/04/18
  290 ///
  291 /// @brief     Retrieves the last call result (blocking call).
  292 /// @return    Result of the last call.
  293 // ============================================================================
  294 CAsyncHttpFile::EWaitResult CAsyncHttpFile::GetResult()
  295 {
  296         HANDLE hHandles[] = { m_hFinishedEvent };
  297         DWORD dwEffect = WaitForMultipleObjects(1, hHandles, FALSE, 0);
  298         if(dwEffect == WAIT_OBJECT_0 + 0 || dwEffect == WAIT_ABANDONED_0 + 0)
  299                 return GetErrorCode() == ERROR_SUCCESS ? CAsyncHttpFile::eFinished : CAsyncHttpFile::eError;
  300         else
  301                 return CAsyncHttpFile::ePending;
  302 }
  303
  304 // ============================================================================
  305 /// CAsyncHttpFile::WaitForResult
  306 /// @date 2009/04/18
  307 ///
  308 /// @brief     Waits for the result with additional 'kill' event.
  309 /// @param[in] hKillEvent  Event handle that would break waiting for result.
  310 /// @return    Result of waiting.
  311 // ============================================================================
  312 CAsyncHttpFile::EWaitResult CAsyncHttpFile::WaitForResult(HANDLE hKillEvent)
  313 {
  314         HANDLE hHandles[] = { hKillEvent, m_hFinishedEvent };
  315         DWORD dwEffect = WaitForMultipleObjects(2, hHandles, FALSE, FORCE_TIMEOUT);
  316         if(dwEffect == 0xffffffff)
  317         {
  318                 SetErrorCode(::GetLastError());
  319                 return CAsyncHttpFile::eError;
  320         }
  321         else if(dwEffect == WAIT_OBJECT_0 + 0 || dwEffect == WAIT_ABANDONED_0 + 0)
  322                 return CAsyncHttpFile::eKilled;
  323         else if(dwEffect == WAIT_OBJECT_0 + 1 || dwEffect == WAIT_ABANDONED_0 + 1)
  324                 return GetErrorCode() == ERROR_SUCCESS ? CAsyncHttpFile::eFinished : CAsyncHttpFile::eError;
  325         else
  326                 return CAsyncHttpFile::eTimeout;
  327 }
  328
  329 // ============================================================================
  330 /// CAsyncHttpFile::InternetStatusCallback
  331 /// @date 2009/04/18
  332 ///
  333 /// @brief     Callback for use with internet API.
  334 /// @param[in] hInternet                                Internet handle.
  335 /// @param[in] dwContext                                Context value.
  336 /// @param[in] dwInternetStatus                 Internet status.
  337 /// @param[in] lpvStatusInformation             Additional status information.
  338 /// @param[in] dwStatusInformationLength Length of lpvStatusInformation.
  339 // ============================================================================
  340 void CALLBACK CAsyncHttpFile::InternetStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
  341 {
  342         CONTEXT_REQUEST* pRequest = (CONTEXT_REQUEST*)dwContext;
  343         BOOST_ASSERT(pRequest && pRequest->pHttpFile);
  344         if(!pRequest || !pRequest->pHttpFile)
  345                 return;
  346
  347         CString strMsg;
  348         strMsg.Format(_T("[CAsyncHttpFile::InternetStatusCallback] hInternet: %p, dwContext: %lu (operation: %lu), dwInternetStatus: %lu, lpvStatusInformation: %p, dwStatusInformationLength: %lu\n"),
  349                 hInternet, dwContext, pRequest ? pRequest->eOperationType : CONTEXT_REQUEST::eNone, dwInternetStatus, lpvStatusInformation, dwStatusInformationLength);
  350         LOG_DEBUG(strMsg);
  351
  352         switch(dwInternetStatus)
  353         {
  354         case INTERNET_STATUS_HANDLE_CREATED:
  355         {
  356                 INTERNET_ASYNC_RESULT* pRes = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
  357                 pRequest->pHttpFile->SetUrlHandle((HINTERNET)(pRes->dwResult));
  358                 break;
  359         }
  360         case INTERNET_STATUS_RESPONSE_RECEIVED:
  361         {
  362                 ATLTRACE(_T("INTERNET_STATUS_RESPONSE_RECEIVED; received %lu bytes."), *(DWORD*)lpvStatusInformation);
  363                 break;
  364         }
  365         case INTERNET_STATUS_REQUEST_COMPLETE:
  366         {
  367                 INTERNET_ASYNC_RESULT* pResult = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
  368                 pRequest->pHttpFile->SetErrorCode(pResult->dwError);
  369                 break;
  370         }
  371         case INTERNET_STATUS_CLOSING_CONNECTION:
  372         {
  373                 pRequest->pHttpFile->SetUrlHandle(NULL);
  374                 break;
  375         }
  376         case INTERNET_STATUS_CONNECTION_CLOSED:
  377         {
  378                 break;
  379         }
  380         default:
  381                 TRACE(_T("[CAsyncHttpFile::InternetStatusCallback()] Unhandled status: %lu\n"), dwInternetStatus);
  382         }
  383
  384         pRequest->pHttpFile->SetCompletionStatus(dwInternetStatus);
  385 }
  386
  387
  388 // ============================================================================
  389 /// CAsyncHttpFile::SetUrlHandle
  390 /// @date 2009/04/18
  391 ///
  392 /// @brief     Sets the url handle.
  393 /// @param[in] hOpenUrl  Handle to be set.
  394 // ============================================================================
  395 void CAsyncHttpFile::SetUrlHandle(HANDLE hOpenUrl)
  396 {
  397         m_hOpenUrl = hOpenUrl;
  398 }
  399
  400 // ============================================================================
  401 /// CAsyncHttpFile::SetErrorCode
  402 /// @date 2009/04/18
  403 ///
  404 /// @brief     Sets the error code.
  405 /// @param[in] dwError  Error code to be set.
  406 // ============================================================================
  407 void CAsyncHttpFile::SetErrorCode(DWORD dwError)
  408 {
  409         m_dwError = dwError;
  410 }
  411
  412 // ============================================================================
  413 /// CAsyncHttpFile::SetCompletionStatus
  414 /// @date 2009/04/18
  415 ///
  416 /// @brief     Sets the completion status.
  417 /// @param[in] dwCurrentState  State to be set.
  418 /// @return    Result of the operation.
  419 // ============================================================================
  420 HRESULT CAsyncHttpFile::SetCompletionStatus(DWORD dwCurrentState)
  421 {
  422         if(!m_hFinishedEvent)
  423                 return E_FAIL;
  424
  425         if(dwCurrentState == m_dwExpectedState || dwCurrentState == INTERNET_STATUS_CLOSING_CONNECTION)
  426                 return ::SetEvent(m_hFinishedEvent) ? S_OK : E_FAIL;
  427         return S_FALSE;
  428 }