Source code of Windows XP (NT5)
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.

2111 lines
57 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1994 **/
  4. /**********************************************************************/
  5. /*
  6. conn.cxx
  7. This module contains the connection class
  8. FILE HISTORY:
  9. Johnl 15-Aug-1994 Created
  10. */
  11. #include "w3p.hxx"
  12. #include <issched.hxx>
  13. #pragma warning( disable:4355 ) // 'this' used in base member initializer list
  14. //
  15. // Used for iisprobe
  16. //
  17. extern "C" {
  18. VOID
  19. DumpW3InfoToHTML(
  20. OUT CHAR * pch,
  21. IN OUT LPDWORD pcb
  22. );
  23. };
  24. //
  25. // Globals
  26. //
  27. #if CC_REF_TRACKING
  28. //
  29. // Ref count trace log size
  30. //
  31. #define C_CLIENT_CONN_REFTRACES 4000
  32. #define C_LOCAL_CONN_REFTRACES 40
  33. #endif
  34. //
  35. // Ref trace log for CLIENT_CONN objects
  36. // NOTE we make this global so other classes can get at it
  37. //
  38. #if DBG
  39. PTRACE_LOG g_pDbgCCRefTraceLog = NULL;
  40. #endif
  41. //
  42. // Private constants.
  43. //
  44. //
  45. // How often to run the free list scavenger (thirty minutes)
  46. //
  47. #define FREE_LIST_SCAVENGE_TIME (30 * 60 * 1000)
  48. //
  49. // Private globals.
  50. //
  51. CRITICAL_SECTION CLIENT_CONN::_csBuffList;
  52. LIST_ENTRY CLIENT_CONN::_BuffListHead;
  53. BOOL CLIENT_CONN::_fGlobalInit = FALSE;
  54. DWORD CLIENT_CONN::_cFree = 0; // Number of items on lookaside free list
  55. DWORD CLIENT_CONN::_FreeListScavengerCookie;
  56. DWORD ErrorRespTable[] = {IDS_EXECUTE_ACCESS_DENIED,
  57. IDS_READ_ACCESS_DENIED,
  58. IDS_WRITE_ACCESS_DENIED,
  59. IDS_SSL_REQUIRED,
  60. IDS_SSL128_REQUIRED,
  61. IDS_ADDR_REJECT,
  62. IDS_CERT_REQUIRED,
  63. IDS_SITE_ACCESS_DENIED,
  64. IDS_TOO_MANY_USERS,
  65. IDS_INVALID_CNFG,
  66. IDS_PWD_CHANGE,
  67. IDS_MAPPER_DENY_ACCESS,
  68. #if defined(CAL_ENABLED)
  69. IDS_CAL_EXCEEDED,
  70. #endif
  71. IDS_CERT_REVOKED,
  72. IDS_DIR_LIST_DENIED,
  73. IDS_SITE_RESOURCE_BLOCKED,
  74. IDS_CERT_BAD,
  75. IDS_CERT_TIME_INVALID,
  76. IDS_SITE_NOT_FOUND
  77. };
  78. DWORD SubStatusTable[] = {MD_ERROR_SUB403_EXECUTE_ACCESS_DENIED,
  79. MD_ERROR_SUB403_READ_ACCESS_DENIED ,
  80. MD_ERROR_SUB403_WRITE_ACCESS_DENIED ,
  81. MD_ERROR_SUB403_SSL_REQUIRED ,
  82. MD_ERROR_SUB403_SSL128_REQUIRED ,
  83. MD_ERROR_SUB403_ADDR_REJECT ,
  84. MD_ERROR_SUB403_CERT_REQUIRED ,
  85. MD_ERROR_SUB403_SITE_ACCESS_DENIED ,
  86. MD_ERROR_SUB403_TOO_MANY_USERS ,
  87. MD_ERROR_SUB403_INVALID_CNFG ,
  88. MD_ERROR_SUB403_PWD_CHANGE ,
  89. MD_ERROR_SUB403_MAPPER_DENY_ACCESS ,
  90. #if defined(CAL_ENABLED)
  91. MD_ERROR_SUB403_CAL_EXCEEDED ,
  92. #endif
  93. MD_ERROR_SUB403_CERT_REVOKED,
  94. MD_ERROR_SUB403_DIR_LIST_DENIED,
  95. MD_ERROR_SUB503_CPU_LIMIT,
  96. MD_ERROR_SUB403_CERT_BAD,
  97. MD_ERROR_SUB403_CERT_TIME_INVALID,
  98. MD_ERROR_SUB404_SITE_NOT_FOUND
  99. };
  100. /*******************************************************************
  101. Maco support for CLIENT_CONN::Reference/Dereference
  102. HISTORY:
  103. DaveK 10-Sep-1997 Added ref trace logging
  104. ********************************************************************/
  105. #if CC_REF_TRACKING
  106. #define CC_LOG_REF_COUNT( cRefs ) \
  107. \
  108. SHARED_LOG_REF_COUNT( \
  109. cRefs \
  110. , (PVOID) this \
  111. , _phttpReq \
  112. , (_phttpReq) \
  113. ? ((HTTP_REQUEST *) _phttpReq)->QueryWamRequest() \
  114. : NULL \
  115. , (_phttpReq) \
  116. ? _phttpReq->QueryState() \
  117. : NULL \
  118. ); \
  119. LOCAL_LOG_REF_COUNT( \
  120. cRefs \
  121. , (PVOID) this \
  122. , _phttpReq \
  123. , (_phttpReq) \
  124. ? ((HTTP_REQUEST *) _phttpReq)->QueryWamRequest() \
  125. : NULL \
  126. , (_phttpReq) \
  127. ? _phttpReq->QueryState() \
  128. : NULL \
  129. );
  130. #else
  131. #define CC_LOG_REF_COUNT( cRefs )
  132. #endif
  133. #if CC_REF_TRACKING
  134. //
  135. // ATQ notification trace
  136. //
  137. // ATQ notification : stored with context1 set magic value fefefefe
  138. //
  139. #define CCA_LOG_REF_COUNT( cRefs,BytesWritten,CompletionStatus, dwSig) \
  140. \
  141. SHARED_LOG_REF_COUNT( \
  142. cRefs \
  143. , (PVOID) this \
  144. , (LPVOID)dwSig \
  145. , (LPVOID)BytesWritten \
  146. , CompletionStatus \
  147. ); \
  148. LOCAL_LOG_REF_COUNT( \
  149. cRefs \
  150. , (PVOID) this \
  151. , (LPVOID)dwSig \
  152. , (LPVOID)BytesWritten \
  153. , CompletionStatus \
  154. );
  155. #else
  156. #define CCA_LOG_REF_COUNT( Ctx,BytesWritten,CompletionStatus, dwSig)
  157. #endif
  158. //
  159. // Private prototypes.
  160. //
  161. VOID
  162. WINAPI
  163. ClientConnTrimScavenger(
  164. PVOID pContext
  165. );
  166. inline BOOL
  167. FastScanForTerminator(
  168. const CHAR * pch,
  169. UINT cbData
  170. )
  171. /*++
  172. Routine Description:
  173. Check if buffer contains a full HTTP header.
  174. Can return false negatives.
  175. Arguments:
  176. pch - request buffer
  177. cbData - # of bytes in pch, excluding trailing '\0'
  178. Return Value:
  179. TRUE if buffer contains a full HTTP header
  180. FALSE if could not insure this, does not mean there is
  181. no full HTTP header.
  182. --*/
  183. {
  184. return ( (cbData > 4) &&
  185. (!memcmp(pch+cbData - sizeof("\r\n\r\n") + 1, "\r\n\r\n",
  186. sizeof("\r\n\r\n") - 1 ) ||
  187. !memcmp(pch+cbData - sizeof("\n\n") + 1, "\n\n",
  188. sizeof("\n\n")-1 )
  189. )
  190. );
  191. } // FastScanForTerminator()
  192. //
  193. // Public functions.
  194. //
  195. BYTE *
  196. ScanForTerminator(
  197. const TCHAR * pch
  198. )
  199. /*++
  200. Routine Description:
  201. Returns the first byte of data after the header
  202. Arguments:
  203. pch - Zero terminated buffer
  204. Return Value:
  205. Pointer to first byte of data after the header or NULL if the
  206. header isn't terminated
  207. --*/
  208. {
  209. while ( *pch )
  210. {
  211. if ( !(pch = strchr( pch, '\n' )))
  212. {
  213. break;
  214. }
  215. //
  216. // If we find an EOL, check if the next character is an EOL character
  217. //
  218. // NYI: UGLY cast is used ...
  219. if ( *(pch = SkipWhite( (char * ) (pch + 1) )) == W3_EOL )
  220. {
  221. return (BYTE *) pch + 1;
  222. }
  223. else if ( *pch )
  224. {
  225. pch++;
  226. }
  227. }
  228. return NULL;
  229. }
  230. //
  231. // Private functions.
  232. //
  233. /*******************************************************************
  234. NAME: CLIENT_CONN
  235. SYNOPSIS: Constructor for client connection
  236. ENTRY: sClient - Client socket
  237. psockaddrLocal - Optional Addressing info of server socket
  238. psockaddrRemote - Addressing info for client
  239. patqContext - Optional ATQ context
  240. HISTORY:
  241. Johnl 15-Aug-1994 Created
  242. ********************************************************************/
  243. CLIENT_CONN::CLIENT_CONN(
  244. IN PCLIENT_CONN_PARAMS ClientParams
  245. ) :
  246. _phttpReq ( NULL),
  247. m_pInstance ( NULL),
  248. _fIsValid ( FALSE )
  249. {
  250. #if CC_REF_TRACKING
  251. _pDbgCCRefTraceLog = CreateRefTraceLog( C_LOCAL_CONN_REFTRACES, 0 );
  252. #endif
  253. Initialize( ClientParams );
  254. //
  255. // initialize statistics pointer to point to global statistics;
  256. // it will point to local copy of the instance when instance
  257. // pointer is set
  258. //
  259. m_pW3Stats = g_pW3Stats;
  260. }
  261. VOID
  262. CLIENT_CONN::Initialize(
  263. IN PCLIENT_CONN_PARAMS ClientParams
  264. )
  265. /*++
  266. Routine Description:
  267. This is a pseudo constructor, called just before this object is given to
  268. somebody who allocated a new client connection
  269. Arguments:
  270. sClient - Same as for constructor
  271. psockaddr - Same as for constructor
  272. --*/
  273. {
  274. CHAR * pchAddr;
  275. PSOCKADDR pAddrLocal = ClientParams->pAddrLocal;
  276. _Signature = CLIENT_CONN_SIGNATURE;
  277. _sClient = ClientParams->sClient;
  278. _ccState = CCS_STARTUP;
  279. _cRef = 1;
  280. _AtqContext = ClientParams->pAtqContext;
  281. _fIsValid = FALSE;
  282. _fReuseContext = TRUE;
  283. _fAbortiveClose = FALSE;
  284. m_atqEndpointObject = ClientParams->pEndpointObject;
  285. //
  286. // Get the endpoint and reference it
  287. //
  288. m_pW3Endpoint = ClientParams->pEndpoint;
  289. //
  290. // Don't actually reference the endpoint. The CLIENT_CONN reference is meaningless
  291. //
  292. // m_pW3Endpoint->Reference( );
  293. _fSecurePort = m_pW3Endpoint->IsSecure( );
  294. _pvInitial = ClientParams->pvInitialBuff;
  295. _cbInitial = ClientParams->cbInitialBuff;
  296. g_pW3Stats->IncrCurrentConnections();
  297. IF_DEBUG( CONNECTION )
  298. {
  299. DBGPRINTF((DBG_CONTEXT,
  300. "Initializing connection object %lx, new user count %d\n",
  301. this,
  302. g_pW3Stats->QueryCurrentConnections() ));
  303. }
  304. LockGlobals();
  305. InsertHeadList( &listConnections, &ListEntry );
  306. UnlockGlobals();
  307. m_remoteIpAddress =
  308. ((PSOCKADDR_IN)ClientParams->pAddrRemote)->sin_addr.s_addr;
  309. _sRemotePort =
  310. ntohs( ((PSOCKADDR_IN)ClientParams->pAddrRemote)->sin_port );
  311. DBG_ASSERT( ClientParams->pAddrRemote->sa_family == AF_INET );
  312. InetNtoa( ((SOCKADDR_IN *)ClientParams->pAddrRemote)->sin_addr, _achRemoteAddr );
  313. _acCheck.BindAddr( ClientParams->pAddrRemote );
  314. //
  315. // look at local address
  316. //
  317. if (pAddrLocal->sa_family == AF_INET)
  318. {
  319. InetNtoa( ((SOCKADDR_IN *)pAddrLocal)->sin_addr, _achLocalAddr );
  320. _sPort = m_pW3Endpoint->QueryPort( );
  321. m_localIpAddress =
  322. ((PSOCKADDR_IN)ClientParams->pAddrLocal)->sin_addr.s_addr;
  323. DBG_ASSERT(_sPort == ntohs( ((SOCKADDR_IN *)pAddrLocal)->sin_port));
  324. DBG_ASSERT(_sPort != 0);
  325. }
  326. else
  327. {
  328. //
  329. // This should not happen
  330. // Remote winsock bug returns all zeros
  331. //
  332. DBGPRINTF((DBG_CONTEXT,
  333. "Invalid socket family %d\n",pAddrLocal->sa_family));
  334. SetLastError( ERROR_INVALID_PARAMETER );
  335. goto error;
  336. }
  337. DBG_ASSERT( (strlen( _achLocalAddr ) + 1) <= sizeof(_achLocalAddr));
  338. if ( _phttpReq != NULL )
  339. {
  340. _phttpReq->InitializeSession( this,
  341. _pvInitial,
  342. _cbInitial );
  343. }
  344. else
  345. {
  346. _phttpReq = new HTTP_REQUEST( this,
  347. _pvInitial,
  348. _cbInitial );
  349. if ( (_phttpReq == NULL) || !_phttpReq->IsValid() )
  350. {
  351. DBGPRINTF((DBG_CONTEXT,"Cannot allocate HTTP_REQUEST object\n"));
  352. if (_phttpReq != NULL)
  353. {
  354. delete _phttpReq;
  355. _phttpReq = NULL;
  356. }
  357. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  358. goto error;
  359. }
  360. }
  361. CC_LOG_REF_COUNT( _cRef );
  362. _fIsValid = TRUE;
  363. return;
  364. error:
  365. CC_LOG_REF_COUNT( _cRef );
  366. //
  367. // Set instance to null so that it will not be dereferenced
  368. // twice (one on failure, one on cleanup)
  369. //
  370. //
  371. // Set AtqContext to null so that it will not be dereferenced
  372. // twice (one on failure, one on cleanup)
  373. // it's like deja vu all over again.
  374. _AtqContext = NULL;
  375. return;
  376. } // CLIENT_CONN::Initialize()
  377. CLIENT_CONN::~CLIENT_CONN()
  378. {
  379. if (_phttpReq != NULL)
  380. {
  381. delete _phttpReq;
  382. }
  383. #if CC_REF_TRACKING
  384. DestroyRefTraceLog( _pDbgCCRefTraceLog );
  385. #endif
  386. _Signature = CLIENT_CONN_SIGNATURE_FREE;
  387. }
  388. VOID
  389. CLIENT_CONN::Reset(
  390. VOID
  391. )
  392. /*++
  393. Routine Description:
  394. This is a pseudo destructor, called just before this object is put back
  395. onto the free list.
  396. Arguments:
  397. --*/
  398. {
  399. //
  400. // before we reset this client-conn:
  401. // we assert that httpreq is null (we may have reached here
  402. // on a failure path) or that our httpreq was unbound from its wamreq
  403. //
  404. DBG_ASSERT(
  405. ( _phttpReq == NULL )
  406. ||
  407. ( ((HTTP_REQUEST *)_phttpReq)->QueryWamRequest() == NULL )
  408. );
  409. IF_DEBUG( CONNECTION )
  410. {
  411. DBGPRINTF((DBG_CONTEXT,
  412. "Resetting connection object %lx, AtqCont = %lx new user count %d\n",
  413. this,
  414. _AtqContext,
  415. g_pW3Stats->QueryCurrentConnections() ));
  416. }
  417. if ( _phttpReq != NULL )
  418. {
  419. _phttpReq->SessionTerminated();
  420. }
  421. if ( QueryAtqContext() != NULL )
  422. {
  423. AtqFreeContext( QueryAtqContext(), _fReuseContext );
  424. _AtqContext = NULL;
  425. }
  426. _acCheck.UnbindAddr();
  427. //
  428. // Dereference the instance
  429. //
  430. if ( m_pInstance != NULL ) {
  431. m_pInstance->DecrementCurrentConnections();
  432. m_pInstance->Dereference( );
  433. m_pInstance = NULL;
  434. //
  435. // Reset statistics pointer to point to global statistics
  436. // as the statistic object is associated with the instance.
  437. //
  438. m_pW3Stats = g_pW3Stats;
  439. }
  440. //
  441. // Dereference the endpoint
  442. //
  443. if ( m_pW3Endpoint != NULL ) {
  444. //
  445. // Don't actually dereference. The CLIENT_CONN references are meaningless anyway.
  446. //
  447. // m_pW3Endpoint->Dereference( );
  448. m_pW3Endpoint = NULL;
  449. }
  450. //
  451. // Remove ourselves from the connection list and knock down our
  452. // connected user count
  453. //
  454. LockGlobals();
  455. RemoveEntryList( &ListEntry );
  456. DBG_ASSERT( ((LONG ) g_pW3Stats->QueryCurrentConnections()) >= 0 );
  457. UnlockGlobals();
  458. g_pW3Stats->DecrCurrentConnections();
  459. _Signature = CLIENT_CONN_SIGNATURE_FREE;
  460. } // CLIENT_CONN::Reset
  461. /*******************************************************************
  462. NAME: CLIENT_CONN::DoWork
  463. SYNOPSIS: Worker method driven by thread pool queue
  464. RETURNS: TRUE while processing should continue on this object
  465. If FALSE is returned, then the object should be deleted
  466. (status codes will already have been communicated to
  467. the client).
  468. NOTES: If an IO request completes with an error, the connection
  469. is immediately closed and everything is cleaned up. The
  470. worker functions will not be called when an error occurs.
  471. HISTORY:
  472. Johnl 15-Aug-1994 Created
  473. ********************************************************************/
  474. BOOL CLIENT_CONN::DoWork( DWORD BytesWritten,
  475. DWORD CompletionStatus,
  476. BOOL fIOCompletion )
  477. {
  478. BOOL fRet = TRUE;
  479. BOOL fFinished = FALSE;
  480. BOOL fAvailableData;
  481. BOOL fDoAgain;
  482. IF_DEBUG( CONNECTION )
  483. {
  484. DBGPRINTF(( DBG_CONTEXT,
  485. "DoWork: Object %lx Last IO error %lu Bytes = %d State = %d\n"
  486. "\tIsIO = %s Ref = %d Sock = %p\n",
  487. this,
  488. CompletionStatus,
  489. BytesWritten,
  490. QueryState(),
  491. fIOCompletion ? "TRUE" : "FALSE",
  492. QueryRefCount(),
  493. (QueryAtqContext() ? QueryAtqContext()->hAsyncIO : (PVOID)-1L) ));
  494. }
  495. //
  496. // If this was a completion generated by an IO request, decrement the
  497. // ref count
  498. //
  499. if ( fIOCompletion )
  500. {
  501. // NOTE negative ref count ==> no change to ref count
  502. CC_LOG_REF_COUNT( -_cRef );
  503. Dereference();
  504. }
  505. //
  506. // If an IO Request completed with an error and we're not already in the
  507. // process of cleaning up, then abort the connection and cleanup. We
  508. // do not send a status code to the client in this instance.
  509. //
  510. if (_phttpReq == NULL)
  511. {
  512. return FALSE;
  513. }
  514. _phttpReq->SetLastCompletionStatus( BytesWritten,
  515. CompletionStatus );
  516. if ( CompletionStatus && !IsCleaningUp() )
  517. {
  518. //
  519. // We want to log this error iff we've received some bytes
  520. // on the connection already. Otherwise this could be a
  521. // 'natural' abort after a successful request.
  522. //
  523. if ( _phttpReq->QueryBytesReceived() != 0)
  524. {
  525. //Log status may still be set to HT_DONT_LOG; if it is, change it
  526. //to "bad request" because we always want to log errors.
  527. DWORD dwLogHttpResponse = _phttpReq->QueryLogHttpResponse();
  528. _phttpReq->SetLogStatus ( ( dwLogHttpResponse == HT_DONT_LOG ?
  529. HT_BAD_REQUEST :
  530. dwLogHttpResponse ),
  531. CompletionStatus );
  532. }
  533. #if DBG
  534. if ( CompletionStatus != ERROR_NETNAME_DELETED &&
  535. CompletionStatus != ERROR_OPERATION_ABORTED )
  536. {
  537. DBGPRINTF(( DBG_CONTEXT,
  538. "DoWork: Aborting client connection %8lx, error %d\n",
  539. this,
  540. CompletionStatus ));
  541. }
  542. #endif
  543. if ( HTR_GATEWAY_ASYNC_IO == _phttpReq->QueryState()) {
  544. DBGPRINTF((
  545. DBG_CONTEXT
  546. , "CLIENT_CONN[%08x]::DoWork async i/o failed "
  547. "_phttpReq[%08x] "
  548. "pWamRequest[%08x] "
  549. "CompletionStatus(%d) "
  550. "\n"
  551. , this
  552. , _phttpReq
  553. , ((HTTP_REQUEST * )_phttpReq)->QueryWamRequest()
  554. , CompletionStatus
  555. ));
  556. // NOTE negative ref count ==> no change to ref count
  557. CC_LOG_REF_COUNT( -_cRef );
  558. // Notify the external gateway application
  559. // NYI: This is a hack for IIS 2.0 We need to fix state machines
  560. // of CLIENT_CONN & HTTP_REQUEST for future extensions
  561. ((HTTP_REQUEST * )_phttpReq)->ProcessAsyncGatewayIO();
  562. } else {
  563. //
  564. // if this is not a WAM request, cleanup Exec, etc...
  565. //
  566. _phttpReq->WriteLogRecord();
  567. }
  568. Disconnect( NULL, 0, 0, FALSE );
  569. // NOTE negative ref count ==> no change to ref count
  570. CC_LOG_REF_COUNT( -_cRef );
  571. goto Exit;
  572. }
  573. switch ( QueryState() )
  574. {
  575. case CCS_STARTUP:
  576. // NOTE negative ref count ==> no change to ref count
  577. CC_LOG_REF_COUNT( -_cRef );
  578. //
  579. // Do this at the beginning of every request
  580. //
  581. fRet = OnSessionStartup( &fDoAgain,
  582. _pvInitial,
  583. _cbInitial,
  584. TRUE);
  585. // NOTE negative ref count ==> no change to ref count
  586. CC_LOG_REF_COUNT( -_cRef );
  587. if ( !fRet || !_pvInitial )
  588. {
  589. break;
  590. }
  591. //
  592. // Fall through
  593. //
  594. case CCS_PROCESSING_CLIENT_REQ:
  595. //
  596. // Set the start time on this request
  597. //
  598. _phttpReq->SetRequestStartTime();
  599. do
  600. {
  601. fDoAgain = FALSE;
  602. // NOTE negative ref count ==> no change to ref count
  603. CC_LOG_REF_COUNT( -_cRef );
  604. fRet = _phttpReq->DoWork( &fFinished );
  605. // NOTE negative ref count ==> no change to ref count
  606. CC_LOG_REF_COUNT( -_cRef );
  607. if ( !fRet )
  608. {
  609. //
  610. // If we were denied access to the resource, then ask the user
  611. // for better authorization. Unless the user is in the process
  612. // of authenticating, we force a disconnect. This prevents the
  613. // case of logging on as anonymous successfully and then failing
  614. // to access the resource.
  615. //
  616. if ( GetLastError() == ERROR_ACCESS_DENIED )
  617. {
  618. if ( !_phttpReq->IsAuthenticating() )
  619. {
  620. _phttpReq->SetKeepConn( FALSE );
  621. }
  622. if ( _phttpReq->IsKeepConnSet() )
  623. {
  624. //
  625. // Zero out the inital buffer so we don't try and reuse
  626. // it next time around
  627. //
  628. _pvInitial = NULL;
  629. _cbInitial = 0;
  630. SetState( CCS_STARTUP );
  631. }
  632. else
  633. {
  634. _phttpReq->SetLogStatus( HT_DENIED, ERROR_ACCESS_DENIED );
  635. SetState( CCS_DISCONNECTING );
  636. }
  637. fRet = _phttpReq->SendAuthNeededResp( &fFinished );
  638. if ( fRet && fFinished )
  639. {
  640. goto Disconnecting;
  641. }
  642. }
  643. }
  644. else if ( fFinished )
  645. {
  646. _phttpReq->WriteLogRecord();
  647. if ( !IsCleaningUp() )
  648. {
  649. if ( !_phttpReq->IsKeepConnSet() ||
  650. !(fRet = OnSessionStartup(&fDoAgain)) )
  651. {
  652. //
  653. // Either we completed the disconnect so
  654. // close the socket or an error
  655. // occurred setting up for the next request w/ KeepConn set
  656. //
  657. Disconnect();
  658. fDoAgain = FALSE;
  659. }
  660. fFinished = FALSE;
  661. }
  662. }
  663. } while ( fDoAgain );
  664. // NOTE negative ref count ==> no change to ref count
  665. CC_LOG_REF_COUNT( -_cRef );
  666. break;
  667. case CCS_DISCONNECTING:
  668. Disconnecting:
  669. //
  670. // Write the log record for this request
  671. //
  672. _phttpReq->WriteLogRecord();
  673. // NOTE negative ref count ==> no change to ref count
  674. CC_LOG_REF_COUNT( -_cRef );
  675. Disconnect();
  676. // NOTE negative ref count ==> no change to ref count
  677. CC_LOG_REF_COUNT( -_cRef );
  678. //
  679. // Fall-through to async i/o cleanup code ...
  680. //
  681. case CCS_SHUTDOWN:
  682. //
  683. // If we are still in async i/o state when shutting down, cancel
  684. //
  685. // NOTE this should only happen in cases like 97842,
  686. // where oop isapi submited async i/o and then crashed
  687. //
  688. if ( _phttpReq->QueryState() == HTR_GATEWAY_ASYNC_IO ) {
  689. DBGPRINTF((
  690. DBG_CONTEXT
  691. , "CLIENT_CONN::DoWork: calling CancelAsyncGatewayIO "
  692. "State = %d, this = %lx, Ref = %d\n"
  693. , QueryState()
  694. , this
  695. , QueryRefCount()
  696. ));
  697. // NOTE negative ref count ==> no change to ref count
  698. CC_LOG_REF_COUNT( -_cRef );
  699. ((HTTP_REQUEST * )_phttpReq)->CancelAsyncGatewayIO();
  700. // NOTE negative ref count ==> no change to ref count
  701. CC_LOG_REF_COUNT( -_cRef );
  702. }
  703. break;
  704. default:
  705. fRet = FALSE;
  706. DBG_ASSERT( FALSE );
  707. }
  708. //
  709. // If an error occurred, disconnect without sending a response
  710. //
  711. if ( !fRet )
  712. {
  713. //
  714. // There was a problem with SetLogStatus blowing away the
  715. // last error, so save it here and assert that it does not
  716. // get changed.
  717. //
  718. DWORD dwTempError = GetLastError();
  719. _phttpReq->SetLogStatus( HT_SERVER_ERROR, dwTempError );
  720. _phttpReq->WriteLogRecord();
  721. // NOTE negative ref count ==> no change to ref count
  722. CC_LOG_REF_COUNT( -_cRef );
  723. _phttpReq->Disconnect( HT_SERVER_ERROR, dwTempError );
  724. // NOTE negative ref count ==> no change to ref count
  725. CC_LOG_REF_COUNT( -_cRef );
  726. }
  727. Exit:
  728. IF_DEBUG( CONNECTION )
  729. {
  730. DBGPRINTF((DBG_CONTEXT,
  731. "DoWork: Leaving, State = %d, this = %lx, Ref = %d\n",
  732. QueryState(),
  733. this,
  734. QueryRefCount() ));
  735. }
  736. //
  737. // This connection's reference count should always be at least one
  738. // at this point
  739. //
  740. DBG_ASSERT( QueryRefCount() > 0 );
  741. return TRUE;
  742. } // CLIENT_CONN::DoWork
  743. /*******************************************************************
  744. NAME: CLIENT_CONN::OnSessionStartup
  745. SYNOPSIS: Initiates the first read to get the client request
  746. PARAMETERS:
  747. RETURNS: TRUE if processing should continue, FALSE to abort the
  748. this connection
  749. HISTORY:
  750. Johnl 15-Aug-1994 Created
  751. ********************************************************************/
  752. BOOL
  753. CLIENT_CONN::OnSessionStartup(
  754. BOOL *pfDoAgain,
  755. PVOID pvInitial,
  756. DWORD cbInitial,
  757. BOOL fFirst
  758. )
  759. {
  760. APIERR err = NO_ERROR;
  761. //
  762. // Associate our client socket with Atq if it hasn't already
  763. //
  764. if ( !QueryAtqContext() )
  765. {
  766. DBG_ASSERT( pvInitial == NULL );
  767. if ( !AtqAddAsyncHandle( &_AtqContext,
  768. m_atqEndpointObject,
  769. this,
  770. W3Completion,
  771. W3_DEF_CONNECTION_TIMEOUT,
  772. (HANDLE) QuerySocket()))
  773. {
  774. DBGPRINTF(( DBG_CONTEXT,
  775. "OnSessionStartup: failed to add Atq handle, error %lu\n",
  776. GetLastError() ));
  777. return FALSE;
  778. }
  779. }
  780. SetState( CCS_PROCESSING_CLIENT_REQ );
  781. return _phttpReq->StartNewRequest( pvInitial,
  782. cbInitial,
  783. fFirst,
  784. pfDoAgain);
  785. }
  786. DWORD
  787. ErrorResponseToSubStatus(
  788. DWORD dwErrorResponse
  789. )
  790. /*++
  791. Routine Description:
  792. Map an 'ErrorResponse' to a substatus for use in custom error lookup.
  793. Arguments:
  794. dwErrorResponse - The error response to be mapped.
  795. Return Value:
  796. The substatus if we can map it, or 0 otherwise.
  797. --*/
  798. {
  799. int i;
  800. DBG_ASSERT((sizeof(ErrorRespTable)/sizeof(DWORD)) == (sizeof(SubStatusTable)/sizeof(DWORD)));
  801. for (i = 0; i < sizeof(ErrorRespTable)/sizeof(DWORD);i++)
  802. {
  803. if (ErrorRespTable[i] == dwErrorResponse)
  804. {
  805. return SubStatusTable[i];
  806. }
  807. }
  808. return 0;
  809. }
  810. #define ERROR_HTML_PREFIX "<head><title>Error</title></head><body>"
  811. /*******************************************************************
  812. NAME: CLIENT_CONN::Disconnect
  813. SYNOPSIS: Initiates a disconnect from the client
  814. ENTRY: pRequest - If not NULL and HTResponse is non-zero, send
  815. a response status before disconnecting
  816. HTResponse - HTTP status code to send
  817. ErrorResponse - Optional information string (system error or
  818. string resource ID).
  819. NOTES: If a response is sent, then the socket won't be disconnected
  820. till the send completes (state goes to
  821. HISTORY:
  822. Johnl 22-Aug-1994 Created
  823. ********************************************************************/
  824. VOID CLIENT_CONN::Disconnect( HTTP_REQ_BASE * pRequest,
  825. DWORD HTResponse,
  826. DWORD ErrorResponse,
  827. BOOL fDoShutdown,
  828. LPBOOL pfFinished )
  829. {
  830. CHAR *pszResp;
  831. BOOL fDone;
  832. DWORD dwSubStatus = 0;
  833. STACK_STR(strError, 80);
  834. STACK_STR(strResp, 80);
  835. CHAR ach[17];
  836. //
  837. // If Disconnect has already been called, then this is a no-op
  838. //
  839. if ( QueryState() == CCS_SHUTDOWN )
  840. {
  841. return;
  842. }
  843. if ( _fAbortiveClose )
  844. {
  845. fDoShutdown = FALSE;
  846. }
  847. if ( pRequest && HTResponse )
  848. {
  849. STACK_STR(strBody, 200);
  850. BOOL bBodyBuilt;
  851. DWORD dwRespLength;
  852. DWORD dwContentLength;
  853. if ( HTResponse == HT_NOT_FOUND )
  854. {
  855. QueryW3StatsObj()->IncrTotalNotFoundErrors();
  856. }
  857. //
  858. // Means we have to wait for the status response before closing
  859. // the socket
  860. //
  861. SetState( CCS_DISCONNECTING );
  862. IF_DEBUG( CONNECTION )
  863. {
  864. DBGPRINTF((DBG_CONTEXT,
  865. "Disconnect: Going to Disconnecting for %lx, ref = %d, response = %d\n",
  866. this,
  867. QueryRefCount(),
  868. HTResponse ));
  869. }
  870. if ( ErrorResponse != 0)
  871. {
  872. dwSubStatus = ErrorResponseToSubStatus(ErrorResponse);
  873. }
  874. if (pRequest->CheckCustomError(&strError, HTResponse, dwSubStatus, &fDone,
  875. &dwContentLength))
  876. {
  877. // Had at least some custom error processing. If we're done, just return.
  878. if (fDone)
  879. {
  880. return;
  881. }
  882. // Otherwise we've got a custom error body. Build up the basic
  883. // status line that we need, convert the length
  884. // to ascii for use as a Content-Length, build up what we need in
  885. // strBody, and keep going.
  886. if ( !HTTP_REQ_BASE::BuildStatusLine( &strResp,
  887. HTResponse,
  888. 0, pRequest->QueryURL(),
  889. NULL))
  890. {
  891. DBGPRINTF((DBG_CONTEXT,
  892. "Disconnect: Failed to send status (error %d), aborting connecting\n",
  893. ::GetLastError()));
  894. goto DisconnectNow;
  895. }
  896. _itoa( dwContentLength, ach, 10 );
  897. bBodyBuilt = strBody.Copy("Content-Length: ",
  898. sizeof("Content-Length: ") - 1) &&
  899. strBody.Append(ach) &&
  900. strBody.Append("\r\n", sizeof("\r\n") - 1) &&
  901. strBody.Append((CHAR *)strError.QueryPtr());
  902. strResp.SetLen(strlen((CHAR *)strResp.QueryPtr()));
  903. }
  904. else
  905. {
  906. //
  907. // No custom error, build the standard status line and body.
  908. //
  909. // Send the requested status response and after that completes close
  910. // the socket
  911. //
  912. if (ErrorResponse != 0 && ErrorResponse < STR_RES_ID_BASE)
  913. {
  914. if (!strError.Copy(ERROR_HTML_PREFIX,
  915. sizeof(ERROR_HTML_PREFIX) - 1))
  916. {
  917. goto DisconnectNow;
  918. }
  919. }
  920. if ( !HTTP_REQ_BASE::BuildStatusLine( &strResp,
  921. HTResponse,
  922. ErrorResponse, pRequest->QueryURL(),
  923. &strError))
  924. {
  925. DBGPRINTF((DBG_CONTEXT,
  926. "Disconnect: Failed to send status (error %d), aborting connecting\n",
  927. ::GetLastError()));
  928. goto DisconnectNow;
  929. }
  930. //
  931. // Now build up the body.
  932. strResp.SetLen(strlen((CHAR *)strResp.QueryPtr()));
  933. dwRespLength = strResp.QueryCB() - CRLF_SIZE;
  934. if (!strBody.Copy("Content-Type: text/html\r\n",
  935. sizeof("Content-Type: text/html\r\n") - 1))
  936. {
  937. DBGPRINTF((DBG_CONTEXT,
  938. "Disconnect: Failed to build error message for error %d, aborting connecting\n",
  939. HTResponse));
  940. goto DisconnectNow;
  941. }
  942. // The body we'll build depends on whether or not there's an
  943. // error string. If there is, then that's it, we'll just copy it
  944. // in. If not, we'll fabricate a short HTML document from the
  945. // status line.
  946. if (strError.IsEmpty())
  947. {
  948. // No error string.
  949. dwContentLength = (dwRespLength * 2) -
  950. (sizeof("HTTP/1.1 XXX ") - 1) +
  951. sizeof("<head><title>") - 1 +
  952. sizeof("<html></title></head>") - 1 +
  953. sizeof("<body><h1>") - 1 +
  954. sizeof("</h1></body></html>") - 1;
  955. _itoa( dwContentLength, ach, 10 );
  956. bBodyBuilt = strBody.Append("Content-Length: ",
  957. sizeof("Content-Length: ") - 1) &&
  958. strBody.Append(ach) &&
  959. strBody.Append("\r\n\r\n", sizeof("\r\n\r\n") - 1) &&
  960. strBody.Append("<html><head><title>",
  961. sizeof("<html><head><title>") - 1) &&
  962. strBody.Append(strResp.QueryStr() +
  963. sizeof("HTTP/1.1 XXX ") - 1,
  964. dwRespLength -
  965. (sizeof("HTTP/1.1 XXX ") - 1)) &&
  966. strBody.Append("</title></head>",
  967. sizeof("</title></head>") - 1) &&
  968. strBody.Append("<body><h1>",
  969. sizeof("<body><h1>") - 1) &&
  970. strBody.Append( strResp.QueryStr(), dwRespLength) &&
  971. strBody.Append( "</h1></body></html>",
  972. sizeof("</h1></body></html>") - 1 );
  973. }
  974. else
  975. {
  976. dwContentLength = strError.QueryCCH() + sizeof("</body>") - 1
  977. + sizeof("<html></html>") - 1;
  978. _itoa( dwContentLength, ach, 10 );
  979. bBodyBuilt = strBody.Append("Content-Length: ",
  980. sizeof("Content-Length: ") - 1) &&
  981. strBody.Append(ach) &&
  982. strBody.Append("\r\n\r\n<html>", sizeof("\r\n\r\n<html>") - 1) &&
  983. strBody.Append(strError) &&
  984. strBody.Append("</body></html>", sizeof("</body></html>") - 1);
  985. }
  986. }
  987. if (!bBodyBuilt)
  988. {
  989. DBGPRINTF((DBG_CONTEXT,
  990. "Disconnect: Failed to build error message for error %d, aborting connecting\n",
  991. HTResponse));
  992. goto DisconnectNow;
  993. }
  994. pRequest->SetKeepConn(FALSE);
  995. pRequest->SetAuthenticationRequested(FALSE);
  996. fDone = FALSE;
  997. if ( !pRequest->BuildBaseResponseHeader( pRequest->QueryRespBuf(),
  998. &fDone,
  999. &strResp,
  1000. HTTPH_NO_CUSTOM))
  1001. {
  1002. DBGPRINTF((DBG_CONTEXT,
  1003. "Disconnect: Failed to send status (error %d), aborting connecting\n",
  1004. ::GetLastError()));
  1005. goto DisconnectNow;
  1006. }
  1007. DBG_ASSERT(!fDone);
  1008. pszResp = pRequest->QueryRespBufPtr();
  1009. dwRespLength = strlen(pszResp);
  1010. if (HTResponse == HT_SVC_UNAVAILABLE)
  1011. {
  1012. CHAR *pszRespEnd;
  1013. // Need to append a 'Retry-After' header in this case.
  1014. if ((dwRespLength + 1 + g_dwPutTimeoutStrlen) >
  1015. pRequest->QueryRespBuf()->QuerySize())
  1016. {
  1017. // Resize the buffer.
  1018. if (!pRequest->QueryRespBuf()->Resize(dwRespLength + 1 + g_dwPutTimeoutStrlen))
  1019. {
  1020. DBGPRINTF((DBG_CONTEXT,
  1021. "Disconnect: Unable to resize response buf for status %d, aborting connecting\n",
  1022. ::GetLastError()));
  1023. goto DisconnectNow;
  1024. }
  1025. pszResp = pRequest->QueryRespBufPtr();
  1026. }
  1027. pszRespEnd = pszResp + dwRespLength;
  1028. APPEND_STRING(pszRespEnd, "Retry-After: ");
  1029. memcpy(pszRespEnd, g_szPutTimeoutString, g_dwPutTimeoutStrlen);
  1030. pszRespEnd += g_dwPutTimeoutStrlen;
  1031. APPEND_STRING(pszRespEnd, "\r\n");
  1032. dwRespLength = DIFF(pszRespEnd - pszResp);
  1033. }
  1034. if (HTResponse == HT_METHOD_NOT_ALLOWED)
  1035. {
  1036. if ((dwRespLength + 1 + MAX_ALLOW_SIZE) >
  1037. pRequest->QueryRespBuf()->QuerySize())
  1038. {
  1039. // Resize the buffer.
  1040. if (!pRequest->QueryRespBuf()->Resize(dwRespLength + 1 + MAX_ALLOW_SIZE))
  1041. {
  1042. DBGPRINTF((DBG_CONTEXT,
  1043. "Disconnect: Unable to resize response buf for status %d, aborting connecting\n",
  1044. ::GetLastError()));
  1045. goto DisconnectNow;
  1046. }
  1047. pszResp = pRequest->QueryRespBufPtr();
  1048. }
  1049. dwRespLength += pRequest->BuildAllowHeader(
  1050. pRequest->QueryURL(),
  1051. pszResp + dwRespLength
  1052. );
  1053. }
  1054. if ((dwRespLength + 1 + strBody.QueryCB()) > pRequest->QueryRespBuf()->QuerySize() )
  1055. {
  1056. if (!pRequest->QueryRespBuf()->Resize(dwRespLength + 1 + strBody.QueryCB()))
  1057. {
  1058. DBGPRINTF((DBG_CONTEXT,
  1059. "Disconnect: Unable to resize response buf for status %d, aborting connecting\n",
  1060. ::GetLastError()));
  1061. goto DisconnectNow;
  1062. }
  1063. pszResp = pRequest->QueryRespBufPtr();
  1064. }
  1065. // If this is an error response to a head request, truncate it now.
  1066. if (pRequest->QueryVerb() == HTV_HEAD)
  1067. {
  1068. CHAR *pszHeaderEnd;
  1069. DWORD dwBodySize;
  1070. pszHeaderEnd = strstr(strBody.QueryStr(), "\r\n\r\n");
  1071. if (pszHeaderEnd == NULL)
  1072. {
  1073. DBG_ASSERT(FALSE);
  1074. goto DisconnectNow;
  1075. }
  1076. dwBodySize = DIFF(pszHeaderEnd - strBody.QueryStr())
  1077. + sizeof("\r\n\r\n") - 1;
  1078. strBody.SetLen(dwBodySize);
  1079. }
  1080. memcpy( pszResp + dwRespLength, strBody.QueryStr(),
  1081. strBody.QueryCCH() + 1 );
  1082. if ( !pRequest->SendHeader( pszResp,
  1083. dwRespLength + strBody.QueryCCH(),
  1084. IO_FLAG_ASYNC,
  1085. &fDone ))
  1086. {
  1087. DBGPRINTF((DBG_CONTEXT,
  1088. "Disconnect: Failed to send status (error %d), aborting connecting\n",
  1089. ::GetLastError()));
  1090. //
  1091. // It's possible a filter failed the writefile, cause the
  1092. // filter code to issue a disconnect by the time we get here,
  1093. // so recheck the state
  1094. //
  1095. if ( QueryState() != CCS_SHUTDOWN )
  1096. {
  1097. goto DisconnectNow;
  1098. }
  1099. }
  1100. if ( fDone )
  1101. {
  1102. goto DisconnectNow;
  1103. }
  1104. }
  1105. else
  1106. {
  1107. DisconnectNow:
  1108. IF_DEBUG( CONNECTION )
  1109. {
  1110. DBGPRINTF((DBG_CONTEXT,
  1111. "Disconnect: Going to Shutdown for %lx, ref count = %d\n",
  1112. this,
  1113. QueryRefCount() ));
  1114. }
  1115. if ( pfFinished )
  1116. {
  1117. *pfFinished = TRUE;
  1118. }
  1119. SetState( CCS_SHUTDOWN );
  1120. //
  1121. // Do a shutdown to avoid a nasty client reset when:
  1122. //
  1123. // The client sent more entity data then they indicated (very common)
  1124. // and we may not have received all of it in our initial receive
  1125. // buffer OR
  1126. //
  1127. // This was a CGI request. The exiting process will cause the
  1128. // socket handle to be deleted but NT will force a reset on the
  1129. // socket because the process didn't do a close/shutdown (the
  1130. // process inherited this socket handle). Atq does the right thing
  1131. // when using TransmitFile.
  1132. //
  1133. #if CC_REF_TRACKING
  1134. CC_LOG_REF_COUNT( -_cRef );
  1135. #endif
  1136. AtqCloseSocket( QueryAtqContext(),
  1137. fDoShutdown );
  1138. //
  1139. // This removes the last reference count to *this except for
  1140. // any outstanding IO requests
  1141. //
  1142. Dereference();
  1143. }
  1144. }
  1145. /*******************************************************************
  1146. NAME: CLIENT_CONN::DisconnectAllUsers
  1147. SYNOPSIS: Static method that walks the connection list and disconnects
  1148. each active connection.
  1149. ENTRY: Instance - Pointer to a server instance. If NULL, then all
  1150. users are disconnected. If !NULL, then only those users
  1151. associated with the specified instance are disconnected.
  1152. HISTORY:
  1153. Johnl 02-Sep-1994 Created
  1154. ********************************************************************/
  1155. VOID CLIENT_CONN::DisconnectAllUsers( PIIS_SERVER_INSTANCE Instance )
  1156. {
  1157. LIST_ENTRY * pEntry;
  1158. CLIENT_CONN * pconn;
  1159. DBGPRINTF((DBG_CONTEXT,
  1160. "DisconnectAllUsers entered\n"));
  1161. LockGlobals();
  1162. for ( pEntry = listConnections.Flink;
  1163. pEntry != &listConnections;
  1164. pEntry = pEntry->Flink )
  1165. {
  1166. pconn = CONTAINING_RECORD( pEntry, CLIENT_CONN, ListEntry );
  1167. DBG_ASSERT( pconn->CheckSignature() );
  1168. if( Instance == NULL ||
  1169. Instance == (PIIS_SERVER_INSTANCE)pconn->QueryW3Instance() ) {
  1170. #if CC_REF_TRACKING
  1171. //
  1172. // log to our various ref logs
  1173. //
  1174. // NOTE negative indicates no change to ref count
  1175. //
  1176. //
  1177. // log to local (per-object) CLIENT_CONN log
  1178. //
  1179. LogRefCountCCLocal(
  1180. - pconn->_cRef
  1181. , pconn
  1182. , pconn->_phttpReq
  1183. , NULL
  1184. , (pconn->_phttpReq ? pconn->_phttpReq->QueryState() : 0)
  1185. );
  1186. #endif
  1187. //
  1188. // If we've already posted a TransmitFile() for the connection
  1189. // before being asked
  1190. // to shutdown, then AtqCloseSocket() by itself will not close
  1191. // the socket (because by default, we will pass TF_DISCONNECT|
  1192. // TS_REUSE_SOCKET and ATQ will assume WinSock will take care of
  1193. // it). The end result is that we will be stuck
  1194. // waiting for the completion (for 2 minutes). Let's force the
  1195. // issue
  1196. //
  1197. AtqContextSetInfo( pconn->QueryAtqContext(),
  1198. ATQ_INFO_FORCE_CLOSE,
  1199. TRUE );
  1200. AtqCloseSocket( pconn->QueryAtqContext(),
  1201. FALSE );
  1202. }
  1203. }
  1204. UnlockGlobals();
  1205. }
  1206. /*******************************************************************
  1207. NAME: CLIENT_CONN::Reference
  1208. SYNOPSIS: Increments the reference count.
  1209. HISTORY:
  1210. DaveK 10-Sep-1997 Added ref trace logging
  1211. ********************************************************************/
  1212. UINT CLIENT_CONN::Reference( VOID )
  1213. {
  1214. LONG cRefs = InterlockedIncrement( &_cRef );
  1215. CC_LOG_REF_COUNT( cRefs );
  1216. return cRefs;
  1217. }
  1218. /*******************************************************************
  1219. NAME: CLIENT_CONN::Dereference
  1220. SYNOPSIS: Increments the reference count.
  1221. HISTORY:
  1222. DaveK 10-Sep-1997 Added ref trace logging
  1223. ********************************************************************/
  1224. UINT CLIENT_CONN::Dereference( VOID )
  1225. {
  1226. //
  1227. // Write the trace log BEFORE the decrement operation :(
  1228. // If we write it after the decrement, we will run into potential
  1229. // race conditions in this object getting freed up accidentally
  1230. // by another thread
  1231. //
  1232. // NOTE we write (_cRef - 1) == ref count AFTER decrement happens
  1233. //
  1234. LONG cRefsAfter = (_cRef - 1);
  1235. CC_LOG_REF_COUNT( cRefsAfter );
  1236. return InterlockedDecrement( &_cRef );
  1237. }
  1238. /*******************************************************************
  1239. More CLIENT_CONN methods
  1240. SYNOPSIS: More methods.
  1241. .
  1242. HISTORY:
  1243. DaveK 10-Sep-1997 Added this comment
  1244. ********************************************************************/
  1245. DWORD
  1246. CLIENT_CONN::Initialize(
  1247. VOID
  1248. )
  1249. {
  1250. DWORD msScavengeTime = ACACHE_REG_DEFAULT_CLEANUP_INTERVAL;
  1251. HKEY hkey;
  1252. INITIALIZE_CRITICAL_SECTION( &_csBuffList );
  1253. InitializeListHead( &_BuffListHead );
  1254. #if CC_REF_TRACKING
  1255. g_pDbgCCRefTraceLog = CreateRefTraceLog( C_CLIENT_CONN_REFTRACES, 0 );
  1256. #endif
  1257. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1258. ACACHE_REG_PARAMS_REG_KEY,
  1259. 0,
  1260. KEY_READ,
  1261. &hkey ) == NO_ERROR )
  1262. {
  1263. msScavengeTime = ReadRegistryDword( hkey,
  1264. ACACHE_REG_LOOKASIDE_CLEANUP_INTERVAL,
  1265. ACACHE_REG_DEFAULT_CLEANUP_INTERVAL );
  1266. DBG_REQUIRE( !RegCloseKey( hkey ));
  1267. }
  1268. msScavengeTime *= 1000; // Convert to milliseconds
  1269. _FreeListScavengerCookie = ScheduleWorkItem( ClientConnTrimScavenger,
  1270. NULL,
  1271. msScavengeTime,
  1272. TRUE );
  1273. _fGlobalInit = TRUE;
  1274. return NO_ERROR;
  1275. }
  1276. VOID
  1277. CLIENT_CONN::Terminate(
  1278. VOID
  1279. )
  1280. {
  1281. CLIENT_CONN * pConn;
  1282. if ( !_fGlobalInit )
  1283. {
  1284. return;
  1285. }
  1286. RemoveWorkItem( _FreeListScavengerCookie );
  1287. EnterCriticalSection( &_csBuffList );
  1288. while ( !IsListEmpty( &_BuffListHead ))
  1289. {
  1290. pConn = CONTAINING_RECORD( _BuffListHead.Flink,
  1291. CLIENT_CONN,
  1292. _BuffListEntry );
  1293. DBG_ASSERT( pConn->_Signature == CLIENT_CONN_SIGNATURE_FREE );
  1294. RemoveEntryList( &pConn->_BuffListEntry );
  1295. delete pConn;
  1296. }
  1297. LeaveCriticalSection( &_csBuffList );
  1298. DeleteCriticalSection( &_csBuffList );
  1299. #if CC_REF_TRACKING
  1300. DestroyRefTraceLog( g_pDbgCCRefTraceLog );
  1301. g_pDbgCCRefTraceLog = NULL;
  1302. #endif
  1303. }
  1304. CLIENT_CONN *
  1305. CLIENT_CONN::Alloc(
  1306. IN PCLIENT_CONN_PARAMS ClientParams
  1307. )
  1308. {
  1309. CLIENT_CONN * pConn;
  1310. EnterCriticalSection( &_csBuffList );
  1311. if ( !IsListEmpty( &_BuffListHead ))
  1312. {
  1313. pConn = CONTAINING_RECORD( _BuffListHead.Flink,
  1314. CLIENT_CONN,
  1315. _BuffListEntry );
  1316. RemoveEntryList( &pConn->_BuffListEntry );
  1317. _cFree--;
  1318. LeaveCriticalSection( &_csBuffList );
  1319. DBG_ASSERT( pConn->_Signature == CLIENT_CONN_SIGNATURE_FREE );
  1320. pConn->Initialize( ClientParams );
  1321. DBG_ASSERT( pConn->CheckSignature() );
  1322. return pConn;
  1323. }
  1324. LeaveCriticalSection( &_csBuffList );
  1325. return new CLIENT_CONN( ClientParams );
  1326. }
  1327. VOID
  1328. CLIENT_CONN::Free(
  1329. CLIENT_CONN * pConn
  1330. )
  1331. {
  1332. DBG_ASSERT( pConn->CheckSignature() );
  1333. pConn->Reset();
  1334. EnterCriticalSection( &_csBuffList );
  1335. InsertHeadList( &_BuffListHead,
  1336. &pConn->_BuffListEntry );
  1337. _cFree++;
  1338. LeaveCriticalSection( &_csBuffList );
  1339. return;
  1340. }
  1341. VOID
  1342. CLIENT_CONN::TrimFreeList(
  1343. VOID
  1344. )
  1345. {
  1346. CLIENT_CONN * pConn;
  1347. LIST_ENTRY ListEntry;
  1348. // Hold the lock only to reset the list to empty. Do the actual
  1349. // traversal and deletion out side the lock.
  1350. EnterCriticalSection( &_csBuffList );
  1351. if ( IsListEmpty( &_BuffListHead ) )
  1352. {
  1353. LeaveCriticalSection( &_csBuffList );
  1354. DBG_ASSERT( _cFree == 0 );
  1355. return;
  1356. }
  1357. else
  1358. {
  1359. ListEntry = _BuffListHead;
  1360. ListEntry.Blink->Flink = &ListEntry;
  1361. ListEntry.Flink->Blink = &ListEntry;
  1362. _cFree = 0;
  1363. InitializeListHead( &_BuffListHead );
  1364. }
  1365. LeaveCriticalSection( &_csBuffList );
  1366. while ( !IsListEmpty( &ListEntry ))
  1367. {
  1368. pConn = CONTAINING_RECORD( ListEntry.Flink,
  1369. CLIENT_CONN,
  1370. _BuffListEntry );
  1371. DBG_ASSERT( pConn->_Signature == CLIENT_CONN_SIGNATURE_FREE );
  1372. RemoveEntryList( &pConn->_BuffListEntry );
  1373. delete pConn;
  1374. }
  1375. }
  1376. VOID
  1377. WINAPI
  1378. ClientConnTrimScavenger(
  1379. PVOID pContext
  1380. )
  1381. {
  1382. CLIENT_CONN::TrimFreeList();
  1383. }
  1384. /*******************************************************************
  1385. NAME: ::W3Completion
  1386. SYNOPSIS: Completion routine for W3 Atq requests
  1387. HISTORY:
  1388. Johnl 20-Aug-1994 Created
  1389. ********************************************************************/
  1390. VOID W3Completion( PVOID Context,
  1391. DWORD BytesWritten,
  1392. DWORD CompletionStatus,
  1393. OVERLAPPED * lpo )
  1394. {
  1395. CLIENT_CONN * pConn = (CLIENT_CONN *) Context;
  1396. DBG_ASSERT( pConn );
  1397. DBG_ASSERT( pConn->CheckSignature() );
  1398. #if 0
  1399. DBGPRINTF((DBG_CONTEXT,
  1400. "W3Completion( %08lx ) byteswritten = %d, status = %08lx, lpo = %08lx\n",
  1401. Context, BytesWritten, CompletionStatus, lpo ));
  1402. #endif
  1403. if ( !((W3_IIS_SERVICE*)g_pInetSvc)->GetReferenceCount() )
  1404. {
  1405. return;
  1406. }
  1407. W3_IIS_SERVICE::ReferenceW3Service( g_pInetSvc );
  1408. #if CC_REF_TRACKING
  1409. //
  1410. // ATQ notification trace
  1411. //
  1412. // Check for magic signature of ATQ notification
  1413. // ATQ generates such a notification for all non-oplock completion
  1414. //
  1415. if ( ((DWORD)(LPVOID)lpo & 0xf0f0f0f0) == 0xf0f0f0f0 )
  1416. {
  1417. pConn->NotifyAtqProcessContext( BytesWritten,
  1418. CompletionStatus,
  1419. (DWORD)(LPVOID)lpo );
  1420. W3_IIS_SERVICE::DereferenceW3Service( g_pInetSvc );
  1421. return;
  1422. }
  1423. #endif
  1424. ReferenceConn( pConn );
  1425. if ( lpo != NULL )
  1426. {
  1427. lpo->Offset = 0;
  1428. }
  1429. TCP_REQUIRE( pConn->DoWork( BytesWritten,
  1430. CompletionStatus,
  1431. lpo != NULL ));
  1432. DereferenceConn( pConn );
  1433. W3_IIS_SERVICE::DereferenceW3Service( g_pInetSvc );
  1434. }
  1435. #if CC_REF_TRACKING
  1436. VOID
  1437. CLIENT_CONN::NotifyAtqProcessContext(
  1438. DWORD BytesWritten,
  1439. DWORD CompletionStatus,
  1440. DWORD dwSig
  1441. )
  1442. /*++
  1443. Routine Description:
  1444. Store ATQ notification in ref log
  1445. Arguments:
  1446. BytesWritten - count of bytes written
  1447. CompletionStatus - completion status
  1448. Return Value:
  1449. Nothing
  1450. --*/
  1451. {
  1452. //
  1453. // Store current ref count as checkpoint
  1454. //
  1455. CCA_LOG_REF_COUNT( -_cRef, BytesWritten, CompletionStatus, dwSig );
  1456. }
  1457. #endif
  1458. /*******************************************************************
  1459. NAME: ::CheckForTermination
  1460. SYNOPSIS: Looks in the passed buffer for a line followed by a blank
  1461. line. If not found, the buffer is resized.
  1462. ENTRY: pfTerminted - Set to TRUE if this block is terminated
  1463. pbuff - Pointer to buffer data
  1464. cbData - Size of pbuff
  1465. ppbExtraData - Receives a pointer to the first byte
  1466. of extra data following the header
  1467. pcbExtraData - Number of bytes in data following the header
  1468. cbReallocSize - Increase buffer by this number of bytes
  1469. if the terminate isn't found
  1470. RETURNS: TRUE if successful, FALSE otherwise
  1471. HISTORY:
  1472. Johnl 28-Sep-1994 Created
  1473. ********************************************************************/
  1474. BOOL CheckForTermination( BOOL * pfTerminated,
  1475. BUFFER * pbuff,
  1476. UINT cbData,
  1477. BYTE * * ppbExtraData,
  1478. DWORD * pcbExtraData,
  1479. UINT cbReallocSize )
  1480. {
  1481. DWORD cbNewSize;
  1482. cbNewSize = cbData + 1 + cbReallocSize;
  1483. if ( !pbuff->Resize( cbNewSize ) )
  1484. {
  1485. return FALSE;
  1486. }
  1487. //
  1488. // Terminate the string but make sure it will fit in the
  1489. // buffer
  1490. //
  1491. CHAR * pchReq = (CHAR *) pbuff->QueryPtr();
  1492. *(pchReq + cbData) = '\0';
  1493. //
  1494. // Scan for double end of line marker
  1495. //
  1496. //
  1497. // if do not care for ptr info, can use fast method
  1498. //
  1499. if ( ppbExtraData == NULL )
  1500. {
  1501. if ( FastScanForTerminator( pchReq, cbData )
  1502. || ScanForTerminator( pchReq ) )
  1503. {
  1504. *pfTerminated = TRUE;
  1505. return TRUE;
  1506. }
  1507. goto not_term;
  1508. }
  1509. *ppbExtraData = ScanForTerminator( pchReq );
  1510. if ( *ppbExtraData )
  1511. {
  1512. *pcbExtraData = cbData - DIFF(*ppbExtraData - (BYTE *) pchReq);
  1513. *pfTerminated = TRUE;
  1514. return TRUE;
  1515. }
  1516. not_term:
  1517. if ( cbNewSize > g_cbMaxClientRequestBuffer )
  1518. {
  1519. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  1520. return FALSE;
  1521. }
  1522. *pfTerminated = FALSE;
  1523. return TRUE;
  1524. }
  1525. BOOL
  1526. CLIENT_CONN::RequestAbortiveClose(
  1527. )
  1528. /*++
  1529. Routine Description:
  1530. Request for abortive close on disconnect
  1531. Arguments:
  1532. None
  1533. Returns:
  1534. TRUE if successful, else FALSE
  1535. --*/
  1536. {
  1537. _fAbortiveClose = TRUE;
  1538. AtqContextSetInfo( QueryAtqContext(), ATQ_INFO_ABORTIVE_CLOSE, TRUE );
  1539. return TRUE;
  1540. }
  1541. BOOL
  1542. CLIENT_CONN::CloseConnection(
  1543. )
  1544. /*++
  1545. Routine Description:
  1546. Cancels any pending async IO by closing the socket
  1547. Arguments:
  1548. None
  1549. Returns:
  1550. TRUE if successful, else FALSE
  1551. --*/
  1552. {
  1553. return AtqCloseSocket( QueryAtqContext(), FALSE );
  1554. }
  1555. #if 0
  1556. BOOL
  1557. CLIENT_CONN::CheckIpAccess(
  1558. LPBOOL pfGranted,
  1559. LPBOOL pfNeedDns,
  1560. LPBOOL pfComplete
  1561. )
  1562. /*++
  1563. Routine Description:
  1564. Check IP access granted
  1565. Arguments:
  1566. pfGranted - updated with grant status
  1567. pfNeedDns - updated with TRUE if need DNS name for DNS check
  1568. pfComplete - updated with TRUE is check complete
  1569. Return Value:
  1570. TRUE if no error, otherwise FALSE
  1571. --*/
  1572. {
  1573. return _acCheck.CheckIpAccess( pfGranted, pfNeedDns, pfComplete );
  1574. }
  1575. BOOL
  1576. CLIENT_CONN::CheckDnsAccess(
  1577. LPBOOL pfGranted
  1578. )
  1579. /*++
  1580. Routine Description:
  1581. Check DNS access granted
  1582. Arguments:
  1583. pfGranted - updated with grant status
  1584. Return Value:
  1585. TRUE if no error, otherwise FALSE
  1586. --*/
  1587. {
  1588. return _acCheck.CheckDnsAccess( pfGranted );
  1589. }
  1590. #endif
  1591. VOID
  1592. DumpW3InfoToHTML(
  1593. OUT CHAR * pch,
  1594. IN OUT LPDWORD pcb
  1595. )
  1596. {
  1597. DWORD cb = 0;
  1598. DBG_ASSERT( *pcb > 1024 );
  1599. cb += wsprintf( pch + cb, "<TABLE BORDER>" );
  1600. cb += wsprintf( pch + cb, "<TR><TD>Current Connections: </TD><TD>%d</TD></TR>", g_pW3Stats->QueryCurrentConnections() );
  1601. cb += wsprintf( pch + cb, "<TR><TD>Connections Attempts: </TD><TD>%d</TD></TR>", g_pW3Stats->QueryConnectionAttempts() );
  1602. cb += wsprintf( pch + cb, "<TR><TD>Current CGIs: </TD><TD>%d</TD></TR>", g_pW3Stats->QueryCurrentCGIRequests() );
  1603. cb += wsprintf( pch + cb, "<TR><TD>Total CGIs: </TD><TD>%d</TD></TR>", g_pW3Stats->QueryTotalCGIRequests() );
  1604. cb += wsprintf( pch + cb, "<TR><TD>Current ISAPI Requests: </TD><TD>%d</TD></TR>", g_pW3Stats->QueryCurrentBGIRequests() );
  1605. cb += wsprintf( pch + cb, "<TR><TD>Total ISAPI Requests: </TD><TD>%d</TD></TR>", g_pW3Stats->QueryTotalBGIRequests() );
  1606. cb += wsprintf( pch + cb, "<TR><TD>Current Connections: </TD><TD>%d</TD></TR>", g_pW3Stats->QueryCurrentConnections() );
  1607. cb += wsprintf( pch + cb, "<TR><TD>Free CLIENT_CONN list: </TD><TD>%d</TD></TR>", CLIENT_CONN::QueryFreeListSize() );
  1608. cb += wsprintf( pch + cb, "</TABLE><p>" );
  1609. DBG_ASSERT( *pcb > cb );
  1610. *pcb = cb;
  1611. }