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.

2517 lines
72 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. send.cxx
  5. Abstract:
  6. This file contains the implementation of the HttpSendRequestA API.
  7. Contents:
  8. HTTP_REQUEST_HANDLE_OBJECT::InitBeginSendRequest
  9. HTTP_REQUEST_HANDLE_OBJECT::CheckClientRequestHeaders
  10. CFsm_HttpSendRequest::RunSM
  11. HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Start
  12. HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Finish
  13. HTTP_REQUEST_HANDLE_OBJECT::UpdateProxyInfo
  14. HTTP_REQUEST_HANDLE_OBJECT::FindConnCloseRequestHeader
  15. Author:
  16. Keith Moore (keithmo) 16-Nov-1994
  17. Revision History:
  18. 29-Apr-97 rfirth
  19. Conversion to FSM
  20. --*/
  21. #include <wininetp.h>
  22. #include <perfdiag.hxx>
  23. #include "httpp.h"
  24. //
  25. // HTTP Request Handle Object methods
  26. //
  27. DWORD
  28. HTTP_REQUEST_HANDLE_OBJECT::InitBeginSendRequest(
  29. IN LPCSTR lpszHeaders OPTIONAL,
  30. IN DWORD dwHeadersLength,
  31. IN LPVOID *lplpOptional,
  32. IN LPDWORD lpdwOptionalLength,
  33. IN DWORD dwOptionalLengthTotal
  34. )
  35. /*++
  36. Routine Description:
  37. Performs Initiatization of the HTTP Request by setting up the necessary
  38. headers and preparing the POST data.
  39. Arguments:
  40. lpszHeaders - Additional headers to be appended to the request.
  41. This may be NULL if there are no additional
  42. headers to append
  43. dwHeadersLength - The length (in characters) of the additional
  44. headers. If this is -1L and lpszAdditional is
  45. non-NULL, then lpszAdditional is assumed to be
  46. zero terminated (ASCIIZ)
  47. lpOptionalData - Any optional data to send immediately after the
  48. request headers. This is typically used for POST
  49. operations. This may be NULL if there is no
  50. optional data to send
  51. dwOptionalDataLength - The length (in BYTEs) of the optional data. This
  52. may be zero if there is no optional data to send
  53. dwOptionalLengthTotal - Total Length for File Upload.
  54. Return Value:
  55. DWORD
  56. Success - ERROR_SUCCESS
  57. Failure - One of the Win32 Error values.
  58. Comments:
  59. --*/
  60. {
  61. DEBUG_ENTER((DBG_HTTP,
  62. Dword,
  63. "HTTP_REQUEST_HANDLE_OBJECT::InitBeginSendRequest",
  64. "%#x, %d, %d, %#x",
  65. lplpOptional ? *lplpOptional : NULL,
  66. lpdwOptionalLength ? *lpdwOptionalLength : NULL,
  67. dwOptionalLengthTotal
  68. ));
  69. DWORD error = ERROR_SUCCESS;
  70. LPVOID lpOptional = *lplpOptional;
  71. DWORD dwOptionalLength = *lpdwOptionalLength;
  72. //
  73. // validate parameters
  74. //
  75. if ((lpOptional == NULL) || (dwOptionalLength == 0)) {
  76. lpOptional = NULL;
  77. dwOptionalLength = 0;
  78. }
  79. //
  80. // the headers lengths can be -1 meaning that we should calculate the
  81. // string lengths. We must do this before calling MakeAsyncRequest()
  82. // which is expecting the parameters to be correct
  83. //
  84. if (dwHeadersLength == -1)
  85. {
  86. dwHeadersLength = lstrlen((LPCSTR)lpszHeaders);
  87. }
  88. //
  89. // if the caller specified some additional headers, then add them before
  90. // we make the request asynchronously
  91. //
  92. if (ARGUMENT_PRESENT(lpszHeaders) && (*lpszHeaders != '\0')) {
  93. //
  94. // we use the API here because the headers came from the app, and
  95. // we don't trust it
  96. //
  97. if (!HttpAddRequestHeaders(GetPseudoHandle(),
  98. lpszHeaders,
  99. dwHeadersLength,
  100. //
  101. // if the object is being re-used then
  102. // replace the headers to avoid
  103. // duplicating original headers
  104. //
  105. IS_VALID_HTTP_STATE(this, REUSE, TRUE)
  106. ? ( HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD ): 0
  107. //? HTTP_ADDREQ_FLAG_REPLACE : 0
  108. )) {
  109. error = GetLastError();
  110. goto quit;
  111. }
  112. }
  113. //
  114. // If we fall through then we are connected and a) either the thing
  115. // is not in the cache or we did a conditional get or c) there was
  116. // some cache error
  117. //
  118. error = ERROR_SUCCESS;
  119. SetStatusCode(0);
  120. //
  121. // if the app supplied a user-agent string to InternetOpen() AND hasn't
  122. // added a "User-Agent:" header, then add it
  123. //
  124. LPSTR userAgent;
  125. DWORD userAgentLength;
  126. userAgent = GetUserAgent(&userAgentLength);
  127. if (userAgent != NULL) {
  128. ReplaceRequestHeader(HTTP_QUERY_USER_AGENT,
  129. userAgent,
  130. userAgentLength,
  131. 0, // dwIndex,
  132. ADD_HEADER_IF_NEW
  133. );
  134. }
  135. //
  136. // do the same thing with the "Host:" header. The header-value is the host
  137. // name supplied to InternetConnect() (or the name of the redirected host)
  138. //
  139. LPSTR hostName;
  140. DWORD hostNameLength;
  141. INTERNET_PORT hostPort;
  142. hostName = GetHostNameNoScopeID(&hostNameLength);
  143. hostPort = GetHostPort();
  144. INET_ASSERT((hostName != NULL) && (hostNameLength > 0));
  145. char hostValue[INTERNET_MAX_HOST_NAME_LENGTH + sizeof(":4294967295")];
  146. if ((hostPort != INTERNET_DEFAULT_HTTP_PORT)
  147. && (hostPort != INTERNET_DEFAULT_HTTPS_PORT)) {
  148. if (lstrlen(hostName) > INTERNET_MAX_HOST_NAME_LENGTH)
  149. {
  150. error = ERROR_INVALID_PARAMETER;
  151. goto quit;
  152. }
  153. hostNameLength = wsprintf(hostValue, "%s:%d", hostName, (hostPort & 0xffff));
  154. hostName = hostValue;
  155. }
  156. ReplaceRequestHeader(HTTP_QUERY_HOST,
  157. hostName,
  158. hostNameLength,
  159. 0, // dwIndex,
  160. ADD_HEADER_IF_NEW
  161. );
  162. //
  163. // if the app requested keep-alive then add the header; if we're going via
  164. // proxy then use the proxy-connection header
  165. //
  166. //if (pRequest->GetOpenFlags() & INTERNET_FLAG_KEEP_CONNECTION) {
  167. // pRequest->SetWantKeepAlive(TRUE);
  168. //}
  169. //
  170. // add the content-length header IF we are sending data OR this is a POST,
  171. // AND ONLY if the app has not already added the header
  172. //
  173. if (dwOptionalLength || dwOptionalLengthTotal)
  174. SetMethodBody();
  175. if (((dwOptionalLength != 0) || (dwOptionalLengthTotal != 0))
  176. //
  177. // BUGBUG - just comparing against a method type is insufficient. We need
  178. // a test of whether the method implies sending data (PUT, etc).
  179. // We make the same test in other places
  180. //
  181. || ((GetMethodType() != HTTP_METHOD_TYPE_GET) && (GetMethodType() != HTTP_METHOD_TYPE_HEAD)))
  182. {
  183. DWORD dwContentLength;
  184. char number[sizeof("4294967295")];
  185. //
  186. // For File Upload we need to add the Content-Length
  187. // header off of the Total Length, Not the current
  188. // data size. Since we get more data via InternetWriteFile
  189. //
  190. if ( dwOptionalLengthTotal != 0 )
  191. {
  192. dwContentLength = dwOptionalLengthTotal;
  193. }
  194. else
  195. {
  196. dwContentLength = dwOptionalLength;
  197. }
  198. // _itoa(dwOptionalLength, number, 10);
  199. wsprintf(number, "%u", dwContentLength);
  200. DWORD numberLength = lstrlen(number);
  201. /*----------------------------------------------------------------------
  202. #62953 NOTE -- Authstate can never be in the AUTHSTATE_NEGOTIATE
  203. state here. It is not necessary to zero out the content length
  204. header here when omitting post data on NTLM negotiate since this
  205. will be done later in the request. The commented-out code is not
  206. necessary.
  207. if ((GetMethodType() == HTTP_METHOD_TYPE_POST)
  208. && (GetAuthState() == AUTHSTATE_NEGOTIATE))
  209. {
  210. ReplaceRequestHeader(HTTP_QUERY_CONTENT_LENGTH,
  211. "0",
  212. 1,
  213. 0, // dwIndex
  214. ADD_HEADER
  215. );
  216. }
  217. ---------------------------------------------------------------------*/
  218. // Normally we don't over-write the content-length
  219. // header if one already exists.
  220. DWORD dwAddHeader;
  221. dwAddHeader = ADD_HEADER_IF_NEW;
  222. // But if we're posting data and have an auth ctx
  223. // over-write the content-length header which will
  224. // have been reset to 0 to omit post data on the
  225. // negotiate phase.
  226. AUTHCTX *pAuthCtx;
  227. pAuthCtx = GetAuthCtx();
  228. if (pAuthCtx)
  229. {
  230. dwAddHeader = ADD_HEADER;
  231. }
  232. ReplaceRequestHeader(HTTP_QUERY_CONTENT_LENGTH,
  233. (LPSTR)number,
  234. numberLength,
  235. 0, // dwIndex
  236. dwAddHeader
  237. );
  238. }
  239. quit:
  240. *lplpOptional = lpOptional;
  241. *lpdwOptionalLength = dwOptionalLength;
  242. DEBUG_LEAVE(error);
  243. return error;
  244. }
  245. DWORD
  246. CFsm_HttpSendRequest::RunSM(
  247. IN CFsm * Fsm
  248. )
  249. /*++
  250. Routine Description:
  251. description-of-function.
  252. Arguments:
  253. Fsm -
  254. Return Value:
  255. DWORD
  256. --*/
  257. {
  258. DEBUG_ENTER((DBG_HTTP,
  259. Dword,
  260. "CFsm_HttpSendRequest::RunSM",
  261. "%#x",
  262. Fsm
  263. ));
  264. DWORD error;
  265. HTTP_REQUEST_HANDLE_OBJECT * pRequest;
  266. START_SENDREQ_PERF();
  267. CFsm_HttpSendRequest * stateMachine = (CFsm_HttpSendRequest *)Fsm;
  268. pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext();
  269. switch (Fsm->GetState()) {
  270. case FSM_STATE_INIT:
  271. case FSM_STATE_CONTINUE:
  272. //CHECK_FSM_OWNED(Fsm);
  273. error = pRequest->HttpSendRequest_Start(stateMachine);
  274. break;
  275. case FSM_STATE_FINISH:
  276. //CHECK_FSM_OWNED(Fsm);
  277. error = pRequest->HttpSendRequest_Finish(stateMachine);
  278. break;
  279. case FSM_STATE_ERROR:
  280. //CHECK_FSM_OWNED(Fsm);
  281. error = Fsm->GetError();
  282. //
  283. // If we block to call GetProxyInfo async, then
  284. // we may get unblocked during a cancel. We need to
  285. // handle it by freeing the object in our destructor.
  286. //
  287. INET_ASSERT( (!stateMachine->m_fOwnsProxyInfoQueryObj) ?
  288. ( error == ERROR_WINHTTP_OPERATION_CANCELLED ||
  289. error == ERROR_WINHTTP_TIMEOUT ) :
  290. TRUE );
  291. Fsm->SetDone();
  292. break;
  293. default:
  294. //CHECK_FSM_OWNED(Fsm);
  295. stateMachine->m_fOwnsProxyInfoQueryObj = TRUE;
  296. error = ERROR_WINHTTP_INTERNAL_ERROR;
  297. Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
  298. INET_ASSERT(FALSE);
  299. break;
  300. }
  301. DEBUG_LEAVE(error);
  302. STOP_SENDREQ_PERF();
  303. return error;
  304. }
  305. DWORD
  306. HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Start(
  307. IN CFsm_HttpSendRequest * Fsm
  308. )
  309. /*++
  310. Routine Description:
  311. Calls SendData() method in a loop, handling redirects (& authentications?)
  312. until we have successfully started to retrieve what was originally requested
  313. Arguments:
  314. Fsm - HTTP send request FSM
  315. Return Value:
  316. DWORD
  317. Success - ERROR_SUCCESS
  318. Operation completed successfully
  319. ERROR_IO_PENDING
  320. Operation will complete asynchronously
  321. Failure - ERROR_WINHTTP_INCORRECT_HANDLE_STATE
  322. The HTTP request handle is in the wrong state for this
  323. request
  324. ERROR_NOT_ENOUGH_MEMORY
  325. Ran out of resources
  326. --*/
  327. {
  328. DEBUG_ENTER((DBG_HTTP,
  329. Dword,
  330. "HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Start",
  331. "%#x",
  332. Fsm
  333. ));
  334. PERF_ENTER(HttpSendRequest_Start);
  335. //CHECK_FSM_OWNED(Fsm);
  336. CFsm_HttpSendRequest & fsm = *Fsm;
  337. //
  338. // we must loop here while the server redirects us or while we authenticate
  339. // the user
  340. //
  341. FSM_STATE state = fsm.GetState();
  342. DWORD error = fsm.GetError();
  343. //
  344. // We set m_fOwnsProxyInfoObj TRUE, because by virtue of being here
  345. // we have know that we now own the pointer in our fsm, pointed to by
  346. // fsm.m_pProxyInfoQuery.
  347. //
  348. // This boolean is used to know when we are allowed to FREE and ACCESS
  349. // this pointer. If FALSE, we CANNOT touch this pointer because
  350. // the auto-proxy thread may be accessing it. The auto-proxy thread
  351. // will unblock us, this releasing its "un-offical" lock on this pointer.
  352. //
  353. fsm.m_fOwnsProxyInfoQueryObj = TRUE;
  354. if (state == FSM_STATE_INIT)
  355. {
  356. if ( fsm.m_arRequest == AR_HTTP_END_SEND_REQUEST )
  357. {
  358. state = FSM_STATE_4;
  359. fsm.SetFunctionState(FSM_STATE_4);
  360. }
  361. }
  362. else
  363. {
  364. state = fsm.GetFunctionState();
  365. }
  366. retry_send_request:
  367. do {
  368. switch (state) {
  369. case FSM_STATE_INIT:
  370. case FSM_STATE_1:
  371. //CHECK_FSM_OWNED(Fsm);
  372. fsm.m_iRetries = 2;
  373. fsm.m_bAuthNotFinished = FALSE;
  374. fsm.m_dwCookieIndex = 0;
  375. //
  376. // Terrible bug that afflicts NS servers while doing SSL,
  377. // they lie (those buggers), and claim they do keep-alive,
  378. // but when we attempt to reuse their Keep-Alive sockets,
  379. // they all fail, so we therefore must increase the retry count
  380. // so we can empty all the bad keep-alive sockets out of the pool
  381. //
  382. if ( (GetOpenFlags() & WINHTTP_FLAG_SECURE) )
  383. {
  384. CServerInfo * pServerInfo = GetServerInfo();
  385. if ( pServerInfo && pServerInfo->IsBadNSServer() )
  386. {
  387. fsm.m_iRetries = 5;
  388. }
  389. }
  390. //
  391. // if we're not in the right state to send, drain the socket
  392. //
  393. if (!IsValidHttpState(SEND)) {
  394. #define DRAIN_SOCKET_BUFFER_LENGTH (1 K)
  395. fsm.m_dwTotalBytesDrained = 0;
  396. if (fsm.m_pBuffer == NULL) {
  397. fsm.m_pBuffer = (LPVOID)ALLOCATE_MEMORY(DRAIN_SOCKET_BUFFER_LENGTH);
  398. if (fsm.m_pBuffer == NULL) {
  399. error = ERROR_NOT_ENOUGH_MEMORY;
  400. goto quit;
  401. }
  402. }
  403. do {
  404. if (!fsm.m_bSink) {
  405. fsm.m_bSink = TRUE;
  406. fsm.SetFunctionState(FSM_STATE_2);
  407. error = ReadData(fsm.m_pBuffer,
  408. DRAIN_SOCKET_BUFFER_LENGTH,
  409. &fsm.m_dwBytesDrained,
  410. TRUE,
  411. 0);
  412. if (error == ERROR_IO_PENDING) {
  413. goto quit;
  414. }
  415. }
  416. //
  417. // fall through to state 2
  418. //
  419. case FSM_STATE_2:
  420. fsm.m_dwTotalBytesDrained += fsm.m_dwBytesDrained;
  421. if (fsm.m_dwTotalBytesDrained > _dwMaxResponseDrainSize)
  422. {
  423. error = ERROR_WINHTTP_RESPONSE_DRAIN_OVERFLOW;
  424. goto quit;
  425. }
  426. fsm.m_bSink = FALSE;
  427. } while ((error == ERROR_SUCCESS) && (fsm.m_dwBytesDrained != 0));
  428. if (error != ERROR_SUCCESS) {
  429. goto quit;
  430. }
  431. if (fsm.m_pBuffer != NULL) {
  432. fsm.m_pBuffer = (LPVOID)FREE_MEMORY(fsm.m_pBuffer);
  433. INET_ASSERT(fsm.m_pBuffer == NULL);
  434. }
  435. ReuseObject();
  436. INET_ASSERT(!IsData());
  437. INET_ASSERT(IS_VALID_HTTP_STATE(this, SEND, TRUE));
  438. //
  439. // BUGBUG - if we're not in the right state?
  440. //
  441. }
  442. //
  443. // generate the correct request headers based
  444. // on what types, or whether we're using
  445. // proxies
  446. //
  447. fsm.SetFunctionState(FSM_STATE_3);
  448. error = UpdateProxyInfo(Fsm, FALSE);
  449. if (error == ERROR_IO_PENDING) {
  450. goto done;
  451. }
  452. //
  453. // set function state to not-FSM_STATE_3 to differentiate interrupted
  454. // path in FSM_STATE_3
  455. //
  456. fsm.SetFunctionState(FSM_STATE_BAD);
  457. //
  458. // fall through
  459. //
  460. case FSM_STATE_3:
  461. if ((error == ERROR_SUCCESS)
  462. && (fsm.GetFunctionState() == FSM_STATE_3)) {
  463. error = UpdateProxyInfo(Fsm, TRUE);
  464. }
  465. if (error != ERROR_SUCCESS) {
  466. fsm.m_bCancelRedoOfProxy = TRUE;
  467. goto quit;
  468. }
  469. //
  470. // get any cookies required for this site, but only if app didn't
  471. // tell us it will handle cookies
  472. //
  473. if (!(GetOpenFlags() & INTERNET_FLAG_NO_COOKIES) )
  474. {
  475. CreateCookieHeaderIfNeeded();
  476. }
  477. //
  478. // if this URL requires authentication then add its header here, but
  479. // only if the app didn't tell us not to
  480. //
  481. if (!(GetOpenFlags() & INTERNET_FLAG_NO_AUTH))
  482. {
  483. SetPPAbort(FALSE); // let's assume Passport is not going to abort the send.
  484. error = AuthOnRequest(this);
  485. if (error != ERROR_SUCCESS)
  486. {
  487. goto quit;
  488. }
  489. if (PPAbort())
  490. {
  491. // Passport needed to abort the send cuz the DA wanted to redirect
  492. // the App to an different site *AND* the app wanted to handle the
  493. // redirect itself.
  494. error = ERROR_WINHTTP_LOGIN_FAILURE;
  495. goto quit;
  496. }
  497. }
  498. try_again:
  499. fsm.SetFunctionState(FSM_STATE_4);
  500. DEBUG_PRINT(HTTP, INFO, ("State_3_Start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
  501. error = DoFsm(New CFsm_SendRequest(fsm.m_lpOptional,
  502. fsm.m_dwOptionalLength,
  503. this
  504. ));
  505. if (error == ERROR_IO_PENDING) {
  506. goto quit;
  507. }
  508. //
  509. // fall through
  510. //
  511. case FSM_STATE_4:
  512. DEBUG_PRINT(HTTP, INFO, ("State_4_start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
  513. //CHECK_FSM_OWNED(Fsm);
  514. fsm.m_bWasKeepAlive = (_bKeepAliveConnection || IsKeepAlive());
  515. if ((error != ERROR_SUCCESS)
  516. || ((GetStatusCode() != HTTP_STATUS_OK) && (GetStatusCode() != 0))) {
  517. //
  518. // must be doing proxy tunnelling request if status code set
  519. //
  520. INET_ASSERT(((GetStatusCode() != HTTP_STATUS_OK)
  521. && (GetStatusCode() != 0))
  522. ? IsTalkingToSecureServerViaProxy()
  523. : TRUE
  524. );
  525. //
  526. // server may have reset keep-alive connection
  527. //
  528. if ((error == ERROR_WINHTTP_CONNECTION_ERROR)
  529. && fsm.m_bWasKeepAlive
  530. && (--fsm.m_iRetries != 0)) {
  531. DEBUG_PRINT(HTTP,
  532. INFO,
  533. ("keep-alive connection failed after send. Retrying\n"
  534. ));
  535. //dprintf("*** retrying k-a connection after send\n");
  536. CloseConnection(TRUE);
  537. goto try_again;
  538. }
  539. goto quit;
  540. }
  541. if (fsm.m_arRequest == AR_HTTP_BEGIN_SEND_REQUEST) {
  542. goto quit;
  543. }
  544. fsm.SetFunctionState(FSM_STATE_5);
  545. error = DoFsm(New CFsm_ReceiveResponse(this));
  546. if (error == ERROR_IO_PENDING) {
  547. goto quit;
  548. }
  549. //
  550. // fall through
  551. //
  552. case FSM_STATE_5:
  553. DEBUG_PRINT(HTTP, INFO, ("State_5_Start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
  554. //CHECK_FSM_OWNED(Fsm);
  555. if (error != ERROR_SUCCESS) {
  556. //dprintf("*** post-receive: error=%d, retries=%d\n", error, fsm.m_iRetries);
  557. //
  558. // server may have reset keep-alive connection
  559. //
  560. if ((error == ERROR_WINHTTP_CONNECTION_ERROR)
  561. && (!IsWriteRequired())
  562. && fsm.m_bWasKeepAlive
  563. && (--fsm.m_iRetries != 0)) {
  564. DEBUG_PRINT(HTTP,
  565. INFO,
  566. ("keep-alive connection failed after receive. Retrying\n"
  567. ));
  568. //dprintf("*** retrying k-a connection after receive\n");
  569. CloseConnection(TRUE);
  570. _ResponseHeaders.FreeHeaders();
  571. ResetResponseVariables();
  572. _ResponseHeaders.Initialize();
  573. goto try_again;
  574. }
  575. goto quit;
  576. }
  577. fsm.SetFunctionState(FSM_STATE_6);
  578. case FSM_STATE_6:
  579. DEBUG_PRINT(HTTP, INFO, ("State_6_Start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
  580. //
  581. // put any received cookie headers in the cookie jar, but only if the
  582. // app didn't tell us not to
  583. //
  584. if (!(GetOpenFlags() & INTERNET_FLAG_NO_COOKIES)
  585. && IsResponseHeaderPresent(HTTP_QUERY_SET_COOKIE) )
  586. {
  587. DWORD dwError;
  588. dwError = ExtractSetCookieHeaders(&fsm.m_dwCookieIndex);
  589. if ( dwError == ERROR_IO_PENDING )
  590. {
  591. error = ERROR_IO_PENDING;
  592. goto quit;
  593. }
  594. }
  595. //
  596. // we need to handle various intermediary return codes:
  597. //
  598. // 30x - redirection
  599. // 40x - authentication
  600. //
  601. // BUT ONLY if the app didn't tell us it wanted to handle these itself
  602. //
  603. DWORD statusCode;
  604. BOOL bNoAuth;
  605. statusCode = GetStatusCode();
  606. bNoAuth = (GetOpenFlags() & INTERNET_FLAG_NO_AUTH) ? TRUE : FALSE;
  607. //
  608. // if the status is 200 (most frequently return header == success)
  609. // and we are not authenticating all responses then we're done
  610. //
  611. if ((statusCode == HTTP_STATUS_OK) && bNoAuth) {
  612. goto quit;
  613. }
  614. //
  615. // handle authentication before checking the cache
  616. //
  617. if (!bNoAuth) {
  618. //
  619. // call packages for basic, ntlm
  620. //
  621. error = AuthOnResponse(this);
  622. // passport1.4 auth could change the status code from 302 to 401 here
  623. statusCode = GetStatusCode();
  624. if (error == ERROR_WINHTTP_FORCE_RETRY) {
  625. // Force a retry error only if Writes are required, otherwise we have all the data for a redirect:
  626. if ( fsm.m_arRequest == AR_HTTP_END_SEND_REQUEST && IsWriteRequired())
  627. {
  628. goto quit;
  629. }
  630. //
  631. // the object has been updated with new info - try again
  632. //
  633. fsm.m_bFinished = FALSE;
  634. fsm.m_bAuthNotFinished = TRUE;
  635. error = ERROR_SUCCESS;
  636. //
  637. // Reset auto-proxy info so we can retry the connection
  638. //
  639. if ( fsm.m_fOwnsProxyInfoQueryObj && fsm.m_pProxyInfoQuery && fsm.m_pProxyInfoQuery->IsAlloced())
  640. {
  641. delete fsm.m_pProxyInfoQuery;
  642. fsm.m_pProxyInfoQuery = NULL;
  643. }
  644. } else if (error == ERROR_WINHTTP_INCORRECT_PASSWORD) {
  645. //
  646. // just return success to the app which will have to check the
  647. // headers and make the request again, with the right password
  648. //
  649. error = ERROR_SUCCESS;
  650. goto quit;
  651. }
  652. }
  653. //
  654. // if we can read from the cache then let us try
  655. //
  656. if ((statusCode == HTTP_STATUS_OK)
  657. || (statusCode == HTTP_STATUS_NOT_MODIFIED)
  658. || (statusCode == HTTP_STATUS_PRECOND_FAILED)
  659. || (statusCode == HTTP_STATUS_PARTIAL_CONTENT)
  660. || (statusCode == 0)) {
  661. }
  662. BOOL fFollowRedirect;
  663. fFollowRedirect = FALSE;
  664. switch (GetDwordOption( WINHTTP_OPTION_REDIRECT_POLICY))
  665. {
  666. case WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS:
  667. case WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP: // leaving SSL is detected/preved in the redirect FSM
  668. fFollowRedirect = TRUE;
  669. break;
  670. case WINHTTP_OPTION_REDIRECT_POLICY_NEVER:
  671. fFollowRedirect = FALSE;;
  672. break;
  673. default:
  674. INET_ASSERT(0);
  675. break;
  676. };
  677. if (_pAuthCtx)
  678. {
  679. if (_pAuthCtx->GetSchemeType() == WINHTTP_AUTH_SCHEME_PASSPORT)
  680. {
  681. PASSPORT_CTX* pPPCtx = (PASSPORT_CTX*)_pAuthCtx;
  682. if (pPPCtx->m_lpszRetUrl)
  683. {
  684. fFollowRedirect = TRUE;
  685. }
  686. }
  687. }
  688. //
  689. // handle redirection
  690. //
  691. fsm.m_tMethodRedirect = HTTP_METHOD_TYPE_UNKNOWN;
  692. fsm.m_bRedirected = FALSE;
  693. if (((statusCode == HTTP_STATUS_AMBIGUOUS) // 300
  694. || (statusCode == HTTP_STATUS_MOVED) // 301
  695. || (statusCode == HTTP_STATUS_REDIRECT) // 302
  696. || (statusCode == HTTP_STATUS_REDIRECT_METHOD) // 303
  697. || (statusCode == HTTP_STATUS_REDIRECT_KEEP_VERB)) // 307
  698. && fFollowRedirect) {
  699. //
  700. // Clean out expired PROXY_STATE
  701. //
  702. error = ERROR_SUCCESS;
  703. if ( fsm.m_fOwnsProxyInfoQueryObj && fsm.m_pProxyInfoQuery && fsm.m_pProxyInfoQuery->IsAlloced())
  704. {
  705. delete fsm.m_pProxyInfoQuery;
  706. fsm.m_pProxyInfoQuery = NULL;
  707. }
  708. //fsm.m_pProxyState = NULL;
  709. SetProxyName(NULL, 0, 0);
  710. //
  711. // if we've already had the max allowable redirects then quit
  712. //
  713. if (fsm.m_dwRedirectCount == 0) {
  714. error = ERROR_HTTP_REDIRECT_FAILED;
  715. fsm.m_bRedirectCountedOut = TRUE;
  716. goto quit;
  717. }
  718. //
  719. // we got 300 (ambiguous), 301 (permanent move), 302 (temporary
  720. // move), or 303 (redirection using new method)
  721. //
  722. switch (statusCode) {
  723. case HTTP_STATUS_AMBIGUOUS:
  724. //
  725. // 300 - multiple choice
  726. //
  727. //
  728. // If there is a Location header, we do an "automatic" redirect
  729. //
  730. if (_ResponseHeaders.LockHeaders())
  731. {
  732. if (! IsResponseHeaderPresent(HTTP_QUERY_LOCATION)) {
  733. _ResponseHeaders.UnlockHeaders();
  734. fsm.m_bFinished = TRUE;
  735. break;
  736. }
  737. _ResponseHeaders.UnlockHeaders();
  738. }
  739. else
  740. {
  741. error = ERROR_NOT_ENOUGH_MEMORY;
  742. goto quit;
  743. }
  744. //
  745. // fall through
  746. //
  747. case HTTP_STATUS_MOVED:
  748. // Table View:
  749. //Method 301 302 303 307
  750. // * * * GET *
  751. //POST GET GET GET POST
  752. //
  753. //Put another way:
  754. //301 & 302 - All methods are redirected to the same method but POST. POST is
  755. // redirected to a GET.
  756. //303 - All methods are redirected to GET
  757. //307 - All methods are redirected to the same method.
  758. //
  759. // 301 - permanently moved
  760. //
  761. //
  762. // fall through
  763. //
  764. case HTTP_STATUS_REDIRECT:
  765. //
  766. // 302 - temporarily moved (POST => GET, everything stays the same)
  767. //
  768. fsm.m_tMethodRedirect = GetMethodType();
  769. if (fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_POST)
  770. //
  771. // A POST change method to a GET
  772. //
  773. {
  774. fsm.m_tMethodRedirect = HTTP_METHOD_TYPE_GET;
  775. // force no optional data on second and subsequent sends
  776. fsm.m_dwOptionalLength = 0;
  777. _fOptionalSaved = FALSE;
  778. }
  779. INET_ASSERT(((fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_GET)
  780. || (fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_HEAD))
  781. ? (fsm.m_dwOptionalLength == 0) : TRUE);
  782. fsm.m_bRedirected = TRUE;
  783. --fsm.m_dwRedirectCount;
  784. break;
  785. case HTTP_STATUS_REDIRECT_METHOD:
  786. //
  787. // 303 - see other (POST => GET)
  788. //
  789. fsm.m_tMethodRedirect = (GetMethodType() == HTTP_METHOD_TYPE_HEAD) ?
  790. HTTP_METHOD_TYPE_HEAD :
  791. HTTP_METHOD_TYPE_GET;
  792. //
  793. // force no optional data on second and subsequent sends
  794. //
  795. fsm.m_dwOptionalLength = 0;
  796. _fOptionalSaved = FALSE;
  797. fsm.m_bRedirected = TRUE;
  798. --fsm.m_dwRedirectCount;
  799. break;
  800. case HTTP_STATUS_REDIRECT_KEEP_VERB:
  801. //
  802. // 307 - see other (POST => POST)
  803. //
  804. //if (IsHttp1_1()) {
  805. fsm.m_tMethodRedirect = GetMethodType();
  806. INET_ASSERT(((fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_GET)
  807. || (fsm.m_tMethodRedirect == HTTP_METHOD_TYPE_HEAD))
  808. ? (fsm.m_dwOptionalLength == 0) : TRUE);
  809. fsm.m_bRedirected = TRUE;
  810. --fsm.m_dwRedirectCount;
  811. break;
  812. default:
  813. fsm.m_tMethodRedirect = HTTP_METHOD_TYPE_GET;
  814. //
  815. // BUGBUG - force no optional data on second and subsequent
  816. // sends
  817. //
  818. fsm.m_dwOptionalLength = 0;
  819. _fOptionalSaved = FALSE;
  820. fsm.m_bRedirected = TRUE;
  821. --fsm.m_dwRedirectCount;
  822. break;
  823. }
  824. //
  825. // Only allow redirect to continue if we are successful.
  826. //
  827. if (fsm.m_bRedirected
  828. && ((fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_UNKNOWN)
  829. || (fsm.m_tMethodRedirect == GetMethodType()))) {
  830. fsm.SetFunctionState(FSM_STATE_7);
  831. error = Redirect(fsm.m_tMethodRedirect, FALSE);
  832. if (error != ERROR_SUCCESS) {
  833. goto quit;
  834. }
  835. }
  836. } else {
  837. //
  838. // not a status that we handle. We're done
  839. // BUT WAIT, we're only finshed if also
  840. // finished retrying HTTP authentication.
  841. //
  842. // if the app told us not to handle authentication auth_not_finished
  843. // will be FALSE
  844. //
  845. if (!fsm.m_bAuthNotFinished) {
  846. fsm.m_bFinished = TRUE;
  847. }
  848. }
  849. //
  850. // fall through
  851. //
  852. case FSM_STATE_7:
  853. DEBUG_PRINT(HTTP, INFO, ("State_7_Start: lpOptional = 0x%x deOptionalLength = %d\n", fsm.m_lpOptional, fsm.m_dwOptionalLength));
  854. //CHECK_FSM_OWNED(Fsm);
  855. if (fsm.m_bRedirected) {
  856. if (error != ERROR_SUCCESS) {
  857. goto quit;
  858. }
  859. INET_ASSERT(error == ERROR_SUCCESS);
  860. //
  861. // cleanup response headers from redirection
  862. //
  863. ReuseObject();
  864. //
  865. // Allow Redirects to exit out and force the HttpEndRequestA
  866. // caller to notice.
  867. //
  868. if ( fsm.m_arRequest == AR_HTTP_END_SEND_REQUEST &&
  869. fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_GET &&
  870. fsm.m_tMethodRedirect != HTTP_METHOD_TYPE_HEAD &&
  871. // Force a retry error only if Writes are required, otherwise we have all the data for a redirect:
  872. IsWriteRequired()
  873. )
  874. {
  875. error = ERROR_WINHTTP_FORCE_RETRY;
  876. }
  877. }
  878. }
  879. state = FSM_STATE_INIT;
  880. } while (!fsm.m_bFinished && (error == ERROR_SUCCESS));
  881. quit:
  882. DEBUG_PRINT(HTTP, INFO, ("Quit1: error = 0x%x\r\n", error));
  883. if (error == ERROR_IO_PENDING) {
  884. goto done;
  885. }
  886. {
  887. AUTHCTX* pAuthCtx = GetAuthCtx();
  888. DWORD eAuthScheme = 0;
  889. if (pAuthCtx != NULL)
  890. {
  891. eAuthScheme = pAuthCtx->GetSchemeType();
  892. }
  893. if (!fsm.m_bCancelRedoOfProxy &&
  894. //(GetStatusCode() != HTTP_STATUS_DENIED) &&
  895. ((eAuthScheme != WINHTTP_AUTH_SCHEME_PASSPORT) || (GetStatusCode() != HTTP_STATUS_DENIED)) && // this is safer
  896. fsm.m_pInternet->RedoSendRequest(&error, fsm.m_pRequest->GetSecureFlags(), fsm.m_pProxyInfoQuery, GetOriginServer(), GetServerInfo()))
  897. {
  898. fsm.m_bFinished = FALSE;
  899. fsm.m_bRedirectCountedOut = FALSE;
  900. fsm.m_dwRedirectCount = _dwMaxHttpAutomaticRedirects;
  901. fsm.SetState(FSM_STATE_INIT);
  902. state = FSM_STATE_INIT;
  903. DEBUG_PRINT(HTTP, INFO, ("Quit2: error = 0x%x\r\n", error));
  904. goto retry_send_request;
  905. }
  906. else
  907. {
  908. //SetProxyName(NULL, 0, 0);
  909. DEBUG_PRINT(HTTP, INFO, ("Quit3: error = 0x%x\r\n", error));
  910. }
  911. }
  912. //
  913. // if ERROR_HTTP_REDIRECT_FAILED then we tried to redirect, but found that
  914. // we couldn't do it (e.g. http:// to ftp:// or file://, etc.) We need to
  915. // defer this to the caller to clean up & make the new request. They will
  916. // have all the header info (plus we probably already indicated the new
  917. // URL during the redirect callback). Rather than returning ERROR_SUCCESS,
  918. // we will now fail with this error.
  919. //
  920. // Cases where we are redirected to the same site will return ERROR_SUCCESS.
  921. //
  922. if ((error == ERROR_HTTP_NOT_REDIRECTED)
  923. && !fsm.m_bRedirectCountedOut) {
  924. error = ERROR_SUCCESS;
  925. }
  926. fsm.SetNextState(FSM_STATE_FINISH);
  927. done:
  928. PERF_LEAVE(HttpSendRequest_Start);
  929. DEBUG_LEAVE(error);
  930. return error;
  931. }
  932. DWORD
  933. HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Finish(
  934. IN CFsm_HttpSendRequest * Fsm
  935. )
  936. /*++
  937. Routine Description:
  938. description-of-function.
  939. Arguments:
  940. Fsm -
  941. Return Value:
  942. DWORD
  943. --*/
  944. {
  945. DEBUG_ENTER((DBG_HTTP,
  946. Dword,
  947. "HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Finish",
  948. "%#x",
  949. Fsm
  950. ));
  951. PERF_ENTER(HttpSendRequest_Finish);
  952. CFsm_HttpSendRequest & fsm = *Fsm;
  953. DWORD error = fsm.GetError();
  954. fsm.m_fOwnsProxyInfoQueryObj = TRUE;
  955. INET_ASSERT(fsm.m_hRequestMapped != NULL);
  956. //if (!IsAsyncHandle() && (fsm.m_hRequestMapped != NULL)) {
  957. // DereferenceObject((LPVOID)fsm.m_hRequestMapped);
  958. //}
  959. //
  960. // we will return FALSE even if this is an async operation and the error is
  961. // ERROR_IO_PENDING
  962. //
  963. fsm.SetDone(error);
  964. //fsm.SetApiResult(error == ERROR_SUCCESS);
  965. PERF_LEAVE(HttpSendRequest_Finish);
  966. DEBUG_LEAVE(error);
  967. return error;
  968. }
  969. DWORD
  970. HTTP_REQUEST_HANDLE_OBJECT::BuildProxyMessage(
  971. IN CFsm_HttpSendRequest * Fsm,
  972. AUTO_PROXY_ASYNC_MSG * pProxyMsg,
  973. IN OUT URL_COMPONENTSA * pUrlComponents
  974. )
  975. /*++
  976. Routine Description:
  977. Calls CrackUrl to parses request URL, and
  978. transfers the information to the AUTO_PROXY_ASYNC_MSG
  979. Arguments:
  980. Fsm - HTTP send request FSM
  981. Return Value:
  982. DWORD
  983. Success - ERROR_SUCCESS
  984. Failure -
  985. ERROR_NOT_ENOUGH_MEMORY
  986. Ran out of resources
  987. --*/
  988. {
  989. DWORD error = ERROR_SUCCESS;
  990. LPSTR currentUrl;
  991. DWORD currentUrlLength;
  992. UNREFERENCED_PARAMETER(Fsm);
  993. //
  994. // Gather the URL off the handle
  995. //
  996. currentUrl = GetURL();
  997. if (currentUrl) {
  998. currentUrlLength = lstrlen(currentUrl);
  999. //
  1000. // BUGBUG [arthurbi] the following can be a slow call,
  1001. // but its too risky to change the complete behavior where
  1002. // we cache it
  1003. //
  1004. //
  1005. // crack the current URL
  1006. //
  1007. memset(pUrlComponents, 0, sizeof(URL_COMPONENTSA));
  1008. pUrlComponents->dwStructSize = sizeof(URL_COMPONENTSA);
  1009. error = CrackUrl(currentUrl,
  1010. currentUrlLength,
  1011. FALSE, // don't escape URL-path
  1012. &(pUrlComponents->nScheme),
  1013. NULL, // don't care about Scheme Name
  1014. NULL,
  1015. &(pUrlComponents->lpszHostName),
  1016. &(pUrlComponents->dwHostNameLength),
  1017. TRUE,
  1018. &(pUrlComponents->nPort),
  1019. NULL, // don't care about user name
  1020. NULL,
  1021. NULL, // or password
  1022. NULL,
  1023. &(pUrlComponents->lpszUrlPath),
  1024. &(pUrlComponents->dwUrlPathLength),
  1025. NULL, // no extra
  1026. NULL,
  1027. NULL
  1028. );
  1029. pProxyMsg->SetProxyMsg(
  1030. pUrlComponents->nScheme,
  1031. currentUrl,
  1032. currentUrlLength,
  1033. pUrlComponents->lpszHostName,
  1034. pUrlComponents->dwHostNameLength,
  1035. pUrlComponents->nPort
  1036. );
  1037. } else {
  1038. INET_ASSERT(FALSE);
  1039. error = ERROR_WINHTTP_INVALID_URL;
  1040. }
  1041. return error;
  1042. }
  1043. DWORD
  1044. HTTP_REQUEST_HANDLE_OBJECT::QueryProxySettings(
  1045. IN CFsm_HttpSendRequest * Fsm,
  1046. INTERNET_HANDLE_OBJECT * pInternet,
  1047. IN OUT URL_COMPONENTSA * pUrlComponents
  1048. )
  1049. /*++
  1050. Routine Description:
  1051. Wrapper over GetProxyInfo call to determine proxy
  1052. settings on our given object
  1053. Arguments:
  1054. Fsm - HTTP send request FSM
  1055. Return Value:
  1056. DWORD
  1057. Success - ERROR_SUCCESS
  1058. Failure -
  1059. ERROR_NOT_ENOUGH_MEMORY
  1060. Ran out of resources
  1061. --*/
  1062. {
  1063. DWORD error = ERROR_SUCCESS;
  1064. CFsm_HttpSendRequest & fsm = *Fsm;
  1065. UNREFERENCED_PARAMETER(pUrlComponents);
  1066. INET_ASSERT(fsm.m_pProxyInfoQuery);
  1067. INET_ASSERT(pInternet);
  1068. SetProxyName(NULL, 0, 0);
  1069. fsm.m_fOwnsProxyInfoQueryObj = FALSE;
  1070. if (IsProxy() || (GetProxyInfo() == PROXY_INFO_DIRECT))
  1071. {
  1072. error = GetProxyInfo(&fsm.m_pProxyInfoQuery);
  1073. }
  1074. else
  1075. {
  1076. error = pInternet->GetProxyInfo(&fsm.m_pProxyInfoQuery);
  1077. }
  1078. //
  1079. // If GetProxyInfo returns pending, then we no longer have
  1080. // access to the pointer that we've passed.
  1081. //
  1082. if ( error == ERROR_IO_PENDING )
  1083. {
  1084. //
  1085. // Bail out, DO NOT TOUCH any OBJECTS or FSMs
  1086. //
  1087. goto quit;
  1088. }
  1089. // then regardless we own it unless GetProxyInfo went pending with the FSM
  1090. fsm.m_fOwnsProxyInfoQueryObj = TRUE;
  1091. if ( error != ERROR_SUCCESS )
  1092. {
  1093. goto quit;
  1094. }
  1095. INET_ASSERT( error == ERROR_SUCCESS );
  1096. if ( ! ((fsm.m_pProxyInfoQuery)->IsUseProxy()) )
  1097. {
  1098. SetIsTalkingToSecureServerViaProxy(FALSE);
  1099. }
  1100. quit:
  1101. return error;
  1102. }
  1103. DWORD
  1104. HTTP_REQUEST_HANDLE_OBJECT::CheckForCachedProxySettings(
  1105. IN AUTO_PROXY_ASYNC_MSG *pProxyMsg,
  1106. OUT CServerInfo **ppProxyServerInfo
  1107. )
  1108. /*++
  1109. Routine Description:
  1110. Attempts to determine and then resolve if there are cached
  1111. proxy settings saved away in the CServerInfo object,
  1112. which is found in our HTTP_REQUEST_ object. This can
  1113. be very useful since calling off to an auto-proxy thread
  1114. can be quite expensive in terms of performance.
  1115. Arguments:
  1116. pProxyMsg - the object containing our current proxy message
  1117. information, that we use to scripple our proxy state for
  1118. a given request
  1119. ppProxyServerInfo - on return, may contain the resultant
  1120. cached ServerInfo object.
  1121. Return Value:
  1122. DWORD
  1123. Success - ERROR_SUCCESS
  1124. Failure -
  1125. ERROR_NOT_ENOUGH_MEMORY
  1126. Ran out of resources
  1127. --*/
  1128. {
  1129. DWORD error = ERROR_WINHTTP_INTERNAL_ERROR;
  1130. CServerInfo * pOriginServer = GetOriginServer();
  1131. CServerInfo * pProxyServer;
  1132. INET_ASSERT(pProxyMsg);
  1133. *ppProxyServerInfo = NULL;
  1134. if (pOriginServer)
  1135. {
  1136. BOOL fCachedEntry;
  1137. pProxyServer =
  1138. pOriginServer->GetCachedProxyServerInfo(
  1139. pProxyMsg->_tUrlProtocol,
  1140. pProxyMsg->_nUrlPort,
  1141. &fCachedEntry
  1142. );
  1143. if (fCachedEntry)
  1144. {
  1145. if ( pProxyServer )
  1146. {
  1147. if (pProxyServer->CopyCachedProxyInfoToProxyMsg(pProxyMsg))
  1148. {
  1149. SetOriginServer();
  1150. *ppProxyServerInfo = pProxyServer;
  1151. error = ERROR_SUCCESS;
  1152. goto quit;
  1153. }
  1154. // nuke extra ref, sideeffect of GetCachedProxy... call
  1155. ::ReleaseServerInfo(pProxyServer);
  1156. }
  1157. else
  1158. {
  1159. // DIRECT, no-proxy cached.
  1160. pProxyMsg->SetUseProxy(FALSE);
  1161. pProxyMsg->_lpszProxyHostName = NULL;
  1162. error = ERROR_SUCCESS;
  1163. goto quit;
  1164. }
  1165. }
  1166. }
  1167. pProxyMsg->SetVersion();
  1168. quit:
  1169. return error;
  1170. }
  1171. DWORD
  1172. HTTP_REQUEST_HANDLE_OBJECT::ProcessProxySettings(
  1173. IN CFsm_HttpSendRequest * Fsm,
  1174. IN OUT URL_COMPONENTSA * pUrlComponents,
  1175. OUT LPSTR * lplpszRequestObject,
  1176. OUT DWORD * lpdwRequestObjectSize
  1177. )
  1178. /*++
  1179. Routine Description:
  1180. Armed with the results of the proxy query, this method takes care of
  1181. assembling the various variables and states to deal with various
  1182. types of proxies.
  1183. More specifally, this handles HTTP Cern Proxies, SOCKS proxies,
  1184. SSL-CONNECT/HTTP proxies, and special cases such as FTP URLs
  1185. with passwords through an HTTP Cern Proxy.
  1186. Arguments:
  1187. Fsm - HTTP send request FSM
  1188. Return Value:
  1189. DWORD
  1190. Success - ERROR_SUCCESS
  1191. Failure -
  1192. ERROR_NOT_ENOUGH_MEMORY
  1193. Ran out of resources
  1194. --*/
  1195. {
  1196. DWORD error = ERROR_SUCCESS;
  1197. CFsm_HttpSendRequest & fsm = *Fsm;
  1198. LPSTR lpszUrlObject = NULL;
  1199. LPSTR lpszObject = pUrlComponents->lpszUrlPath;
  1200. DWORD dwcbObject = pUrlComponents->dwUrlPathLength;
  1201. if ((fsm.m_pProxyInfoQuery)->GetProxyScheme() == INTERNET_SCHEME_SOCKS)
  1202. {
  1203. SetSocksProxyName((fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
  1204. (fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength,
  1205. (fsm.m_pProxyInfoQuery)->_nProxyHostPort
  1206. );
  1207. (fsm.m_pProxyInfoQuery)->_lpszProxyHostName = NULL;
  1208. (fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength = 0;
  1209. }
  1210. else if (pUrlComponents->nScheme == INTERNET_SCHEME_HTTPS)
  1211. {
  1212. SetIsTalkingToSecureServerViaProxy(TRUE);
  1213. }
  1214. else
  1215. {
  1216. SetIsTalkingToSecureServerViaProxy(FALSE); // default value.
  1217. //
  1218. // if this request is going via proxy then we send the entire URL as the
  1219. // request
  1220. //
  1221. DWORD urlLength;
  1222. //
  1223. // in secure proxy tunnelling case we are going to send the request
  1224. // "CONNECT <host>:<port>"
  1225. //
  1226. if (IsTunnel()) {
  1227. urlLength = pUrlComponents->dwHostNameLength + sizeof(":65535");
  1228. } else {
  1229. urlLength = INTERNET_MAX_URL_LENGTH;
  1230. }
  1231. lpszUrlObject = (LPSTR)ResizeBuffer(NULL, urlLength, FALSE);
  1232. if (lpszUrlObject == NULL)
  1233. {
  1234. error = ERROR_NOT_ENOUGH_MEMORY;
  1235. goto quit;
  1236. }
  1237. if (IsTunnel())
  1238. {
  1239. // When tunneling, the scheme is http for the CONNECT and the port
  1240. // info may be stripped to 0 if the default http port was specified
  1241. // in the original SSL tunneling URL.
  1242. if (pUrlComponents->nPort == INTERNET_INVALID_PORT_NUMBER)
  1243. {
  1244. INET_ASSERT (pUrlComponents->nScheme == INTERNET_SCHEME_HTTP);
  1245. pUrlComponents->nPort = INTERNET_DEFAULT_HTTP_PORT;
  1246. }
  1247. memcpy (lpszUrlObject, pUrlComponents->lpszHostName, pUrlComponents->dwHostNameLength);
  1248. wsprintf (lpszUrlObject + pUrlComponents->dwHostNameLength, ":%d", pUrlComponents->nPort);
  1249. }
  1250. else
  1251. {
  1252. //
  1253. // there may be a user name & password (only if FTP)
  1254. //
  1255. LPSTR userName;
  1256. DWORD userNameLength;
  1257. LPSTR password;
  1258. DWORD passwordLength;
  1259. userName = NULL;
  1260. userNameLength = 0;
  1261. password = NULL;
  1262. passwordLength = 0;
  1263. if (pUrlComponents->nPort == INTERNET_INVALID_PORT_NUMBER)
  1264. {
  1265. switch (pUrlComponents->nScheme)
  1266. {
  1267. case INTERNET_SCHEME_HTTP:
  1268. pUrlComponents->nPort = INTERNET_DEFAULT_HTTP_PORT;
  1269. break;
  1270. case INTERNET_SCHEME_HTTPS:
  1271. pUrlComponents->nPort = INTERNET_DEFAULT_HTTPS_PORT;
  1272. break;
  1273. default:
  1274. INET_ASSERT(FALSE);
  1275. break;
  1276. }
  1277. }
  1278. pUrlComponents->lpszUserName = userName;
  1279. pUrlComponents->dwUserNameLength = userNameLength;
  1280. pUrlComponents->lpszPassword = password;
  1281. pUrlComponents->dwPasswordLength = passwordLength;
  1282. for (int i=0; i<2; i++)
  1283. {
  1284. if (!WinHttpCreateUrlA(pUrlComponents, 0, lpszUrlObject, &urlLength))
  1285. {
  1286. error = GetLastError();
  1287. if ((error == ERROR_INSUFFICIENT_BUFFER)
  1288. && (i==0))
  1289. {
  1290. LPSTR pTemp = (LPSTR)ResizeBuffer(lpszUrlObject,
  1291. urlLength,
  1292. FALSE);
  1293. if (pTemp)
  1294. {
  1295. lpszUrlObject = pTemp;
  1296. continue;
  1297. }
  1298. else
  1299. {
  1300. error = ERROR_NOT_ENOUGH_MEMORY;
  1301. }
  1302. }
  1303. goto quit;
  1304. }
  1305. else
  1306. {
  1307. error = ERROR_SUCCESS;
  1308. break;
  1309. }
  1310. }
  1311. //
  1312. // shrink the buffer to fit
  1313. //
  1314. lpszUrlObject = (LPSTR)ResizeBuffer(lpszUrlObject,
  1315. (urlLength + 1) * sizeof(TCHAR),
  1316. FALSE
  1317. );
  1318. INET_ASSERT(lpszUrlObject != NULL);
  1319. if (lpszUrlObject == NULL)
  1320. {
  1321. error = ERROR_NOT_ENOUGH_MEMORY;
  1322. goto quit;
  1323. }
  1324. }
  1325. SetRequestUsingProxy(TRUE);
  1326. lpszObject = lpszUrlObject;
  1327. dwcbObject = lstrlen(lpszUrlObject);
  1328. }
  1329. quit:
  1330. *lplpszRequestObject = lpszObject;
  1331. *lpdwRequestObjectSize = dwcbObject;
  1332. return error;
  1333. }
  1334. DWORD
  1335. HTTP_REQUEST_HANDLE_OBJECT::UpdateRequestInfo(
  1336. IN CFsm_HttpSendRequest * Fsm,
  1337. IN LPSTR lpszObject,
  1338. IN DWORD dwcbObject,
  1339. IN OUT URL_COMPONENTSA * pUrlComponents,
  1340. IN OUT CServerInfo **ppProxyServerInfo
  1341. )
  1342. /*++
  1343. Routine Description:
  1344. Based on object and URL information, for a given HTTP request,
  1345. this function assembles the "special cases" and modifes the
  1346. request headers in prepartion of making the actual request.
  1347. The "special cases" includes the handling of HTTP versioning,
  1348. HTTP 1.0/1.1 keep-alives, and Pragma headers.
  1349. This function also deals with the update the ServerInfo object
  1350. that contains the host resolution information.
  1351. Arguments:
  1352. Fsm - HTTP send request FSM
  1353. Return Value:
  1354. DWORD
  1355. Success - ERROR_SUCCESS
  1356. Failure -
  1357. ERROR_NOT_ENOUGH_MEMORY
  1358. Ran out of resources
  1359. --*/
  1360. {
  1361. DWORD error = ERROR_SUCCESS;
  1362. LPSTR lpszVersion = NULL;
  1363. DWORD dwVersionLen = 0;
  1364. CFsm_HttpSendRequest & fsm = *Fsm;
  1365. if ( lpszObject == NULL)
  1366. {
  1367. lpszObject = pUrlComponents->lpszUrlPath;
  1368. dwcbObject = pUrlComponents->dwUrlPathLength;
  1369. }
  1370. INET_ASSERT(dwcbObject > 0 );
  1371. if (!_RequestHeaders.LockHeaders())
  1372. {
  1373. error = ERROR_NOT_ENOUGH_MEMORY;
  1374. goto quit;
  1375. }
  1376. //
  1377. // if a CONNECT request, then ensure the request version is HTTP/1.0
  1378. //
  1379. if (GetMethodType() == HTTP_METHOD_TYPE_CONNECT)
  1380. {
  1381. lpszVersion = "HTTP/1.0";
  1382. dwVersionLen = sizeof("HTTP/1.0") - 1;
  1383. }
  1384. ModifyRequest(GetMethodType(),
  1385. lpszObject,
  1386. dwcbObject,
  1387. lpszVersion,
  1388. dwVersionLen
  1389. );
  1390. if ((fsm.m_pProxyInfoQuery)->IsUseProxy() )
  1391. {
  1392. SetProxyName( (fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
  1393. (fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength,
  1394. (fsm.m_pProxyInfoQuery)->_nProxyHostPort
  1395. );
  1396. if ((fsm.m_pProxyInfoQuery)->_lpszProxyHostName != NULL) {
  1397. TRACE_PRINT_API(SOCKETS,
  1398. INFO,
  1399. ("Using proxy server: %s:%d\n",
  1400. (fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
  1401. (fsm.m_pProxyInfoQuery)->_nProxyHostPort
  1402. ));
  1403. if (_ServerInfo != NULL)
  1404. {
  1405. _ServerInfo->SetProxyByPassed(FALSE);
  1406. }
  1407. //
  1408. // changing server info from origin server to proxy server. Keep
  1409. // pointer to origin server so that we can update connect and
  1410. // round-trip times
  1411. //
  1412. SetOriginServer();
  1413. if (*ppProxyServerInfo) {
  1414. // cached server info
  1415. SetServerInfo(*ppProxyServerInfo);
  1416. *ppProxyServerInfo = NULL;
  1417. }
  1418. else
  1419. {
  1420. error = SetServerInfo((fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
  1421. (fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength
  1422. );
  1423. if (error != ERROR_SUCCESS) {
  1424. goto Cleanup;
  1425. }
  1426. }
  1427. }
  1428. }
  1429. else
  1430. {
  1431. if (_ServerInfo != NULL)
  1432. {
  1433. _ServerInfo->SetProxyByPassed(TRUE);
  1434. if ( pUrlComponents->lpszHostName )
  1435. {
  1436. error = SetServerInfo(pUrlComponents->lpszHostName,
  1437. pUrlComponents->dwHostNameLength
  1438. );
  1439. if (error != ERROR_SUCCESS) {
  1440. goto Cleanup;
  1441. }
  1442. }
  1443. }
  1444. }
  1445. //
  1446. // determine whether we use persistent connections and ensure the correct
  1447. // type and number of keep-alive headers are present
  1448. //
  1449. //
  1450. // BUGBUG - we need to check for "Connection: keep-alive". There may be
  1451. // other types of "Connection" header, and the keep-alive header
  1452. // may contain additional information
  1453. //
  1454. DWORD dwHeaderNameIndex;
  1455. if (IsRequestUsingProxy()) {
  1456. RemoveAllRequestHeadersByName(HTTP_QUERY_CONNECTION);
  1457. dwHeaderNameIndex = HTTP_QUERY_PROXY_CONNECTION;
  1458. } else {
  1459. RemoveAllRequestHeadersByName(HTTP_QUERY_PROXY_CONNECTION);
  1460. dwHeaderNameIndex = HTTP_QUERY_CONNECTION;
  1461. }
  1462. //
  1463. // if keep-alives have been disabled, the ensure
  1464. // no keep-alive headers are sent.
  1465. //
  1466. if (!(GetOpenFlags() & INTERNET_FLAG_KEEP_CONNECTION))
  1467. {
  1468. RemoveAllRequestHeadersByName(HTTP_QUERY_CONNECTION);
  1469. RemoveAllRequestHeadersByName(HTTP_QUERY_PROXY_CONNECTION);
  1470. if (IsRequestHttp1_1())
  1471. {
  1472. //
  1473. // Add "Connection: Close" header because we're not doing
  1474. // keep-alive on this Request, needed for HTTP 1.1
  1475. //
  1476. (void)ReplaceRequestHeader(HTTP_QUERY_CONNECTION,
  1477. CLOSE_SZ,
  1478. CLOSE_LEN,
  1479. 0,
  1480. ADD_HEADER_IF_NEW
  1481. );
  1482. }
  1483. }
  1484. else
  1485. {
  1486. //
  1487. // if the app requested keep-alive then add the header; if we're going via
  1488. // proxy then use the proxy-connection header
  1489. //
  1490. SetWantKeepAlive(TRUE);
  1491. (void)ReplaceRequestHeader(dwHeaderNameIndex,
  1492. KEEP_ALIVE_SZ,
  1493. KEEP_ALIVE_LEN,
  1494. 0,
  1495. ADD_HEADER_IF_NEW
  1496. );
  1497. }
  1498. error = ERROR_SUCCESS;
  1499. //
  1500. // if app added "connection: close" then we don't want keep-alive
  1501. //
  1502. if (IsRequestHttp1_1()) {
  1503. BOOL bClose = FindConnCloseRequestHeader(dwHeaderNameIndex);
  1504. BOOL bWantKeepAlive;
  1505. DWORD dwOpenFlags = GetOpenFlags();
  1506. if (bClose || (IsTunnel() && GetAuthState() != AUTHSTATE_CHALLENGE))
  1507. {
  1508. // Nested tunneling requests do not add Connection: close headers,
  1509. // so don't worry about checking to see if one should be removed.
  1510. bWantKeepAlive= FALSE;
  1511. dwOpenFlags &= ~INTERNET_FLAG_KEEP_CONNECTION;
  1512. } else {
  1513. bWantKeepAlive = TRUE;
  1514. dwOpenFlags |= INTERNET_FLAG_KEEP_CONNECTION;
  1515. }
  1516. SetWantKeepAlive(bWantKeepAlive);
  1517. SetOpenFlags(dwOpenFlags);
  1518. }
  1519. if (GetOpenFlags() & WINHTTP_FLAG_BYPASS_PROXY_CACHE)
  1520. {
  1521. // add "Pragma: No-Cache" header
  1522. ReplaceRequestHeader(HTTP_QUERY_CACHE_CONTROL,
  1523. NO_CACHE_SZ,
  1524. NO_CACHE_LEN,
  1525. 0, // dwIndex
  1526. ADD_HEADER_IF_NEW
  1527. );
  1528. // add "Cache-Control: No-Cache" header for HTTP 1.1
  1529. if (IsRequestHttp1_1())
  1530. {
  1531. ReplaceRequestHeader(HTTP_QUERY_PRAGMA,
  1532. NO_CACHE_SZ,
  1533. NO_CACHE_LEN,
  1534. 0, // dwIndex
  1535. ADD_HEADER_IF_NEW
  1536. );
  1537. }
  1538. }
  1539. Cleanup:
  1540. _RequestHeaders.UnlockHeaders();
  1541. quit:
  1542. return error;
  1543. }
  1544. DWORD
  1545. HTTP_REQUEST_HANDLE_OBJECT::UpdateProxyInfo(
  1546. IN CFsm_HttpSendRequest * Fsm,
  1547. IN BOOL fCallback
  1548. )
  1549. /*++
  1550. Routine Description:
  1551. Queries Proxy Information, and based on the proxy info it assembles the appropriate
  1552. HTTP request.
  1553. Arguments:
  1554. Fsm - HTTP send request FSM
  1555. Return Value:
  1556. DWORD
  1557. Success - ERROR_SUCCESS
  1558. Failure -
  1559. ERROR_NOT_ENOUGH_MEMORY
  1560. Ran out of resources
  1561. --*/
  1562. {
  1563. DEBUG_ENTER((DBG_HTTP,
  1564. Dword,
  1565. "HTTP_REQUEST_HANDLE_OBJECT::UpdateProxyInfo",
  1566. "%#x, %B",
  1567. Fsm,
  1568. fCallback
  1569. ));
  1570. PERF_ENTER(UpdateProxyInfo);
  1571. DWORD error = ERROR_SUCCESS;
  1572. CFsm_HttpSendRequest & fsm = *Fsm;
  1573. CServerInfo *pProxyServer = NULL;
  1574. INTERNET_HANDLE_OBJECT * pInternet;
  1575. AUTO_PROXY_ASYNC_MSG proxyInfoQuery;
  1576. URL_COMPONENTSA urlComponents;
  1577. LPSTR lpszObject = NULL;
  1578. DWORD dwcbObject = 0;
  1579. // once we're woken up, we own the obj stored in our FSM.
  1580. INET_ASSERT(fsm.m_fOwnsProxyInfoQueryObj);
  1581. //
  1582. // Get the Obj Pointers we care about
  1583. //
  1584. pInternet = GetRootHandle (this);
  1585. //
  1586. // Clear our handle state in regards to proxy settings
  1587. //
  1588. SetSocksProxyName(NULL, NULL, NULL);
  1589. SetRequestUsingProxy(FALSE);
  1590. //
  1591. // Parse URL, I have to do this every time,
  1592. // and even worse we need to do this before our caching code
  1593. // gets hit, but we can't move it because the quit code
  1594. // depends on the parsed URL. In the future we should cache this!!
  1595. //
  1596. error = BuildProxyMessage(
  1597. Fsm,
  1598. &proxyInfoQuery,
  1599. &urlComponents
  1600. );
  1601. if (error != ERROR_SUCCESS) {
  1602. goto quit;
  1603. }
  1604. //
  1605. // No proxy installed on this object, bail out
  1606. //
  1607. if ( ((GetProxyInfo() == PROXY_INFO_DIRECT) || (!IsProxy() && ! pInternet->IsProxy()))
  1608. && ! IsOverrideProxyMode() )
  1609. {
  1610. INET_ASSERT(fsm.m_pProxyInfoQuery == NULL);
  1611. fsm.m_pProxyInfoQuery = &proxyInfoQuery; // !!! put our local in the FSM
  1612. goto quit;
  1613. }
  1614. //
  1615. // If we're in the callback, just retrieve the results,
  1616. // from the orginal blocking call to proxy code
  1617. //
  1618. if ( fsm.m_pProxyInfoQuery )
  1619. {
  1620. fCallback = TRUE;
  1621. if ( ! (fsm.m_pProxyInfoQuery)->IsBackroundDetectionPending()) {
  1622. (fsm.m_pProxyInfoQuery)->SetQueryOnCallback(TRUE);
  1623. }
  1624. error = QueryProxySettings(Fsm, pInternet, &urlComponents);
  1625. if ( error != ERROR_SUCCESS || !(fsm.m_pProxyInfoQuery)->IsUseProxy())
  1626. {
  1627. goto quit;
  1628. }
  1629. }
  1630. else
  1631. {
  1632. fsm.m_pProxyInfoQuery = &proxyInfoQuery; // !!! put our local in the FSM
  1633. proxyInfoQuery.SetBlockUntilCompletetion(TRUE);
  1634. proxyInfoQuery.SetShowIndication(TRUE);
  1635. if (!IsTunnel() && !IsOverrideProxyMode())
  1636. {
  1637. error = QueryProxySettings(Fsm, pInternet, &urlComponents);
  1638. if ( error != ERROR_SUCCESS || !(fsm.m_pProxyInfoQuery)->IsUseProxy()) {
  1639. goto quit;
  1640. }
  1641. }
  1642. else // fall-back
  1643. {
  1644. //
  1645. // Get the current proxy information,
  1646. // if we're in an nested SSL tunnell
  1647. //
  1648. GetProxyName(&(fsm.m_pProxyInfoQuery)->_lpszProxyHostName,
  1649. &(fsm.m_pProxyInfoQuery)->_dwProxyHostNameLength,
  1650. &(fsm.m_pProxyInfoQuery)->_nProxyHostPort
  1651. );
  1652. (fsm.m_pProxyInfoQuery)->_tProxyScheme = INTERNET_SCHEME_DEFAULT;
  1653. (fsm.m_pProxyInfoQuery)->SetUseProxy(TRUE);
  1654. }
  1655. }
  1656. //
  1657. // Need to figure out whether we're actually talking
  1658. // to a Server via proxy. In this case we need to
  1659. // special case some logic in the Send so we create
  1660. // a sub-request to the proxy-server, and then do this
  1661. // request to the main SSL server.
  1662. //
  1663. if ( (fsm.m_pProxyInfoQuery)->IsUseProxy() )
  1664. {
  1665. error = ProcessProxySettings(
  1666. Fsm,
  1667. &urlComponents,
  1668. &lpszObject,
  1669. &dwcbObject
  1670. );
  1671. }
  1672. else
  1673. {
  1674. // Ensure this is false in case of very slim chance of
  1675. // redirect from internet https to intranet http
  1676. SetIsTalkingToSecureServerViaProxy(FALSE);
  1677. }
  1678. quit:
  1679. //
  1680. // If we didn't fail with pending,
  1681. // go ahead and process the request headers
  1682. //
  1683. if ( error != ERROR_IO_PENDING)
  1684. {
  1685. if ( error == ERROR_SUCCESS ) {
  1686. error = UpdateRequestInfo(Fsm, lpszObject, dwcbObject, &urlComponents, &pProxyServer);
  1687. }
  1688. //
  1689. // Now, Unlink the proxyinfomsg struc from the fsm,
  1690. // if its our stack based variable that we used as a temp
  1691. //
  1692. if ( fsm.m_fOwnsProxyInfoQueryObj &&
  1693. fsm.m_pProxyInfoQuery &&
  1694. ! (fsm.m_pProxyInfoQuery)->IsAlloced() )
  1695. {
  1696. fsm.m_pProxyInfoQuery = NULL;
  1697. }
  1698. }
  1699. //
  1700. // Don't leak objects, Give a hoot, don't pollute !!
  1701. //
  1702. if ( pProxyServer != NULL )
  1703. {
  1704. ::ReleaseServerInfo(pProxyServer);
  1705. }
  1706. if ( lpszObject != NULL &&
  1707. lpszObject != urlComponents.lpszUrlPath)
  1708. {
  1709. FREE_MEMORY(lpszObject);
  1710. }
  1711. PERF_LEAVE(UpdateProxyInfo);
  1712. DEBUG_LEAVE(error);
  1713. return error;
  1714. }
  1715. BOOL
  1716. HTTP_REQUEST_HANDLE_OBJECT::FindConnCloseRequestHeader(
  1717. IN DWORD dwIndex
  1718. )
  1719. /*++
  1720. Routine Description:
  1721. Determine if Connection: Close added to request headers
  1722. Arguments:
  1723. dwIndex - id of Connection header to search for (Connection or
  1724. Proxy-Connection)
  1725. Return Value:
  1726. BOOL
  1727. TRUE - header found
  1728. FALSE - header not found
  1729. --*/
  1730. {
  1731. DEBUG_ENTER((DBG_HTTP,
  1732. Bool,
  1733. "HTTP_REQUEST_HANDLE_OBJECT::FindConnCloseRequestHeader",
  1734. "%d [%s]",
  1735. dwIndex,
  1736. InternetMapHttpOption(dwIndex)
  1737. ));
  1738. BOOL bFound = FALSE;
  1739. if (CheckedConnCloseRequest()) {
  1740. bFound = IsConnCloseRequest(dwIndex == HTTP_QUERY_PROXY_CONNECTION);
  1741. } else {
  1742. LPSTR ptr;
  1743. DWORD len;
  1744. DWORD index = 0;
  1745. while (FastQueryRequestHeader(dwIndex,
  1746. (LPVOID *)&ptr,
  1747. &len,
  1748. index) == ERROR_SUCCESS) {
  1749. if ((len == CLOSE_LEN) && (strnicmp(ptr, CLOSE_SZ, len) == 0)) {
  1750. bFound = TRUE;
  1751. break;
  1752. }
  1753. index++;
  1754. }
  1755. SetCheckedConnCloseRequest(dwIndex == HTTP_QUERY_PROXY_CONNECTION, bFound);
  1756. }
  1757. DEBUG_LEAVE(bFound);
  1758. return bFound;
  1759. }
  1760. //
  1761. //
  1762. //INTERNET_HANDLE_BASE::SetDwordOption() and HTTP_REQUEST_HANDLE_OBJECT::SetDwordOption()
  1763. //maintain DWORD options with the following semantics:
  1764. // 1) Settings affect behavior of request and are per-request configureable.
  1765. // 2) Defaults for new requests are stored in the session and those defaults are per-session configureable.
  1766. //
  1767. //
  1768. /*
  1769. * When called from API functions,
  1770. * caller should SetLastError() in case of failure
  1771. */
  1772. BOOL
  1773. HTTP_REQUEST_HANDLE_OBJECT::SetDwordOption(
  1774. IN DWORD dwDwordOption,
  1775. IN DWORD dwDwordValue
  1776. )
  1777. {
  1778. BOOL bRetval = TRUE;
  1779. switch (dwDwordOption)
  1780. {
  1781. case WINHTTP_OPTION_RESOLVE_TIMEOUT:
  1782. _dwResolveTimeout = dwDwordValue;
  1783. break;
  1784. case WINHTTP_OPTION_CONNECT_TIMEOUT:
  1785. _dwConnectTimeout = dwDwordValue;
  1786. break;
  1787. case WINHTTP_OPTION_CONNECT_RETRIES:
  1788. _dwConnectRetries = dwDwordValue;
  1789. break;
  1790. case WINHTTP_OPTION_SEND_TIMEOUT:
  1791. _dwSendTimeout = dwDwordValue;
  1792. break;
  1793. case WINHTTP_OPTION_RECEIVE_TIMEOUT:
  1794. _dwReceiveTimeout = dwDwordValue;
  1795. // Ensure that the ReceiveResponse timeout is not less than the Receive timeout
  1796. _dwReceiveResponseTimeout = max(dwDwordValue, _dwReceiveResponseTimeout);
  1797. break;
  1798. case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
  1799. // Ensure that the ReceiveResponse timeout is not less than the Receive timeout
  1800. _dwReceiveResponseTimeout = max(dwDwordValue, _dwReceiveTimeout);
  1801. break;
  1802. case WINHTTP_OPTION_REDIRECT_POLICY:
  1803. _dwRedirectPolicy = dwDwordValue;
  1804. break;
  1805. case WINHTTP_OPTION_AUTOLOGON_POLICY:
  1806. _dwAutoLogonPolicy = dwDwordValue;
  1807. break;
  1808. case WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS:
  1809. _dwMaxHttpAutomaticRedirects = dwDwordValue;
  1810. break;
  1811. case WINHTTP_OPTION_MAX_HTTP_STATUS_CONTINUE:
  1812. _dwMaxHttpStatusContinues = dwDwordValue;
  1813. break;
  1814. case WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE:
  1815. _dwMaxResponseHeaderSize = dwDwordValue;
  1816. break;
  1817. case WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE:
  1818. _dwMaxResponseDrainSize = dwDwordValue;
  1819. break;
  1820. default:
  1821. INET_ASSERT(FALSE);
  1822. bRetval = FALSE;
  1823. break;
  1824. }
  1825. return bRetval;
  1826. }
  1827. /*
  1828. * When called from API functions,
  1829. * caller should SetLastError() in case of failure
  1830. */
  1831. BOOL
  1832. HTTP_REQUEST_HANDLE_OBJECT::SetTimeout(
  1833. IN DWORD dwTimeoutOption,
  1834. IN DWORD dwTimeoutValue
  1835. )
  1836. {
  1837. switch (dwTimeoutOption)
  1838. {
  1839. case WINHTTP_OPTION_RESOLVE_TIMEOUT:
  1840. case WINHTTP_OPTION_CONNECT_TIMEOUT:
  1841. case WINHTTP_OPTION_CONNECT_RETRIES:
  1842. case WINHTTP_OPTION_SEND_TIMEOUT:
  1843. case WINHTTP_OPTION_RECEIVE_TIMEOUT:
  1844. case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
  1845. case WINHTTP_OPTION_REDIRECT_POLICY:
  1846. return SetDwordOption( dwTimeoutOption, dwTimeoutValue);
  1847. default:
  1848. INET_ASSERT(FALSE);
  1849. return FALSE;
  1850. }
  1851. }
  1852. /*
  1853. * When called from API functions,
  1854. * caller should SetLastError() in case of failure
  1855. */
  1856. DWORD
  1857. HTTP_REQUEST_HANDLE_OBJECT::GetDwordOption(
  1858. IN DWORD dwDwordOption
  1859. )
  1860. {
  1861. switch (dwDwordOption)
  1862. {
  1863. case WINHTTP_OPTION_RESOLVE_TIMEOUT:
  1864. return _dwResolveTimeout;
  1865. case WINHTTP_OPTION_CONNECT_TIMEOUT:
  1866. return _dwConnectTimeout;
  1867. case WINHTTP_OPTION_CONNECT_RETRIES:
  1868. return _dwConnectRetries;
  1869. case WINHTTP_OPTION_SEND_TIMEOUT:
  1870. return _dwSendTimeout;
  1871. case WINHTTP_OPTION_RECEIVE_TIMEOUT:
  1872. return _dwReceiveTimeout;
  1873. case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
  1874. return _dwReceiveResponseTimeout;
  1875. case WINHTTP_OPTION_REDIRECT_POLICY:
  1876. return _dwRedirectPolicy;
  1877. case WINHTTP_OPTION_AUTOLOGON_POLICY:
  1878. return _dwAutoLogonPolicy;
  1879. case WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS:
  1880. return _dwMaxHttpAutomaticRedirects;
  1881. case WINHTTP_OPTION_MAX_HTTP_STATUS_CONTINUE:
  1882. return _dwMaxHttpStatusContinues;
  1883. case WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE:
  1884. return _dwMaxResponseHeaderSize;
  1885. case WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE:
  1886. return _dwMaxResponseDrainSize;
  1887. }
  1888. INET_ASSERT(FALSE);
  1889. // we should not be here, but in case we are, return 0
  1890. return 0;
  1891. }
  1892. /*
  1893. * When called from API functions,
  1894. * caller should SetLastError() in case of failure
  1895. */
  1896. DWORD
  1897. HTTP_REQUEST_HANDLE_OBJECT::GetTimeout(
  1898. IN DWORD dwTimeoutOption
  1899. )
  1900. {
  1901. switch (dwTimeoutOption)
  1902. {
  1903. case WINHTTP_OPTION_RESOLVE_TIMEOUT:
  1904. case WINHTTP_OPTION_CONNECT_TIMEOUT:
  1905. case WINHTTP_OPTION_CONNECT_RETRIES:
  1906. case WINHTTP_OPTION_SEND_TIMEOUT:
  1907. case WINHTTP_OPTION_RECEIVE_TIMEOUT:
  1908. case WINHTTP_OPTION_RECEIVE_RESPONSE_TIMEOUT:
  1909. case WINHTTP_OPTION_REDIRECT_POLICY:
  1910. return GetDwordOption( dwTimeoutOption);
  1911. default:
  1912. INET_ASSERT(0);
  1913. return 0;
  1914. }
  1915. }
  1916. /*
  1917. * When called from API functions,
  1918. * caller should SetLastError() in case of failure
  1919. */
  1920. BOOL
  1921. HTTP_REQUEST_HANDLE_OBJECT::SetTimeouts(
  1922. IN DWORD dwResolveTimeout,
  1923. IN DWORD dwConnectTimeout,
  1924. IN DWORD dwSendTimeout,
  1925. IN DWORD dwReceiveTimeout
  1926. )
  1927. {
  1928. _dwResolveTimeout = dwResolveTimeout;
  1929. _dwConnectTimeout = dwConnectTimeout;
  1930. _dwSendTimeout = dwSendTimeout;
  1931. _dwReceiveTimeout = dwReceiveTimeout;
  1932. // Ensure that the ReceiveResponse timeout is not less than the Receive timeout
  1933. _dwReceiveResponseTimeout = max(dwReceiveTimeout, _dwReceiveResponseTimeout);
  1934. return TRUE;
  1935. }