/*++ Copyright (c) 1994 Microsoft Corporation Module Name: sendapi.cxx Abstract: This file contains the implementation of the HttpSendRequestA API. Contents: WinHttpSendRequest HttpSendRequestA WinHttpReceiveResponse HttpWrapSendRequest Author: Keith Moore (keithmo) 16-Nov-1994 Revision History: 29-Apr-97 rfirth Conversion to FSM --*/ #include #include // // private prototypes // PRIVATE BOOL HttpWrapSendRequest( IN HINTERNET hRequest, IN LPCSTR lpszHeaders OPTIONAL, IN DWORD dwHeadersLength, IN LPVOID lpOptional OPTIONAL, IN DWORD dwOptionalLength, IN DWORD dwOptionalLengthTotal, IN AR_TYPE arRequest, IN DWORD_PTR dwContext=NULL ); // // functions // INTERNETAPI BOOL WINAPI HttpSendRequestA( IN HINTERNET hRequest, IN LPCSTR lpszHeaders OPTIONAL, IN DWORD dwHeadersLength, IN LPVOID lpOptional OPTIONAL, IN DWORD dwOptionalLength ) /*++ Routine Description: Sends the specified request to the HTTP server. Arguments: hRequest - An open HTTP request handle returned by HttpOpenRequest() lpszHeaders - Additional headers to be appended to the request. This may be NULL if there are no additional headers to append dwHeadersLength - The length (in characters) of the additional headers. If this is -1L and lpszAdditional is non-NULL, then lpszAdditional is assumed to be zero terminated (ASCIIZ) lpOptionalData - Any optional data to send immediately after the request headers. This is typically used for POST operations. This may be NULL if there is no optional data to send dwOptionalDataLength - The length (in BYTEs) of the optional data. This may be zero if there is no optional data to send Return Value: BOOL Success - TRUE Failure - FALSE. For more information call GetLastError(). If the request was async, then GetLastError() will return ERROR_IO_PENDING which means that the operation initially succeeded, and that the caller should wait for the status callback to discover the final success/failure status --*/ { DEBUG_ENTER_API((DBG_API, Bool, "HttpSendRequestA", "%#x, %.80q, %d, %#x, %d", hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength )); BOOL fRet= HttpWrapSendRequest( hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, 0, AR_HTTP_SEND_REQUEST ); DEBUG_LEAVE_API(fRet); return fRet; } INTERNETAPI BOOL WINAPI WinHttpSendRequest( IN HINTERNET hRequest, IN LPCWSTR lpszHeaders OPTIONAL, IN DWORD dwHeadersLength, IN LPVOID lpOptional OPTIONAL, IN DWORD dwOptionalLength, IN DWORD dwTotalLength, IN DWORD_PTR dwContext ) /*++ Routine Description: Sends the specified request to the HTTP server. Arguments: hRequest - An open HTTP request handle returned by HttpOpenRequest() lpszHeaders - Additional headers to be appended to the request. This may be NULL if there are no additional headers to append dwHeadersLength - The length (in characters) of the additional headers. If this is -1L and lpszAdditional is non-NULL, then lpszAdditional is assumed to be zero terminated (ASCIIZ) lpOptionalData - Any optional data to send immediately after the request headers. This is typically used for POST operations. This may be NULL if there is no optional data to send dwOptionalDataLength - The length (in BYTEs) of the optional data. This may be zero if there is no optional data to send Return Value: BOOL Success - TRUE Failure - FALSE. For more information call GetLastError(). If the request was async, then GetLastError() will return ERROR_IO_PENDING which means that the operation initially succeeded, and that the caller should wait for the status callback to discover the final success/failure status --*/ { DEBUG_ENTER_API((DBG_API, Bool, "WinHttpSendRequest", "%#x, %.80wq, %d, %#x, %d, %d, %x", hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength, dwContext )); DWORD dwErr = ERROR_SUCCESS; BOOL fResult = FALSE; MEMORYPACKET mpHeaders; if (!hRequest) { dwErr = ERROR_INVALID_HANDLE; goto cleanup; } if (lpszHeaders && IsBadReadPtr(lpszHeaders, 1)) { dwErr = ERROR_INVALID_PARAMETER; goto cleanup; } if (dwHeadersLength == -1L) { dwHeadersLength = lpszHeaders ? lstrlenW(lpszHeaders) : 0; } if (lpszHeaders) { if ((dwHeadersLength == -1) ? IsBadStringPtrW(lpszHeaders, -1) : IsBadReadPtr(lpszHeaders, dwHeadersLength)) { dwErr = ERROR_INVALID_PARAMETER; goto cleanup; } ALLOC_MB(lpszHeaders, dwHeadersLength, mpHeaders); if (!mpHeaders.psStr) { dwErr = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } UNICODE_TO_ANSI(lpszHeaders, mpHeaders); } if (lpOptional && dwOptionalLength && IsBadReadPtr(lpOptional, dwOptionalLength) ) { dwErr = ERROR_INVALID_PARAMETER; goto cleanup; } AR_TYPE ar; // Always require a WinHttpReceiveResponse to initiate // FSM_STATE_4 onwards in HTTP_REQUEST_HANDLE_OBJECT::HttpSendRequest_Start: if (dwOptionalLength <= dwTotalLength) { ar = AR_HTTP_BEGIN_SEND_REQUEST; } else { dwErr = ERROR_INVALID_PARAMETER; goto cleanup; } fResult = HttpWrapSendRequest(hRequest, mpHeaders.psStr, mpHeaders.dwSize, lpOptional, dwOptionalLength, dwTotalLength, ar, dwContext); // This calls SetLastError if fResult is FALSE. cleanup: if (dwErr!=ERROR_SUCCESS) { SetLastError(dwErr); DEBUG_ERROR(HTTP, dwErr); } DEBUG_LEAVE_API(fResult); return fResult; } INTERNETAPI BOOL WINAPI WinHttpReceiveResponse( IN HINTERNET hRequest, IN LPVOID lpBuffersOut OPTIONAL ) /*++ Routine Description: description-of-function. Arguments: hRequest - lpBuffersOut - dwFlags - dwContext - Return Value: BOOL --*/ { DEBUG_ENTER_API((DBG_API, Bool, "WinHttpReceiveResponse", "%#x, %#x", hRequest, lpBuffersOut )); DWORD dwErr = ERROR_SUCCESS; BOOL fResult = FALSE; if (!hRequest) { dwErr = ERROR_INVALID_HANDLE; } else if (lpBuffersOut) { dwErr = ERROR_INVALID_PARAMETER; } else { fResult = HttpWrapSendRequest(hRequest, NULL, 0, NULL, 0, 0, AR_HTTP_END_SEND_REQUEST); } if (dwErr!=ERROR_SUCCESS) { SetLastError(dwErr); DEBUG_ERROR(HTTP, dwErr); } DEBUG_LEAVE_API(fResult); return fResult; } PRIVATE BOOL HttpWrapSendRequest( IN HINTERNET hRequest, IN LPCSTR lpszHeaders OPTIONAL, IN DWORD dwHeadersLength, IN LPVOID lpOptional OPTIONAL, IN DWORD dwOptionalLength, IN DWORD dwOptionalLengthTotal, IN AR_TYPE arRequest, IN DWORD_PTR dwContext ) /*++ Routine Description: Sends the specified request to the HTTP server. Arguments: hRequest - An open HTTP request handle returned by HttpOpenRequest() lpszHeaders - Additional headers to be appended to the request. This may be NULL if there are no additional headers to append dwHeadersLength - The length (in characters) of the additional headers. If this is -1L and lpszAdditional is non-NULL, then lpszAdditional is assumed to be zero terminated (ASCIIZ) lpOptionalData - Any optional data to send immediately after the request headers. This is typically used for POST operations. This may be NULL if there is no optional data to send dwOptionalDataLength - The length (in BYTEs) of the optional data. This may be zero if there is no optional data to send dwOptionalLengthTotal - Total length need to be sent for File Upload. arRequest - Which API the caller is making, assumed to be HttpEndRequestA, HttpSendRequestExA, or HttpSendRequestA Return Value: BOOL Success - TRUE Failure - FALSE. For more information call GetLastError(). If the request was async, then GetLastError() will return ERROR_IO_PENDING which means that the operation initially succeeded, and that the caller should wait for the status callback to discover the final success/failure status Comments: --*/ { DEBUG_ENTER((DBG_HTTP, Bool, "HttpWrapSendRequest", "%#x, %.80q, %d, %#x, %d, %d, %x", hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwOptionalLengthTotal, dwContext )); PERF_ENTER(HttpWrapSendRequest); DWORD error = ERROR_SUCCESS; HINTERNET hRequestMapped = NULL; BOOL bDeref = TRUE; if (!GlobalDataInitialized) { error = ERROR_WINHTTP_NOT_INITIALIZED; goto done; } // // we will need the thread info for several items // LPINTERNET_THREAD_INFO lpThreadInfo; lpThreadInfo = InternetGetThreadInfo(); if (lpThreadInfo == NULL) { error = ERROR_WINHTTP_INTERNAL_ERROR; goto done; } // // the only FSMs that can come before this one are InternetOpenUrl() or // HttpSendRequest() when we are performing nested send for https:// // tunnelling through proxy // INET_ASSERT((lpThreadInfo->Fsm == NULL) || (lpThreadInfo->Fsm->GetType() == FSM_TYPE_PARSE_HTTP_URL) || (lpThreadInfo->Fsm->GetType() == FSM_TYPE_OPEN_PROXY_TUNNEL) ); INET_ASSERT( arRequest == AR_HTTP_SEND_REQUEST || arRequest == AR_HTTP_BEGIN_SEND_REQUEST || arRequest == AR_HTTP_END_SEND_REQUEST ); // // map the handle // error = MapHandleToAddress(hRequest, (LPVOID *)&hRequestMapped, FALSE); if ((error != ERROR_SUCCESS) && (hRequestMapped == NULL)) { goto quit; } // // Cast it to the object that we know. We are going to do caching // semantics with this // HTTP_REQUEST_HANDLE_OBJECT * pRequest; pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)hRequestMapped; // // set the context and handle info & reset the error variables, // but only if not for a ReceiveResponse call. // if (arRequest != AR_HTTP_END_SEND_REQUEST) { pRequest->SetContext(dwContext); INET_ASSERT( (INTERNET_CONNECT_HANDLE_OBJECT *)pRequest->GetParent() ); ((INTERNET_CONNECT_HANDLE_OBJECT *)pRequest->GetParent())->SetContext(dwContext); // We need this information to special-case for Redirects and Auth because of RR FSM changes: pRequest->SetWriteRequired(dwOptionalLength < dwOptionalLengthTotal); } _InternetSetObjectHandle(lpThreadInfo, hRequest, hRequestMapped); _InternetClearLastError(lpThreadInfo); // // quit now if the handle was invalid // if (error != ERROR_SUCCESS) { goto quit; } // // use RIsHandleLocal() to discover 4 things: // // 1. Handle is valid // 2. Handle is of expected type (HTTP Request in this case) // 3. Handle is local or remote // 4. Handle supports async I/O // BOOL isLocal; BOOL isAsync; error = RIsHandleLocal(hRequestMapped, &isLocal, &isAsync, TypeHttpRequestHandle ); if (error != ERROR_SUCCESS) { goto quit; } if (isAsync) { error = InitializeAsyncSupport(); if (error != ERROR_SUCCESS) { goto quit; } } // // For SEND_REQUEST, and BEGIN_SEND_REQUEST, we need // to do some basic initalization // if ( arRequest == AR_HTTP_SEND_REQUEST || arRequest == AR_HTTP_BEGIN_SEND_REQUEST) { error = pRequest->InitBeginSendRequest(lpszHeaders, dwHeadersLength, &lpOptional, &dwOptionalLength, dwOptionalLengthTotal ); if ( error != ERROR_SUCCESS) { goto quit; } // (Re)set flag to indicate WinHttpReceiveResponse needs to be called. pRequest->SetReceiveResponseState(FALSE); // ReceiveResponse FSM won't be exec'ed/queued until the client calls // WinHttpReceiveResponse to mark the end of writing additional data. pRequest->SetWriteDataNeeded( arRequest == AR_HTTP_BEGIN_SEND_REQUEST ? TRUE : FALSE); } else if (arRequest == AR_HTTP_END_SEND_REQUEST) { pRequest->SetReceiveResponseState(TRUE); // Previously this would be a case where WinHttpReceiveResponse did // not need to be called. Now, in this case, simply return whether or // not we've reached the object data state. if (!pRequest->CheckWriteDataNeeded()) { // Nothing needs to be queued, so let the original send request fsm // complete, if needed, and indicate the current state. if (!isAsync || ((pRequest->GetState() & 0x0F) >= (HttpRequestStateObjectData & 0x0F))) { error = ERROR_SUCCESS; goto quit; } else { // Async sendrequest is still pending, so there's no need to // create another fsm. This call isn't async, so leave deref // set to TRUE. error = ERROR_IO_PENDING; goto quit; } } } // // send the request to the server. This may involve redirections and user // authentication // //error = DoFsm(New CFsm_HttpSendRequest(lpOptional, dwOptionalLength, pRequest, arRequest)); //if (error == ERROR_IO_PENDING) { // bDeref = FALSE; //} CFsm_HttpSendRequest * pFsm; pFsm = New CFsm_HttpSendRequest(lpOptional, dwOptionalLength, pRequest, arRequest); if (pFsm != NULL) { if (isAsync && !lpThreadInfo->IsAsyncWorkerThread) { error = DoAsyncFsm(pFsm, pRequest); } else { pFsm->SetPushPop(TRUE); pFsm->Push(); error = DoFsm(pFsm); } if (error == ERROR_IO_PENDING) { bDeref = FALSE; } } else { error = ERROR_NOT_ENOUGH_MEMORY; } quit: // // if we went async don't deref the handle // if (bDeref && (hRequestMapped != NULL)) { DereferenceObject((LPVOID)hRequestMapped); } done: BOOL success = TRUE; // SetLastError must be called after PERF_LEAVE !!! PERF_LEAVE(HttpWrapSendRequest); if (error != ERROR_SUCCESS) { SetLastError(error); DEBUG_ERROR(HTTP, error); success = FALSE; } DEBUG_LEAVE(success); return success; }