Search code examples
c++httpshttp-headerswinhttpwinhttprequest

Failure while sending png image to the server using winHttp & C++


I am new to winHttp / client server communication, and stuck with sending a png image to server as attachment . I created an exe using C++ just to solve this problem . Somehow I managed to hit the server , and it is giving below response ,

{"code":"9999","message":"org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'attachment' is not present","cause":".","correlationID":"***-***-43f4-82b2-***","apiURL":"POST : /file/4856.png","origin":"PartMfgServer","timestamp":1704814609769}

here is my BE code ,

@PostMapping(path = "/{fileName}",  consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFileFromServer(@RequestHeader("File-Type") String contentType,
                                   @PathVariable("fileName") String fileName,
                                   @RequestParam(value = "fileRole", required = false) FILE_ROLE fileRole,
                                   @RequestParam("attachment") MultipartFile fileToUpload) 
{.......}

here is my code ,

int main() {
    LPCWSTR server = L"domain.com";
    LPCWSTR path = L"api/v2307/file/4856.png";

    HINTERNET hSession = WinHttpOpen(L"ImageUploader/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (!hSession) {
        return -1;
    }

    HINTERNET hConnect = WinHttpConnect(hSession, server, INTERNET_DEFAULT_HTTPS_PORT, 0);
    if (!hConnect) {
        std::cerr << "WinHttpConnect failed!" << std::endl;
        WinHttpCloseHandle(hSession);
        return -1;
    }

    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
    if (!hRequest) {
        // Handle connection error
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return -1;
    }

    // Open image file for reading
    HANDLE hFile = CreateFile(L"C:\\Users\\m5zmfk\\AppData\\Local\\Temp\\nxOpen\\partfiles\\4856\\4856.png", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        // Handle file open error
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return -1;
    }


    DWORD fileSize = GetFileSize(hFile, NULL);
    std::wstring boundary = L"---------------------------1234567890";
 //   std::wstring endBoundary = L"\r\n--" + boundary + L"--\r\n";


    std::wstring formDataBody = L"--" + boundary + L"\r\n"
        L"Content-Disposition: form-data; name=\"attachment\"; filename=\"4856.png\"\r\n"
        L"\r\n--" + boundary + L"--\r\n";

    std::wstring headers = L"authorization: ****\r\n"
        L"Content-Type: multipart/form-data; boundary=" + boundary + L"\r\n"
        L"File-Type: image/png\r\n"
        L"Content-Length: " + std::to_wstring(fileSize + formDataBody.length()) +L"\r\n";

    bool success = WinHttpAddRequestHeaders(hRequest, headers.c_str(), headers.length(), WINHTTP_ADDREQ_FLAG_ADD);

    if (!success) {
        std::cout << "Error adding headers" << std::endl;
        printLastError(GetLastError());
    }

    DWORD timeout = 60000 * 2; // Set timeout to 60 seconds (adjust as needed)
    WinHttpSetOption(hRequest, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeout, sizeof(timeout));
    WinHttpSetOption(hRequest, WINHTTP_OPTION_SEND_TIMEOUT, &timeout, sizeof(timeout));

    if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
        // Handle request send error
        printLastError(GetLastError());
        CloseHandle(hFile);
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return -1;
    }

    DWORD bytesWritten;
    // Build the form-data request body
   
    success = WinHttpWriteData(hRequest, formDataBody.c_str(), formDataBody.length(), &bytesWritten);

    if (!success) {
        printLastError(GetLastError());
        CloseHandle(hFile);
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return -1;
    }

    // Write the file content
    BYTE buffer[1024];
    DWORD bytesRead;
    while (ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead > 0) {
        success = WinHttpWriteData(hRequest, buffer, bytesRead, &bytesWritten);
        if (!success) {
            printLastError(GetLastError());
            CloseHandle(hFile);
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            return -1;
        }
    }

    if (!WinHttpReceiveResponse(hRequest, NULL)) {
        // Handle response error
        printLastError(GetLastError());
        CloseHandle(hFile);
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return -1;
    }

    // Read and print the response
    DWORD resRead;
    BYTE resBuffer[1024];
    while (WinHttpReadData(hRequest, resBuffer, sizeof(resBuffer), &resRead) && resRead > 0) {
        // Process the received data (you can print it or save it to a file, etc.)
        std::cout.write(reinterpret_cast<const char*>(resBuffer), resRead);
    }

    // Close the file and WinHTTP handles
    CloseHandle(hFile);
    WinHttpCloseHandle(hRequest);
    WinHttpCloseHandle(hConnect);
    WinHttpCloseHandle(hSession);

    return 0;
}

Can anyone help me with what is going wrong .

I tried giving formDataBody as header using WinHttpAddRequestHeaders. I got error code 87.

 std::wstring formDataBody = L"--" + boundary + L"\r\n"
        L"Content-Disposition: form-data; name=\"attachment\"; filename=\"4856.png\"\r\n"
        L"\r\n--" + boundary + L"--\r\n";

Solution

  • You are not sending the PNG data correctly. It needs to be inside your formDataBody variable, as the body of the attachment MIME part, after its headers and before its closing boundary. You are sending the PNG data after all of the MIME data has been sent, which is wrong.

    Also, MIME is based on 7bit ASCII, so you should not be using a std::wstring for your formDataBody variable, use std::string instead (using std::wstring for all of the other WinHTTP function parameters is fine).

    Also, File-Type is not a standard HTTP header. You should instead specify a standard Content-Type header inside the attachment MIME part, and then have your uploadFileFromServer() handler retrieve the file type from that header (you can get it from the MultipartFile.getContentType() method).

    With that said, try something more like this:

    bool readAll(HANDLE hFile, void *buffer, DWORD bufsize) {
        char *ptr = static_cast<char*>(buffer);
        DWORD bytesRead;
    
        while (bufsize != 0) {
            if (!ReadFile(hFile, ptr, bufsize, &bytesRead, NULL))
                return false;
            ptr     += bytesRead;
            bufsize -= bytesRead;
        }
        
        return true;
    }
    
    void printFailure(const char *funcName) {
        DWORD error = GetLastError();
        std::cerr << funcName << " failed!" << std::endl;
        printLastError(error);
    }
    
    int main() {
        LPCWSTR server = L"amn.dm-labs.com";
        LPCWSTR path = L"api/v2307/file/4856.png";
        
        HANDLE hFile = CreateFile(L"C:\\Users\\m5zmfk\\AppData\\Local\\Temp\\nxOpen\\partfiles\\4856\\4856.png", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE) {
            printFailure("CreateFile");
            return -1;
        }
    
        DWORD fileSize = GetFileSize(hFile, NULL);
        if (fileSize == ERROR_INVALID_FILE_SIZE) {
            printFailure("GetFileSize");
            CloseHandle(hFile);
            return -1;
        }
    
        std::string fileData;
        if (fileSize > 0) {
            fileData.resize(fileSize);
            if (!readAll(hFile, &fileData[0], fileSize)) {
                printFailure("ReadFile");
                CloseHandle(hFile);
                return -1;
            }
        }
    
        CloseHandle(hFile);
    
        HINTERNET hSession = WinHttpOpen(L"ImageUploader/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
        if (!hSession) {
            printFailure("WinHttpOpen");
            return -1;
        }
    
        HINTERNET hConnect = WinHttpConnect(hSession, server, INTERNET_DEFAULT_HTTPS_PORT, 0);
        if (!hConnect) {
            printFailure("WinHttpConnect");
            WinHttpCloseHandle(hSession);
            return -1;
        }
    
        HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
        if (!hRequest) {
            printFailure("WinHttpOpenRequest");
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            return -1;
        }
        
        std::string  sBoundary =  "---------------------------1234567890";
        std::wstring wBoundary = L"---------------------------1234567890";
    
        std::string formDataBody = "--" + sBoundary + "\r\n"
            "Content-Disposition: form-data; name=\"attachment\"; filename=\"4856.png\"\r\n"
            "Content-Type: image/png\r\n"
            "\r\n" +
            sFileData + "\r\n"
            "--" + sBoundary + "--\r\n";
    
        std::wstring headers = L"authorization: ****\r\n"
            L"Content-Type: multipart/form-data; boundary=" + wBoundary + L"\r\n";
    
        DWORD timeout = 60000 * 2; // Set timeout to 60 seconds (adjust as needed)
        WinHttpSetOption(hRequest, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeout, sizeof(timeout));
        WinHttpSetOption(hRequest, WINHTTP_OPTION_SEND_TIMEOUT, &timeout, sizeof(timeout));
    
        if (!WinHttpSendRequest(hRequest, headers.c_str(), headers.length(), formDataBody.c_str(), formDataBody.length(), formDataBody.length(), 0)) {
            printFailure("WinHttpSendRequest");
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            return -1;
        }
    
        if (!WinHttpReceiveResponse(hRequest, NULL)) {
            printFailure("WinHttpReceiveResponse");
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            return -1;
        }
    
        char resBuffer[1024];
        DWORD resRead;
    
        do {
            if (!WinHttpReadData(hRequest, resBuffer, sizeof(resBuffer), &resRead)) {
                printFailure("WinHttpReadData");
                WinHttpCloseHandle(hRequest);
                WinHttpCloseHandle(hConnect);
                WinHttpCloseHandle(hSession);
                return -1;
            }
            
            if (resRead == 0)
                break;
    
            std::cout.write(resBuffer, resRead);
        }
        while (true);
    
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
    
        return 0;
    }
    
    @PostMapping(path = "/{fileName}",  consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String uploadFileFromServer(@PathVariable("fileName") String fileName,
                                       @RequestParam(value = "fileRole", required = false) FILE_ROLE fileRole,
                                       @RequestParam("attachment") MultipartFile fileToUpload) 
    {
        String contentType = fileToUpload.getContentType();
        ...
    }
    

    If you really want to use WinHttpWriteData() to send the PNG data in chunks, then it needs to look more like this instead:

    void printFailure(const char *funcName) {
        DWORD error = GetLastError();
        std::cerr << funcName << " failed!" << std::endl;
        printLastError(error);
    }
    
    int main() {
        LPCWSTR server = L"amn.dm-labs.com";
        LPCWSTR path = L"api/v2307/file/4856.png";
        
        HANDLE hFile = CreateFile(L"C:\\Users\\m5zmfk\\AppData\\Local\\Temp\\nxOpen\\partfiles\\4856\\4856.png", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE) {
            printFailure("CreateFile");
            return -1;
        }
    
        DWORD fileSize = GetFileSize(hFile, NULL);
        if (fileSize == ERROR_INVALID_FILE_SIZE) {
            printFailure("GetFileSize");
            CloseHandle(hFile);
            return -1;
        }
    
        HINTERNET hSession = WinHttpOpen(L"ImageUploader/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
        if (!hSession) {
            printFailure("WinHttpOpen");
            CloseHandle(hFile);
            return -1;
        }
    
        HINTERNET hConnect = WinHttpConnect(hSession, server, INTERNET_DEFAULT_HTTPS_PORT, 0);
        if (!hConnect) {
            printFailure("WinHttpConnect");
            WinHttpCloseHandle(hSession);
            CloseHandle(hFile);
            return -1;
        }
    
        HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
        if (!hRequest) {
            printFailure("WinHttpOpenRequest");
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            CloseHandle(hFile);
            return -1;
        }
    
        std::string  sBoundary =  "---------------------------1234567890";
        std::wstring wBoundary = L"---------------------------1234567890";
    
        std::string formDataBody1 = "--" + sBoundary + "\r\n"
            "Content-Disposition: form-data; name=\"attachment\"; filename=\"4856.png\"\r\n"
            "Content-Type: image/png\r\n"
            "\r\n";
    
        std::string formDataBody2 = "\r\n"
            "--" + sBoundary + "--\r\n";
    
        DWORD totalLength = formDataBody1.length() + fileSize + formDataBody2.length();
        DWORD bytesRead, bytesWritten;
        
        std::wstring headers = L"authorization: ****\r\n"
            L"Content-Type: multipart/form-data; boundary=" + wBoundary + L"\r\n";
    
        DWORD timeout = 60000 * 2; // Set timeout to 60 seconds (adjust as needed)
        WinHttpSetOption(hRequest, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeout, sizeof(timeout));
        WinHttpSetOption(hRequest, WINHTTP_OPTION_SEND_TIMEOUT, &timeout, sizeof(timeout));
    
        if (!WinHttpSendRequest(hRequest, headers.c_str(), headers.length(), WINHTTP_NO_REQUEST_DATA, 0, totalLength, 0)) {
            printFailure("WinHttpSendRequest");
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            CloseHandle(hFile);
            return -1;
        }
    
        if (!WinHttpWriteData(hRequest, formDataBody1.c_str(), formDataBody1.length(), &bytesWritten)) {
            printFailure("WinHttpWriteData");
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            CloseHandle(hFile);
            return -1;
        }
    
        if (fileSize > 0) {
            BYTE buffer[1024];
            do {
                if (!ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL)) {
                    printFailure("ReadFile");
                    WinHttpCloseHandle(hRequest);
                    WinHttpCloseHandle(hConnect);
                    WinHttpCloseHandle(hSession);
                    CloseHandle(hFile);
                    return -1;
                }
            
                if (bytesRead == 0)
                    break;
                
                if (!WinHttpWriteData(hRequest, buffer, bytesRead, &bytesWritten)) {
                    printFailure("WinHttpWriteData");
                    WinHttpCloseHandle(hRequest);
                    WinHttpCloseHandle(hConnect);
                    WinHttpCloseHandle(hSession);
                    CloseHandle(hFile);
                    return -1;
                }
            }
            while (true);
        }
        
        CloseHandle(hFile);
    
        if (!WinHttpWriteData(hRequest, formDataBody2.c_str(), formDataBody2.length(), &bytesWritten)) {
            printFailure("WinHttpWriteData");
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            return -1;
        }
    
        if (!WinHttpReceiveResponse(hRequest, NULL)) {
            printFailure("WinHttpReceiveResponse");
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            return -1;
        }
    
        char resBuffer[1024];
    
        do {
            if (!WinHttpReadData(hRequest, resBuffer, sizeof(resBuffer), &bytesRead)) {
                printFailure("WinHttpReadData");
                WinHttpCloseHandle(hRequest);
                WinHttpCloseHandle(hConnect);
                WinHttpCloseHandle(hSession);
                return -1;
            }
            
            if (bytesRead == 0)
                break;
                
            std::cout.write(resBuffer, bytesRead);
        }
        while (true);
    
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
    
        return 0;
    }