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.

2906 lines
82 KiB

  1. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. //
  3. // ECB.CPP
  4. //
  5. // Implementation of CEcb methods and non-member functions
  6. //
  7. // Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  8. //
  9. #include "_davprs.h"
  10. #include "ecb.h"
  11. #include "instdata.h"
  12. #include "ecbimpl.h"
  13. // ========================================================================
  14. //
  15. // CLASS IEcb
  16. //
  17. // ------------------------------------------------------------------------
  18. //
  19. // IEcb::~IEcb()
  20. //
  21. // Out of line virtual destructor necessary for proper deletion
  22. // of objects of derived classes via this class.
  23. //
  24. IEcb::~IEcb()
  25. {
  26. }
  27. #ifdef DBG // ECB logging
  28. const CHAR gc_szDbgECBLogging[] = "ECB Logging";
  29. // ========================================================================
  30. //
  31. // CLASS CEcbLog (DBG only)
  32. //
  33. class CEcbLog : private Singleton<CEcbLog>
  34. {
  35. //
  36. // Friend declarations required by Singleton template
  37. //
  38. friend class Singleton<CEcbLog>;
  39. //
  40. // Critical section to serialize writes to
  41. // the log file
  42. //
  43. CCriticalSection m_cs;
  44. //
  45. // Handle to the log file
  46. //
  47. auto_handle<HANDLE> m_hfLog;
  48. //
  49. // Monotonically increasing unique identifier
  50. // for ECB logging;
  51. //
  52. LONG m_lMethodID;
  53. // CREATORS
  54. //
  55. // Declared private to ensure that arbitrary instances
  56. // of this class cannot be created. The Singleton
  57. // template (declared as a friend above) controls
  58. // the sole instance of this class.
  59. //
  60. CEcbLog();
  61. // NOT IMPLEMENTED
  62. //
  63. CEcbLog( const CEcbLog& );
  64. CEcbLog& operator=( const CEcbLog& );
  65. public:
  66. // STATICS
  67. //
  68. //
  69. // Instance creating/destroying routines provided
  70. // by the Singleton template.
  71. //
  72. using Singleton<CEcbLog>::CreateInstance;
  73. using Singleton<CEcbLog>::DestroyInstance;
  74. static void LogString( const EXTENSION_CONTROL_BLOCK * pecb,
  75. LONG lMethodID,
  76. LPCSTR szLocation );
  77. static LONG LNextMethodID();
  78. };
  79. // ------------------------------------------------------------------------
  80. //
  81. // CEcbLog::CEcbLog()
  82. //
  83. CEcbLog::CEcbLog() :
  84. m_lMethodID(0)
  85. {
  86. CHAR rgch[MAX_PATH];
  87. // Init our ECB log file.
  88. if (GetPrivateProfileString( gc_szDbgECBLogging,
  89. gc_szDbgLogFile,
  90. "",
  91. rgch,
  92. sizeof(rgch),
  93. gc_szDbgIni ))
  94. {
  95. m_hfLog = CreateFile( rgch,
  96. GENERIC_WRITE,
  97. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  98. NULL,
  99. CREATE_ALWAYS,
  100. FILE_ATTRIBUTE_NORMAL, // | FILE_FLAG_SEQUENTIAL_SCAN
  101. NULL );
  102. }
  103. else
  104. m_hfLog = INVALID_HANDLE_VALUE;
  105. }
  106. // ------------------------------------------------------------------------
  107. //
  108. // CEcbLog::LogString()
  109. //
  110. void
  111. CEcbLog::LogString( const EXTENSION_CONTROL_BLOCK * pecb,
  112. LONG lMethodID,
  113. LPCSTR szLocation )
  114. {
  115. if ( INVALID_HANDLE_VALUE == Instance().m_hfLog )
  116. return;
  117. Assert( pecb );
  118. CHAR rgch[MAX_PATH];
  119. int cch;
  120. // Dump a line to the log:
  121. // Thread: <tid> pECB <ecb> MethodID: <id> <meth name> <szLocation>
  122. //
  123. cch = _snprintf( rgch, CElems(rgch), "Thread: %08x pECB: 0x%08p MethodID: 0x%08x %hs %hs %hs\n",
  124. GetCurrentThreadId(),
  125. pecb,
  126. lMethodID,
  127. gc_szSignature,
  128. pecb->lpszMethod,
  129. szLocation );
  130. // If we hit the buffer length then we won't be NULL terminated
  131. //
  132. rgch[CElems(rgch)-1] = '\0';
  133. // If the trace is bigger than the buffer then cch will be negative
  134. // but the buffer will have been filled.
  135. //
  136. if ( 0 > cch )
  137. cch = CElems(rgch)-1;
  138. DWORD cbActual;
  139. CSynchronizedBlock sb(Instance().m_cs);
  140. WriteFile( Instance().m_hfLog,
  141. rgch,
  142. cch,
  143. &cbActual,
  144. NULL );
  145. }
  146. // ------------------------------------------------------------------------
  147. //
  148. // CEcbLog::LNextMethodID()
  149. //
  150. LONG
  151. CEcbLog::LNextMethodID()
  152. {
  153. return InterlockedIncrement(&Instance().m_lMethodID);
  154. }
  155. void InitECBLogging()
  156. {
  157. CEcbLog::CreateInstance();
  158. }
  159. void DeinitECBLogging()
  160. {
  161. CEcbLog::DestroyInstance();
  162. }
  163. #endif // DBG ECB logging
  164. // ========================================================================
  165. //
  166. // CLASS IIISAsyncIOCompleteObserver
  167. //
  168. // ------------------------------------------------------------------------
  169. //
  170. // IIISAsyncIOCompleteObserver::~IIISAsyncIOCompleteObserver()
  171. //
  172. // Out of line virtual destructor necessary for proper deletion
  173. // of objects of derived classes via this class
  174. //
  175. IIISAsyncIOCompleteObserver::~IIISAsyncIOCompleteObserver() {}
  176. // ========================================================================
  177. //
  178. // CLASS CAsyncErrorResponseInterlock
  179. //
  180. class CAsyncErrorResponseInterlock
  181. {
  182. enum
  183. {
  184. STATE_ENABLED,
  185. STATE_DISABLED,
  186. STATE_TRIGGERED
  187. };
  188. // Interlock state
  189. //
  190. LONG m_lState;
  191. // NOT IMPLEMENTED
  192. //
  193. CAsyncErrorResponseInterlock( const CAsyncErrorResponseInterlock& );
  194. CAsyncErrorResponseInterlock& operator=( const CAsyncErrorResponseInterlock& );
  195. public:
  196. CAsyncErrorResponseInterlock() :
  197. m_lState(STATE_ENABLED)
  198. {
  199. }
  200. // ------------------------------------------------------------------------
  201. //
  202. // CAsyncErrorResponseInterlock::FDisable()
  203. //
  204. // Tries to disable the interlock. Returns TRUE if successful; subsequent
  205. // calls to FTrigger() will return FALSE.
  206. //
  207. BOOL FDisable()
  208. {
  209. // Return TRUE if the lock is already disabled OR if the lock is
  210. // still enabled and we succeeded in disabling it. Return FALSE
  211. // otherwise.
  212. //
  213. return STATE_DISABLED == m_lState ||
  214. (STATE_ENABLED == m_lState &&
  215. STATE_ENABLED == InterlockedCompareExchange(
  216. &m_lState,
  217. STATE_DISABLED,
  218. STATE_ENABLED));
  219. }
  220. // ------------------------------------------------------------------------
  221. //
  222. // CAsyncErrorResponseInterlock::FTrigger()
  223. //
  224. // Tries to trigger the interlock. Returns TRUE if successful; subsequent
  225. // calls to FDisable() will return FALSE.
  226. //
  227. BOOL FTrigger()
  228. {
  229. // We can only trigger the lock once.
  230. //
  231. Assert(STATE_TRIGGERED != m_lState);
  232. // Return TRUE if the lock is still enabled and we succeed in
  233. // triggering it. Return FALSE otherwise.
  234. //
  235. return STATE_ENABLED == m_lState &&
  236. STATE_ENABLED == InterlockedCompareExchange(
  237. &m_lState,
  238. STATE_TRIGGERED,
  239. STATE_ENABLED);
  240. }
  241. };
  242. // ========================================================================
  243. //
  244. // CLASS CEcb
  245. //
  246. // Implementation of the caching ECB
  247. //
  248. class CEcb : public CEcbBaseImpl<IEcb>
  249. {
  250. // Cached user impersonation token
  251. //
  252. mutable HANDLE m_hTokUser;
  253. // Cached instance data -- owned by the instance cache,
  254. // not us, so don't free it (not an auto-ptr!!).
  255. //
  256. mutable CInstData * m_pInstData;
  257. // Cached HTTP version (e.g. "HTTP/1.1")
  258. //
  259. mutable CHAR m_rgchVersion[10];
  260. // Cached Connection: header
  261. //
  262. mutable auto_heap_ptr<WCHAR> m_pwszConnectionHeader;
  263. // Cached metadata
  264. //
  265. auto_ref_ptr<IMDData> m_pMD;
  266. // State in which we leave the connection
  267. // when we're done.
  268. //
  269. mutable enum
  270. {
  271. UNKNOWN, // Don't know yet
  272. CLOSE, // Close it
  273. KEEP_ALIVE // Keep it open
  274. } m_connState;
  275. // Brief'ness
  276. //
  277. enum { BRIEF_UNKNOWN = -1, BRIEF_NO, BRIEF_YES };
  278. mutable LONG m_lBrief;
  279. // Acceptable transfer coding method:
  280. //
  281. // TC_UNKNOWN - Acceptable transfer coding has not yet been determined.
  282. // TC_CHUNKED - Chunked transfer coding is acceptable.
  283. // TC_IDENTITY - No transfer coding is acceptable.
  284. //
  285. mutable TRANSFER_CODINGS m_tcAccepted;
  286. // Authentication State information:
  287. // Bit Means
  288. // ============================
  289. // 31 Queried against ECB
  290. // 30-4 Unused
  291. // 3 Kerberos
  292. // 2 NTLM
  293. // 1 Basic
  294. // 0 Authenticated
  295. mutable DWORD m_rgbAuthState;
  296. // Init flag set to TRUE once we've registered our
  297. // I/O completion routine with IIS.
  298. //
  299. enum { NO_COMPLETION, IO_COMPLETION, CUSTERR_COMPLETION, EXECURL_COMPLETION };
  300. LONG m_lSetIISIOCompleteCallback;
  301. // Flag stating whether a child ISAPI has been successfully executed. If this is the
  302. // case, we don't want to reset dwHttpStatusCode later or we will lose whatever
  303. // status code they set.
  304. //
  305. BOOL m_fChildISAPIExecSuccess;
  306. //
  307. // Interlock used to prevent a race condition between a thread
  308. // sending a normal response and a thread sending an error in response
  309. // to an async event such as an exception or epoxy shutdown.
  310. //
  311. CAsyncErrorResponseInterlock m_aeri;
  312. // Status string for async custom error response.
  313. // Format "nnn reason".
  314. //
  315. auto_heap_ptr<CHAR> m_pszStatus;
  316. //
  317. // Refcount to track number of outstanding async I/O operations.
  318. // There should never be more than one.
  319. //
  320. LONG m_cRefAsyncIO;
  321. //
  322. // Pointer to the current async I/O completion observer
  323. //
  324. IIISAsyncIOCompleteObserver * m_pobsAsyncIOComplete;
  325. #ifdef DBG
  326. LONG m_lEcbLogMethodID;
  327. #endif
  328. // ECB tracing (not to be confused with ECB logging!)
  329. //
  330. #ifdef DBG
  331. void TraceECB() const;
  332. #else
  333. void TraceECB() const {}
  334. #endif
  335. //
  336. // Async I/O
  337. //
  338. SCODE ScSetIOCompleteCallback(LONG lCompletion);
  339. static VOID WINAPI IISIOComplete( const EXTENSION_CONTROL_BLOCK * pecbIIS,
  340. CEcb * pecb,
  341. DWORD dwcbIO,
  342. DWORD dwLastError );
  343. static VOID WINAPI CustomErrorIOCompletion( const EXTENSION_CONTROL_BLOCK * pecbIIS,
  344. CEcb * pecb,
  345. DWORD dwcbIO,
  346. DWORD dwLastError );
  347. static VOID WINAPI ExecuteUrlIOCompletion( const EXTENSION_CONTROL_BLOCK * pecbIIS,
  348. CEcb * pecb,
  349. DWORD dwcbIO,
  350. DWORD dwLastError );
  351. // NOT IMPLEMENTED
  352. //
  353. CEcb( const CEcb& );
  354. CEcb& operator=( const CEcb& );
  355. SCODE ScSyncExecuteChildWide60Before( LPCWSTR pwszUrl,
  356. LPCSTR pszQueryString,
  357. BOOL fCustomErrorUrl );
  358. SCODE ScAsyncExecUrlWide60After( LPCWSTR pwszUrl,
  359. LPCSTR pszQueryString,
  360. BOOL fCustomErrorUrl );
  361. public:
  362. CEcb( EXTENSION_CONTROL_BLOCK& ecb );
  363. BOOL FInitialize( BOOL fUseRawUrlMappings );
  364. ~CEcb();
  365. // URL prefix
  366. //
  367. UINT CchUrlPortW( LPCWSTR * ppwszPort ) const;
  368. // Instance data access
  369. //
  370. CInstData& InstData() const;
  371. // Impersonation token access
  372. //
  373. HANDLE HitUser() const;
  374. // ACCESSORS
  375. //
  376. LPCSTR LpszVersion() const;
  377. BOOL FKeepAlive() const;
  378. BOOL FCanChunkResponse() const;
  379. BOOL FAuthenticated() const;
  380. BOOL FProcessingCEUrl() const;
  381. BOOL FIIS60OrAfter() const
  382. {
  383. return (m_pecb->dwVersion >= IIS_VERSION_6_0);
  384. }
  385. BOOL FSyncTransmitHeaders( const HSE_SEND_HEADER_EX_INFO& shei );
  386. SCODE ScAsyncRead( BYTE * pbBuf,
  387. UINT * pcbBuf,
  388. IIISAsyncIOCompleteObserver& obs );
  389. SCODE ScAsyncWrite( BYTE * pbBuf,
  390. DWORD dwcbBuf,
  391. IIISAsyncIOCompleteObserver& obs );
  392. SCODE ScAsyncTransmitFile( const HSE_TF_INFO& tfi,
  393. IIISAsyncIOCompleteObserver& obs );
  394. SCODE ScAsyncCustomError60After( const HSE_CUSTOM_ERROR_INFO& cei,
  395. LPSTR pszStatus );
  396. SCODE ScAsyncExecUrl60After( const HSE_EXEC_URL_INFO& eui );
  397. SCODE ScExecuteChild( LPCWSTR pwszURI, LPCSTR pszQueryString, BOOL fCustomErrorUrl )
  398. {
  399. // IIS 6.0 or after has a different way of executing child
  400. //
  401. if (m_pecb->dwVersion >= IIS_VERSION_6_0)
  402. {
  403. return ScAsyncExecUrlWide60After (pwszURI, pszQueryString, fCustomErrorUrl);
  404. }
  405. else
  406. {
  407. return ScSyncExecuteChildWide60Before (pwszURI, pszQueryString, fCustomErrorUrl);
  408. }
  409. }
  410. SCODE ScSendRedirect( LPCSTR lpszURI );
  411. IMDData& MetaData() const
  412. {
  413. Assert( m_pMD.get() );
  414. return *m_pMD;
  415. }
  416. BOOL FBrief () const;
  417. LPCWSTR PwszMDPathVroot() const
  418. {
  419. Assert( m_pInstData );
  420. return m_pInstData->GetNameW();
  421. }
  422. #ifdef DBG
  423. virtual void LogString( LPCSTR lpszLocation ) const
  424. {
  425. if ( DEBUG_TRACE_TEST(ECBLogging) )
  426. CEcbLog::LogString( m_pecb, m_lEcbLogMethodID, lpszLocation );
  427. }
  428. #endif
  429. // MANIPULATORS
  430. //
  431. VOID SendAsyncErrorResponse( DWORD dwStatusCode,
  432. LPCSTR pszStatusDescription,
  433. DWORD cchzStatusDescription,
  434. LPCSTR pszBody,
  435. DWORD cchzBody );
  436. DWORD HSEHandleException();
  437. // Session handling
  438. //
  439. VOID DoneWithSession( BOOL fKeepAlive );
  440. // To be used ONLY by request/response.
  441. //
  442. void SetStatusCode( UINT iStatusCode );
  443. void SetConnectionHeader( LPCWSTR pwszValue );
  444. void SetAcceptLanguageHeader( LPCSTR pszValue );
  445. void CloseConnection();
  446. };
  447. // ------------------------------------------------------------------------
  448. //
  449. // CEcb Constructor/Destructor
  450. //
  451. CEcb::CEcb( EXTENSION_CONTROL_BLOCK& ecb ) :
  452. CEcbBaseImpl<IEcb>(ecb),
  453. m_hTokUser(NULL),
  454. m_pInstData(NULL),
  455. m_connState(UNKNOWN),
  456. m_tcAccepted(TC_UNKNOWN),
  457. m_rgbAuthState(0),
  458. m_cRefAsyncIO(0),
  459. m_lSetIISIOCompleteCallback(NO_COMPLETION),
  460. m_fChildISAPIExecSuccess(FALSE),
  461. m_lBrief(BRIEF_UNKNOWN)
  462. {
  463. #ifdef DBG
  464. if ( DEBUG_TRACE_TEST(ECBLogging) )
  465. m_lEcbLogMethodID = CEcbLog::LNextMethodID();
  466. #endif
  467. // Auto-pointers will be init'd by their own ctors.
  468. //
  469. // Zero the first char of the m_rgchVersion.
  470. //
  471. *m_rgchVersion = '\0';
  472. // Clear out the status code in the EXTENSION_CONTROL_BLOCK so that
  473. // we will be able to tell whether we should try to send a 500
  474. // Server Error response in the event of an exception.
  475. //
  476. SetStatusCode(0);
  477. // Set up our instance data now. We need it for the perf counters below.
  478. //
  479. m_pInstData = &g_inst.GetInstData( *this );
  480. // And trace out the ECB info (If we're debug, if we're tracing....)
  481. //
  482. TraceECB();
  483. }
  484. // ------------------------------------------------------------------------
  485. //
  486. // CEcb::FInitialize()
  487. //
  488. BOOL
  489. CEcb::FInitialize( BOOL fUseRawUrlMappings )
  490. {
  491. auto_heap_ptr<WCHAR> pwszMDUrlOnHeap;
  492. //
  493. // Fault in a few things like the vroot (LPSTR) and its length
  494. // and the corresponding path information.
  495. // The mapex info is already "faulted in" during the CEcb constructor.
  496. // (ctor calls GetInstData, which calls CEcbBaseImpl<>::GetMapExInfo)
  497. // However, fault in other pieces, like our translated request URI
  498. // and our MD path.
  499. //
  500. //
  501. // Cache the metabase paths for both the vroot and the request URI.
  502. //
  503. // Note that the metabase path for the vroot is just the instance name.
  504. //
  505. // Special case: If '*' is the request URI.
  506. //
  507. // IMPORTANT: this is only valid for an OPTIONS request. The handling
  508. // of the validitiy of the request is handled later. For now, lookup
  509. // the data for the default site root.
  510. //
  511. // IMPORTANT:
  512. // LpszRequestUrl() will return FALSE in the case of a bad URL (in
  513. // DAVEX, if the ScNormalizeUrl() call fails). If that happens,
  514. // set our status code to HSC_BAD_REQUEST and return FALSE from here.
  515. // The calling code (NewEcb()) will handle this gracefully.
  516. //
  517. LPCWSTR pwszRequestUrl = LpwszRequestUrl();
  518. if (!pwszRequestUrl)
  519. {
  520. SetStatusCode(HSC_BAD_REQUEST);
  521. return FALSE;
  522. }
  523. LPCWSTR pwszMDUrl;
  524. if ( L'*' == pwszRequestUrl[0] &&
  525. L'\0' == pwszRequestUrl[1] )
  526. {
  527. pwszMDUrl = PwszMDPathVroot();
  528. }
  529. else
  530. {
  531. pwszMDUrlOnHeap = static_cast<LPWSTR>(ExAlloc(CbMDPathW(*this, pwszRequestUrl)));
  532. if (NULL == pwszMDUrlOnHeap.get())
  533. return FALSE;
  534. pwszMDUrl = pwszMDUrlOnHeap.get();
  535. MDPathFromURIW( *this, pwszRequestUrl, const_cast<LPWSTR>(pwszMDUrl) );
  536. }
  537. //
  538. //$REVIEW It would be nice to propagate out the specific HRESULT
  539. //$REVIEW so that we could send back an appropriate suberror,
  540. //$REVIEW but sending a suberror could be difficult if we can't
  541. //$REVIEW get to the metadata that contains the suberror mappings....
  542. //
  543. return SUCCEEDED(HrMDGetData( *this,
  544. pwszMDUrl,
  545. PwszMDPathVroot(),
  546. m_pMD.load() ));
  547. }
  548. // ------------------------------------------------------------------------
  549. //
  550. // CEcb::~CEcb()
  551. //
  552. CEcb::~CEcb()
  553. {
  554. //
  555. // If we've already given back the EXTENSION_CONTROL_BLOCK then
  556. // we don't need to do anything else here. Otherwise we should
  557. // return it (call HSE_REQ_DONE_WITH_SESSION) with the appropriate
  558. // keep-alive.
  559. //
  560. if ( m_pecb )
  561. {
  562. //
  563. // At this point someone should have generated a response,
  564. // even in the case of an exception (see HSEHandleException()).
  565. //
  566. Assert( m_pecb->dwHttpStatusCode != 0 );
  567. //
  568. // Tell IIS that we're done for this request.
  569. //
  570. DoneWithSession( FKeepAlive() );
  571. }
  572. }
  573. // ========================================================================
  574. //
  575. // PRIVATE CEcb methods
  576. //
  577. // ------------------------------------------------------------------------
  578. //
  579. // CEcb::DoneWithSession()
  580. //
  581. // Called whenever we are done with the raw EXTENSION_CONTROL_BLOCK.
  582. //
  583. VOID
  584. CEcb::DoneWithSession( BOOL fKeepAlive )
  585. {
  586. //
  587. // We should only call DoneWithSession() once. We null out m_pecb
  588. // at the end, so we can assert that we are only called once by
  589. // checking m_pecb here.
  590. //
  591. Assert( m_pecb );
  592. //
  593. // We should never release the EXTENSION_CONTROL_BLOCK if there
  594. // is async I/O outstanding.
  595. //
  596. Assert( 0 == m_cRefAsyncIO );
  597. //
  598. // "Release" the raw EXTENSION_CONTROL_BLOCK inherited from IEcb.
  599. //
  600. static const DWORD sc_dwKeepConn = HSE_STATUS_SUCCESS_AND_KEEP_CONN;
  601. (VOID) m_pecb->ServerSupportFunction(
  602. m_pecb->ConnID,
  603. HSE_REQ_DONE_WITH_SESSION,
  604. fKeepAlive ? const_cast<DWORD *>(&sc_dwKeepConn) : NULL,
  605. NULL,
  606. NULL );
  607. //
  608. // We can no longer use the EXTENSION_CONTROL_BLOCK so remove any
  609. // temptation to do so by nulling out the pointer.
  610. //
  611. m_pecb = NULL;
  612. }
  613. // ------------------------------------------------------------------------
  614. //
  615. // CEcb::SendAsyncErrorResponse()
  616. //
  617. VOID
  618. CEcb::SendAsyncErrorResponse( DWORD dwStatusCode,
  619. LPCSTR pszStatusDescription,
  620. DWORD cchzStatusDescription,
  621. LPCSTR pszBody,
  622. DWORD cchzBody )
  623. {
  624. // Try to trigger the async error response mechanism. If successful
  625. // then we are responsible for sending the entire repsonse. If not
  626. // then we are already sending a response on some other thread, so
  627. // don't confuse things by sending any thing here.
  628. //
  629. if (!m_aeri.FTrigger())
  630. {
  631. DebugTrace( "CEcb::SendAsyncErrorResponse() - Non-error response already in progress\n" );
  632. return;
  633. }
  634. HSE_SEND_HEADER_EX_INFO shei;
  635. CHAR rgchStatusDescription[256];
  636. // Blow away any previously set status code in favor of
  637. // the requested status code. Even though there may have
  638. // been an old status code set, it was never sent -- the
  639. // fact that our interlock triggered proves that no other
  640. // response has been sent.
  641. //
  642. SetStatusCode(dwStatusCode);
  643. // If we don't have a status description then fetch the default
  644. // for the given status code.
  645. //
  646. if ( !pszStatusDescription )
  647. {
  648. LpszLoadString( dwStatusCode,
  649. LcidAccepted(),
  650. rgchStatusDescription,
  651. sizeof(rgchStatusDescription) );
  652. pszStatusDescription = rgchStatusDescription;
  653. }
  654. shei.pszStatus = pszStatusDescription;
  655. shei.cchStatus = cchzStatusDescription;
  656. // Don't send any body unless we are given one.
  657. //
  658. shei.pszHeader = pszBody;
  659. shei.cchHeader = cchzBody;
  660. // Always close the connection on errors -- and we should
  661. // only ever be called for serious server errors.
  662. //
  663. Assert(dwStatusCode >= 400);
  664. shei.fKeepConn = FALSE;
  665. // Send the response. We don't care at all about the return
  666. // value because there's nothing we can do if the response
  667. // cannot be sent.
  668. //
  669. (VOID) m_pecb->ServerSupportFunction(
  670. m_pecb->ConnID,
  671. HSE_REQ_SEND_RESPONSE_HEADER_EX,
  672. &shei,
  673. NULL,
  674. NULL );
  675. }
  676. // ------------------------------------------------------------------------
  677. //
  678. // CEcb::HSEHandleException()
  679. //
  680. DWORD
  681. CEcb::HSEHandleException()
  682. {
  683. //
  684. // !!! IMPORTANT !!!
  685. //
  686. // This function is called after an exception has occurred.
  687. // Don't do ANYTHING outside of a try/catch block or a secondary
  688. // exception could take out the whole IIS process.
  689. //
  690. try
  691. {
  692. //
  693. // Translate async Win32 exceptions into thrown C++ exceptions.
  694. // This must be placed inside the try block!
  695. //
  696. CWin32ExceptionHandler win32ExceptionHandler;
  697. //
  698. // Send a 500 Server Error response. We use the async error
  699. // response mechanism, because we may have been in the middle
  700. // of sending some other response on another thread.
  701. //
  702. SendAsyncErrorResponse(500,
  703. gc_szDefErrStatusLine,
  704. gc_cchszDefErrStatusLine,
  705. gc_szDefErrBody,
  706. gc_cchszDefErrBody);
  707. }
  708. catch ( CDAVException& )
  709. {
  710. //
  711. // We blew up trying to send a response. Oh well.
  712. //
  713. }
  714. //
  715. // Tell IIS that we are done with the EXTENSION_CONTROL_BLOCK that
  716. // it gave us. We must do this or IIS will not be able to shut down.
  717. // We would normally do this from within our destructor, but since
  718. // we are handling an exception, there is no guarantee that our
  719. // destructor will ever be called -- that is, there may be outstanding
  720. // refs that will never be released (i.e. we will leak).
  721. //
  722. DWORD dwHSEStatusRet;
  723. try
  724. {
  725. //
  726. // Translate async Win32 exceptions into thrown C++ exceptions.
  727. // This must be placed inside the try block!
  728. //
  729. CWin32ExceptionHandler win32ExceptionHandler;
  730. DoneWithSession( FALSE );
  731. //
  732. // If this call succeeds, we MUST return HSE_STATUS_PENDING to
  733. // let IIS know that we claimed a ref on the EXTENSION_CONTROL_BLOCK.
  734. //
  735. dwHSEStatusRet = HSE_STATUS_PENDING;
  736. }
  737. catch ( CDAVException& )
  738. {
  739. //
  740. // We blew up trying to tell IIS that we were done with
  741. // the EXTENSION_CONTROL_BLOCK. There is absolutely nothing
  742. // we can do at this point. IIS will probably hang on shutdown.
  743. //
  744. dwHSEStatusRet = HSE_STATUS_ERROR;
  745. }
  746. return dwHSEStatusRet;
  747. }
  748. // ------------------------------------------------------------------------
  749. //
  750. // CEcb::TraceECB()
  751. //
  752. // Traces out the EXTENSION_CONTROL_BLOCK
  753. //
  754. #ifdef DBG
  755. void
  756. CEcb::TraceECB() const
  757. {
  758. EcbTrace( "ECB Contents:\n" );
  759. EcbTrace( "\tcbSize = %lu\n", m_pecb->cbSize );
  760. EcbTrace( "\tdwVersion = %lu\n", m_pecb->dwVersion );
  761. EcbTrace( "\tlpszMethod = \"%s\"\n", m_pecb->lpszMethod );
  762. EcbTrace( "\tlpszQueryString = \"%s\"\n", m_pecb->lpszQueryString );
  763. EcbTrace( "\tlpszPathInfo = \"%s\"\n", m_pecb->lpszPathInfo );
  764. EcbTrace( "\tlpszPathTranslated = \"%s\"\n", m_pecb->lpszPathTranslated );
  765. EcbTrace( "\tcbTotalBytes = %lu\n", m_pecb->cbTotalBytes );
  766. EcbTrace( "\tcbAvailable = %lu\n", m_pecb->cbAvailable );
  767. EcbTrace( "\tlpszContentType = \"%s\"\n", m_pecb->lpszContentType );
  768. EcbTrace( "\n" );
  769. {
  770. char rgch[256];
  771. DWORD dwCbRgch;
  772. dwCbRgch = sizeof(rgch);
  773. (void) m_pecb->GetServerVariable( m_pecb->ConnID, "SCRIPT_NAME", rgch, &dwCbRgch );
  774. EcbTrace( "Script name = \"%s\"\n", rgch );
  775. dwCbRgch = sizeof(rgch);
  776. (void) m_pecb->GetServerVariable( m_pecb->ConnID, "SCRIPT_MAP", rgch, &dwCbRgch );
  777. EcbTrace( "Script map = \"%s\"\n", rgch );
  778. dwCbRgch = sizeof(rgch);
  779. (void) m_pecb->GetServerVariable( m_pecb->ConnID, "HTTP_REQUEST_URI", rgch, &dwCbRgch );
  780. EcbTrace( "Request URI = \"%s\"\n", rgch );
  781. dwCbRgch = sizeof(rgch);
  782. (void) m_pecb->ServerSupportFunction( m_pecb->ConnID, HSE_REQ_MAP_URL_TO_PATH, rgch, &dwCbRgch, NULL );
  783. EcbTrace( "Path from request URI = \"%s\"\n", rgch );
  784. }
  785. }
  786. #endif // defined(DBG)
  787. // ========================================================================
  788. //
  789. // PUBLIC CEcb methods
  790. //
  791. // ------------------------------------------------------------------------
  792. //
  793. // CEcb::CchUrlPortW
  794. //
  795. // Get the string with the port based on the fact if we are secure
  796. //
  797. UINT
  798. CEcb::CchUrlPortW( LPCWSTR * ppwszPort ) const
  799. {
  800. Assert (ppwszPort);
  801. // If we are secure...
  802. //
  803. if (FSsl())
  804. {
  805. *ppwszPort = gc_wszUrl_Port_443;
  806. return gc_cchUrl_Port_443;
  807. }
  808. *ppwszPort = gc_wszUrl_Port_80;
  809. return gc_cchUrl_Port_80;
  810. }
  811. // ------------------------------------------------------------------------
  812. //
  813. // CEcb::InstData
  814. //
  815. // Fetch and cache our per-vroot instance data
  816. //
  817. CInstData&
  818. CEcb::InstData() const
  819. {
  820. Assert( m_pInstData );
  821. return *m_pInstData;
  822. }
  823. // ------------------------------------------------------------------------
  824. //
  825. // CEcb::HitUser
  826. //
  827. // Fetch and cache our impersonation token
  828. //
  829. HANDLE
  830. CEcb::HitUser() const
  831. {
  832. if ( m_hTokUser == NULL )
  833. {
  834. ULONG cb = sizeof(HANDLE);
  835. m_pecb->ServerSupportFunction( m_pecb->ConnID,
  836. HSE_REQ_GET_IMPERSONATION_TOKEN,
  837. &m_hTokUser,
  838. &cb,
  839. NULL );
  840. }
  841. return m_hTokUser;
  842. }
  843. // ------------------------------------------------------------------------
  844. //
  845. // CEcb::LpszVersion()
  846. //
  847. LPCSTR
  848. CEcb::LpszVersion() const
  849. {
  850. if ( !*m_rgchVersion )
  851. {
  852. DWORD cbVersion = sizeof(m_rgchVersion);
  853. if ( !FGetServerVariable( gc_szHTTP_Version,
  854. m_rgchVersion,
  855. &cbVersion ) )
  856. {
  857. //
  858. // If we are unable to get a value for HTTP_VERSION then
  859. // the string is probably longer than the buffer we gave.
  860. // Rather than deal with a potentially arbitrarily long
  861. // string, default to HTTP/1.1. This is consistent with
  862. // observed IIS behavior (cf. NT5:247826).
  863. //
  864. memcpy( m_rgchVersion,
  865. gc_szHTTP_1_1,
  866. sizeof(gc_szHTTP_1_1) );
  867. }
  868. else if ( !*m_rgchVersion )
  869. {
  870. //
  871. // No value for HTTP_VERSION means that nothing was
  872. // specified on the request line, which means HTTP/0.9
  873. //
  874. memcpy( m_rgchVersion,
  875. gc_szHTTP_0_9,
  876. sizeof(gc_szHTTP_0_9) );
  877. }
  878. }
  879. return m_rgchVersion;
  880. }
  881. // ------------------------------------------------------------------------
  882. //
  883. // CEcb::FKeepAlive()
  884. //
  885. // Returns whether to keep alive the client connection after sending
  886. // the response.
  887. //
  888. // The connection logic has changed over the various HTTP versions;
  889. // this function uses the logic appropriate to the HTTP version
  890. // of the request.
  891. //
  892. BOOL
  893. CEcb::FKeepAlive() const
  894. {
  895. //
  896. // If we haven't already determined what we want, then begin
  897. // the process of figuring it out...
  898. //
  899. if ( m_connState == UNKNOWN )
  900. {
  901. //
  902. // If someone set a Connection: header then pay attention to it
  903. //
  904. if (m_pwszConnectionHeader.get())
  905. {
  906. //
  907. // Set the connection state based on the current value of
  908. // the request's Connection: header, and the HTTP version.
  909. // NOTE: The request MUST forward us any updates to the
  910. // Connection: header for this to work!
  911. // NOTE: Comparing the HTTP version strings using C-runtime strcmp,
  912. // because the version string is pure ASCII.
  913. //
  914. //
  915. // HTTP/1.1
  916. //
  917. // (Consider the HTTP/1.1 case FIRST to minimize the number
  918. // of string compares in this most common case).
  919. //
  920. if ( !strcmp( LpszVersion(), gc_szHTTP_1_1 ) )
  921. {
  922. http_1_1:
  923. //
  924. // The default for HTTP/1.1 is to keep the connection alive
  925. //
  926. m_connState = KEEP_ALIVE;
  927. //
  928. // But if the request's Connection: header says close,
  929. // then close.
  930. //
  931. // This compare should be case-insensitive.
  932. //
  933. // Using CRT skinny-string func here 'cause this header is pure ASCII,
  934. // AND because _stricmp (and his brother _strcmpi) doesn't cause us
  935. // a context-switch!
  936. //
  937. if ( !_wcsicmp( m_pwszConnectionHeader.get(), gc_wszClose ) )
  938. m_connState = CLOSE;
  939. }
  940. //
  941. // HTTP/1.0
  942. //
  943. else if ( !strcmp( LpszVersion(), gc_szHTTP_1_0 ) )
  944. {
  945. //
  946. // For HTTP/1.0 requests, the default is to close the connection
  947. // unless a "Connection: Keep-Alive" header exists.
  948. //
  949. m_connState = CLOSE;
  950. if ( !_wcsicmp( m_pwszConnectionHeader.get(), gc_wszKeep_Alive ) )
  951. m_connState = KEEP_ALIVE;
  952. }
  953. //
  954. // HTTP/0.9
  955. //
  956. else if ( !strcmp( LpszVersion(), gc_szHTTP_0_9 ) )
  957. {
  958. //
  959. // For HTTP/0.9, always close the connection. There was no
  960. // other option for HTTP/0.9.
  961. //
  962. m_connState = CLOSE;
  963. }
  964. //
  965. // Other (future) HTTP versions
  966. //
  967. else
  968. {
  969. //
  970. // We really are only guessing what to do here, but assuming
  971. // that the HTTP spec doesn't change the Connection behavior
  972. // again, we should behave like HTTP/1.1
  973. //
  974. goto http_1_1;
  975. }
  976. }
  977. //
  978. // If no one set a Connection: header, than use whatever IIS
  979. // tells us to use.
  980. //
  981. // NOTE: Currently, this value can only be ADDED, never DELETED.
  982. // If that fact changes, FIX this code!
  983. //
  984. else
  985. {
  986. BOOL fKeepAlive;
  987. if ( !m_pecb->ServerSupportFunction( m_pecb->ConnID,
  988. HSE_REQ_IS_KEEP_CONN,
  989. &fKeepAlive,
  990. NULL,
  991. NULL ))
  992. {
  993. DebugTrace( "CEcb::FKeepAlive--Failure (0x%08x) from SSF(IsKeepConn).\n",
  994. GetLastError() );
  995. // No big deal? If we're getting errors like this
  996. // we probably want to close the connection anyway....
  997. //
  998. m_connState = CLOSE;
  999. }
  1000. m_connState = fKeepAlive ? KEEP_ALIVE : CLOSE;
  1001. }
  1002. }
  1003. //
  1004. // By now we must know what we want
  1005. //
  1006. Assert( m_connState == KEEP_ALIVE || m_connState == CLOSE );
  1007. return m_connState == KEEP_ALIVE;
  1008. }
  1009. // ------------------------------------------------------------------------
  1010. //
  1011. // CEcb::FCanChunkResponse()
  1012. //
  1013. // Returns TRUE if the client will accept a chunked response.
  1014. //
  1015. BOOL
  1016. CEcb::FCanChunkResponse() const
  1017. {
  1018. if ( TC_UNKNOWN == m_tcAccepted )
  1019. {
  1020. //
  1021. // According to the HTTP/1.1 draft, section 14.39 TE:
  1022. //
  1023. // "A server tests whether a transfer-coding is acceptable,
  1024. // acording to a TE field, using these rules:
  1025. //
  1026. // 1.
  1027. // The "chunked" transfer-coding is always acceptable.
  1028. //
  1029. // [...]"
  1030. //
  1031. // and section 3.6 Transfer Codings, last paragraph:
  1032. //
  1033. // "A server MUST NOT send transfer-codings to an HTTP/1.0
  1034. // client."
  1035. //
  1036. // Therefore, deciding whether a client accepts a chunked
  1037. // transfer coding is simple: If the request is an HTTP/1.1
  1038. // request, then it accepts chunked coding. Otherwise it doesn't.
  1039. //
  1040. m_tcAccepted = strcmp( gc_szHTTP_1_1, LpszVersion() ) ?
  1041. TC_IDENTITY :
  1042. TC_CHUNKED;
  1043. }
  1044. Assert( m_tcAccepted != TC_UNKNOWN );
  1045. return TC_CHUNKED == m_tcAccepted;
  1046. }
  1047. BOOL
  1048. CEcb::FBrief() const
  1049. {
  1050. // If we don't have a value yet...
  1051. //
  1052. if (BRIEF_UNKNOWN == m_lBrief)
  1053. {
  1054. CHAR rgchBrief[8] = {0};
  1055. ULONG cbBrief = 8;
  1056. // Brief is expected when:
  1057. //
  1058. // The "brief" header has a value of "t"
  1059. //
  1060. // NOTE: The default is brief to false.
  1061. //
  1062. // We addapt overwrite checking model here. Just first letter for true case.
  1063. //
  1064. // NOTE also: the default value if there is no Brief: header
  1065. // is FALSE -- give the full response.
  1066. //
  1067. FGetServerVariable("HTTP_BRIEF", rgchBrief, &cbBrief);
  1068. if ((rgchBrief[0] != 't') && (rgchBrief[0] != 'T'))
  1069. m_lBrief = BRIEF_NO;
  1070. else
  1071. m_lBrief = BRIEF_YES;
  1072. }
  1073. return (BRIEF_YES == m_lBrief);
  1074. }
  1075. // ------------------------------------------------------------------------
  1076. //
  1077. // CEcb::FAuthenticated()
  1078. //
  1079. const DWORD c_AuthStateQueried = 0x80000000;
  1080. const DWORD c_AuthStateAuthenticated = 0x00000001;
  1081. const DWORD c_AuthStateBasic = 0x00000002;
  1082. const DWORD c_AuthStateNTLM = 0x00000004;
  1083. const DWORD c_AuthStateKerberos = 0x00000008;
  1084. const DWORD c_AuthStateUnknown = 0x00000010;
  1085. const CHAR c_szBasic[] = "Basic";
  1086. const CHAR c_szNTLM[] = "NTLM";
  1087. const CHAR c_szKerberos[] = "Kerberos";
  1088. BOOL
  1089. CEcb::FAuthenticated() const
  1090. {
  1091. if (!(m_rgbAuthState & c_AuthStateQueried))
  1092. {
  1093. CHAR szAuthType[32];
  1094. ULONG cb = sizeof(szAuthType);
  1095. Assert(m_rgbAuthState == 0);
  1096. if (FGetServerVariable(gc_szAuth_Type, szAuthType, &cb))
  1097. {
  1098. // For now, lets just check the first character (it's cheaper).
  1099. // If this proves problematic then we can do a full string
  1100. // compair. Also, SSL by itself is not to be considered a form
  1101. // of domain authentication. The only time that SSL does imply
  1102. // and authenticated connection is when Cert Mapping is enabled
  1103. // and I don't think this is an interesting scenario. (russsi)
  1104. if (*szAuthType == 'B')
  1105. m_rgbAuthState = (c_AuthStateAuthenticated | c_AuthStateBasic);
  1106. else if (*szAuthType == 'N')
  1107. m_rgbAuthState = (c_AuthStateAuthenticated | c_AuthStateNTLM);
  1108. else if (*szAuthType == 'K')
  1109. m_rgbAuthState = (c_AuthStateAuthenticated | c_AuthStateKerberos);
  1110. else
  1111. m_rgbAuthState = c_AuthStateUnknown; // it could be "SSL/PCT"
  1112. }
  1113. m_rgbAuthState |= c_AuthStateQueried;
  1114. }
  1115. return (m_rgbAuthState & c_AuthStateAuthenticated);
  1116. }
  1117. // ------------------------------------------------------------------------
  1118. //
  1119. // CEcb::SetStatusCode()
  1120. //
  1121. // Sets the HTTP status code that IIS uses in logging
  1122. //
  1123. void
  1124. CEcb::SetStatusCode( UINT iStatusCode )
  1125. {
  1126. // If we have executed a child ISAPI successfully, we don't want to overwrite the
  1127. // status code in the ECB. This will end up causing IIS to log this status code
  1128. // rather than the one left in the ECB by the ISAPI.
  1129. //
  1130. if (!m_fChildISAPIExecSuccess)
  1131. m_pecb->dwHttpStatusCode = iStatusCode;
  1132. }
  1133. // MANIPULATORS
  1134. // To be used ONLY by request/response.
  1135. //
  1136. // NOTE: These member vars start out NULL. Inside CEcb, we are using NULL
  1137. // as a special value that means the data has NEVER been set, so if
  1138. // we get an lpszValue of NULL (meaning to delete the header), store
  1139. // an empty string instead, so that we know the data has been forcefully erased.
  1140. //
  1141. void CEcb::SetConnectionHeader( LPCWSTR pwszValue )
  1142. {
  1143. auto_heap_ptr<WCHAR> pwszOld;
  1144. pwszOld.take_ownership(m_pwszConnectionHeader.relinquish());
  1145. // If they want to delete the value, set an empty string.
  1146. //
  1147. if (!pwszValue)
  1148. pwszValue = gc_wszEmpty;
  1149. m_pwszConnectionHeader = WszDupWsz( pwszValue );
  1150. }
  1151. void CEcb::CloseConnection()
  1152. {
  1153. m_connState = CLOSE;
  1154. }
  1155. void CEcb::SetAcceptLanguageHeader( LPCSTR pszValue )
  1156. {
  1157. auto_heap_ptr<CHAR> pszOld;
  1158. pszOld.take_ownership(m_pszAcceptLanguage.relinquish());
  1159. // If they want to delete the value, set an empty string.
  1160. //
  1161. if (!pszValue)
  1162. pszValue = gc_szEmpty;
  1163. m_pszAcceptLanguage = LpszAutoDupSz( pszValue );
  1164. }
  1165. SCODE CEcb::ScAsyncRead( BYTE * pbBuf,
  1166. UINT * pcbBuf,
  1167. IIISAsyncIOCompleteObserver& obs )
  1168. {
  1169. SCODE sc = S_OK;
  1170. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncRead() called...\n", GetCurrentThreadId(), this );
  1171. //
  1172. // If there is another async IO outstanding we do not want to start one more. IIS will fail
  1173. // us out, and we ourselves will not be able to handle the completion of initial async IO
  1174. // properly. So just kill the connection and return. This may happen when we attempt to
  1175. // send the response before the read is finished.
  1176. //
  1177. if (0 != InterlockedCompareExchange(&m_cRefAsyncIO,
  1178. 1,
  1179. 0))
  1180. {
  1181. // The function bellow is not supported starting from IIS 6.0 but let us call it anyway
  1182. // just in case support becomes available - and we want to call it if the binary is
  1183. // running on IIS 5.0. It does not matter that much, as the bad side of not closing the
  1184. // connection may hang the client, or error out on subsequent request. That is ok as
  1185. // the path is supposed to be hit in abnormal/error conditions when clients for example
  1186. // send in invalid requests trying to cause denial of service or similar things.
  1187. // So on IIS 6.0 the connection will not be closed, we will just error out. We have
  1188. // not seen this path hit on IIS 6.0 anyway when runing denial of service scripts as it
  1189. // handles custom errors differently.
  1190. //
  1191. if (m_pecb->ServerSupportFunction(m_pecb->ConnID,
  1192. HSE_REQ_CLOSE_CONNECTION,
  1193. NULL,
  1194. NULL,
  1195. NULL))
  1196. {
  1197. EcbTrace( "CEcb::ScAsyncRead() - More than 1 async operation. Connection closed. Failing out with error 0x%08lX\n", E_ABORT );
  1198. sc = E_ABORT;
  1199. goto ret;
  1200. }
  1201. else
  1202. {
  1203. EcbTrace( "CEcb::ScAsyncRead() - More than 1 async operation. ServerSupportFunction(HSE_REQ_CLOSE_CONNECTION) "
  1204. "failed with last error 0x%08lX. Overriding with fatal error 0x%08lX\n", GetLastError(), E_FAIL );
  1205. sc = E_FAIL;
  1206. goto ret;
  1207. }
  1208. }
  1209. //
  1210. // IIS allows only one async I/O operation at a time. But for performance reasons it
  1211. // leaves it up to the ISAPI to heed the restriction. For the same reasons, we push
  1212. // that responsibility off to the DAV impl. A simple refcount tells us whether
  1213. // the impl has done so.
  1214. //
  1215. AssertSz( 1 == m_cRefAsyncIO,
  1216. "CEcb::ScAsyncRead() - m_cRefAsyncIO wrong on entry" );
  1217. //
  1218. // We need to hold a ref on the process-wide instance data for the duration of the I/O
  1219. // so that if IIS tells us to shut down while the I/O is still pending we will keep
  1220. // the instance data alive until we're done with the I/O.
  1221. //
  1222. AddRefImplInst();
  1223. //
  1224. // Set the async I/O completion observer
  1225. //
  1226. m_pobsAsyncIOComplete = &obs;
  1227. //
  1228. // Set up the async I/O completion routine and start reading.
  1229. // Add a ref for the I/O completion thread. Use auto_ref_ptr
  1230. // to make things exception-proof.
  1231. //
  1232. {
  1233. auto_ref_ptr<CEcb> pRef(this);
  1234. sc = ScSetIOCompleteCallback(IO_COMPLETION);
  1235. if (SUCCEEDED(sc))
  1236. {
  1237. if (m_pecb->ServerSupportFunction( m_pecb->ConnID,
  1238. HSE_REQ_ASYNC_READ_CLIENT,
  1239. pbBuf,
  1240. reinterpret_cast<LPDWORD>(pcbBuf),
  1241. NULL ))
  1242. {
  1243. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncRead() I/O pending...\n", GetCurrentThreadId(), this );
  1244. pRef.relinquish();
  1245. }
  1246. else
  1247. {
  1248. EcbTrace( "CEcb::ScAsyncRead() - ServerSupportFunction(HSE_REQ_ASYNC_READ_CLIENT) failed with last error 0x%08lX\n", GetLastError() );
  1249. sc = HRESULT_FROM_WIN32(GetLastError());
  1250. }
  1251. }
  1252. else
  1253. {
  1254. EcbTrace( "CEcb::ScAsyncRead() - ScSetIOCompleteCallback() failed with error 0x%08lX\n", sc );
  1255. }
  1256. if (FAILED(sc))
  1257. {
  1258. LONG cRefAsyncIO;
  1259. //
  1260. // Release the instance ref we added above.
  1261. //
  1262. ReleaseImplInst();
  1263. //
  1264. // Decrement the async I/O refcount added above.
  1265. //
  1266. cRefAsyncIO = InterlockedDecrement(&m_cRefAsyncIO);
  1267. AssertSz( 0 == cRefAsyncIO,
  1268. "CEcb::ScAsyncRead() - m_cRefAsyncIO wrong after failed async read" );
  1269. goto ret;
  1270. }
  1271. }
  1272. ret:
  1273. return sc;
  1274. }
  1275. BOOL CEcb::FSyncTransmitHeaders( const HSE_SEND_HEADER_EX_INFO& shei )
  1276. {
  1277. //
  1278. // At this point someone should have generated a response.
  1279. //
  1280. Assert( m_pecb->dwHttpStatusCode != 0 );
  1281. //
  1282. // Try to disable the async error response mechanism. If we succeed, then we
  1283. // can send a response. If we fail then we must not send a response -- the
  1284. // async error response mechanism already sent one.
  1285. //
  1286. if ( !m_aeri.FDisable() )
  1287. {
  1288. DebugTrace( "CEcb::FSyncTransmitHeaders() - Async error response already in progress\n" );
  1289. // Do not forget to set the error, as callers will be confused if the function
  1290. // returns FALSE, but GetLastError() will return S_OK.
  1291. //
  1292. SetLastError(static_cast<ULONG>(E_FAIL));
  1293. return FALSE;
  1294. }
  1295. //
  1296. // Send the response
  1297. //
  1298. if ( !m_pecb->ServerSupportFunction( m_pecb->ConnID,
  1299. HSE_REQ_SEND_RESPONSE_HEADER_EX,
  1300. const_cast<HSE_SEND_HEADER_EX_INFO *>(&shei),
  1301. NULL,
  1302. NULL ) )
  1303. {
  1304. DebugTrace( "CEcb::FSyncTransmitHeaders() - SSF::HSE_REQ_SEND_RESPONSE_HEADER_EX failed (%d)\n", GetLastError() );
  1305. return FALSE;
  1306. }
  1307. return TRUE;
  1308. }
  1309. SCODE CEcb::ScAsyncWrite( BYTE * pbBuf,
  1310. DWORD dwcbBuf,
  1311. IIISAsyncIOCompleteObserver& obs )
  1312. {
  1313. SCODE sc = S_OK;
  1314. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncWrite() called...\n", GetCurrentThreadId(), this );
  1315. //
  1316. // At this point someone should have generated a response.
  1317. //
  1318. Assert( m_pecb->dwHttpStatusCode != 0 );
  1319. //
  1320. // Try to disable the async error response mechanism. If we succeed, then we
  1321. // can send a response. If we fail then we must not send a response -- the
  1322. // async error response mechanism already sent one.
  1323. //
  1324. if ( !m_aeri.FDisable() )
  1325. {
  1326. EcbTrace( "CEcb::ScAsyncWrite() - Async error response already in progress. Failing out with 0x%08lX\n", E_FAIL );
  1327. // Do not forget to set the error, as callers will be confused if the function
  1328. // returns FALSE, but GetLastError() will return S_OK.
  1329. //
  1330. sc = E_FAIL;
  1331. goto ret;
  1332. }
  1333. //
  1334. // If there is another async IO outstanding we do not want to start one more. IIS will fail
  1335. // us out, and we ourselves will not be able to handle the completion of initial async IO
  1336. // properly. So just kill the connection and return. This may happen when we attempt to
  1337. // send the response before the read is finished.
  1338. //
  1339. if (0 != InterlockedCompareExchange(&m_cRefAsyncIO,
  1340. 1,
  1341. 0))
  1342. {
  1343. // The function bellow is not supported starting from IIS 6.0 but let us call it anyway
  1344. // just in case support becomes available - and we want to call it if the binary is
  1345. // running on IIS 5.0. It does not matter that much, as the bad side of not closing the
  1346. // connection may hang the client, or error out on subsequent request. That is ok as
  1347. // the path is supposed to be hit in abnormal/error conditions when clients for example
  1348. // send in invalid requests trying to cause denial of service or similar things.
  1349. // So on IIS 6.0 the connection will not be closed, we will just error out. We have
  1350. // not seen this path hit on IIS 6.0 anyway when runing denial of service scripts as it
  1351. // handles custom errors differently.
  1352. //
  1353. if (m_pecb->ServerSupportFunction(m_pecb->ConnID,
  1354. HSE_REQ_CLOSE_CONNECTION,
  1355. NULL,
  1356. NULL,
  1357. NULL))
  1358. {
  1359. EcbTrace( "CEcb::ScAsyncWrite() - More than 1 async operation. Connection closed. Failing out with error 0x%08lX\n", E_ABORT );
  1360. sc = E_ABORT;
  1361. goto ret;
  1362. }
  1363. else
  1364. {
  1365. EcbTrace( "CEcb::ScAsyncWrite() - More than 1 async operation. ServerSupportFunction(HSE_REQ_CLOSE_CONNECTION) "
  1366. "failed with last error 0x%08lX. Overriding with fatal error 0x%08lX\n", GetLastError(), E_FAIL );
  1367. sc = E_FAIL;
  1368. goto ret;
  1369. }
  1370. }
  1371. //
  1372. // IIS allows only one async I/O operation at a time. But for performance reasons it
  1373. // leaves it up to the ISAPI to heed the restriction. For the same reasons, we push
  1374. // that responsibility off to the DAV impl. A simple refcount tells us whether
  1375. // the impl has done so.
  1376. //
  1377. AssertSz( 1 == m_cRefAsyncIO,
  1378. "CEcb::ScAsyncWrite() - m_cRefAsyncIO wrong on entry" );
  1379. //
  1380. // We need to hold a ref on the process-wide instance data for the duration of the I/O
  1381. // so that if IIS tells us to shut down while the I/O is still pending we will keep
  1382. // the instance data alive until we're done with the I/O.
  1383. //
  1384. AddRefImplInst();
  1385. //
  1386. // Set the async I/O completion observer
  1387. //
  1388. m_pobsAsyncIOComplete = &obs;
  1389. //
  1390. // Set up the async I/O completion routine and start writing.
  1391. // Add a ref for the I/O completion thread. Use auto_ref_ptr
  1392. // to make things exception-proof.
  1393. //
  1394. {
  1395. auto_ref_ptr<CEcb> pRef(this);
  1396. sc = ScSetIOCompleteCallback(IO_COMPLETION);
  1397. if (SUCCEEDED(sc))
  1398. {
  1399. if (m_pecb->WriteClient( m_pecb->ConnID, pbBuf, &dwcbBuf, HSE_IO_ASYNC ))
  1400. {
  1401. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncWrite() I/O pending...\n", GetCurrentThreadId(), this );
  1402. pRef.relinquish();
  1403. }
  1404. else
  1405. {
  1406. EcbTrace( "CEcb::ScAsyncWrite() - _EXTENSION_CONTROL_BLOCK::WriteClient() failed with last error 0x%08lX\n", GetLastError() );
  1407. sc = HRESULT_FROM_WIN32(GetLastError());
  1408. }
  1409. }
  1410. else
  1411. {
  1412. EcbTrace( "CEcb::ScAsyncWrite() - ScSetIOCompleteCallback() failed with error 0x%08lX\n", sc );
  1413. }
  1414. if (FAILED(sc))
  1415. {
  1416. LONG cRefAsyncIO;
  1417. //
  1418. // Release the instance ref we added above.
  1419. //
  1420. ReleaseImplInst();
  1421. //
  1422. // Decrement the async I/O refcount added above.
  1423. //
  1424. cRefAsyncIO = InterlockedDecrement(&m_cRefAsyncIO);
  1425. AssertSz( 0 == cRefAsyncIO,
  1426. "CEcb::ScAsyncWrite() - m_cRefAsyncIO wrong after failed async write" );
  1427. goto ret;
  1428. }
  1429. }
  1430. ret:
  1431. return sc;
  1432. }
  1433. SCODE CEcb::ScAsyncTransmitFile( const HSE_TF_INFO& tfi,
  1434. IIISAsyncIOCompleteObserver& obs )
  1435. {
  1436. SCODE sc = S_OK;
  1437. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncTransmitFile() called...\n", GetCurrentThreadId(), this );
  1438. //
  1439. // At this point someone should have generated a response.
  1440. //
  1441. Assert( m_pecb->dwHttpStatusCode != 0 );
  1442. //
  1443. // Try to disable the async error response mechanism. If we succeed, then we
  1444. // can send a response. If we fail then we must not send a response -- the
  1445. // async error response mechanism already sent one.
  1446. //
  1447. if ( !m_aeri.FDisable() )
  1448. {
  1449. EcbTrace( "CEcb::ScAsyncTransmitFile() - Async error response already in progress. Failing out with 0x%08lX\n", E_FAIL );
  1450. // Do not forget to set the error, as callers will be confused if the function
  1451. // returns FALSE, but GetLastError() will return S_OK.
  1452. //
  1453. sc = E_FAIL;
  1454. goto ret;
  1455. }
  1456. //
  1457. // If there is another async IO outstanding we do not want to start one more. IIS will fail
  1458. // us out, and we ourselves will not be able to handle the completion of initial async IO
  1459. // properly. So just kill the connection and return. This may happen when we attempt to
  1460. // send the response before the read is finished.
  1461. //
  1462. if (0 != InterlockedCompareExchange(&m_cRefAsyncIO,
  1463. 1,
  1464. 0))
  1465. {
  1466. // The function bellow is not supported starting from IIS 6.0 but let us call it anyway
  1467. // just in case support becomes available - and we want to call it if the binary is
  1468. // running on IIS 5.0. It does not matter that much, as the bad side of not closing the
  1469. // connection may hang the client, or error out on subsequent request. That is ok as
  1470. // the path is supposed to be hit in abnormal/error conditions when clients for example
  1471. // send in invalid requests trying to cause denial of service or similar things.
  1472. // So on IIS 6.0 the connection will not be closed, we will just error out. We have
  1473. // not seen this path hit on IIS 6.0 anyway when runing denial of service scripts as it
  1474. // handles custom errors differently.
  1475. //
  1476. if (m_pecb->ServerSupportFunction(m_pecb->ConnID,
  1477. HSE_REQ_CLOSE_CONNECTION,
  1478. NULL,
  1479. NULL,
  1480. NULL))
  1481. {
  1482. EcbTrace( "CEcb::ScAsyncTransmitFile() - More than 1 async operation. Connection closed. Failing out with error 0x%08lX\n", E_ABORT );
  1483. sc = E_ABORT;
  1484. goto ret;
  1485. }
  1486. else
  1487. {
  1488. EcbTrace( "CEcb::ScAsyncTransmitFile() - More than 1 async operation. ServerSupportFunction(HSE_REQ_CLOSE_CONNECTION) "
  1489. "failed with last error 0x%08lX. Overriding with fatal error 0x%08lX\n", GetLastError(), E_FAIL );
  1490. sc = E_FAIL;
  1491. goto ret;
  1492. }
  1493. }
  1494. //
  1495. // IIS allows only one async I/O operation at a time. But for performance reasons it
  1496. // leaves it up to the ISAPI to heed the restriction. For the same reasons, we push
  1497. // that responsibility off to the DAV impl. A simple refcount tells us whether
  1498. // the impl has done so.
  1499. //
  1500. AssertSz( 1 == m_cRefAsyncIO,
  1501. "CEcb::ScAsyncTransmitFile() - m_cRefAsyncIO wrong on entry" );
  1502. //
  1503. // We need to hold a ref on the process-wide instance data for the duration of the I/O
  1504. // so that if IIS tells us to shut down while the I/O is still pending we will keep
  1505. // the instance data alive until we're done with the I/O.
  1506. //
  1507. AddRefImplInst();
  1508. //
  1509. // Async I/O completion routine and context should have been passed
  1510. // in as parameters. Callers should NOT use the corresponding members
  1511. // of the HSE_TF_INFO structure. IIS has to call CEcb::IISIOComplete()
  1512. // so that it can release the critsec.
  1513. //
  1514. Assert( !tfi.pfnHseIO );
  1515. Assert( !tfi.pContext );
  1516. //
  1517. // Verify that the caller has set the async I/O flag
  1518. //
  1519. Assert( tfi.dwFlags & HSE_IO_ASYNC );
  1520. //
  1521. // Set the async I/O completion observer
  1522. //
  1523. m_pobsAsyncIOComplete = &obs;
  1524. //
  1525. // Set up the async I/O completion routine and start transmitting.
  1526. // Add a ref for the I/O completion thread. Use auto_ref_ptr
  1527. // to make things exception-proof.
  1528. //
  1529. {
  1530. auto_ref_ptr<CEcb> pRef(this);
  1531. sc = ScSetIOCompleteCallback(IO_COMPLETION);
  1532. if (SUCCEEDED(sc))
  1533. {
  1534. if (m_pecb->ServerSupportFunction( m_pecb->ConnID,
  1535. HSE_REQ_TRANSMIT_FILE,
  1536. const_cast<HSE_TF_INFO *>(&tfi),
  1537. NULL,
  1538. NULL ))
  1539. {
  1540. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncTransmitFile() I/O pending...\n", GetCurrentThreadId(), this );
  1541. pRef.relinquish();
  1542. }
  1543. else
  1544. {
  1545. EcbTrace( "CEcb::ScAsyncTransmitFile() - ServerSupportFunction(HSE_REQ_TRANSMIT_FILE) failed with last error 0x%08lX\n", GetLastError() );
  1546. sc = HRESULT_FROM_WIN32(GetLastError());
  1547. }
  1548. }
  1549. else
  1550. {
  1551. EcbTrace( "CEcb::ScAsyncTransmitFile() - ScSetIOCompleteCallback() failed with error 0x%08lX\n", sc );
  1552. }
  1553. if (FAILED(sc))
  1554. {
  1555. LONG cRefAsyncIO;
  1556. //
  1557. // Release the instance ref we added above.
  1558. //
  1559. ReleaseImplInst();
  1560. //
  1561. // Decrement the async I/O refcount added above.
  1562. //
  1563. cRefAsyncIO = InterlockedDecrement(&m_cRefAsyncIO);
  1564. AssertSz( 0 == cRefAsyncIO,
  1565. "CEcb::ScAsyncTransmitFile() - m_cRefAsyncIO wrong after failed async transmit file" );
  1566. goto ret;
  1567. }
  1568. }
  1569. ret:
  1570. return sc;
  1571. }
  1572. // Other functions that start async IO with IIS. These functions are for IIS 6.0 or later
  1573. // only. We will not even use observers on them, as the completion esentially will serve
  1574. // just to signal some cleanup on a single string, which does not make much sense to wrap
  1575. // it up as the observer.
  1576. //
  1577. SCODE CEcb::ScAsyncCustomError60After( const HSE_CUSTOM_ERROR_INFO& cei,
  1578. LPSTR pszStatus )
  1579. {
  1580. SCODE sc = S_OK;
  1581. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncCustomError60After() called...\n", GetCurrentThreadId(), this );
  1582. //
  1583. // At this point someone should have generated a response.
  1584. //
  1585. Assert( m_pecb->dwHttpStatusCode != 0 );
  1586. //
  1587. // Try to disable the async error response mechanism. If we succeed, then we
  1588. // can send a response. If we fail then we must not send a response -- the
  1589. // async error response mechanism already sent one.
  1590. //
  1591. if ( !m_aeri.FDisable() )
  1592. {
  1593. EcbTrace( "CEcb::ScAsyncCustomError60After() - Async error response already in progress. Failing out with 0x%08lX\n", E_FAIL );
  1594. // Do not forget to set the error, as callers will be confused if the function
  1595. // returns FALSE, but GetLastError() will return S_OK.
  1596. //
  1597. sc = E_FAIL;
  1598. goto ret;
  1599. }
  1600. //
  1601. // If there is another async IO outstanding we do not want to start one more. IIS will fail
  1602. // us out, and we ourselves will not be able to handle the completion of initial async IO
  1603. // properly. So just kill the connection and return. This may happen when we attempt to
  1604. // send the response before the read is finished.
  1605. //
  1606. if (0 != InterlockedCompareExchange(&m_cRefAsyncIO,
  1607. 1,
  1608. 0))
  1609. {
  1610. // The function bellow is not supported starting from IIS 6.0 but let us call it anyway
  1611. // just in case support becomes available - and we want to call it if the binary is
  1612. // running on IIS 5.0. It does not matter that much, as the bad side of not closing the
  1613. // connection may hang the client, or error out on subsequent request. That is ok as
  1614. // the path is supposed to be hit in abnormal/error conditions when clients for example
  1615. // send in invalid requests trying to cause denial of service or similar things.
  1616. // So on IIS 6.0 the connection will not be closed, we will just error out. We have
  1617. // not seen this path hit on IIS 6.0 anyway when runing denial of service scripts as it
  1618. // handles custom errors differently.
  1619. //
  1620. if (m_pecb->ServerSupportFunction(m_pecb->ConnID,
  1621. HSE_REQ_CLOSE_CONNECTION,
  1622. NULL,
  1623. NULL,
  1624. NULL))
  1625. {
  1626. EcbTrace( "CEcb::ScAsyncCustomError60After() - More than 1 async operation. Connection closed. Failing out with error 0x%08lX\n", E_ABORT );
  1627. sc = E_ABORT;
  1628. goto ret;
  1629. }
  1630. else
  1631. {
  1632. EcbTrace( "CEcb::ScAsyncCustomError60After() - More than 1 async operation. ServerSupportFunction(HSE_REQ_CLOSE_CONNECTION) "
  1633. "failed with last error 0x%08lX. Overriding with fatal error 0x%08lX\n", GetLastError(), E_FAIL );
  1634. sc = E_FAIL;
  1635. goto ret;
  1636. }
  1637. }
  1638. //
  1639. // IIS allows only one async I/O operation at a time. But for performance reasons it
  1640. // leaves it up to the ISAPI to heed the restriction. For the same reasons, we push
  1641. // that responsibility off to the DAV impl. A simple refcount tells us whether
  1642. // the impl has done so.
  1643. //
  1644. AssertSz( 1 == m_cRefAsyncIO,
  1645. "CEcb::ScAsyncCustomError60After() - m_cRefAsyncIO wrong on entry" );
  1646. //
  1647. // We need to hold a ref on the process-wide instance data for the duration of the I/O
  1648. // so that if IIS tells us to shut down while the I/O is still pending we will keep
  1649. // the instance data alive until we're done with the I/O.
  1650. //
  1651. AddRefImplInst();
  1652. //
  1653. // Verify that the caller has set the async I/O flag
  1654. //
  1655. Assert( TRUE == cei.fAsync );
  1656. //
  1657. // Set up the async I/O completion routine and start transmitting.
  1658. // Add a ref for the I/O completion thread. Use auto_ref_ptr
  1659. // to make things exception-proof.
  1660. //
  1661. {
  1662. auto_ref_ptr<CEcb> pRef(this);
  1663. sc = ScSetIOCompleteCallback(CUSTERR_COMPLETION);
  1664. if (SUCCEEDED(sc))
  1665. {
  1666. if (m_pecb->ServerSupportFunction( m_pecb->ConnID,
  1667. HSE_REQ_SEND_CUSTOM_ERROR,
  1668. const_cast<HSE_CUSTOM_ERROR_INFO *>(&cei),
  1669. NULL,
  1670. NULL ))
  1671. {
  1672. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncCustomError60After() I/O pending...\n", GetCurrentThreadId(), this );
  1673. pRef.relinquish();
  1674. }
  1675. else
  1676. {
  1677. EcbTrace( "CEcb::ScAsyncCustomError60After() - ServerSupportFunction(HSE_REQ_SEND_CUSTOM_ERROR) failed with last error 0x%08lX\n", GetLastError() );
  1678. sc = HRESULT_FROM_WIN32(GetLastError());
  1679. }
  1680. }
  1681. else
  1682. {
  1683. EcbTrace( "CEcb::ScAsyncCustomError60After() - ScSetIOCompleteCallback() failed with error 0x%08lX\n", sc );
  1684. }
  1685. if (FAILED(sc))
  1686. {
  1687. LONG cRefAsyncIO;
  1688. //
  1689. // Release the instance ref we added above.
  1690. //
  1691. ReleaseImplInst();
  1692. //
  1693. // Decrement the async I/O refcount added above.
  1694. //
  1695. cRefAsyncIO = InterlockedDecrement(&m_cRefAsyncIO);
  1696. AssertSz( 0 == cRefAsyncIO,
  1697. "CEcb::ScAsyncCustomError60After() - m_cRefAsyncIO wrong after failed async custom error" );
  1698. goto ret;
  1699. }
  1700. }
  1701. // We need to take ownership of the status string that was passed in in the case of success
  1702. // From looking in the code in IIS it does not mater if we keep it alive until completion,
  1703. // as it is anyway realocated before going onto another thread. But just in case something
  1704. // changes and to be doing what IIS people asked us to do we will keep it alive. String has
  1705. // the format of "nnn reason".
  1706. //
  1707. m_pszStatus.take_ownership(pszStatus);
  1708. ret:
  1709. return sc;
  1710. }
  1711. SCODE CEcb::ScAsyncExecUrl60After( const HSE_EXEC_URL_INFO& eui )
  1712. {
  1713. SCODE sc = S_OK;
  1714. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncExecUrl60After() called...\n", GetCurrentThreadId(), this );
  1715. //
  1716. // At this point someone should have generated a response.
  1717. //
  1718. Assert( m_pecb->dwHttpStatusCode != 0 );
  1719. //
  1720. // Try to disable the async error response mechanism. If we succeed, then we
  1721. // can send a response. If we fail then we must not send a response -- the
  1722. // async error response mechanism already sent one.
  1723. //
  1724. if ( !m_aeri.FDisable() )
  1725. {
  1726. EcbTrace( "CEcb::ScAsyncExecUrl60After() - Async error response already in progress. Failing out with 0x%08lX\n", E_FAIL );
  1727. sc = E_FAIL;
  1728. goto ret;
  1729. }
  1730. //
  1731. // If there is another async IO outstanding we do not want to start one more. IIS will fail
  1732. // us out, and we ourselves will not be able to handle the completion of initial async IO
  1733. // properly. So just kill the connection and return. This may happen when we attempt to
  1734. // send the response before the read is finished.
  1735. //
  1736. if (0 != InterlockedCompareExchange(&m_cRefAsyncIO,
  1737. 1,
  1738. 0))
  1739. {
  1740. // The function bellow is not supported starting from IIS 6.0 but let us call it anyway
  1741. // just in case support becomes available - and we want to call it if the binary is
  1742. // running on IIS 5.0. It does not matter that much, as the bad side of not closing the
  1743. // connection may hang the client, or error out on subsequent request. That is ok as
  1744. // the path is supposed to be hit in abnormal/error conditions when clients for example
  1745. // send in invalid requests trying to cause denial of service or similar things.
  1746. // So on IIS 6.0 the connection will not be closed, we will just error out. We have
  1747. // not seen this path hit on IIS 6.0 anyway when runing denial of service scripts as it
  1748. // handles custom errors differently.
  1749. //
  1750. if (m_pecb->ServerSupportFunction(m_pecb->ConnID,
  1751. HSE_REQ_CLOSE_CONNECTION,
  1752. NULL,
  1753. NULL,
  1754. NULL))
  1755. {
  1756. EcbTrace( "CEcb::ScAsyncExecUrl60After() - More than 1 async operation. Connection closed. Failing out with error 0x%08lX\n", E_ABORT );
  1757. sc = E_ABORT;
  1758. goto ret;
  1759. }
  1760. else
  1761. {
  1762. EcbTrace( "CEcb::ScAsyncExecUrl60After() - More than 1 async operation. ServerSupportFunction(HSE_REQ_CLOSE_CONNECTION) "
  1763. "failed with last error 0x%08lX. Overriding with fatal error 0x%08lX\n", GetLastError(), E_FAIL );
  1764. sc = E_FAIL;
  1765. goto ret;
  1766. }
  1767. }
  1768. //
  1769. // IIS allows only one async I/O operation at a time. But for performance reasons it
  1770. // leaves it up to the ISAPI to heed the restriction. For the same reasons, we push
  1771. // that responsibility off to the DAV impl. A simple refcount tells us whether
  1772. // the impl has done so.
  1773. //
  1774. AssertSz( 1 == m_cRefAsyncIO,
  1775. "CEcb::ScAsyncExecUrl60After() - m_cRefAsyncIO wrong on entry" );
  1776. //
  1777. // We need to hold a ref on the process-wide instance data for the duration of the I/O
  1778. // so that if IIS tells us to shut down while the I/O is still pending we will keep
  1779. // the instance data alive until we're done with the I/O.
  1780. //
  1781. AddRefImplInst();
  1782. //
  1783. // Set up the async I/O completion routine and start transmitting.
  1784. // Add a ref for the I/O completion thread. Use auto_ref_ptr
  1785. // to make things exception-proof.
  1786. //
  1787. {
  1788. auto_ref_ptr<CEcb> pRef(this);
  1789. sc = ScSetIOCompleteCallback(EXECURL_COMPLETION);
  1790. if (SUCCEEDED(sc))
  1791. {
  1792. if (m_pecb->ServerSupportFunction( m_pecb->ConnID,
  1793. HSE_REQ_EXEC_URL,
  1794. const_cast<HSE_EXEC_URL_INFO *>(&eui),
  1795. NULL,
  1796. NULL ))
  1797. {
  1798. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::ScAsyncExecUrl60After() I/O pending...\n", GetCurrentThreadId(), this );
  1799. pRef.relinquish();
  1800. }
  1801. else
  1802. {
  1803. EcbTrace( "CEcb::ScAsyncExecUrl60After() - ServerSupportFunction(HSE_REQ_EXEC_URL) failed with last error 0x%08lX\n", GetLastError() );
  1804. sc = HRESULT_FROM_WIN32(GetLastError());
  1805. }
  1806. }
  1807. else
  1808. {
  1809. EcbTrace( "CEcb::ScAsyncExecUrl60After() - ScSetIOCompleteCallback() failed with error 0x%08lX\n", sc );
  1810. }
  1811. if (FAILED(sc))
  1812. {
  1813. LONG cRefAsyncIO;
  1814. //
  1815. // Release the instance ref we added above.
  1816. //
  1817. ReleaseImplInst();
  1818. //
  1819. // Decrement the async I/O refcount added above.
  1820. //
  1821. cRefAsyncIO = InterlockedDecrement(&m_cRefAsyncIO);
  1822. AssertSz( 0 == cRefAsyncIO,
  1823. "CEcb::ScAsyncExecUrl60After() - m_cRefAsyncIO wrong after failed async exec url" );
  1824. goto ret;
  1825. }
  1826. }
  1827. ret:
  1828. return sc;
  1829. }
  1830. SCODE CEcb::ScSetIOCompleteCallback( LONG lCompletion )
  1831. {
  1832. SCODE sc = S_OK;
  1833. //
  1834. // Do not reset the completion function if it is already set to the
  1835. // same one we want to set. There is no need to protect the member
  1836. // variable against multithreaded access as the callers of this
  1837. // function are already protecting against the overlap of 2 async
  1838. // IO-s and this function is the only one changing variable value
  1839. // and is always called within protected zone.
  1840. //
  1841. if ( lCompletion != m_lSetIISIOCompleteCallback )
  1842. {
  1843. //
  1844. // Figure out what completion function we need
  1845. //
  1846. PFN_HSE_IO_COMPLETION pfnCallback;
  1847. if (IO_COMPLETION == lCompletion)
  1848. {
  1849. pfnCallback = reinterpret_cast<PFN_HSE_IO_COMPLETION>(IISIOComplete);
  1850. }
  1851. else if (CUSTERR_COMPLETION == lCompletion)
  1852. {
  1853. pfnCallback = reinterpret_cast<PFN_HSE_IO_COMPLETION>(CustomErrorIOCompletion);
  1854. }
  1855. else if (EXECURL_COMPLETION == lCompletion)
  1856. {
  1857. pfnCallback = reinterpret_cast<PFN_HSE_IO_COMPLETION>(ExecuteUrlIOCompletion);
  1858. }
  1859. else
  1860. {
  1861. EcbTrace( "CEcb::ScSetIOCompleteCallback() - attempting to set unknown completion function. Failing out with 0x%08lX\n", E_FAIL );
  1862. sc = E_FAIL;
  1863. goto ret;
  1864. }
  1865. // Set the IIS I/O completion routine to the requested one. Some of those
  1866. // routines will simply handle the completion, others will forward to the
  1867. // right observer.
  1868. //
  1869. if (!m_pecb->ServerSupportFunction(m_pecb->ConnID,
  1870. HSE_REQ_IO_COMPLETION,
  1871. pfnCallback,
  1872. NULL,
  1873. reinterpret_cast<LPDWORD>(this)))
  1874. {
  1875. EcbTrace( "CEcb::ScSetIOCompleteCallback() - ServerSupportFunction(HSE_REQ_IO_COMPLETION) failed with last error 0x%08lX\n", GetLastError() );
  1876. sc = HRESULT_FROM_WIN32(GetLastError());
  1877. goto ret;
  1878. }
  1879. m_lSetIISIOCompleteCallback = lCompletion;
  1880. }
  1881. ret:
  1882. return sc;
  1883. }
  1884. VOID WINAPI
  1885. CEcb::IISIOComplete( const EXTENSION_CONTROL_BLOCK * pecbIIS,
  1886. CEcb * pecb,
  1887. DWORD dwcbIO,
  1888. DWORD dwLastError )
  1889. {
  1890. BOOL fCaughtException = FALSE;
  1891. // PLEASE SEE *** EXTREMELY IMPORTANT NOTE *** near the bottom of this
  1892. // function for more information on the proper way to unwind (deinit())
  1893. // this auto_ref_ptr!
  1894. //
  1895. auto_ref_ptr<CEcb> pThis;
  1896. //
  1897. // Don't let thrown C++ exceptions propagate out of this entrypoint.
  1898. //
  1899. try
  1900. {
  1901. //
  1902. // Translate async Win32 exceptions into thrown C++ exceptions.
  1903. // This must be placed inside the try block!
  1904. //
  1905. CWin32ExceptionHandler win32ExceptionHandler;
  1906. LONG cRefAsyncIO;
  1907. //
  1908. // Take ownership of the reference added
  1909. // on our behalf by the thread that started the async I/O.
  1910. //
  1911. pThis.take_ownership(pecb);
  1912. EcbTrace( "DAV: TID %3d: 0x%08lX: CEcb::IISIOComplete() called\n", GetCurrentThreadId(), pecb );
  1913. //
  1914. // A quick sanity check to make sure the context
  1915. // is really us...
  1916. //
  1917. Assert( !IsBadReadPtr( pecb, sizeof(CEcb) ) );
  1918. Assert( pecb->m_pecb == pecbIIS );
  1919. IIISAsyncIOCompleteObserver * pobsAsyncIOComplete = pThis->m_pobsAsyncIOComplete;
  1920. //
  1921. // Decrement the async I/O refcount added by the routine that
  1922. // started the async I/O. Do this before calling the I/O
  1923. // completion routine which can start new async I/O.
  1924. //
  1925. cRefAsyncIO = InterlockedDecrement(&pThis->m_cRefAsyncIO);
  1926. AssertSz( 0 == cRefAsyncIO,
  1927. "CEcb::IISIOComplete() - m_cRefAsyncIO wrong after async I/O complete" );
  1928. // Tell the observer that the I/O is complete
  1929. //
  1930. pobsAsyncIOComplete->IISIOComplete( dwcbIO, dwLastError );
  1931. }
  1932. catch ( CDAVException& )
  1933. {
  1934. fCaughtException = TRUE;
  1935. }
  1936. //
  1937. // If we caught an exception then handle it as best we can
  1938. //
  1939. if ( fCaughtException )
  1940. {
  1941. //
  1942. // If we have a CEcb then use it to handle the exception.
  1943. // If we don't have one then there's nothing we can do --
  1944. // there is no way to return any status from this function.
  1945. //
  1946. if ( pThis.get() )
  1947. (VOID) pThis->HSEHandleException();
  1948. }
  1949. //
  1950. // Release the instance ref added by the routine that started the async I/O.
  1951. // We must do this as the VERY LAST THING(tm) before returning control back
  1952. // to IIS because during shutdown, this could be the last reference to the
  1953. // instance data.
  1954. //
  1955. // EXTREMELY IMPORTANT NOTE: If this is the last reference on the instance
  1956. // data, everything will get torn down (we're finished with everything, so
  1957. // we can clean up everything). Specifically, our HEAPS will be DESTROYED
  1958. // here in this situation. Hence, we need to clear out the auto_ref_ptr
  1959. // from above ********** BEFORE ********** we call ReleaseImplInst().
  1960. // Otherwise, we could end up trying to touch the reference count on the
  1961. // CEcb object pointed to by the auto_ref_ptr AFTER we have destroyed the
  1962. // heap it was allocated on. This is A BAD THING(tm).
  1963. //
  1964. // This bug was found in IIS stress on 18 June 1999, and was filed as NTRAID
  1965. // bug #358578.
  1966. //
  1967. // Per "EXTREMELY IMPORTANT NOTE" above: CLEAR the auto_ref_ptr
  1968. // ********** BEFORE ********** calling ReleaseImplInst().
  1969. //
  1970. pThis.clear();
  1971. // Now it is safe to call ReleaseImplInst().
  1972. //
  1973. ReleaseImplInst();
  1974. }
  1975. VOID WINAPI
  1976. CEcb::CustomErrorIOCompletion ( const EXTENSION_CONTROL_BLOCK * pecbIIS,
  1977. CEcb * pecb,
  1978. DWORD dwcbIO,
  1979. DWORD dwLastError )
  1980. {
  1981. auto_ref_ptr<CEcb> pThis;
  1982. LONG cRefAsyncIO;
  1983. //
  1984. // Take ownership of the reference added
  1985. // on our behalf by the thread that started the async I/O.
  1986. //
  1987. pThis.take_ownership(pecb);
  1988. //
  1989. // Decrement the async I/O refcount added by the routine that
  1990. // started the async I/O.
  1991. //
  1992. cRefAsyncIO = InterlockedDecrement(&pThis->m_cRefAsyncIO);
  1993. AssertSz( 0 == cRefAsyncIO,
  1994. "CEcb::CustomErrorIOCompletion() - m_cRefAsyncIO wrong after async I/O complete" );
  1995. EcbTrace( "Custom Error finished with dwcbIO = %d, error = %d\n", dwcbIO, dwLastError);
  1996. EcbTrace( "More info about the request:\n");
  1997. EcbTrace( "\tcbSize = %lu\n", pecbIIS->cbSize );
  1998. EcbTrace( "\tdwVersion = %lu\n", pecbIIS->dwVersion );
  1999. EcbTrace( "\tlpszMethod = \"%s\"\n", pecbIIS->lpszMethod );
  2000. EcbTrace( "\tlpszQueryString = \"%s\"\n", pecbIIS->lpszQueryString );
  2001. EcbTrace( "\tlpszPathInfo = \"%s\"\n", pecbIIS->lpszPathInfo );
  2002. EcbTrace( "\tlpszPathTranslated = \"%s\"\n", pecbIIS->lpszPathTranslated );
  2003. EcbTrace( "\tcbTotalBytes = %lu\n", pecbIIS->cbTotalBytes );
  2004. EcbTrace( "\tcbAvailable = %lu\n", pecbIIS->cbAvailable );
  2005. EcbTrace( "\tlpszContentType = \"%s\"\n", pecbIIS->lpszContentType );
  2006. EcbTrace( "\n" );
  2007. // We need to make sure that last release of memory is finished before we release
  2008. // ref on CImplInst (as when CImplInst goes away so does our heap and we do not
  2009. // want to do operations on memory if the heap itself is gone).
  2010. //
  2011. pThis.clear();
  2012. ReleaseImplInst();
  2013. }
  2014. VOID WINAPI
  2015. CEcb::ExecuteUrlIOCompletion( const EXTENSION_CONTROL_BLOCK * pecbIIS,
  2016. CEcb * pecb,
  2017. DWORD dwcbIO,
  2018. DWORD dwLastError )
  2019. {
  2020. auto_ref_ptr<CEcb> pThis;
  2021. LONG cRefAsyncIO;
  2022. //
  2023. // Take ownership of the reference added
  2024. // on our behalf by the thread that started the async I/O.
  2025. //
  2026. pThis.take_ownership(pecb);
  2027. //
  2028. // Decrement the async I/O refcount added by the routine that
  2029. // started the async I/O.
  2030. //
  2031. cRefAsyncIO = InterlockedDecrement(&pThis->m_cRefAsyncIO);
  2032. AssertSz( 0 == cRefAsyncIO,
  2033. "CEcb::CustomErrorIOCompletion() - m_cRefAsyncIO wrong after async I/O complete" );
  2034. EcbTrace( "Exec_URL finished with dwcbIO = %d, error = %d\n", dwcbIO, dwLastError);
  2035. EcbTrace( "More info about the request:\n");
  2036. EcbTrace( "\tcbSize = %lu\n", pecbIIS->cbSize );
  2037. EcbTrace( "\tdwVersion = %lu\n", pecbIIS->dwVersion );
  2038. EcbTrace( "\tlpszMethod = \"%s\"\n", pecbIIS->lpszMethod );
  2039. EcbTrace( "\tlpszQueryString = \"%s\"\n", pecbIIS->lpszQueryString );
  2040. EcbTrace( "\tlpszPathInfo = \"%s\"\n", pecbIIS->lpszPathInfo );
  2041. EcbTrace( "\tlpszPathTranslated = \"%s\"\n", pecbIIS->lpszPathTranslated );
  2042. EcbTrace( "\tcbTotalBytes = %lu\n", pecbIIS->cbTotalBytes );
  2043. EcbTrace( "\tcbAvailable = %lu\n", pecbIIS->cbAvailable );
  2044. EcbTrace( "\tlpszContentType = \"%s\"\n", pecbIIS->lpszContentType );
  2045. EcbTrace( "\n" );
  2046. // We need to make sure that last release of memory is finished before we release
  2047. // ref on CImplInst (as when CImplInst goes away so does our heap and we do not
  2048. // want to do operations on memory if the heap itself is gone).
  2049. //
  2050. pThis.clear();
  2051. ReleaseImplInst();
  2052. }
  2053. // This is how we execute a child in any IIS version before IIS 6.0
  2054. //
  2055. SCODE CEcb::ScSyncExecuteChildWide60Before( LPCWSTR pwszUrl,
  2056. LPCSTR pszQueryString,
  2057. BOOL fCustomErrorUrl )
  2058. {
  2059. SCODE sc = S_OK;
  2060. auto_heap_ptr<CHAR> pszUrlEscaped;
  2061. CStackBuffer<CHAR, MAX_PATH> pszUrl;
  2062. DWORD dwExecFlags;
  2063. LPCSTR pszUrlToForward;
  2064. LPCSTR pszVerb = NULL;
  2065. UINT cch;
  2066. UINT cb;
  2067. UINT cbQueryString = 0;
  2068. Assert( m_pecb );
  2069. Assert( pwszUrl );
  2070. cch = static_cast<UINT>(wcslen(pwszUrl));
  2071. Assert(L'\0' == pwszUrl[cch]);
  2072. cb = cch * 3;
  2073. if (pszQueryString)
  2074. {
  2075. cbQueryString = static_cast<UINT>(strlen(pszQueryString));
  2076. }
  2077. // Resize the buffer to the sufficient size, leave place for '\0' termination.
  2078. // We also add the length of the query string there, although it is not necessary
  2079. // at this step - but in many cases it will save us the allocation afterwards,
  2080. // as we already will have sufficient buffer even for escaped version of the string.
  2081. //
  2082. if (!pszUrl.resize(cb + cbQueryString + 1))
  2083. {
  2084. sc = E_OUTOFMEMORY;
  2085. DebugTrace("CEcb::ScSyncExecuteChildWide60Before() - Error while allocating memory 0x%08lX\n", sc);
  2086. goto ret;
  2087. }
  2088. // Convert URL to skinny including '\0' termination.
  2089. //
  2090. cb = WideCharToMultiByte(CP_UTF8,
  2091. 0,
  2092. pwszUrl,
  2093. cch + 1,
  2094. pszUrl.get(),
  2095. cb + 1,
  2096. 0,
  2097. 0);
  2098. if (0 == cb)
  2099. {
  2100. sc = HRESULT_FROM_WIN32(GetLastError());
  2101. DebugTrace("CEcb::ScSyncExecuteChildWide60Before() - WideCharToMultiByte() failed 0x%08lX\n", sc);
  2102. goto ret;
  2103. }
  2104. // Escape the URL
  2105. //
  2106. HttpUriEscape( pszUrl.get(), pszUrlEscaped );
  2107. // Handle the query string
  2108. //
  2109. if (cbQueryString)
  2110. {
  2111. // Find out the length of new URL
  2112. //
  2113. cb = static_cast<UINT>(strlen(pszUrlEscaped.get()));
  2114. // Resize the buffer to the sufficient size, leave place for '\0' termination.
  2115. //
  2116. if (!pszUrl.resize(cb + cbQueryString + 1))
  2117. {
  2118. sc = E_OUTOFMEMORY;
  2119. DebugTrace("CEcb::ScSyncExecuteChildWide60Before() - Error while allocating memory 0x%08lX\n", sc);
  2120. goto ret;
  2121. }
  2122. // Copy the escaped version of the URL
  2123. //
  2124. memcpy(pszUrl.get(), pszUrlEscaped.get(), cb);
  2125. // Copy the query string at the end together with it's '\0' termination.
  2126. //
  2127. memcpy(pszUrl.get() + cb, pszQueryString, cbQueryString + 1);
  2128. // Point to the constructed URL
  2129. //
  2130. pszUrlToForward = pszUrl.get();
  2131. }
  2132. else
  2133. {
  2134. // In the case we do not have query string then the URL to forward to
  2135. // is the same as the escaped URL.
  2136. //
  2137. pszUrlToForward = pszUrlEscaped.get();
  2138. }
  2139. // Depending on the fact if we are doing custom error or executing script
  2140. // determine the execution flags and the verb.
  2141. //
  2142. if ( fCustomErrorUrl )
  2143. {
  2144. // We enable wildcard processing here, since we want
  2145. // to give one chance to all CE URLs.
  2146. // Calling into ourselves is fine here and we prevent
  2147. // recusrion using another scheme!
  2148. //
  2149. dwExecFlags = HSE_EXEC_CUSTOM_ERROR;
  2150. if (!strcmp(LpszMethod(), gc_szHEAD))
  2151. {
  2152. // If this is a HEAD request, tell whomever we
  2153. // forward this to to only pass back the status
  2154. // line and headers.
  2155. //
  2156. pszVerb = gc_szHEAD;
  2157. }
  2158. else
  2159. {
  2160. pszVerb = gc_szGET;
  2161. }
  2162. // If we're doing a custom error then someone has
  2163. // already set the status code.
  2164. //
  2165. Assert( m_pecb->dwHttpStatusCode != 0 );
  2166. }
  2167. else
  2168. {
  2169. // When we are executing scripts, we disable wild card
  2170. // execution to prevent recursion.
  2171. // Also the verb field is allowed to be NULL, it is optional and
  2172. // used only for custom error processing.
  2173. //
  2174. dwExecFlags = HSE_EXEC_NO_ISA_WILDCARDS;
  2175. pszVerb = NULL;
  2176. // We need to set the status code here to 200 (like it
  2177. // was set when we were first called) just in case
  2178. // child ISAPIs depend on it.
  2179. //
  2180. SetStatusCode(200);
  2181. }
  2182. if (!m_pecb->ServerSupportFunction( m_pecb->ConnID,
  2183. HSE_REQ_EXECUTE_CHILD,
  2184. const_cast<LPSTR>(pszUrlToForward),
  2185. reinterpret_cast<LPDWORD>(const_cast<LPSTR>(pszVerb)),
  2186. &dwExecFlags ))
  2187. {
  2188. // Reset the status code back to 0 if we failed to execute
  2189. // the child ISAPI because we will be handling the request
  2190. // ourselves later (probably by sending an error).
  2191. //
  2192. if (!fCustomErrorUrl)
  2193. {
  2194. SetStatusCode(0);
  2195. }
  2196. sc = HRESULT_FROM_WIN32(GetLastError());
  2197. DebugTrace("CEcb::ScSyncExecuteChildWide60Before() - ServerSupportFunction(HSE_REQ_EXECUTE_CHILD) failed with error 0x%08lX\n", sc);
  2198. goto ret;
  2199. }
  2200. // Set the flag stating that we have successfully executed a child ISAPI.
  2201. //
  2202. m_fChildISAPIExecSuccess = TRUE;
  2203. ret:
  2204. return sc;
  2205. }
  2206. SCODE CEcb::ScAsyncExecUrlWide60After( LPCWSTR pwszUrl,
  2207. LPCSTR pszQueryString,
  2208. BOOL fCustomErrorUrl )
  2209. {
  2210. SCODE sc = S_OK;
  2211. HSE_EXEC_URL_INFO execUrlInfo;
  2212. auto_heap_ptr<CHAR> pszUrlEscaped;
  2213. CStackBuffer<CHAR, MAX_PATH> pszUrl;
  2214. UINT cch;
  2215. UINT cb;
  2216. UINT cbQueryString = 0;
  2217. Assert( m_pecb );
  2218. Assert( pwszUrl );
  2219. Assert(!fCustomErrorUrl); // In IIS60 custom error is NOT done by execute URL
  2220. cch = static_cast<UINT>(wcslen(pwszUrl));
  2221. Assert(L'\0' == pwszUrl[cch]);
  2222. cb = cch * 3;
  2223. if (pszQueryString)
  2224. {
  2225. cbQueryString = static_cast<UINT>(strlen(pszQueryString));
  2226. }
  2227. // Resize the buffer to the sufficient size, leave place for '\0' termination.
  2228. // We also add the length of the query string there, although it is not necessary
  2229. // at this step - but in many cases it will save us the allocation afterwards,
  2230. // as we already will have sufficient buffer even for escaped version of the string.
  2231. //
  2232. if (!pszUrl.resize(cb + cbQueryString + 1))
  2233. {
  2234. sc = E_OUTOFMEMORY;
  2235. DebugTrace("CEcb::ScAsyncExecUrlWide60After() - Error while allocating memory 0x%08lX\n", sc);
  2236. goto ret;
  2237. }
  2238. // Convert to skinny including '\0' termination
  2239. //
  2240. cb = WideCharToMultiByte(CP_UTF8,
  2241. 0,
  2242. pwszUrl,
  2243. cch + 1,
  2244. pszUrl.get(),
  2245. cb + 1,
  2246. 0,
  2247. 0);
  2248. if (0 == cb)
  2249. {
  2250. sc = HRESULT_FROM_WIN32(GetLastError());
  2251. DebugTrace("CEcb::ScAsyncExecUrlWide60After() - WideCharToMultiByte() failed 0x%08lX\n", sc);
  2252. goto ret;
  2253. }
  2254. // Escape the URL
  2255. //
  2256. HttpUriEscape( pszUrl.get(), pszUrlEscaped );
  2257. // Handle the query string
  2258. //
  2259. if (cbQueryString)
  2260. {
  2261. // Find out the length of new URL
  2262. //
  2263. cb = static_cast<UINT>(strlen(pszUrlEscaped.get()));
  2264. // Resize the buffer to the sufficient size, leave place for '\0' termination.
  2265. //
  2266. if (!pszUrl.resize(cb + cbQueryString + 1))
  2267. {
  2268. sc = E_OUTOFMEMORY;
  2269. DebugTrace("CEcb::ScAsyncExecUrlWide60After() - Error while allocating memory 0x%08lX\n", sc);
  2270. goto ret;
  2271. }
  2272. // Copy the escaped version of the URL
  2273. //
  2274. memcpy(pszUrl.get(), pszUrlEscaped.get(), cb);
  2275. // Copy the query string at the end together with it's '\0' termination.
  2276. //
  2277. memcpy(pszUrl.get() + cb, pszQueryString, cbQueryString + 1);
  2278. // Point to the constructed URL
  2279. //
  2280. execUrlInfo.pszUrl = pszUrl.get();
  2281. }
  2282. else
  2283. {
  2284. // In the case we do not have query string then the URL to forward to
  2285. // is the same as the escaped URL.
  2286. //
  2287. execUrlInfo.pszUrl = pszUrlEscaped.get();
  2288. }
  2289. // Initialize method name
  2290. //
  2291. execUrlInfo.pszMethod = NULL;
  2292. // Initialize child headers
  2293. //
  2294. execUrlInfo.pszChildHeaders = NULL;
  2295. // We don't need a new user context,
  2296. //
  2297. execUrlInfo.pUserInfo = NULL;
  2298. // We don't need a new entity either
  2299. //
  2300. execUrlInfo.pEntity = NULL;
  2301. // Pick up the execution flags
  2302. //
  2303. execUrlInfo.dwExecUrlFlags = HSE_EXEC_URL_DISABLE_CUSTOM_ERROR;
  2304. // We need to set the status code here to 200 (like it
  2305. // was set when we were first called) just in case
  2306. // child ISAPIs depend on it.
  2307. //
  2308. SetStatusCode(200);
  2309. sc = ScAsyncExecUrl60After( execUrlInfo );
  2310. if (FAILED(sc))
  2311. {
  2312. // Reset the status code back to 0 if we failed to execute
  2313. // the child ISAPI because we will be handling the request
  2314. // ourselves later (probably by sending an error).
  2315. //
  2316. SetStatusCode(0);
  2317. DebugTrace("CEcb::ScAsyncExecUrlWide60After() - CEcb::ScAsyncExecUrl60After() failed with error 0x%08lX\n", sc);
  2318. goto ret;
  2319. }
  2320. // Set the flag stating that we have successfully executed a child ISAPI.
  2321. //
  2322. m_fChildISAPIExecSuccess = TRUE;
  2323. ret:
  2324. return sc;
  2325. }
  2326. SCODE CEcb::ScSendRedirect( LPCSTR pszURI )
  2327. {
  2328. SCODE sc = S_OK;
  2329. //
  2330. // We cannot assert that the dwHttpStatusCode status code in ECB is
  2331. // some particular value. On IIS 5.X it will be 0, IIS 6.0 has changed
  2332. // the behaviour and will shuffle 200 into it upon calling us. Also we
  2333. // have seen ourselves be called from IIS 6.0 when If-Modified-Since,
  2334. // Translate: t request is given which is the bug in IIS (WB 277208),
  2335. // as in that case IIS must be handling that. So untill we could assert
  2336. // for the response code to be 0 (IIS 5.X) or 200 (IIS 6.0) we will have
  2337. // to wait till IIS 6.0 is fixed up.
  2338. //
  2339. //
  2340. // Fill in the appropriate status code in the ECB
  2341. // (for IIS logging).
  2342. //
  2343. SetStatusCode(HSC_MOVED_TEMPORARILY);
  2344. //
  2345. // Attempt to send a redirection response. If successful then
  2346. // the response will be handled by IIS. If unsuccessful
  2347. // then we will handle the response later.
  2348. //
  2349. Assert( pszURI );
  2350. DWORD cbURI = static_cast<DWORD>(strlen( pszURI ) * sizeof(CHAR));
  2351. if ( !m_pecb->ServerSupportFunction( m_pecb->ConnID,
  2352. HSE_REQ_SEND_URL_REDIRECT_RESP,
  2353. const_cast<LPSTR>(pszURI),
  2354. &cbURI,
  2355. NULL ) )
  2356. {
  2357. //
  2358. // Reset the status code back to 0 if we failed to send
  2359. // the redirect because we will be handling the request
  2360. // ourselves later (probably by sending an error).
  2361. //
  2362. SetStatusCode(0);
  2363. sc = HRESULT_FROM_WIN32(GetLastError());
  2364. DebugTrace( "CEcb::ScSendRedirect() - ServerSupportFunction(HSE_REQ_SEND_URL_REDIRECT_RESP) failed with error 0x%08lX\n", sc );
  2365. goto ret;
  2366. }
  2367. ret:
  2368. return sc;
  2369. }
  2370. // ------------------------------------------------------------------------
  2371. //
  2372. // CEcb::FProcessingCEUrl()
  2373. //
  2374. // Find out if we are called with a CE URL.
  2375. // Important to avoid recursive invocation while
  2376. // doing custom error URLs.
  2377. //
  2378. BOOL
  2379. CEcb::FProcessingCEUrl( ) const
  2380. {
  2381. // Assume that we are not doing custom error processing.
  2382. // For IIS 6.0 and after it is always the right thing to assume,
  2383. // due to the changes in behaviour from IIS 5.x
  2384. //
  2385. BOOL fCustErr = FALSE;
  2386. // In the case of IIS 5.x we do the usual determinatin.
  2387. //
  2388. if (m_pecb->dwVersion < IIS_VERSION_6_0)
  2389. {
  2390. // By default assume custom error processing.
  2391. // Why? Suppose somebody forgets to tell us that
  2392. // it is a custom error url. We dont want recursive
  2393. // calls in that case. So it is safer to assume that
  2394. // we are doing custom error processing. This is only
  2395. // used to determine if we want to invoke custom
  2396. // error URLs and hence has no other side effects.
  2397. //
  2398. DWORD dwExecFlags = HSE_EXEC_CUSTOM_ERROR;
  2399. if (!(m_pecb->ServerSupportFunction( m_pecb->ConnID,
  2400. HSE_REQ_GET_EXECUTE_FLAGS,
  2401. NULL,
  2402. NULL,
  2403. &dwExecFlags )))
  2404. {
  2405. DebugTrace("CEcb::FProcessingCEUrl Server supportFunction call failed.\n");
  2406. DebugTrace("CEcb::FProcessingCEUrl Assuming custom error processing.\n");
  2407. }
  2408. fCustErr = !!(dwExecFlags & HSE_EXEC_CUSTOM_ERROR);
  2409. }
  2410. return fCustErr;
  2411. }
  2412. // ========================================================================
  2413. //
  2414. // FREE FUNCTIONS
  2415. //
  2416. // ------------------------------------------------------------------------
  2417. //
  2418. // NewEcb
  2419. //
  2420. IEcb * NewEcb( EXTENSION_CONTROL_BLOCK& ecbRaw,
  2421. BOOL fUseRawUrlMappings,
  2422. DWORD * pdwHSEStatusRet )
  2423. {
  2424. Assert( !IsBadWritePtr(pdwHSEStatusRet, sizeof(DWORD)) );
  2425. auto_ref_ptr<CEcb> pecb;
  2426. pecb.take_ownership(new CEcb(ecbRaw));
  2427. if ( pecb->FInitialize(fUseRawUrlMappings) )
  2428. return pecb.relinquish();
  2429. //
  2430. // If we couldn't init, there are two cases: If there is a status code
  2431. // set into the (raw) ECB, then the error was that LpszRequestUrl() returned
  2432. // NULL (bad URL). In this case, we need to go ahead and send a response to
  2433. // the client that is appropriate (400 Bad Request) and then continue on
  2434. // the way we normally would. Since the CEcb was already constructed, we can
  2435. // do everything just as we would for a normal request, calling
  2436. // DoneWithSession() and then returning HSE_STATUS_PENDING to IIS.
  2437. //
  2438. // If there was NOT a status code in the (raw) ECB, we just treat it as an
  2439. // exception. Think of this as functionally equal to throwing from a
  2440. // constructor.
  2441. //
  2442. if (ecbRaw.dwHttpStatusCode)
  2443. {
  2444. //
  2445. // The only path that can get here right now is if LpszRequestUrl()
  2446. // returns NULL during the CEcb FInitialize() call. Assert that
  2447. // we had 400 (Bad Request) in the ECB.
  2448. //
  2449. Assert(HSC_BAD_REQUEST == ecbRaw.dwHttpStatusCode);
  2450. //
  2451. // Send a 400 Bad Request response. We use the async error
  2452. // response mechanism, but that technically doesn't matter since we
  2453. // are for sure are not in the middle of sending some other response on
  2454. // another thread (we've not even dispatched out to the actual methods
  2455. // (DAV*) yet).
  2456. //
  2457. Assert(pecb.get());
  2458. pecb->SendAsyncErrorResponse(HSC_BAD_REQUEST,
  2459. gc_szDefErr400StatusLine,
  2460. CchConstString(gc_szDefErr400StatusLine),
  2461. NULL,
  2462. 0);
  2463. //
  2464. // Tell IIS that we are done with the EXTENSION_CONTROL_BLOCK that
  2465. // it gave us. We must do this or IIS will not be able to shut down.
  2466. // If this call succeeds (doesn't except), we MUST return
  2467. // HSE_STATUS_PENDING to let IIS know that we claimed a ref on the
  2468. // EXTENSION_CONTROL_BLOCK.
  2469. //
  2470. pecb->DoneWithSession( FALSE );
  2471. *pdwHSEStatusRet = HSE_STATUS_PENDING;
  2472. }
  2473. else
  2474. {
  2475. *pdwHSEStatusRet = pecb->HSEHandleException();
  2476. }
  2477. return NULL;
  2478. }
  2479. // ------------------------------------------------------------------------
  2480. //
  2481. // CbMDPathW
  2482. //
  2483. ULONG CbMDPathW (const IEcb& ecb, LPCWSTR pwszURI)
  2484. {
  2485. // The number of bytes we returned could be more than the path needs
  2486. //
  2487. return static_cast<UINT>((wcslen(ecb.InstData().GetNameW()) + wcslen(pwszURI) + 1) * sizeof(WCHAR));
  2488. }
  2489. // ------------------------------------------------------------------------
  2490. //
  2491. // MDPathFromURIW
  2492. //
  2493. VOID MDPathFromURIW (const IEcb& ecb, LPCWSTR pwszURI, LPWSTR pwszMDPath)
  2494. {
  2495. LPCWSTR pwszVroot;
  2496. // If the URI is fully qualified, then somebody is not
  2497. // playing fair. Gently nudge them.
  2498. //
  2499. Assert (pwszURI);
  2500. Assert (pwszURI == PwszUrlStrippedOfPrefix (pwszURI));
  2501. // Copy the root name the instance -- MINUS the vroot
  2502. //
  2503. UINT cch = static_cast<UINT>(wcslen(ecb.InstData().GetNameW()));
  2504. cch -= ecb.CchGetVirtualRootW(&pwszVroot);
  2505. memcpy (pwszMDPath, ecb.InstData().GetNameW(), sizeof(WCHAR) * cch);
  2506. // Copy the rest that is after the vroot path.
  2507. //
  2508. wcscpy (pwszMDPath + cch, pwszURI);
  2509. }