/*++ Copyright (c) 1994 Microsoft Corporation Module Name: makeconn.cxx Abstract: This file contains the MakeConnection method Contents: CFsm_MakeConnection::RunSM HTTP_REQUEST_HANDLE_OBJECT::MakeConnection_Fsm Author: Keith Moore (keithmo) 16-Nov-1994 Revision History: 29-Apr-97 rfirth Conversion to FSM --*/ #include #include #include "httpp.h" // // HTTP Request Handle Object methods // DWORD CFsm_MakeConnection::RunSM( IN CFsm * Fsm ) /*++ Routine Description: description-of-function. Arguments: Fsm - Return Value: DWORD --*/ { DEBUG_ENTER((DBG_HTTP, Dword, "CFsm_MakeConnection::RunSM", "%#x", Fsm )); START_SENDREQ_PERF(); CFsm_MakeConnection * stateMachine = (CFsm_MakeConnection *)Fsm; HTTP_REQUEST_HANDLE_OBJECT * pRequest; DWORD error; pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext(); switch (Fsm->GetState()) { case FSM_STATE_INIT: case FSM_STATE_CONTINUE: error = pRequest->MakeConnection_Fsm(stateMachine); break; case FSM_STATE_ERROR: error = Fsm->GetError(); INET_ASSERT (error == ERROR_WINHTTP_OPERATION_CANCELLED); Fsm->SetDone(); break; default: error = ERROR_WINHTTP_INTERNAL_ERROR; Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR); INET_ASSERT(FALSE); break; } STOP_SENDREQ_PERF(); DEBUG_LEAVE(error); return error; } DWORD HTTP_REQUEST_HANDLE_OBJECT::MakeConnection_Fsm( IN CFsm_MakeConnection * Fsm ) /*++ Routine Description: description-of-function. Arguments: Fsm - Return Value: DWORD --*/ { DEBUG_ENTER((DBG_HTTP, Dword, "HTTP_REQUEST_HANDLE_OBJECT::MakeConnection_Fsm", "%#x", Fsm )); PERF_ENTER(MakeConnection_Fsm); CFsm_MakeConnection & fsm = *Fsm; FSM_STATE state = fsm.GetState(); DWORD error = fsm.GetError(); INTERNET_HANDLE_OBJECT * pInternet; pInternet = GetRootHandle (this); if (state == FSM_STATE_INIT) { if (GetAuthState() == AUTHSTATE_NEEDTUNNEL) { state = FSM_STATE_1; } else if (IsTalkingToSecureServerViaProxy()) { state = FSM_STATE_3; } else { state = FSM_STATE_6; } } else { state = fsm.GetFunctionState(); } switch (state) { case FSM_STATE_1: // // If we're attempting to do NTLM authentication using Proxy tunnelling // and we don't have a keep-alive socket to use, then create one // if (!(IsWantKeepAlive() && (_Socket != NULL) && _Socket->IsOpen())) { fsm.SetFunctionState(FSM_STATE_2); error = OpenProxyTunnel(); if ((error != ERROR_SUCCESS) || ((GetStatusCode() != HTTP_STATUS_OK) && (GetStatusCode() != 0))) { goto quit; } } else { goto quit; } // // fall through // case FSM_STATE_2: if ((error != ERROR_SUCCESS) || ((GetStatusCode() != HTTP_STATUS_OK) && (GetStatusCode() != 0))) { goto quit; } // // Bind Socket Object with Proper HostName, // so we can check for valid common name // in the handshake. // if (_Socket->IsSecure()) { /* SCLE ref */ error = ((ICSecureSocket *)_Socket)->SetHostName(GetHostName(), GetHostPort(), pInternet->GetSslSessionCache()); if (error != ERROR_SUCCESS) { goto quit; } } // // Undo the proxy-ified info found in this Request Object, make it seem like // we're doing a connect connection, since we're about to do something like it // ( a tunnelled connection through the firewall ) // error = SetServerInfoWithScheme(INTERNET_SCHEME_HTTP, FALSE); if (error != ERROR_SUCCESS) { goto quit; } LPSTR urlPath; DWORD urlPathLength; // // get URL-path again if it was changed during tunnel creation // error = CrackUrl(GetURL(), lstrlen(GetURL()), FALSE, // don't escape URL-path NULL, // don't care about scheme type NULL, // or scheme name NULL, // or scheme name length NULL, // or host name NULL, // or host name length TRUE, NULL, // or port NULL, // or user name NULL, // or user name length NULL, // or password NULL, // or password length &urlPath, &urlPathLength, NULL, // don't care about extra NULL, // or extra length NULL ); if (error != ERROR_SUCCESS) { goto quit; } if (LockHeaders()) { ModifyRequest(HTTP_METHOD_TYPE_GET, urlPath, urlPathLength, NULL, 0 ); UnlockHeaders(); } else { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } //SetProxyNTLMTunnelling(FALSE); SetRequestUsingProxy(FALSE); // don't generate proxy stuff. break; case FSM_STATE_3: // // Hack for SSL2 Client Hello bug in IIS Servers. // Need to ReOpen connection after failure with // a Client Hello Message. // if (_Socket != NULL) { ((ICSecureSocket *)_Socket)->SetProviderIndex(0); } attempt_ssl_connect: // // Attempt to do the connect // fsm.SetFunctionState(FSM_STATE_4); error = OpenProxyTunnel(); if (error == ERROR_IO_PENDING) { goto quit; } // // fall through // case FSM_STATE_4: if ((error != ERROR_SUCCESS) || (GetStatusCode() != HTTP_STATUS_OK)) { goto quit; } // // Bind Socket Object with Proper HostName, // so we can check for valid common name // in the handshake. // INET_ASSERT(_Socket->IsSecure()); /* SCLE ref */ error = ((ICSecureSocket *)_Socket)->SetHostName(GetHostName(), GetHostPort(), pInternet->GetSslSessionCache()); if (error != ERROR_SUCCESS) { goto quit; } // // if the app wants a secure channel (PCT/SSL) then we must negotiate // the security here // // // dwProviderIndex will be managed by SecureHandshakeWithServer, // And will be set to 0 when we can't try anymore. // DWORD asyncFlags; // // find out if we're async. N.B. see Assumes // asyncFlags = IsAsyncHandle() ? (SF_NON_BLOCKING|SF_OVERLAPPED) : 0; // // If we're Posting or sending data, make sure // the SSL connection knows about it, for the // purposes of generating errors. // if ((GetMethodType() == HTTP_METHOD_TYPE_POST) || (GetMethodType() == HTTP_METHOD_TYPE_PUT)) { asyncFlags |= SF_SENDING_DATA; } fsm.SetFunctionState(FSM_STATE_5); error = ((ICSecureSocket *)_Socket)->SecureHandshakeWithServer( (asyncFlags | SF_ENCRYPT), &fsm.m_bAttemptReconnect); if (error == ERROR_IO_PENDING) { goto quit; } // // fall through // case FSM_STATE_5: if (error != ERROR_SUCCESS) { if (error == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) { if (_Socket->IsSecure()) { if(m_pSecurityInfo) { /* SCLE ref */ m_pSecurityInfo->Release(); } /* SCLE ref */ m_pSecurityInfo = ((ICSecureSocket *)_Socket)->GetSecurityEntry(); } } // // we disconnected the socket and we won't attempt to reconnect. We // need to release the connection to balance the connection limiter // if (!fsm.m_bAttemptReconnect) { ReleaseConnection(TRUE, // bClose FALSE, // bIndicate TRUE // bDispose ); } else { _Socket->Disconnect(); } } // // SSL2 hack for old IIS servers. // We re-open the socket, and call again. // if (fsm.m_bAttemptReconnect) { goto attempt_ssl_connect; } break; case FSM_STATE_6: fsm.SetFunctionState(FSM_STATE_7); error = OpenConnection(FALSE, FALSE); if (error == ERROR_IO_PENDING) { break; } case FSM_STATE_7: //dprintf("HTTP connect took %d msec\n", GetTickCount() - _dwQuerySetCookieHeader); //hack if (error == ERROR_SUCCESS && _Socket && _Socket->IsSecure() && m_pSecurityInfo == NULL ) { /* SCLE ref */ m_pSecurityInfo = ((ICSecureSocket *)_Socket)->GetSecurityEntry(); if (m_pSecurityInfo) { m_pSecurityInfo->SetSecureFlags(_dwSecurityFlags); } } break; default: INET_ASSERT(FALSE); error = ERROR_WINHTTP_INTERNAL_ERROR; break; } quit: if (error != ERROR_IO_PENDING) { fsm.SetDone(); // PERF_LEAVE(MakeConnection_Fsm); } PERF_LEAVE(MakeConnection_Fsm); DEBUG_LEAVE(error); return error; }