Leaked source code of windows server 2003
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.
 
 
 
 
 
 

547 lines
13 KiB

#include "stdafx.h"
#include <malloc.h>
#if !defined(BITS_V12_ON_NT4)
#include "request.tmh"
#endif
CBitsCommandRequest::CBitsCommandRequest(
URL_INFO * UrlInfo
) : m_UrlInfo( UrlInfo ),
m_hRequest( 0 )
{
THROW_HRESULT( OpenHttpRequest( L"BITS_POST", L"HTTP/1.1", *UrlInfo, &m_hRequest ));
}
CBitsCommandRequest::~CBitsCommandRequest()
{
InternetCloseHandle(m_hRequest);
}
void
CBitsCommandRequest::AddContentName(
StringHandle FullPath
)
{
wchar_t Template[] = L"Content-Name: %s\r\n";
StringHandle DirectoryName;
StringHandle FileName;
DirectoryName = BITSCrackFileName( FullPath, FileName );
size_t Length = RTL_NUMBER_OF(Template) + wcslen(FileName);
CAutoString Header ( new wchar_t[ Length ] );
THROW_HRESULT( StringCchPrintf( Header.get(), Length, Template, LPCWSTR(FileName) ));
// add the header
if (!HttpAddRequestHeaders(m_hRequest,
Header.get(),
-1L,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
{
ThrowLastError();
}
}
void
CBitsCommandRequest::AddPacketType(
wchar_t * type
)
{
LogDl("upload: adding packet type '%S'", type );
//
// Assemble the header.
//
wchar_t Template[] = _T("BITS-Packet-Type: %s\r\n");
size_t Length = RTL_NUMBER_OF(Template) + wcslen(type);
CAutoString Header ( new wchar_t[ Length ] );
THROW_HRESULT( StringCchPrintf( Header.get(), Length, Template, type ));
// add the header
if (!HttpAddRequestHeaders(m_hRequest,
Header.get(),
-1L,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
{
ThrowLastError();
}
}
void
CBitsCommandRequest::AddContentRange(
UINT64 RangeStart,
UINT64 RangeEnd,
UINT64 Size
)
{
wchar_t Template[] = _T("Content-Range: bytes %I64u-%I64u/%I64u\r\n");
size_t Length = RTL_NUMBER_OF(Template) + INT64_DIGITS + INT64_DIGITS + INT64_DIGITS;
CAutoString header ( new wchar_t[ Length ] );
THROW_HRESULT( StringCchPrintf( header.get(), Length, Template, RangeStart, RangeEnd, Size ));
if (!HttpAddRequestHeaders(m_hRequest,
header.get(),
-1L,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
{
ThrowLastError();
}
}
void
CBitsCommandRequest::AddSessionId(
StringHandle & id
)
{
//
// Assemble the header.
//
wchar_t Template[] = _T("BITS-Session-Id: %s\r\n");
size_t Length = RTL_NUMBER_OF(Template) + wcslen(id);
CAutoString header ( new wchar_t[ Length ] );
THROW_HRESULT( StringCchPrintf( header.get(), Length, Template, LPCWSTR(id) ));
if (!HttpAddRequestHeaders(m_hRequest,
header.get(),
-1L,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
{
ThrowLastError();
}
}
void
CBitsCommandRequest::AddSupportedProtocols()
{
wchar_t header[] = L"BITS-Supported-Protocols: {7df0354d-249b-430f-820d-3d2a9bef4931}\r\n";
// add the header
if (!HttpAddRequestHeaders(m_hRequest,
header,
-1L,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
{
ThrowLastError();
}
}
// Upload Protocol ID: {7df0354d-249b-430f-820d-3d2a9bef4931}
// DEFINE_GUID(UploadProtocolId,0x7df0354d,0x249b,0x430f,0x82,0x0d,0x3d,0x2a,0x9b,0xef,0x49,0x31);
static const GUID UploadProtocolId =
{ 0x7df0354d, 0x249b, 0x430f, {0x82, 0x0d, 0x3d, 0x2a, 0x9b, 0xef, 0x49, 0x31} };
HRESULT
CBitsCommandRequest::CheckResponseProtocol( GUID *pGuid )
{
HRESULT hr = S_OK;
if (!IsEqualGUID(*pGuid,UploadProtocolId))
{
hr = BG_E_CLIENT_SERVER_PROTOCOL_MISMATCH;
}
return hr;
}
DWORD
CBitsCommandRequest::Send(
CAbstractDataReader * Reader
)
{
bool bNeedLock;
try
{
ReleaseWriteLock( bNeedLock );
//
// Send the request, and read the reply code.
//
THROW_HRESULT( SendRequest( m_hRequest, m_UrlInfo, Reader ));
DWORD dwStatus;
{
DWORD dwLength;
dwLength = sizeof(dwStatus);
if (! HttpQueryInfo(m_hRequest,
HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
(LPVOID)&dwStatus,
&dwLength,
NULL))
{
ThrowLastError();
}
}
if (dwStatus == 200 ||
dwStatus == 201)
{
THROW_HRESULT( CheckReplyPacketType() );
}
DrainReply();
ReclaimWriteLock( bNeedLock );
return dwStatus;
}
catch ( ComError err )
{
ReclaimWriteLock( bNeedLock );
throw;
}
}
void
CBitsCommandRequest::DrainReply()
/*
Server replies often contain an entity body even though BITS did not ask for one.
For example, a 404 error may be accompanied by HTML explaining how to contact the
system administrator. BITS needs to read all of the reply entity body before sending
the next request on the HTTP connection, or it will read data instead of headers after
the next request.
*/
{
//
// Look for a length header.
//
HRESULT hr;
UINT64 Length;
hr = GetContentLength( &Length );
if (hr == BG_E_HEADER_NOT_FOUND)
{
//
// If a content length is not specified, then the response may be in chunked encoding,
// terminated by a connection close, or invalid. In those cases, the client won't be reading
// more from this connection anyway, so reading all the data is not necessary.
//
return;
}
THROW_HRESULT( hr );
//
// drain the pipe.
//
DWORD BytesRead;
while ( Length > 0 )
{
if (!InternetReadFile( m_hRequest,
g_FileDataBuffer,
FILE_DATA_BUFFER_LEN,
&BytesRead
))
{
LogWarning("read failed %d", GetLastError() );
break;
}
Length -= BytesRead;
if (BytesRead == 0)
{
// graceful closure
return;
}
}
return;
}
HRESULT
CBitsCommandRequest::GetServerRange(
UINT64 * RangeEnd
)
{
const wchar_t Name[] = L"BITS-Received-Content-Range";
wchar_t Header[ INT64_DIGITS+1 ];
RETURN_HRESULT( GetMandatoryHeaderCb( WINHTTP_QUERY_CUSTOM, Name, Header, sizeof(Header), __LINE__));
// parse the data
if (1 != _stscanf( Header, _T("%I64u"), RangeEnd ))
{
return BG_E_INVALID_SERVER_RESPONSE;
}
LogInfo("content range: %I64u", *RangeEnd );
return S_OK;
}
HRESULT
CBitsCommandRequest::GetContentLength(
UINT64 * Length
)
{
*Length = 0;
wchar_t Header[INT64_DIGITS+1];
RETURN_HRESULT( GetOptionalHeaderCb( WINHTTP_QUERY_CONTENT_LENGTH, WINHTTP_HEADER_NAME_BY_INDEX, Header, sizeof(Header), __LINE__));
if ( 1 != swscanf( Header, L"%I64u", Length ) )
{
return BG_E_INVALID_SERVER_RESPONSE;
}
LogInfo("Content-Length was %I64u", *Length );
return S_OK;
}
HRESULT
CBitsCommandRequest::GetProtocol(
GUID * id
)
{
const wchar_t Name[] = L"BITS-Protocol";
wchar_t Header[ MAX_GUID_CHARS ];
RETURN_HRESULT( GetMandatoryHeaderCb( WINHTTP_QUERY_CUSTOM, Name, Header, sizeof(Header), __LINE__));
HRESULT hr = IIDFromString( Header, id );
if (hr == E_INVALIDARG)
{
hr = BG_E_INVALID_SERVER_RESPONSE;
}
return hr;
}
HRESULT
CBitsCommandRequest::GetSessionId(
StringHandle * id
)
{
const wchar_t Name[] = L"BITS-Session-Id";
wchar_t Header[ MAX_SESSION_ID ];
RETURN_HRESULT( GetMandatoryHeaderCb( WINHTTP_QUERY_CUSTOM, Name, Header, sizeof(Header), __LINE__));
try
{
// this may throw an out-of-memory exception
//
*id = Header;
}
catch ( ComError err )
{
return err.Error();
}
return S_OK;
}
HRESULT
CBitsCommandRequest::CheckReplyPacketType()
{
const wchar_t Name[] = L"BITS-Packet-Type";
wchar_t Header[ MAX_PACKET_TYPE ];
RETURN_HRESULT( GetMandatoryHeaderCb( WINHTTP_QUERY_CUSTOM, Name, Header, sizeof(Header), __LINE__));
if (0 != _wcsicmp( Header, L"Ack"))
{
return BG_E_INVALID_SERVER_RESPONSE;
}
return S_OK;
}
HRESULT
CBitsCommandRequest::GetBitsError(
HRESULT * phr
)
{
const wchar_t Name[] = L"BITS-Error";
wchar_t Header[ INT_DIGITS+3+1 ]; // allow space for "0x" prefix and negative sign - in case we later allow those
RETURN_HRESULT( GetOptionalHeaderCb( WINHTTP_QUERY_CUSTOM, Name, Header, sizeof(Header), __LINE__));
LogWarning("BITS-Error was '%S'", Header );
// parse the data
if (1 != _stscanf( Header, _T("0x%x"), phr ))
{
return BG_E_INVALID_SERVER_RESPONSE;
}
return S_OK;
}
HRESULT
CBitsCommandRequest::GetBitsErrorContext(
DWORD * pdw
)
{
const wchar_t Name[] = L"BITS-Error-Context";
wchar_t Header[ INT_DIGITS+1 ];
RETURN_HRESULT( GetOptionalHeaderCb( WINHTTP_QUERY_CUSTOM, Name, Header, sizeof(Header), __LINE__));
LogWarning("BITS-Error-Context was '%S'", Header );
// parse the data
if (1 != _stscanf( Header, _T("0x%x"), pdw ) ||
(*pdw != BG_ERROR_CONTEXT_REMOTE_FILE && *pdw != BG_ERROR_CONTEXT_REMOTE_APPLICATION))
{
return BG_E_INVALID_SERVER_RESPONSE;
}
return S_OK;
}
HRESULT
CBitsCommandRequest::GetReplyUrl(
CAutoString & ReplyUrl
)
{
const wchar_t Name[] = L"BITS-Reply-Url";
HRESULT hr = GetRequestHeader( m_hRequest, HTTP_QUERY_CUSTOM, Name, ReplyUrl, INTERNET_MAX_URL_LENGTH );
if (hr == S_FALSE)
{
return BG_E_HEADER_NOT_FOUND;
}
return hr;
}
HRESULT
CBitsCommandRequest::GetHostId(
StringHandle * pstr
)
{
*pstr = StringHandle();
CAutoString Value;
const wchar_t Name[] = L"BITS-Host-Id";
HRESULT hr = GetRequestHeader( m_hRequest, HTTP_QUERY_CUSTOM, Name, Value, MAX_HOST_ID );
if (hr == S_FALSE)
{
return S_OK;
}
if (hr != S_OK)
{
return hr;
}
//
// The StringHandle will copy Value.get() instead of taking ownership of it,
// so this fn still needs to delete the original.
//
*pstr = Value.get();
return S_OK;
}
HRESULT
CBitsCommandRequest::GetHostIdFallbackTimeout(
DWORD * pVal
)
{
*pVal = 0xFFFFFFFF;
const wchar_t * Name = L"BITS-Host-Id-Fallback-Timeout";
wchar_t Header[ INT_DIGITS+1 ];
HRESULT hr = GetOptionalHeaderCb( WINHTTP_QUERY_CUSTOM, Name, Header, sizeof(Header), __LINE__);
if (hr == BG_E_HEADER_NOT_FOUND)
{
return S_OK;
}
if (FAILED(hr))
{
return hr;
}
if ( !swscanf( Header, L"%u", pVal ) )
return BG_E_INVALID_SERVER_RESPONSE;
return S_OK;
}
HRESULT
CBitsCommandRequest::GetOptionalHeaderCb(
DWORD dwInfoLevel,
LPCWSTR Name,
LPWSTR Value,
DWORD ValueBytes,
DWORD Line
)
{
if (WinHttpQueryHeaders( m_hRequest,
dwInfoLevel,
Name,
Value,
&ValueBytes,
WINHTTP_NO_HEADER_INDEX
))
{
return S_OK;
}
DWORD s = GetLastError();
if (s == ERROR_INSUFFICIENT_BUFFER)
{
LogError("line %d: header is too large", Line);
return BG_E_INVALID_SERVER_RESPONSE;
}
// note that BG_E_HEADER_NOT_FOUND is the same as HRESULT_FROM_WIN32(ERROR_WINHTTP_HEADER_NOT_FOUND)
return HRESULT_FROM_WIN32( s );
}
HRESULT
CBitsCommandRequest::GetMandatoryHeaderCb(
DWORD dwInfoLevel,
LPCWSTR Name,
LPWSTR Value,
DWORD ValueBytes,
DWORD Line
)
{
HRESULT hr;
hr = GetOptionalHeaderCb( dwInfoLevel, Name, Value, ValueBytes, Line );
if (hr == BG_E_HEADER_NOT_FOUND)
{
LogError("line %d: header not present", Line);
return BG_E_INVALID_SERVER_RESPONSE;
}
return hr;
}