0

I'm still relatively new to c++ so this really bothering me for a while....

What I'm trying to do here is to send a bunch of data on windows MFC to the server API and get data back. The only library I can use now is the winhttp.h.I have a couple data sets want to send for example:

name : AA
code : BBB
type : CCC

Because the WinHttpSendRequest has its own format as below, I need to reformat the header and body. I'm reallly unfamiliar with these types used but I googled a lot and trying to get some formatting done.

WinHttpSendRequest
(
    IN HINTERNET hRequest,
    _In_reads_opt_(dwHeadersLength) LPCWSTR lpszHeaders,
    IN DWORD dwHeadersLength,
    _In_reads_bytes_opt_(dwOptionalLength) LPVOID lpOptional,
    IN DWORD dwOptionalLength,
    IN DWORD dwTotalLength,
    IN DWORD_PTR dwContext
);
CStringW header;
header += _T("__id: "+ GetID());

CString body_builder;
body_builder = "name: "+m_name+"\r\n";
body_builder+= "code: "+m_code+"\r\n";
body_builder+= "type: "+m_type+"\r\n";

LPCSTR body = (LPCSTR)(LPCTSTR)body_builder;

response = SendPOSTRequest(m_postUri, header, (DWORD)header.length() body, body_builder.GetLength());

The SendPOSTRequest function will basically pass parameters to the WinHttpSendRequest.

The problem is the response from the server is always

HTTP_STATUS_BAD_REQUEST         400 // invalid syntax

I cannot find any good resources about how to format body and header in winhttp c++. Can anyone explain to me how the body and header should be formatted?

Edit: Here's the definition to SendPOSTRequest and futher called functions.

WebServiceResponse CCommonWebService::SendPOSTRequest(LPCSTR uri, CStringW headers, LPCSTR body, DWORD body_length) {
    return send_request(uri, L"POST", headers, body, body_length);
}
WebServiceResponse WebServices::send_request(LPCSTR uri, LPCWSTR method, CStringW headers, LPCSTR body, DWORD body_length)
{
    WebServiceResponse response;
    CStringW wuri = to_wchar(uri);
    HandleObject request;

    if (!m_init)
    {
        // WebServices is not initialized
        return response;
    }
        
    if (m_protocol == PROTOCOL_HTTP)
    {
        request.handle = WinHttpOpenRequest(m_handle_connection, method, wuri, REQUEST_HTTP_VERSION, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, FLAGS_HTTP);
    }
    else
    {
        request.handle = WinHttpOpenRequest(m_handle_connection, method, wuri, REQUEST_HTTP_VERSION, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, FLAGS_HTTPS);

        DWORD dwOptions   = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
                          | SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;

        // Set HTTPS security flags
        WinHttpSetOption(request.handle, WINHTTP_OPTION_SECURITY_FLAGS, &dwOptions, sizeof(DWORD));
    }

    // Set custom timeout values for request handle
    WinHttpSetTimeouts(request, 0, 30000, 15000, 15000);

    if (m_try_proxy)
    {
        setup_proxy(request.handle, wuri);
    }

    // Sending request with len(header) and len(body)
    if (!WinHttpSendRequest(request.handle, headers, (DWORD)headers.GetLength(), (LPVOID)body, body_length, body_length, 0))
    {
        DWORD last_err = GetLastError();
        // WinHttpSendRequest() failed with error last_err
        response.text.Format("Failed when sending request to connect to server (Error %d).", (int)last_err);

        return response;
    }

    // Request sent, waiting for response
    if (WinHttpReceiveResponse(request.handle, NULL))
    {
        response.code = get_response_code(request.handle);
        
        if (response.code == HTTP_STATUS_NO_CONTENT)
        {
            return response;
        }
        
        DWORD content_length = get_response_content_length(request.handle);

        if (content_length < 1)
        {
            return response;
        }

        DWORD dwSize = sizeof(DWORD);
        if (WinHttpQueryDataAvailable(request.handle, &dwSize))
        {
            DWORD dwDownloaded = 0;

            char* buffer = response.text.GetBufferSetLength(content_length+1);
            memset(buffer, '\0', content_length+1);
            response.length = content_length;
            if (WinHttpReadData(request.handle, buffer, content_length+1, &dwDownloaded))
            {
                if (dwDownloaded != content_length)
                {
                    // Did not read full content length
                }
            }

            response.text.ReleaseBuffer();
            
        }
    }
    else
    {
        DWORD last_err = GetLastError();
        // WinHttpReceiveResponse() failed with error last_err;
        response.text.Format("Failed when receiving response from server (Error %d).", (int)last_err);
    }

    return response;
}
Arthur V
  • 23
  • 4
  • 1
    Any cast is a lie (e.g. `(LPCSTR)(LPCTSTR)`). Some of those lies are necessary and meaningful. Here, however, it is harmful. You are constructing a UTF-16 encoded string (`CStringW`) and then move on to let the system know: *"You know, I'm just kidding. Pretend that this were ASCII"*. Which, when it does returns a string consisting of the first character only. Surely, the server is right in rejecting this request header. – IInspectable Nov 02 '21 at 07:28
  • @IInspectable Thanks for the reply. Do you mean the header and body_builder should be passed to the WinHttpSendRequest directly? Like, CString to LPCWSTR and CString to LPVOID automatically? – Arthur V Nov 02 '21 at 15:52
  • Are you using Unicode or ANSI? Can you show the definition for `SendPOSTRequest`? – Barmak Shemirani Nov 02 '21 at 17:34
  • @BarmakShemirani I've attached more codes to that function. – Arthur V Nov 02 '21 at 22:08
  • 1
    This seems to be missing some keywords, example `"Content-Type: ..."`. Use this minimum example https://stackoverflow.com/a/47893635/4603670 see if you can use that code to post. – Barmak Shemirani Nov 02 '21 at 23:37

0 Answers0