WinHTTP Null 바이트를 다운로드하거나 결과 버퍼를 잘못 복사합니까?
-
13-11-2019 - |
문제
나는 최근에 WinHTTP에 완전히 작동하는 WinInet 프로그램을 이식했습니다. 다음은 전체 코드의 한 줄로 전체 GET 요청을 랩핑하기 위해 작성한 기능입니다.
bool Get(Url url, std::vector<char>& data, ProgressCallbackFunction progressCallback = nullptr) throw()
{
long cl = -1;
DWORD clSize = sizeof(cl);
DWORD readCount = 0;
DWORD totalReadCount = 0;
DWORD availableBytes = 0;
std::vector<char> buf;
if (_session != NULL)
throw std::exception("Concurrent sessions are not supported");
_session = ::WinHttpOpen(_userAgent.c_str(), WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, NULL);
auto connection = ::WinHttpConnect(_session, url.HostName.c_str(), url.Port, 0);
auto request = ::WinHttpOpenRequest(connection, TEXT("GET"), url.GetPathAndQuery().c_str(), NULL, NULL, NULL, WINHTTP_FLAG_REFRESH);
if (request == NULL)
{
_lastError = ::GetLastError();
::WinHttpCloseHandle(_session);
_session = NULL;
return false;
}
auto sendRequest = ::WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, NULL, WINHTTP_NO_REQUEST_DATA, NULL, NULL, NULL);
if (sendRequest == FALSE)
{
_lastError = ::GetLastError();
::WinHttpCloseHandle(request);
::WinHttpCloseHandle(_session);
_session = NULL;
return false;
}
if (::WinHttpReceiveResponse(request, NULL))
{
if (progressCallback != nullptr && progressCallback != NULL)
{
if (!::WinHttpQueryHeaders(request, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, reinterpret_cast<LPVOID>(&cl), &clSize, 0))
{
cl = -1;
}
}
while (::WinHttpQueryDataAvailable(request, &availableBytes))
{
if (availableBytes)
{
buf.resize(availableBytes + 1);
auto hasRead = ::WinHttpReadData(request, &buf[0], availableBytes, &readCount);
totalReadCount += readCount;
data.insert(data.end(), buf.begin(), buf.begin() + readCount);
buf.clear();
if (progressCallback != nullptr && progressCallback != NULL)
{
progressCallback(totalReadCount, cl, getProgress(totalReadCount, cl));
}
}
else
break;
}
}
else
{
_lastError = ::GetLastError();
::WinHttpCloseHandle(request);
::WinHttpCloseHandle(_session);
_session = NULL;
return false;
}
::WinHttpCloseHandle(request);
::WinHttpCloseHandle(_session);
_session = NULL;
return true;
}
.
코드 은 요청 된 URL을 다운로드한다는 점에서 작동합니다. 문제는 서버가 콘텐츠 길이 헤더 ( 가장 많이 )를 반환하지 않을 때 발생합니다. 코드는 여전히 모든 데이터를 다운로드하지만 문자열로 변환 될 때 내장 된 null 바이트가 있습니다.
위의 코드는 다음과 같이 호출됩니다.
.
Url url(TEXT("http://msdn.microsoft.com/en-us/site/aa384376"));
Client wc;
std::vector<char> results;
wc.Get(url, results);
StdString html(results.begin(), results.end());
StdOut << html << endl;
stdstring은 typedef std :: basic_string
임베디드 널이므로 모든 응답이 콘솔에 표시되는 것은 아닙니다. 여기에서 볼 수있는 코드를 디버깅하는 코드를 실행할 때 표시되는 출력은 여기에서 볼 수 있습니다 (줄 바꿈은 텍스트가 내 콘솔에 래핑됩니다). 첫 번째 널은 맨 끝에 "__in"직후에 보입니다. "계속할 키를 누르십시오."출력이 표시됩니다. 다음은 출력의 화면 캡입니다.
여기에서 널이 보이는 위치와 정확히 나타나는 HTML 변수의 값의 텍스트 시각화 화면 캡입니다.
해결책
출력을 추가로 검토하면 nulls가 아닙니다.그들은 잘못 저장되기 때문에 콘솔이 표시 할 수없는 문자를 유니 코드 (따라서 잘못 변환합니다).를 변경하여 Get 메서드 (및 호출 코드)에서 문제를 해결할 수있었습니다.
.
std::vector<char>
~
.
std::vector<unsigned char>
과 지금은 모두 잘됩니다.