Uploading PNG File (HTTP POST) with C++ Winsock API - c++

I'm trying to upload a PNG file through Winsock2 HTTP Post. Here's my request string:
boundary = "-----rueldotme";
request += "POST " + uri + " HTTP/1.1\r\n";
request += "Host: " + hostname + "\r\n";
request += "User-Agent: " + UserAgent + "\r\n";
request += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
request += "Accept-Language: en-us,en;q=0.5\r\n";
request += "Accept-Encoding: gzip,deflate\r\n";
request += "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n";
request += "Keep-Alive: 115\r\n";
request += "Connection: keep-alive\r\n";
request += "Content-Length: " + fileSize + "\r\n";
request += "Content-Type: multipart/form-data, boundary=" + boundary + "\r\n";
request += "\r\n";
request += "--" + boundary + "\r\n";
request += "Content-Disposition: form-data; name=\"filename\"; filename=\"" + fileName + "\"\r\n";
request += "Content-Type: image/png; charset=binary\r\n";
request += "Content-Transfer-Encoding: binary\r\n";
request += "\r\n";
request += "%s\r\n";
request += "\r\n";
The connection is OK, no errors and such, the fileCon by the way is from ReadFile(). And there's no error code. The number of bytes read is the same as the output of GetFileSize(). I tried displaying the contents of fileCon but only gave me this:
Don't mind the title "Error" (I set it).
Also, the request takes ages to complete, and gives me a blank response. Yep, blank with no HTTP headers. Am I doing the request right? Should I really have to include the file contents at the POST data?
Thanks in advance.
EDIT: The PNG size is about 256KB. I'm in a 1mbps connection.
EDIT: Sorry if the information was insufficient. Anyway, here's what I did lately:
int flz;
char bdata[BSIZE];
DWORD dwe, bytesRead = 0;
HANDLE fh = CreateFile(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
LPVOID fbuff = NULL;
flz = GetFileSize(fh, NULL);
fbuff = malloc(flz);
ReadFile(fh, fbuff, flz, &bytesRead, NULL));
...
sprintf_s(bdata, BSIZE, request.c_str(), reinterpret_cast<char *>(fbuff)); //BSIZE = 1024
...
send(sock, bdata, std::strlen(bdata), 0);

Not enough information to solve the problem, so I'll give a meta-answer instead:
Use a packet sniffer (e.g., wireshark) to check exactly what data is actually being sent and received. This will let you verify that the request is as it should be, and that the "blank response" you're getting really is blank.
One wild stab in the dark:
You haven't included any variable declarations in your code snippet, so I don't know what type "fileCon" is, but don't forget that the PNG data is likely to contain null bytes, which will mess up a default conversion from a char* to a std::string.
Edit:
Your modification contains the same bug that the std::string based version had, namely, that the PNG data is likely to contain null bytes. Perhaps this code will explain more clearly:
const char* data = "Hello\0world."; // some data that contains a null byte
std::string dataStr(data);
std::cout << dataStr << "\n"; // will print "Hello".
std::cout << dataStr.size() << "\n"; // will print "5"
char buf[512];
sprintf_s(buf, sizeof(buf), "Data: %s\n", data);
std::cout << buf; // will print "Data: Hello"
Both the conversion to std::string and formatting with sprintf will interpret the null byte as being the end of the data, and so the rest of the original data ("world.") will never be used.

Related

Why does chrome not establish the connection directly after the dns lookup?

I have the problem that Chrome is waiting for something after the DNS lookup before establishing the connection.
My problem -> (https://imgur.com/qA5ADOa)
On the picture you can see that after the dns lookup there is a big gap and the request took 313ms although all values together are maximum 16ms.
Apache on this picture -> (https://imgur.com/UI4ToDu).
What does apache do that problem does not occur? On this picture you can see very well that problem does not occur at apache.
I am writing a simple web server in C++ just for practice. I test the webserver with the Chrome Browser. The code is very simple and without errorhandling, here the part after the socket creation with hard coded response:
std::memset(&myAddr, 0, sizeof(myAddr));
myAddr.sin_family = AF_INET;
myAddr.sin_addr.s_addr = INADDR_ANY;
myAddr.sin_port = htons(80);
bind(acceptSock, (sockaddr *)&myAddr, sizeof(myAddr));
listen(acceptSock, 5);
clientSock = accept(acceptSock, (sockaddr *)&clientAddr, &clientAddrLen);
std::vector<char> recvbuf(BUFFER_LENTH);
recv(clientSock, &recvbuf[0], recvbuf.size(), 0);
std::ifstream file("index.html");
if (file.is_open()) {
std::string content;
std::string buf;
while (std::getline(file, buf)) {
content += buf;
buf.clear();
}
file.close();
size_t contentLength = content.size();
std::string sendbuf = "HTTP/1.1 200 OK\r\n";
sendbuf += "Content-Type: text/html; charset=UTF-8\r\n";
sendbuf += "Content-Length: " + std::to_string(contentLength) + "\r\n";
sendbuf += "Server: meinWebServer\r\n";
sendbuf += "\r\n";
sendbuf += content;
send(clientSock, sendbuf.c_str(), sendbuf.size(), 0);
}
What can I do to prevent this problem?
Why and what is Chrome waiting for after the lookup?

Add strings to a file buffer, for message body of cURL smtp send email

I'm trying to modify a working cURL email send example to add a message body.
I'm unsure why all of the curl email-with-attachment examples I'm finding have no message body.
I'm needing to send short text emails with a PDF file attachment.
This is what I have tried so far, with the lines un-commented, it compiles and runs, but fails to send. I understand that the message body should be separated from the Subject by one "\r\n" (blank line), but this isn't the correct method.
//Create structure of email to be sent
fileBuf = new char[ADD_SIZE + no_of_rows][CHARS]; //ADD_SIZE for TO,FROM,SUBJECT,CONTENT-TYPE,CONTENT-TRANSFER-
//ENCODING,CONETNT-DISPOSITION and \r\n
strcpy(fileBuf[len++],"To: " TO "\r\n");
buffer_size += strlen(fileBuf[len-1]);
strcpy(fileBuf[len++],"From: " FROM "\r\n");
buffer_size += strlen(fileBuf[len-1]);
strcpy(fileBuf[len++],"Subject: SMTP TLS example message\r\n");
buffer_size += strlen(fileBuf[len-1]);
//strcpy(fileBuf[len++],"\r\n");
//buffer_size += strlen(fileBuf[len-1]);
//strcpy(fileBuf[len++],"Message goes here, hopefully...\r\n");
//buffer_size += strlen(fileBuf[len-1]);
strcpy(fileBuf[len++],"Content-Type: application/x-msdownload; name=\"" FILENAME "\"\r\n");
buffer_size += strlen(fileBuf[len-1]);
strcpy(fileBuf[len++],"Content-Transfer-Encoding: base64\r\n");
buffer_size += strlen(fileBuf[len-1]);
strcpy(fileBuf[len++],"Content-Disposition: attachment; filename=\"" FILENAME "\"\r\n");
buffer_size += strlen(fileBuf[len-1]);
strcpy(fileBuf[len++],"\r\n");
buffer_size += strlen(fileBuf[len-1]);
The full project code is here See Solution 7.
Any advice on how to accomplish this would be greatly appreciated.
[edit] Test using very simple cURL, produced 0 Byte attachment:
#define FILENAME "Rpt05162017.pdf"
static const char *payload_text[] = {
"To: " TO "\r\n",
"From: " FROM "(Example User)\r\n",
//"Cc: " CC "(Another example User)\r\n",
"Subject: SMTPS Example\r\n",
"Date: 17-May-2017\r\n",
"User-Agent: My eMail Client\r\n",
"MIME-Version: 1.0\r\n",
"Content-Type: multipart/mixed;\r\n",
" boundary=\"------------030203080101020302070708\"\r\n",
"\r\nThis is a multi-part message in MIME format.\r\n",
"--------------030203080101020302070708\r\n",
"Content-Type: text/plain; charset=utf-8; format=flowed\r\n",
"Content-Transfer-Encoding: 7bit\r\n",
"\r\n", // empty line to divide headers from body, see RFC5322
"The body of the message starts here.\r\n",
"\r\n",
"It could be a lot of lines, could be MIME encoded, whatever.\r\n",
"Check RFC5322.\r\n\r\n",
"--------------030203080101020302070708\r\n",
"Content-Type: application/x-msdownload; name=\"" FILENAME "\"\r\n",
"Content-Transfer-Encoding: base64\r\n",
"Content-Disposition: attachment; filename=\"" FILENAME "\"\r\n",
"\r\n--------------030203080101020302070708--",
NULL
};
It's not clear how you send all that stuff. You created 2d-array of strings and you strcpy into each your headers. All of them have padding bytes that will corrupt your message.
Try to simplify it maybe?
std:string header =
"To: " TO "\r\n"
"From: " FROM "\r\n"
"Subject: SMTP TLS example message\r\n"
"Content-Type: application/x-msdownload; name=\"" FILENAME "\"\r\n"
"Content-Transfer-Encoding: base64\r\n"
"Content-Disposition: attachment; filename=\"" FILENAME "\"\r\n"
"\r\n";
and then simply send your message header followed by the body of your message.
With your updated payload_text it's not a surprise that you get 0-size attachments because you are sending empty file. After you send your payload_text you have to send your body of the file and then after the body you need to append multipart closing suffix:
"\r\n--------------030203080101020302070708--"

Forming a HTTP response with strings

I am working on a HTTP server with boost, and I have some questions about forming the HTTP response, particularly the header.
Here's the code to assemble the GET response :
std::string h = statusCodes[200]; // The status code is already finished with a '\r\n'
std::string t = "Date: " + daytime_() + "\r\n";
std::string s = "Server: Muffin 1.0\r\n";
std::string content = search->second();
std::string type = "Content-Type: text/html\r\n";
std::string length = "Content-Length: " + std::to_string(content.size()) + "\r\n";
res = h + t + s + length + type + "\r\n" + content + "\r\n";
As they say on this website, here's the header spec :
The format of the request and response messages are similar, and
English-oriented. Both kinds of messages consist of:
an initial line, zero or more header lines,
a blank line (i.e. a CRLF by itself),
and an optional message body (e.g. a file, or query data, or query output).
But when I do a request on the server, only the date goes in the header, the rest is directly in the content
HTTP/1.1 200 OK // Header
Date: Tue May 24 10:28:58 2016 // Header
Server: Muffin 1.0 // Content
Content-Length: 31
Content-Type: text/html
This is supposed to be an ID
I don't know what's wrong in that, it's the first time I'm dealing with HTTP response.
Thanks for your help
I finally found the bug.
My daytime function was returning a string with a newline character.
This was the original function, which uses the depreciated ctime
std::string
Response::daytime_()
{
std::time_t now = std::time(0);
return std::ctime(&now);
}
And now the new function with strftime
std::string
Response::daytime_()
{
time_t rawtime;
struct tm * timeinfo;
char buffer[80];
time (&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer,80,"%a %b %d %H:%M:%S %Y",timeinfo);
std::string time(buffer);
return time;
}
And the new way to form the responses, using just a '\n'
std::string res = "";
std::string h = statusCodes[200];
std::string t = "Date: " + daytime_() + "\r\n";
std::string s = "Server: Muffin 1.0\r\n";
std::string content = search->second();
std::string type = "Content-Type: text/html\r\n";
std::string length = "Content-Length: " + std::to_string(content.size()) + "\r\n";
res = h + t + s + length + type + "\n" + content + "\r\n";

http server response using sockets

My sockets server is receiving a GET request for an image, the image is 2MB so it doesn't fit in a single send(), this is what I am sending in the first send():
std::stringstream wsss;
wsss << "HTTP/1.1 200 OK\r\n"
<< "Connection: keep-alive\r\n"
<< "Content-Type: image/x-icon\r\n"
<< "Content-Length: " << imageSize << "\r\n"
<< "\r\n";
wsss.write(imageData, imageSize);
Does every subsequent send() of this image needs the header fields?
I am sending a .ico image, are the header fields correct?
the image is 2MB so it doesn't fit in a single send()
send() is not guaranteed to send as many bytes as you ask it to send. It can send fewer bytes. Its return value tells you how many bytes it actually accepted for sending. So you should call send() in a loop until all bytes have been accepted. If you move this loop into its own reusable function, that will also allow you to send the icon data without having to first copy it into the std::stringstream.
Try something like this:
int sendData(int sckt, void *data, int datalen)
{
unsigned char *pdata = (unsigned char *) data;
int numSent;
// send() can send fewer bytes than requested,
// so call it in a loop until the specified data
// has been sent in full...
while (datalen > 0) {
numSent = send(sckt, pdata, datalen, 0);
if (numSent == -1) return -1;
pdata += numSent;
datalen -= numSent;
}
return 0;
}
std::stringstream wsss;
wsss << "HTTP/1.1 200 OK\r\n"
<< "Connection: keep-alive\r\n"
<< "Content-Type: image/x-icon\r\n"
<< "Content-Length: " << imageSize << "\r\n"
<< "\r\n";
// do not append the image data to the stringstream...
//wsss.write(imageData, imageSize);
// send the headers first...
std::string headers = wsss.str();
int res = sendData(TheSocket, headers.c_str(), headers.size());
if (res == -1) ...
// now send the image data...
res = sendData(TheSocket, imageData, imageSize);
if (res == -1) ...
Does every subsequent send() of this image needs the header fields?
Every HTTP response to every HTTP request for the same image needs to send the same headers1. But every send() for any particular response does not need to repeat the headers, they only need to be sent once. Just keep sending whatever bytes have not been sent yet. That is why you have to pay attention to the return value of send() so you know how many bytes have been sent so far and how many bytes are still need to be sent.
I am sending a .ico image, are the header fields correct?
In general, yes1.
1: assuming that either:
the client sent an HTTP 1.1 request without a Connection: close request header.
the client sent an HTTP 1.0 request with a Connection: keep-alive request header.
Otherwise, your Connection: keep-alive header would be erroneous, you should be sending a Connection: close header instead, and then close the socket after sending the complete response.

Convert tiff image into String to be posted as binary application/octet-stream (C++)

I'm using C++ Sockets to make an HTTP Multipart POST containing a TIFF image to a server. This server expects a binary octet-stream.
What I've tried is this:
// Convert out data into a string that can be appended to the body
std::ifstream fin(fileName, std::ios::binary);
std::ostringstream ostrm;
ostrm << fin.rdbuf();
string data(ostrm.str());
Unfortunately, I just get II*, when the data should be much longer. My guess is that the data contains a NULL character, which makes C++ think the String is finished. Any ideas on how to resolve this issue?
If it helps, this is my body constructing code:
string constructBody(string batchId, string fileString, string fileName) {
string body;
string CRLF = "\r\n";
// first we add the args
body.append("--" + string(BOUNDARY) + CRLF);
body.append("Content-Disposition: form-data; name=\"batch_id\"" + CRLF);
body.append(CRLF);
body.append(batchId + CRLF);
body.append("--" + string(BOUNDARY) + CRLF);
// now we add the file
body.append("Content-Disposition: form-data; name=\"front\"; filename=\"" + string(fileName) + "\"" + CRLF);
body.append("Content-Type: application/octet-stream" + CRLF);
body.append("Content-Transfer-Encording: binary" + CRLF);
body.append(CRLF);
body.append(fileString + CRLF);
body.append("--" + string(BOUNDARY) + "--" + CRLF);
body.append(CRLF);
return body;
}
And here is the posting code:
string body = constructBody(batchId, data, "Front.tif");
char header[1024];
sprintf(header, "POST %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Content-Length: %d\r\n"
"Connection: Keep-Alive\r\n"
"Content-Type: multipart/form-data; boundary=%s\r\n"
"Accept-Charset: utf-8\r\n\r\n", RECEIVER, IP, strlen(body.c_str()), BOUNDARY);
int p = send(dataSock, header, strlen(header), 0);
int k = send(dataSock, body.c_str(), strlen(body.c_str()), 0);
Thanks in advance!
You cannot use functions that uses the null as a string terminator:
int k = send(dataSock, body.c_str(), strlen(body.c_str()), 0);
You're using strlen above. This is not correct.
The fix for this is to use the length() function for std::string:
int k = send(dataSock, body.c_str(), body.length()), 0);
You make the same error in other places, such as when you create the header:
"Accept-Charset: utf-8\r\n\r\n", RECEIVER, IP, strlen(body.c_str()), BOUNDARY);
should be:
"Accept-Charset: utf-8\r\n\r\n", RECEIVER, IP, body.length(), BOUNDARY);