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
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;
|
|
}
|
|
|
|
|