Search code examples
c++winhttpwinhttprequestgetlasterror

GetLastError returns 6 after call to WinHttpOpenRequest


Im not sure if this is lack of knowledge in msdn or not. i Have the following code:

    #include "stdafx.h"
    #include <iostream>
    #include <string>
    #include <windows.h>
    #include <WinHttp.h>
    #include "myHTTP.h"
int main(){
    WinHTTP newHTTP("test", "test");
        bool myResult;

        newHTTP.httpConnect(L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36",
        L"https://pages.awscloud.com/awsomedayonlineconference-reg.html",
        1,
        L"GET");

        newHTTP.httpAddHeader(L"Content-Type: application/x-www-form-urlencoded\r\n");
        newHTTP.httpSend();
        myResult = newHTTP.httpReceive();



        newHTTP.closeHandles();
 return 0;
}

I have a class for this and the following line is being executed: // open the request - not connected at this point

hRequest = WinHttpOpenRequest(hConnect, protocol.c_str(), NULL, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

if (!hRequest) {
    printf("error2: %d", GetLastError());
}

SO basically when i run my software, it returns here because !hrequest is empty when calling winhttpopenrequest. I have added getlasterror to see why this is but the only output i get is:

error2: 6

I then read the msdn for winhttp and i can see the errors returned from the function here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa384099(v=vs.85).aspx but this mysterious error 6 is making no sense to me.

Any help on seeing why the handle is invalid?

full class code:

#pragma once
// WinHTTP wrapper for web protocol -- windows 8.1+
class WinHTTP {

private:
    std::string siteUsername, sitePassword;
    std::wstring UA, URL;
    bool bResult = false;
    DWORD dwSize = sizeof(DWORD); // used to handle reading data in bytes
    LPSTR pszOutBuffer; // used to Allocate space for the buffer.
    DWORD dwDownloaded = 0; // set to null if using asynchronously and use in callback function only
    HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;

public:
    WinHTTP(std::string myuser = "", std::string mypass = "") {
        siteUsername = myuser;
        sitePassword = mypass;
    }

    // TODO: update to be able to add proxy details either here or before. do check if proxy has been detected in here and open/connect accordingly 
    void httpConnect(std::wstring userAgent, std::wstring myURL, int isHTTPS, std::wstring protocol) {

        UA = userAgent;
        URL = myURL;

        std::wstring acceptTypes = L"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8";

        int portToUse;
        if (isHTTPS == 1) {
            portToUse = 443;
        }
        else {
            portToUse = 80;
        }

        //initialize http and return session handle -- use c_str to convert wstring to LPCWSTR
        hSession = WinHttpOpen(UA.c_str(),
            WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

        //make the connection request
        if (hSession) {
            hConnect = WinHttpConnect(hSession, URL.c_str(), portToUse, 0);
        }
        else {
            std::cout << "winhttpconnect error " << GetLastErrorAsString();
        }

        // open the request - not connected at this point
        hRequest = WinHttpOpenRequest(hConnect, protocol.c_str(), NULL, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

        if (!hRequest) {
            std::cout << "winhttpopenrequest error " << GetLastErrorAsString();
        }

    }

    std::string GetLastErrorAsString()
    {
        //Get the error message, if any.
        DWORD errorMessageID = ::GetLastError();
        if (errorMessageID == 0)
            return std::string(); //No error message has been recorded

        LPSTR messageBuffer = nullptr;
        size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);

        std::string message(messageBuffer, size);

        //Free the buffer.
        LocalFree(messageBuffer);

        return message;
    }

    void httpAddHeader(std::wstring myheader) {

        if (hRequest) {
            bResult = WinHttpAddRequestHeaders(hRequest, myheader.c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD);
        }

    }

    bool httpSend() {
        if (hRequest) {
            bResult = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
        }

        if (!bResult) {
            std::cout << "winhttpsendrequest error " << GetLastErrorAsString();
            return false;
        }
        else {
            return true;
        }
    }

    bool httpReceive() {

        if (bResult) {
            bResult = WinHttpReceiveResponse(hRequest, NULL);
        }

        if (bResult) {

            do
            {
                // Check for available data.
                dwSize = 0; //query data available looks for data in bytes
                if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
                {
                    std::cout << "WinHttpQueryDataAvailable error " << GetLastErrorAsString();
                    break;
                }

                // No more available data.
                if (!dwSize)
                    return false;

                // Allocate space for the buffer. as dwSize now holds the size of the request
                pszOutBuffer = new char[dwSize + 1];  // just a way of freeing up memory 
                if (!pszOutBuffer)
                {
                    printf("Out of memory\n"); // couldnt allocate enough
                    return false;
                }

                ZeroMemory(pszOutBuffer, dwSize + 1); // fills a block of memory with 0s

                                                      // we know the expect size and have the pszoutbffer to write to - read the Data.
                if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer,
                    dwSize, &dwDownloaded))
                {
                        std::cout << "WinHttpReadData error " << GetLastErrorAsString();
                    return false;
                }
                else
                {
                    printf("%s", pszOutBuffer);
                }

                // Free the memory allocated to the buffer.
                delete[] pszOutBuffer;
                return true;

                // This condition should never be reached since WinHttpQueryDataAvailable
                // reported that there are bits to read.
                if (!dwDownloaded)
                    return false;

            } while (dwSize > 0);

        }

        return false;

    }

    void closeHandles() {
        if (hRequest) WinHttpCloseHandle(hRequest);
        if (hConnect) WinHttpCloseHandle(hConnect);
        if (hSession) WinHttpCloseHandle(hSession);
    }

};

Solution

  • Why you get error code 6

    Error code 6 is the System Error Code ERROR_INVALID_HANDLE.

    It tells you that the hConnect you pass into WinHttpOpenRequest is invalid.

    You only check hSession after your call to WinHttpOpen, and hRequest after your call to WinHttpOpenRequest. But you never check if hConnect is valid.

    You need to check the hConnect returned by WinHttpConnect as well, and if it's invalid check GetLastError() before calling another WINAPI method:

    hSession = WinHttpOpen(UA.c_str(), WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    
    if (hSession) {
        hConnect = WinHttpConnect(hSession, URL.c_str(), portToUse, 0);
    
        if (hConnect) {
            hRequest = WinHttpOpenRequest(hConnect, protocol.c_str(), NULL, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
    
            if (!hRequest) {
                std::cout << "WinHttpOpenRequest error " << GetLastErrorAsString();
            }
        }
        else {
            std::cout << "WinHttpConnect error " << GetLastErrorAsString();
        }
    }
    else {
        std::cout << "WinHttpOpen error " << GetLastErrorAsString();
    }
    

    Why your call to WinHttpConnect fails

    The docs tell us that WinHttpConnect expects a server name, not a URL:

    pswzServerName [in]
    Pointer to a null-terminated string that contains the host name of an HTTP server. Alternately, the string can contain the IP address of the site in ASCII, for example, 10.0.1.45.

    However the string you provide contains a URL, which is not valid. You need to change "https://pages.awscloud.com/awsomedayonlineconference-reg.html" to "pages.awscloud.com", and then provide the path to the page as parameter in WinHttpOpenRequest:

    hRequest = WinHttpOpenRequest(hConnect, protocol.c_str(), path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
    

    where path is a string containing "/awsomedayonlineconference-reg.html".

    You could split your URL into parts for this, or change your httpConnect method to get servername and path as separate parameters.