Windows2003-3790/inetcore/winhttp/v5/api/sendapi.cxx
2020-09-30 16:53:55 +02:00

649 lines
18 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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 <wininetp.h>
#include <perfdiag.hxx>
//
// 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;
}