Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1088 lines
28 KiB

  1. /************************************************************************
  2. Copyright (c) 2001 - 2002 Microsoft Corporation
  3. Module Name :
  4. uploader.cpp
  5. Abstract :
  6. Implements HTTP upload and upload-reply transactions.
  7. Author :
  8. Jeff Roberts
  9. ***********************************************************************/
  10. #include "stdafx.h"
  11. #include "uploader.tmh"
  12. void
  13. CProgressiveDL::Upload(
  14. CUploadJob * job,
  15. ITransferCallback * Callbacks,
  16. HANDLE Token,
  17. QMErrInfo & ErrInfo
  18. )
  19. {
  20. try
  21. {
  22. ErrInfo.Clear();
  23. ErrInfo.result = QM_IN_PROGRESS;
  24. THROW_HRESULT( CheckLanManHashDisabled());
  25. CNestedImpersonation imp( Token );
  26. Uploader uploader( this, m_Network, job, Token, Callbacks, ErrInfo );
  27. uploader.Transfer();
  28. }
  29. catch ( ComError err )
  30. {
  31. if (err.m_error == S_FALSE)
  32. {
  33. // abort detected while taking the global lock.
  34. //
  35. ErrInfo.result = QM_FILE_ABORTED;
  36. }
  37. else
  38. {
  39. if (!ErrInfo.IsSet())
  40. {
  41. Uploader::SetResult( ErrInfo, SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, err.Error() );
  42. }
  43. if (ErrInfo.result == QM_IN_PROGRESS)
  44. {
  45. ErrInfo.result = CategorizeError( ErrInfo );
  46. }
  47. }
  48. }
  49. //
  50. // Map any connection failure to BG_E_NETWORK_DISCONNECTED, if no nets are active.
  51. //
  52. if (ErrInfo.result == QM_FILE_TRANSIENT_ERROR)
  53. {
  54. if (g_Manager->m_NetworkMonitor.GetAddressCount() == 0)
  55. {
  56. ErrInfo.Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_NETWORK_DISCONNECTED, NULL );
  57. }
  58. }
  59. }
  60. void
  61. Uploader::SetResult(
  62. QMErrInfo & err,
  63. ERROR_SOURCE source,
  64. ERROR_STYLE style,
  65. DWORD code,
  66. char * comment
  67. )
  68. {
  69. err.Set( source, style, code, comment );
  70. err.result = CategorizeError( err );
  71. }
  72. Uploader::ResponseTable Uploader::CreateSession_ResponseTable =
  73. {
  74. GenericServerError,
  75. {
  76. { HTTP_STATUS_OK, CreateSession_NewSession },
  77. { HTTP_STATUS_CREATED, CreateSession_NewSession },
  78. { NULL, NULL }
  79. }
  80. };
  81. Uploader::ResponseTable Uploader::SendData_ResponseTable =
  82. {
  83. SendData_Failure,
  84. {
  85. { HTTP_STATUS_OK, SendData_Success },
  86. { HTTP_STATUS_RANGE_NOT_SATISFIABLE, SendData_Success },
  87. { NULL, NULL }
  88. }
  89. };
  90. Uploader::ResponseTable Uploader::CancelSession_ResponseTable =
  91. {
  92. CancelSession_Failure,
  93. {
  94. { HTTP_STATUS_OK, CancelSession_Success },
  95. { NULL, NULL }
  96. }
  97. };
  98. Uploader::ResponseTable Uploader::CloseSession_ResponseTable =
  99. {
  100. CloseSession_Failure,
  101. {
  102. { HTTP_STATUS_OK, CloseSession_Success },
  103. { NULL, NULL }
  104. }
  105. };
  106. Uploader::Uploader(
  107. Downloader * dl,
  108. CNetworkInterface & net,
  109. CUploadJob * job,
  110. HANDLE Token,
  111. ITransferCallback * Callbacks,
  112. QMErrInfo & ErrorInfo
  113. )
  114. :
  115. m_Network( net ),
  116. m_Token( Token ),
  117. m_Callbacks( Callbacks ),
  118. m_Credentials( &job->QueryCredentialsList() ),
  119. m_job( job ),
  120. m_file( job->GetUploadFile() ),
  121. m_data( job->GetUploadData() ),
  122. m_JobType( job->GetType() ),
  123. m_ErrorInfo( ErrorInfo ),
  124. m_Downloader( dl ),
  125. m_Restarts( 0 )
  126. {
  127. m_ErrorInfo.Clear();
  128. //
  129. // Open the local file.
  130. //
  131. auto_HANDLE<NULL> hFile;
  132. try
  133. {
  134. hFile = m_file->OpenLocalFileForUpload();
  135. }
  136. catch ( ComError err )
  137. {
  138. ErrorInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, err.Error() );
  139. throw;
  140. }
  141. //
  142. // Create a connection to the server and init the network object.
  143. //
  144. m_UrlInfo = ContactServer();
  145. UINT64 BytesRemaining = m_file->_GetBytesTotal() - m_file->_GetBytesTransferred();
  146. m_Network.CalculateIntervalAndBlockSize( BytesRemaining );
  147. //
  148. // Success: now the object owns the file handle.
  149. //
  150. m_hFile = hFile.release();
  151. }
  152. Uploader::~Uploader()
  153. {
  154. CloseHandle( m_hFile );
  155. }
  156. void
  157. Uploader::Transfer()
  158. {
  159. bool fRetry;
  160. do
  161. {
  162. fRetry = false;
  163. try
  164. {
  165. while (!m_ErrorInfo.IsSet() &&
  166. !InTerminalState())
  167. {
  168. if (m_Callbacks->PollAbort())
  169. {
  170. throw ComError( S_FALSE );
  171. }
  172. switch (m_data.State)
  173. {
  174. case UPLOAD_STATE_CREATE_SESSION: CreateSession(); break;
  175. case UPLOAD_STATE_SEND_DATA: SendData(); break;
  176. case UPLOAD_STATE_GET_REPLY: GetReply(); break;
  177. case UPLOAD_STATE_CLOSE_SESSION: CloseSession(); break;
  178. case UPLOAD_STATE_CANCEL_SESSION: CancelSession(); break;
  179. }
  180. }
  181. }
  182. catch (ComError err )
  183. {
  184. if (err.Error() == E_RETRY)
  185. {
  186. fRetry = true;
  187. }
  188. else
  189. {
  190. throw;
  191. }
  192. }
  193. }
  194. while ( fRetry );
  195. }
  196. void
  197. Uploader::AnalyzeResponse(
  198. CBitsCommandRequest & request,
  199. DWORD result,
  200. ResponseTable & table
  201. )
  202. {
  203. ResponseEntry * entry = table.Entries;
  204. LogDl( "HTTP status %d", result );
  205. while (entry->Fn != NULL)
  206. {
  207. if (result == entry->Code)
  208. {
  209. (this->*(entry->Fn))( request, result );
  210. return;
  211. }
  212. ++entry;
  213. }
  214. (this->*(table.DefaultFn))( request, result );
  215. }
  216. void
  217. Uploader::CreateSession()
  218. {
  219. CBitsCommandRequest Request( m_UrlInfo.get() );
  220. Request.AddPacketType( L"Create-Session" );
  221. Request.AddContentName( m_file->GetLocalName() );
  222. Request.AddSupportedProtocols();
  223. DWORD result = Request.Send();
  224. AnalyzeResponse( Request, result, CreateSession_ResponseTable );
  225. }
  226. void
  227. Uploader::CreateSession_NewSession(
  228. CBitsCommandRequest & request,
  229. DWORD result
  230. )
  231. {
  232. // new upload established.
  233. THROW_HRESULT( request.GetProtocol( &m_data.Protocol ));
  234. THROW_HRESULT( request.CheckResponseProtocol( &m_data.Protocol ));
  235. THROW_HRESULT( request.GetSessionId( &m_data.SessionId ));
  236. THROW_HRESULT( request.GetHostId( &m_data.HostId ) );
  237. THROW_HRESULT( request.GetHostIdFallbackTimeout( &m_data.HostIdFallbackTimeout ) );
  238. if ( m_data.HostId.Size() )
  239. {
  240. m_UrlInfo = ContactServer();
  241. }
  242. SetState( UPLOAD_STATE_SEND_DATA );
  243. m_file->SetBytesTransferred( 0 );
  244. }
  245. auto_ptr<URL_INFO>
  246. Uploader::ContactServer()
  247. {
  248. bool bNeedLock;
  249. try
  250. {
  251. ReleaseWriteLock( bNeedLock );
  252. //
  253. // Open the remote file.
  254. //
  255. auto_ptr<URL_INFO> UrlInfo;
  256. UrlInfo = auto_ptr<URL_INFO>( ConnectToUrl( m_file->GetRemoteName(),
  257. &m_job->QueryProxySettings(),
  258. m_Credentials,
  259. m_data.HostId,
  260. &m_ErrorInfo
  261. ));
  262. if (!UrlInfo.get())
  263. {
  264. ASSERT( m_ErrorInfo.IsSet());
  265. THROW_HRESULT( E_FAIL );
  266. }
  267. //
  268. // Ping the server to set up the HTTP connection.
  269. //
  270. CBitsCommandRequest Request( UrlInfo.get() );
  271. Request.AddPacketType( L"Ping" );
  272. DWORD result = Request.Send();
  273. if (result != HTTP_STATUS_OK)
  274. {
  275. HRESULT hr;
  276. HRESULT Error;
  277. hr = Request.GetBitsError( &Error );
  278. if (hr != S_OK && hr != BG_E_HEADER_NOT_FOUND)
  279. {
  280. THROW_HRESULT( hr );
  281. }
  282. if (SUCCEEDED( hr ))
  283. {
  284. SetResult( m_ErrorInfo, SOURCE_HTTP_SERVER, ERROR_STYLE_HRESULT, Error );
  285. }
  286. else
  287. {
  288. SetResult( m_ErrorInfo, SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, result );
  289. }
  290. throw ComError( E_FAIL );
  291. }
  292. // Update proxy and NIC info.
  293. //
  294. THROW_HRESULT( UrlInfo->GetProxyUsage( Request.Query(), &m_ErrorInfo ));
  295. THROW_HRESULT( m_Network.SetInterfaceIndex( UrlInfo.get()->fProxy ? UrlInfo.get()->ProxyHost.get() : UrlInfo.get()->HostName ));
  296. ReclaimWriteLock( bNeedLock );
  297. return UrlInfo;
  298. }
  299. catch ( ComError err )
  300. {
  301. ReclaimWriteLock( bNeedLock );
  302. throw;
  303. }
  304. }
  305. void
  306. Uploader::CreateSession_InProgress(
  307. CBitsCommandRequest & request,
  308. DWORD result
  309. )
  310. {
  311. // upload already in progress
  312. SetState( UPLOAD_STATE_SEND_DATA );
  313. SendData_Success( request, result );
  314. }
  315. class CFileDataReader : public CAbstractDataReader
  316. /*
  317. SendRequest() uses CAbstractDataReader to read data and rewind if a retry is necessary.
  318. CFileDataReader is the implementation used by Uploader::SendData().
  319. It reads from an NT file handle. The handle must be seekable so that Rewind() works.
  320. */
  321. {
  322. private:
  323. HANDLE m_hFile;
  324. LARGE_INTEGER m_OriginalOffset;
  325. DWORD m_Length;
  326. public:
  327. CFileDataReader( HANDLE hFile, UINT64 Offset, DWORD Length )
  328. : m_hFile( hFile ), m_Length( Length )
  329. {
  330. m_OriginalOffset.QuadPart = Offset;
  331. }
  332. virtual DWORD GetLength() const
  333. {
  334. return m_Length;
  335. }
  336. virtual HRESULT Rewind()
  337. {
  338. if (!SetFilePointerEx( m_hFile, m_OriginalOffset, NULL, FILE_BEGIN ))
  339. {
  340. return HRESULT_FROM_WIN32( GetLastError() );
  341. }
  342. return S_OK;
  343. }
  344. virtual HRESULT Read(PVOID Buffer, DWORD Length, DWORD * pBytesRead)
  345. {
  346. if (!ReadFile( m_hFile, Buffer, Length, pBytesRead, NULL ))
  347. {
  348. DWORD s = GetLastError();
  349. LogError("ReadFile failed %!winerr!", s);
  350. return HRESULT_FROM_WIN32( s );
  351. }
  352. if (*pBytesRead != Length)
  353. {
  354. LogWarning("only read %d bytes of %d", *pBytesRead, Length );
  355. }
  356. if (*pBytesRead == 0)
  357. {
  358. LogInfo("at EOF");
  359. return S_FALSE;
  360. }
  361. return S_OK;
  362. }
  363. virtual bool IsCancelled( DWORD BytesRead )
  364. {
  365. if (g_Manager->m_TaskScheduler.PollAbort() ||
  366. g_Manager->CheckForQuantumTimeout())
  367. {
  368. return true;
  369. }
  370. return false;
  371. }
  372. };
  373. void
  374. Uploader::SendData()
  375. {
  376. //
  377. // If the block size is zero, we still have to send a packet in two cases:
  378. // 1. The file has length zero.
  379. // 2. The file is completely uploaded, but the server app hasn't replied yet.
  380. //
  381. if (m_Network.m_BlockSize == 0 &&
  382. (m_file->_GetBytesTotal() - m_file->_GetBytesTransferred()) > 0)
  383. {
  384. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_START );
  385. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_END );
  386. }
  387. else
  388. {
  389. UINT64 FileOffset = m_file->_GetBytesTransferred();
  390. UINT64 BodyLength = min( m_Network.m_BlockSize, m_file->_GetBytesTotal() - FileOffset );
  391. m_ExpectedServerOffset = FileOffset + BodyLength;
  392. CFileDataReader Reader( m_hFile, FileOffset, BodyLength );
  393. CBitsCommandRequest Request( m_UrlInfo.get() );
  394. Request.AddSessionId( m_data.SessionId );
  395. Request.AddPacketType( L"Fragment" );
  396. Request.AddContentName( m_file->GetLocalName() );
  397. Request.AddContentRange( FileOffset, FileOffset + BodyLength-1, m_file->_GetBytesTotal() );
  398. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_START );
  399. DWORD result = Request.Send( &Reader );
  400. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_END );
  401. AnalyzeResponse( Request, result, SendData_ResponseTable );
  402. }
  403. //
  404. // Allow other apps to use the network for the rest of the time interval,
  405. // then take the end-of-interval snapshot.
  406. //
  407. LogInfo("waiting for end of interval");
  408. {
  409. bool bNeedLock;
  410. try
  411. {
  412. ReleaseWriteLock( bNeedLock );
  413. m_Network.Wait();
  414. ReclaimWriteLock( bNeedLock );
  415. }
  416. catch ( ComError err )
  417. {
  418. ReclaimWriteLock( bNeedLock );
  419. throw;
  420. }
  421. }
  422. HRESULT hr = m_Network.TakeSnapshot( CNetworkInterface::BLOCK_INTERVAL_END );
  423. if (FAILED(hr))
  424. {
  425. if (hr == HRESULT_FROM_WIN32( ERROR_INVALID_DATA ))
  426. {
  427. //
  428. // If the snapshot fails with ERROR_INVALID_DATA and the downloads
  429. // keep working, then our NIC has been removed and the networking
  430. // layer has silently transferred our connection to another available
  431. // NIC. We need to identify the NIC that we are now using.
  432. //
  433. LogWarning("NIC is no longer valid. Requesting retry.");
  434. hr = E_RETRY;
  435. }
  436. throw ComError( hr );
  437. }
  438. UINT64 BytesRemaining = m_file->_GetBytesTotal() - m_file->_GetBytesTransferred();
  439. m_Network.SetInterfaceSpeed();
  440. m_Network.CalculateIntervalAndBlockSize( BytesRemaining );
  441. }
  442. void
  443. Uploader::SendData_Success(
  444. CBitsCommandRequest & request,
  445. DWORD result
  446. )
  447. {
  448. HRESULT hr;
  449. UINT64 RangeEnd;
  450. // update our notion of the server's data range
  451. hr = request.GetServerRange( &RangeEnd );
  452. if (FAILED(hr))
  453. {
  454. if (hr == BG_E_HEADER_NOT_FOUND)
  455. {
  456. hr = BG_E_INVALID_SERVER_RESPONSE;
  457. }
  458. throw ComError( hr );
  459. }
  460. UINT64 Total = m_file->_GetBytesTotal();
  461. if (RangeEnd > Total)
  462. {
  463. throw ComError( BG_E_INVALID_SERVER_RESPONSE );
  464. }
  465. //
  466. // If the received range fails to move forward several times, then the connection is flaky and
  467. // it begins to look like an error condition.
  468. //
  469. if (RangeEnd <= m_file->_GetBytesTransferred())
  470. {
  471. if (++m_Restarts >= 3)
  472. {
  473. throw ComError( BG_E_NO_PROGRESS );
  474. }
  475. }
  476. m_Callbacks->UploaderProgress( RangeEnd );
  477. //
  478. // If the server adjusted the received-range, move the file pointer to match it.
  479. //
  480. if (RangeEnd != m_ExpectedServerOffset)
  481. {
  482. LARGE_INTEGER Offset;
  483. Offset.QuadPart = RangeEnd;
  484. if (!SetFilePointerEx( m_hFile, Offset, NULL, FILE_BEGIN ))
  485. {
  486. m_ErrorInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, HRESULT_FROM_WIN32( GetLastError() ) );
  487. ThrowLastError();
  488. }
  489. }
  490. // check for end of data upload
  491. if (RangeEnd == Total)
  492. {
  493. if (m_JobType == BG_JOB_TYPE_UPLOAD_REPLY)
  494. {
  495. CAutoString ReplyUrl;
  496. hr = request.GetReplyUrl( ReplyUrl );
  497. if (hr == BG_E_HEADER_NOT_FOUND )
  498. {
  499. if (result == HTTP_STATUS_RANGE_NOT_SATISFIABLE)
  500. {
  501. //
  502. // If the client didn't see a server ACK, its range may be incorrect
  503. // and the server might not send the reply URL.
  504. // So send another [zero-length] request.
  505. //
  506. }
  507. else
  508. {
  509. m_job->SetReplyFile( new CFile( m_job,
  510. BG_JOB_TYPE_DOWNLOAD,
  511. m_file->GetRemoteName(),
  512. m_job->QueryReplyFileName()
  513. ));
  514. m_job->QueryReplyFile()->SetBytesTotal( 0 );
  515. SetState( UPLOAD_STATE_CLOSE_SESSION );
  516. }
  517. }
  518. else if (FAILED(hr))
  519. {
  520. throw ComError(hr);
  521. }
  522. else
  523. {
  524. //
  525. // Impersonate the user while checking file access.
  526. //
  527. CNestedImpersonation imp( m_Token );
  528. StringHandle AbsoluteUrl = CombineUrl( m_file->GetRemoteName(),
  529. ReplyUrl.get(),
  530. 0 // flags
  531. );
  532. m_job->SetReplyFile( new CFile( m_job,
  533. BG_JOB_TYPE_DOWNLOAD,
  534. AbsoluteUrl,
  535. m_job->QueryReplyFileName()
  536. ));
  537. SetState( UPLOAD_STATE_GET_REPLY );
  538. }
  539. }
  540. else
  541. {
  542. SetState( UPLOAD_STATE_CLOSE_SESSION );
  543. }
  544. }
  545. }
  546. void
  547. Uploader::SendData_Failure(
  548. CBitsCommandRequest & request,
  549. DWORD result
  550. )
  551. {
  552. HRESULT hr;
  553. UINT64 RangeEnd;
  554. // update our notion of the server's data range
  555. hr = request.GetServerRange( &RangeEnd );
  556. if (hr == S_OK)
  557. {
  558. UINT64 Total = m_file->_GetBytesTotal();
  559. if (RangeEnd > Total)
  560. {
  561. throw ComError( BG_E_INVALID_SERVER_RESPONSE );
  562. }
  563. m_Callbacks->UploaderProgress( RangeEnd );
  564. }
  565. HRESULT Error = S_OK;
  566. hr = request.GetBitsError( &Error );
  567. if (hr != S_OK && hr != BG_E_HEADER_NOT_FOUND)
  568. {
  569. THROW_HRESULT( hr );
  570. }
  571. if (SUCCEEDED( hr ))
  572. {
  573. //
  574. // if the server doesn't recognize the session, retry from the beginning.
  575. // If the server resets us three times, it is probably messed up. Go into transient error state and retry later.
  576. //
  577. if (Error == BG_E_SESSION_NOT_FOUND)
  578. {
  579. if (++m_Restarts >= 3)
  580. {
  581. throw ComError( BG_E_NO_PROGRESS );
  582. }
  583. SetState( UPLOAD_STATE_CREATE_SESSION );
  584. m_Callbacks->UploaderProgress( 0 );
  585. LARGE_INTEGER Offset;
  586. Offset.QuadPart = 0;
  587. if (!SetFilePointerEx( m_hFile, Offset, NULL, FILE_BEGIN ))
  588. {
  589. m_ErrorInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, HRESULT_FROM_WIN32( GetLastError() ) );
  590. ThrowLastError();
  591. }
  592. return;
  593. }
  594. }
  595. GenericServerError( request, result );
  596. }
  597. void
  598. Uploader::GetReply()
  599. {
  600. CFile * file = m_job->QueryReplyFile();
  601. const PROXY_SETTINGS & ProxySettings = m_job->QueryProxySettings();
  602. //
  603. // Make sure the file size is known; neded by the downloader.
  604. //
  605. if (file->_GetBytesTotal() == BG_SIZE_UNKNOWN)
  606. {
  607. file->DiscoverBytesTotal( m_Token, ProxySettings, m_Credentials, m_ErrorInfo);
  608. switch (m_ErrorInfo.result)
  609. {
  610. case QM_FILE_DONE: break;
  611. case QM_FILE_ABORTED: throw ComError( S_FALSE );
  612. case QM_SERVER_FILE_CHANGED: ASSERT( 0 );
  613. case QM_IN_PROGRESS: ASSERT( 0 );
  614. case QM_FILE_TRANSIENT_ERROR: return;
  615. case QM_FILE_FATAL_ERROR: return;
  616. }
  617. }
  618. //
  619. // Download the reply URL.
  620. //
  621. file->Transfer( m_Token,
  622. m_job->_GetPriority(),
  623. ProxySettings,
  624. m_Credentials,
  625. m_ErrorInfo
  626. );
  627. switch (m_ErrorInfo.result)
  628. {
  629. case QM_FILE_DONE: SetState( UPLOAD_STATE_CLOSE_SESSION ); break;
  630. case QM_FILE_ABORTED: throw ComError( S_FALSE );
  631. case QM_SERVER_FILE_CHANGED: file->SetBytesTotal( BG_SIZE_UNKNOWN ); break;
  632. case QM_FILE_TRANSIENT_ERROR: break;
  633. case QM_FILE_FATAL_ERROR: break;
  634. case QM_IN_PROGRESS: ASSERT( 0 ); break;
  635. }
  636. }
  637. void
  638. Uploader::CloseSession()
  639. {
  640. CBitsCommandRequest Request( m_UrlInfo.get() );
  641. Request.AddSessionId( m_data.SessionId );
  642. Request.AddPacketType( L"Close-Session" );
  643. Request.AddContentName( m_file->GetLocalName() );
  644. DWORD result = Request.Send();
  645. AnalyzeResponse( Request, result, CloseSession_ResponseTable );
  646. }
  647. void
  648. Uploader::CloseSession_Success(
  649. CBitsCommandRequest & request,
  650. DWORD result
  651. )
  652. {
  653. SetState( UPLOAD_STATE_CLOSED );
  654. m_ErrorInfo.result = QM_FILE_DONE;
  655. }
  656. void
  657. Uploader::CloseSession_Failure(
  658. CBitsCommandRequest & request,
  659. DWORD result
  660. )
  661. {
  662. //
  663. // If the session was cleaned up, we need to retry.
  664. // If the server resets us three times, it is probably messed up. Go into transient error state and retry later.
  665. //
  666. HRESULT hr;
  667. HRESULT Error = S_OK;
  668. hr = request.GetBitsError( &Error );
  669. if (hr != S_OK && hr != BG_E_HEADER_NOT_FOUND)
  670. {
  671. THROW_HRESULT( hr );
  672. }
  673. if (SUCCEEDED( hr ))
  674. {
  675. if (Error == BG_E_SESSION_NOT_FOUND)
  676. {
  677. if (++m_Restarts >= 3)
  678. {
  679. throw ComError( BG_E_NO_PROGRESS );
  680. }
  681. SetState( UPLOAD_STATE_CREATE_SESSION );
  682. m_Callbacks->UploaderProgress( 0 );
  683. LARGE_INTEGER Offset;
  684. Offset.QuadPart = 0;
  685. if (!SetFilePointerEx( m_hFile, Offset, NULL, FILE_BEGIN ))
  686. {
  687. m_ErrorInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, HRESULT_FROM_WIN32( GetLastError() ) );
  688. ThrowLastError();
  689. }
  690. return;
  691. }
  692. }
  693. if (result >= 500 && result <= 599)
  694. {
  695. HRESULT Error = S_OK;
  696. // temporary server error; retry later
  697. GenericServerError( request, result );
  698. return;
  699. }
  700. //
  701. // Either success (100 and 200 series) or a permanent failure (300 and 400 series).
  702. //
  703. CloseSession_Success( request, result );
  704. }
  705. void
  706. Uploader::CancelSession()
  707. {
  708. CBitsCommandRequest Request( m_UrlInfo.get() );
  709. Request.AddSessionId( m_data.SessionId );
  710. Request.AddPacketType( L"Cancel-Session" );
  711. Request.AddContentName( m_file->GetLocalName() );
  712. DWORD result = Request.Send();
  713. AnalyzeResponse( Request, result, CancelSession_ResponseTable );
  714. }
  715. void
  716. Uploader::CancelSession_Success(
  717. CBitsCommandRequest & request,
  718. DWORD result
  719. )
  720. {
  721. SetState( UPLOAD_STATE_CANCELLED );
  722. m_ErrorInfo.result = QM_FILE_DONE;
  723. }
  724. void
  725. Uploader::CancelSession_Failure(
  726. CBitsCommandRequest & request,
  727. DWORD result
  728. )
  729. {
  730. if (result >= 500 && result <= 599)
  731. {
  732. HRESULT hr;
  733. HRESULT Error = S_OK;
  734. hr = request.GetBitsError( &Error );
  735. if (hr != S_OK && hr != BG_E_HEADER_NOT_FOUND)
  736. {
  737. THROW_HRESULT( hr );
  738. }
  739. if (SUCCEEDED( hr ))
  740. {
  741. if (Error == BG_E_SESSION_NOT_FOUND)
  742. {
  743. CancelSession_Success( request, result );
  744. return;
  745. }
  746. }
  747. // temporary server error; retry later
  748. GenericServerError( request, result );
  749. return;
  750. }
  751. //
  752. // Either success (100 and 200 series) or a permanent failure (300 and 400 series).
  753. //
  754. CancelSession_Success( request, result );
  755. }
  756. void
  757. Uploader::GenericServerError(
  758. CBitsCommandRequest & request,
  759. DWORD result
  760. )
  761. {
  762. HRESULT Error;
  763. HRESULT hr;
  764. DWORD context;
  765. ERROR_SOURCE InternalContext;
  766. //
  767. // BITS-Error-Context is optional, defaulting to REMOTE_FILE
  768. //
  769. hr = request.GetBitsErrorContext( &context );
  770. if (hr == BG_E_HEADER_NOT_FOUND)
  771. {
  772. context = BG_ERROR_CONTEXT_REMOTE_FILE;
  773. }
  774. else if (FAILED(hr))
  775. {
  776. throw ComError( hr );
  777. }
  778. switch (context)
  779. {
  780. case BG_ERROR_CONTEXT_REMOTE_FILE:
  781. InternalContext = SOURCE_HTTP_SERVER;
  782. break;
  783. case BG_ERROR_CONTEXT_REMOTE_APPLICATION:
  784. InternalContext = SOURCE_HTTP_SERVER_APP;
  785. break;
  786. default:
  787. //
  788. // Don't understand; map it to something reasonable.
  789. //
  790. InternalContext = SOURCE_HTTP_SERVER;
  791. break;
  792. }
  793. //
  794. // BITS-Error is mandatory.
  795. //
  796. hr = request.GetBitsError( &Error );
  797. if (hr == BG_E_HEADER_NOT_FOUND)
  798. {
  799. SetResult( m_ErrorInfo, InternalContext, ERROR_STYLE_HTTP, result );
  800. return;
  801. }
  802. if (FAILED(hr))
  803. {
  804. throw ComError( hr );
  805. }
  806. SetResult( m_ErrorInfo, InternalContext, ERROR_STYLE_HRESULT, Error );
  807. }
  808. FILE_DOWNLOAD_RESULT
  809. CategorizeError(
  810. QMErrInfo & ErrInfo
  811. )
  812. {
  813. if ( ErrInfo.Style == ERROR_STYLE_HRESULT )
  814. {
  815. switch( LONG(ErrInfo.Code) )
  816. {
  817. case S_OK:
  818. return QM_FILE_DONE;
  819. case S_FALSE:
  820. return QM_FILE_ABORTED;
  821. // These codes indicate dynamic content or
  822. // an unsupported server so no retries are necessary.
  823. case BG_E_MISSING_FILE_SIZE:
  824. case BG_E_INSUFFICIENT_HTTP_SUPPORT:
  825. case BG_E_INSUFFICIENT_RANGE_SUPPORT:
  826. case BG_E_INVALID_SERVER_RESPONSE:
  827. case BG_E_LOCAL_FILE_CHANGED:
  828. case BG_E_TOO_LARGE:
  829. case BG_E_CLIENT_SERVER_PROTOCOL_MISMATCH:
  830. case HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ):
  831. case HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ):
  832. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_ERRORS ):
  833. case HRESULT_FROM_WIN32( ERROR_INTERNET_INVALID_CA ):
  834. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_CN_INVALID ):
  835. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_DATE_INVALID ):
  836. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_REV_FAILED ):
  837. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_REVOKED ):
  838. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_NO_REV ):
  839. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_INVALID_CERT ):
  840. return QM_FILE_FATAL_ERROR;
  841. default:
  842. return QM_FILE_TRANSIENT_ERROR;
  843. }
  844. }
  845. else if (ErrInfo.Style == ERROR_STYLE_HTTP)
  846. {
  847. switch ((ErrInfo.Code / 100))
  848. {
  849. case 1:
  850. case 2:
  851. LogError("HTTP code %u treated as an error", ErrInfo.Code);
  852. ASSERT( 0 );
  853. return QM_FILE_TRANSIENT_ERROR;
  854. case 3:
  855. return QM_FILE_TRANSIENT_ERROR;
  856. case 4:
  857. if (ErrInfo.Code == 408 ||
  858. ErrInfo.Code == 409)
  859. {
  860. return QM_FILE_TRANSIENT_ERROR;
  861. }
  862. return QM_FILE_FATAL_ERROR;
  863. case 5:
  864. default:
  865. if (ErrInfo.Code == 501)
  866. {
  867. return QM_FILE_FATAL_ERROR;
  868. }
  869. return QM_FILE_TRANSIENT_ERROR;
  870. }
  871. }
  872. else if (ErrInfo.Style == ERROR_STYLE_WIN32)
  873. {
  874. switch( ErrInfo.Code )
  875. {
  876. case ERROR_FILE_NOT_FOUND:
  877. case ERROR_PATH_NOT_FOUND:
  878. return QM_FILE_FATAL_ERROR;
  879. default:
  880. return QM_FILE_TRANSIENT_ERROR;
  881. }
  882. }
  883. else
  884. {
  885. ASSERT( 0 );
  886. return QM_FILE_TRANSIENT_ERROR;
  887. }
  888. }