649 lines
18 KiB
C++
649 lines
18 KiB
C++
/*++
|
||
|
||
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;
|
||
}
|