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