WinSock Manual HTTP File Upload - c++

I am playing around writing some HTTP communication in C++ using the Winsock APIs. I have no trouble performing GET requests and receiving the response, however I am having a problem when trying to perform a file upload via a POST request.
So first of all, I will share the code of my PHP file which receives the upload request (upload.php):
<?php
if(isset($_FILES['file'])){
$errors= array();
$file_name = $_FILES['file']['name'];
$file_size =$_FILES['file']['size'];
$file_tmp =$_FILES['file']['tmp_name'];
$file_type=$_FILES['file']['type'];
$file_ext=strtolower(end(explode('.',$_FILES['file']['name'])));
if(empty($errors)==true){
move_uploaded_file($file_tmp,"uploads/".$file_name);
echo "Success";
}
}
else{
echo "Was no file";
}
?>
<form action="" method="POST" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit"/>
</form>
Now I know that there is nothing wrong with this code, because I succeed to perform a file upload to it by using WinInet APIs (HttpSendRequest). My WinInet code which works on this PHP file is comprised of the following main steps:
HttpOpenRequest(..., "POST", "uploader/upload.php", "HTTP/1.0", NULL, NULL, NULL, NULL)
HttpSendRequest with headers I set to: "Content-Type: multipart/form-data; boundary=---------FILE_BOUNDARY----------". I printed out the request body that was built by my program as you can see here, sorry I didn't print it out in a way which I could copy and paste here: http://i.imgur.com/mMmo7Xd.png
This works beautifully, the file is uploaded properly. However my problem arises when I try to "port" this code to use winsock API's instead of wininet. With winsock, as you may know, I must completely manually construct the whole request (headers and body). I assume this must be where my problem is, because the main body of the request itself is the same as when I am using wininet APIs. Here is a printout of my winsock request that is being sent: http://i.imgur.com/TkLNGrq.png
PS I have no idea why MessageBox put the boundary part of the header on another line, the is no "\r\n" there in my code. Could this have something to do with my issue? Here is my code for building the entire request string. Please don't give me pointers on security and buffer overruns, this is not production code:
wsprintfA(FullReqStr,
// Headers
"POST %s HTTP/1.0\r\n"
"Host: %s\r\n"
"Content-Type: multipart/form-data; boundary=---------FILE_BOUNDARY----------\r\n\r\n"
// Body
"-----------FILE_BOUNDARY----------\r\n"
"Content-Disposition: form-data; name=\"file\"; filename=\"file.log\"\r\nContent-Type: application/octet-stream\r\n\r\n"
"%s\r\n"
"-----------FILE_BOUNDARY----------\r\n"
"Content-Disposition: form-data; name=\"submit\"\r\n\r\n"
"Submit\r\n"
"-----------FILE_BOUNDARY------------\r\n",
GatePath, Server, FileBody
);
And yes the file bytes in the winsock request are different, I used different file bytes for testing. That is besides the point. Basically, when I send that request and receive the response, it hits the "Was no file" else in the PHP file. I feel like I must be missing some required field in the headers of my winsock request, one that is otherwise automatically added when using WinInet APIs. I cannot think of anything else, however I'm not sure what I'm missing.
Could anybody point me to what is wrong in my Winsock code? I would greatly appreciate it. Thank you. By the way please not I am not looking for any security or anything in my upload script, obviously. I'm just playing around trying to get this to work. It is not being applied to any system that needs to be secure or anything.
Thanks.

Looks like I didn't read the RFC closely enough. http://www.ietf.org/rfc/rfc1867.txt
I needed to add the "Content-Length" field to my HTTP header. Now I construct the body of the request first so I can take the length, and then construct the entire request using that. Working fine.

Related

How do I encode binary file data to ISO-8859-1 to attach to the body of an HTTP post message

I'm trying to convert the full string of bytes from a file opened in binary mode to a string encoded in the ISO-8859-1 character set. My understanding is that by converting to ISO-8859-1 all binary information from the file is retained which is why it is being converted to that format. Is this a valid statement?
I'm working in C++ (Visual Studio 2017) building an executable to be used on the Windows platform. I'm not experienced in HTTP programming.
I have prototype code written in PowerShell code which successfully performs the functionality that I'm trying to duplicate in C++. In the power shell code an HTTP message is sent to upload a firmware file to a device.
The message includes headers and a body:
The message is using the multipart/form-data protocol.
An example of the headers of the message are:
Authorization: Bearer Yosda0IDRQuVaU_L0SnV5g==
Content-Type: multipart/form-data; boundary=42b745c8-4da8-454e-8c13-cbb5c1f7694f
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
An example of the body of the message is:
--1a2fc07a-d882-4470-a1da-79716d34cd9b
Content-Disposition: form-data; name="upgrade_file"; filename=""
Content-Type: application/octet-stream
// File data (encoded in ISO-8859-1 format) goes here //
--1a2fc07a-d882-4470-a1da-79716d34cd9b
Content-Disposition: form-data; name="submit"
Install OS
--1a2fc07a-d882-4470-a1da-79716d34cd9b--
The PowerShell script is converting the file with the following lines of code:
$bytes = [System.IO.File]::ReadAllBytes($file.FullName);
if ( $bytes )
{
$enc = [System.Text.Encoding]::GetEncoding(iso-8859-1);
$data = $enc.GetString($bytes);
}
Once the headers and body are setup the message is sent and the firmware is uploaded when running the PowerShell code.
In my C++ code I've got the headers and the pre-body and post-body codes worked out. I think (not sure) that I just need to convert the file to iso-8859-1 to get the message to work now.
I'm using libcurl to send the message.
res = curl_easy_perform(pCurl);
Currently (without converting file to iso-8859-1) I get the following error message from the function call:
Failure when receiving data from the peer
When the message is sent I can see that only some bytes are uploaded. I assume that could be because the file data is not properly encoded and when it is reading that data it reaches some point where the data is in a format that it cannot handle.

Sending a STOMP frame through websocket

Since there's no webstomp (STOMP through Websocket) C++ implementation anywhere, I'm developing my own. I have a webstomp server set up already, and I've confirmed that it works using the javascript implementation of webstomp.
Now I'm relying on QT's implementation of websocket. I tested and works with a regular websocket. So now comes the implementation of STOMP. Looking at the STOMP frames the first frame I have to send could be something like this:
CONNECT
login: <username>
passcode: <passcode>
^#
With ^#being the null-character. The problem I'm having is that no matter what I do, I can't seem to get any type of response from the server. I tried different encodings, different messages, different connect frames, etc. I was wondering if I was doing something wrong or if I was missing something. An example of the above frame looks like this:
void WebSTOMP::onConnected()
{
if (m_debug)
qDebug() << "WebSocket connected";
connect(&m_webSocket, &QWebSocket::textMessageReceived,
this, &EchoClient::onTextMessageReceived);
std::string myMessage = "CONNECT \nlogin: test\npasscode : test\n\n\0";
m_webSocket.sendTextMessage(QString::fromUtf8(myMessage.c_str()));
}
And then I never get a response back.
Thanks in advance =)
Add one to the length of the message due to the string's null character terminator
Solved it. For the future: had to manually specify the length due to including a null character terminator.

PDF download corrupted / HTTP header problems

I have some legacy code in one of application which is used for PDF file download (PDF file is around 350-400KB size) and recently we had complains (from around 1% customers) saying PDF download is failing with damaged/corrupted file errors.
Here is snippet of code (C++ application) setting headers for download
String header;
header.append("Content-type: application/force-download\r\n");
header.append("Content-Transfer-Encoding: Binary\r\n");
header.append("Content-length: %d\r\n", filebuf.length());
header.append("Connection: Close\r\n");
header.append("Content-disposition: attachment; filename=%s\r\n\r\n", filename_to_download.chars());
String class and append method is just for an example.
I understand above headers are not the best way to trigger PDF file download (I've simplified headers by having "Content-Type application/octet-stream and Content-Disposition : attachment; filename=example.pdf" and seems like its working for me).
But I am not able to understand why above original code should not work 1% of time.
I was trying to understand browser/adobe combination but seems there is no pattern here, YES one thing few of customers mentioned is when they changed to "chrome browser" it worked most of the times.
Any pointers?
After couple of days of struggle finally figured out whats happening in here.
We are setting content length as size of buffer (pdf file size) in header from our code and sending this data to client but in-between apache module mod_gzip/mod_deflate is compressing data buffer and what reaches client/browser is "Content-Length: 100 bytes" but actual data is say 60-70 bytes.
Not every browser complains about this mismatch but certain browsers treats this as FATAL error and shows message "couldn't download file" (we've seen this issue frequently in Win8/IE10 and Win8/IE11, there could be some other security settings too causing this on browser!).
For the fix, we've removed "Content-Length" from header.

Overwrite CURL internal headers

Currently, I'm working on a HTTP Proxy using libcurl in C++.
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
Above is my code to customize HTTP request header. The headers parameter is a curl_slist pointer variable. I have append all necessary heads information like Accept, Keep Alive, Referer and so on using curl_slist_append() method. However, when I used wireshark to observe the network traffic, I found that CURL didn't use my customized headers while it used "Accept: * / *\r\n".
Does anyone know how to disable the internal header of CURL?
try CURLOPT_ENCODING, don't know about C++ but in PHP:
The contents of the "Accept-Encoding: " header. This enables decoding
of the response. Supported encodings are "identity", "deflate", and
"gzip". If an empty string, "", is set, a header containing all
supported encoding types is sent.
CURLOPT_HTTPHEADER is the way to add, remove or replace internally used headers. If you have problems with this, show us a full program that repeats the problem and we might be able to help out.

How to get the length of a file without downloading the file in a cURL binary get request

I want to create a cURL request in some C++ code which will get me the length of a file in a server without downloading the file. For that, I use some cURL options to tell I only want headers in the request response, and then I examine the response to get the file length.
I'm setting the following request options:
curl_easy_setopt(_curl_handle, CURLOPT_HEADER, 1);
curl_easy_setopt(_curl_handle, CURLOPT_NOBODY, 1);
Then processing the request, waiting for the response, which shows a OK=200, and finally enquiring about the file length:
curl_easy_getinfo(_curl_handle, CURLINFO_CONTENT_LENGTH_UPLOAD, &dResult);
But I get a file length of -1. According to cURL documentation, that means size is unknown. How can it happen that cURL doesn't get the file length information from the server?
CURLINFO_CONTENT_LENGTH_UPLOAD is the number of bytes uploaded. You need to use CURLINFO_CONTENT_LENGTH_DOWNLOAD instead.
Note that if the server dynamically generates the data, the length may be different when you actualy download the file versus just downloading its headers.
Also note that if the server sends data as compressed when downloaded, there may not be any size available in the headers (if the Transfer-Encoding header is used instead of the Content-Length header), so CURLINFO_CONTENT_LENGTH_DOWNLOAD would still return -1. The only way to know the size in that situation would be to download it in full.
Have you tried with CURLINFO_CONTENT_LENGTH_DOWNLOAD instead?
need call perform()
curl_easy_setopt(_curl_handle, CURLOPT_HEADER, 1);
curl_easy_setopt(_curl_handle, CURLOPT_NOBODY, 1);
curl_easy_perform(_curl_handle);
curl_easy_getinfo(_curl_handle, CURLINFO_CONTENT_LENGTH_UPLOAD,
&dResult);