Send data using WinHttpClient and https c++ - c++

I have a c++ application that sending data using http with WinHttpClient
and i want to start sanding https
bool ret = false;
WinHttpClient client(wstring(url.begin(), url.end()));
ret = client.SetAdditionalDataToSend((BYTE *)data.c_str(), (unsigned int)data.length());
if (ret)
{
// Set request headers.
wchar_t szSize[50] = L"";
swprintf_s(szSize, L"%d", data.size());
wstring headers = L"Content-Length: ";
headers += szSize;
headers += L"\r\nContent-Type: application/x-www-form-urlencoded\r\n";
string apiVersion = ...;
headers += L"\r\nAccept-Version: " + wstring(apiVersion.begin(), apiVersion.end()) + L"\r\n";
ret = client.SetAdditionalRequestHeaders(headers);
client.SetUserAgent(L"..." + to_wstring(...));
}
// Send http post request.
if (ret) ret = client.SendHttpRequest(L"POST");
i have read that i need to add
client.SetRequireValidSslCertificates(false);
after i create the client, but when i tried noting change
so the question it how to send it using https?

Related

How do you upload a file using Emscripten in C++?

I'm trying to upload a file to a server. I have been successful in downloading data using Emscripten's Fetch API with a GET request, but so far have been unsuccessful with POST requests.
Here is my current implementation: (the file is being opened and read as expected, but the server is not receiving the file)
void uploadSucceeded(emscripten_fetch_t* fetch)
{
printf("Successful upload of %llu bytes to %s.\n", fetch->numBytes, fetch->url);
// The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1];
emscripten_fetch_close(fetch); // Free data associated with the fetch.
}
void uploadFailed(emscripten_fetch_t* fetch)
{
printf("Failed upload to %s - HTTP failure status code: %d.\n", fetch->url, fetch->status);
emscripten_fetch_close(fetch); // Also free data on failure.
}
bool UploadFile(const std::string& url, const std::string& file_name)
{
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "POST");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
attr.onsuccess = uploadSucceeded;
attr.onerror = uploadFailed;
// Set headers:
const char* headers[] = { "Content-Type", "application/x-www-form-urlencoded", 0 };
attr.requestHeaders = headers;
// Read file data:
std::ifstream in_file(file_name.c_str(), std::ios::binary);
//
in_file.seekg(0, std::ios::end);
int file_size = in_file.tellg();
//
in_file.seekg(0, std::ios::beg);
std::stringstream buffer;
buffer << in_file.rdbuf();
//
char *cstr = new char[buffer.str().length() + 1];
strcpy(cstr, buffer.str().c_str());
//
attr.requestData = cstr;
attr.requestDataSize = file_size;
// Send HTTP request:
emscripten_fetch(&attr, url.c_str());
return true;
}
You need to make sure that the request header has:
"Content-Type", "multipart/form-data; boundary=[custom-boundary]\r\n"
...where [custom-boundary] is a string of your choice.
Then in the request data, you start with that custom boundary, followed by "\r\n", then you have another header, such as:
"Content-Disposition: form-data; name=\"myFile\" filename=\"G0000U00000R01.html\"\r\n"
"Content-Transfer-Encoding: binary\r\n"
"Content-Type: text/html\r\n\r\n"
...followed by the file contents, followed by "\r\n" again, and finally followed by the same custom boundary as before.

How to implement EventSource (SSE) server side from scratch?

I'm trying to implement simple, bare-bones mini web server, it's main and only task would be to send simple html/js page to client and then do real-time updates to it. Implementing transport layer is pretty simple, but I met surprising difficulties in server-side implementation of EventSource... Initially I tried this straightforward approach:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#define PORT 80
using namespace std;
string head =
"HTTP/1.1 200 OK\n\
Content-Type: text/html\n\
Content-Length: ";
string update_head =
"HTTP/1.1 200 OK\n\
Content-Type: text/event-stream\n\
Cache-Control: no-cache\n\
Content-Length: ";
string update = "retry: 10000\ndata: SERVER SAYS: ";
string response =
"<!DOCTYPE html>\
<html>\n\
<head>\n\
</head>\n\
<body>\n\
<div id=\"serverData\">Here is where the server sent data will appear</div>\n\
<script>\n\
if(typeof(EventSource)!==\"undefined\") {\n\
var eSource = new EventSource(\"/\");\n\
eSource.onmessage = function(event) {\n\
document.getElementById(\"serverData\").innerHTML = event.data;\n\
};\n\
}\n\
else {\n\
document.getElementById(\"serverData\").innerHTML=\"Whoops! Your browser doesn't receive server-sent events.\";\n\
}\n\
</script>\n\
</body>\n\
</html>";
int serverMain()
{
int listen_sock, new_sock;
struct sockaddr_in addr;
int addr_len = sizeof(addr);
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sock == 0)
{
perror("Error creating socket");
return 1;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
int ret = bind(listen_sock, (struct sockaddr*)&addr, addr_len);
if(ret < 0)
{
perror("Error binding socket");
return 2;
}
ret = listen(listen_sock, 10);
if(ret < 0)
{
perror("Error setting up server as listеner");
return 3;
}
while(1)
{
char buff[2048] = {0};
printf("Waiting for clients...\n\n");
new_sock = accept(listen_sock, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
if(new_sock < 0)
{
perror("Error accepting client connection into new socket");
return 4;
}
long bytes_read = read(new_sock, buff, 2048);
printf("------------------Client-Request------------------\n%s\
\n------------------Client-Request------------------\n", buff);
string reply = head + to_string(response.size()) + "\n\n" + response;
write(new_sock, reply.c_str(), reply.size());
printf("Server response sent.\n\n");
bytes_read = read(new_sock, buff, 2048);
printf("------------------Client-Request------------------\n%s\
\n------------------Client-Request------------------\n", buff);
for(int i = 0; i < 60; ++i)
{
sleep(1);
string msg = update + to_string(i) + "\n\ndata: some other stufff morestuff "
+ to_string(i) + "\n\n";
string upd = update_head + to_string(msg.size()) + "\n\n" + msg;
write(new_sock, upd.c_str(), upd.size());
printf("Server UPDATE %d sent.\n", i);
}
close(new_sock);
}
return 0;
}
TLDR: basically, I was just pushing "updates" wrapped in a header every second. Result was not good at all:
Only first update was actually received by browser, all subsequent updates where ignored. What's even worse — after browser sent another request for EventStream data 10 s later (look at retry: 10000\n I sent with each massage), server crashed with no error messages (I still have no idea what was the reason).
After this I tried another approach:
for(int i = 0; i < 60; ++i)
{
bytes_read = read(new_sock, buff, 2048);
printf("------------------Client-Request------------------\n%s\
\n------------------Client-Request------------------\n", buff);
string msg = update + to_string(i) + "\n\ndata: some other stufff morestuff "
+ to_string(i) + "\n\n";
string upd = update_head + to_string(msg.size()) + "\n\n" + msg;
write(new_sock, upd.c_str(), upd.size());
printf("Server UPDATE %d sent.\n", i);
}
I removed sleep(1) from the server update loop and allowed client to send me requests, and only after that server could send update (header + data). This, kind of, sort of worked:
In a way that, yes, browser really received all updates and correctly displayed it in the html page. But something is still off... I need 1 second intervals. Of course, I can set retry: 1000\n and browser will send requests every second and everything will work "perfectly". But actually not so. Because its not server who decides when to push update, its client who does it. It's not much different to clicking "refresh page" button every second...
In php and node.js examples I saw on the Internet, it seems to me that they somehow send data continuously without waiting for client. Maybe they use some sort of buffer or memory mapping or something?
So, apparently, I was doing everything in the right direction apart from tiny little undocumented (at least I didn't found anything at all about it) detail on how exactly to send updates correctly.
First change header to this:
string update_head =
"HTTP/1.1 200 OK\n\
Content-Type: text/event-stream\n\
Cache-Control: no-cache\n\n";
There is no need for content length! Now, after sending actual HTML page, client will send request for text/event-stream. You need to read it and reply with bare header (important, no data or anything else!).
write(new_sock, update_head.c_str(), update_head.size());
printf("Server HEAD UPDATE sent.\n");
And only after this you can start sending actual updates without any header or Content-Length:
for(int i = 0; i < 60; ++i)
{
sleep(1);
string msg = update + to_string(i) + "\n\ndata: some other stufff morestuff "
+ to_string(i) + "\n\n";
write(new_sock, msg.c_str(), msg.size());
printf("Server UPDATE %d sent.\n", i);
}
This results in browser correctly interpreting event stream:

Unable to send POST request via CHttpFile - C++/MFC

My Goal: I'm trying to submit a post request
Issue: The post request is not being sent or is not formatted correctly or missing some information. I'm not sure about the exact problem.
What is a Successful Post Request: A successful post request to the website will change the user's Country location, this is part of the website feature.
Current Program Output: I get a 200 -OK response and No errors
Additional information: In my code below, I am making 2 requests.
From the first request, i receive and read an html page to find the RequestDigest value. This value is needed to perform a post request to a SharePoint website.
For the second request, I use the RequestDigest Value found and add it in post request and change the user's country to Canada ("CA").
Note: I'm new to C++ / MFC and I have been stuck on the problem for over a week! any help and/or guidance will be greatly appreciated
void CUserPassDiag::Connect(){
CInternetSession session(_T("My Session"));
CHttpConnection* pServer = NULL;
CHttpFile* pFile = NULL;
char *szBuff = new char[100000];
try
{
// First Request to retrieve the Digest value
CString strServerName = _T("boration.ptt.ca");
CString headers = _T("Content-Type: application/x-www-form-urlencoded\r\n");
headers += _T("Host: boration.ptt.ca\r\n");
headers += _T("Method: POST\r\n");
headers += _T("Pragma: no-cache\r\n");
headers += _T("Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, *//*\r\n");
headers += _T("UserAgent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 1.1.4322; .NET4.0C; .NET4.0E; InfoPath.3\r\n");
headers += _T("Keep-Alive: true\r\n");
headers += _T("Accept-Language: en-CA\r\n");\
headers += _T("Referer: http://boration.ptt.ca/team/physicallocation/_Layouts/Ptt.PhysicalLocationWidget/Page.aspx\r\n");
// Headers Ready
CString szHeaders = _T(headers);
DWORD dwRet;
// URL
CString strObject = _T("/team/D135/SitePages/PttHome.aspx");
// get connection with username and password
pServer = session.GetHttpConnection(strServerName, INTERNET_DEFAULT_HTTP_PORT, _T("user"), _T("pass"));
// open request
pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, strObject);
pFile->AddRequestHeaders(szHeaders);
pFile->SendRequest();
pFile->QueryInfoStatusCode(dwRet); // response is 200 OK
// Data is retrieve and can be queried for digest value
pFile->Read(szBuff,100000);
CString digest = CString(szBuff);
std::string str = digest.GetString();
std::smatch match;
std::regex re("0x(\\d|[A-Z]).*0000");
if (std::regex_search(str, match, re)){
digest = CString(std::string(match[0]).c_str());
}
// close the server and chttpfile
pServer->Close();
pFile->Close();
/*
* Second Request Preperation
*/
// Post data - CA stands for Canada and this value is added to the post reuquest to change the users country
CString country = "CA";
// add a new header
CString xRequestDigest = "X-RequestDigest: " + digest + _T("\r\n");
headers += xRequestDigest;
// formatting the digest value to be placed in the post request
digest.Replace(_T(" "),_T("+"));
digest.Replace(_T(":"),_T("%3A"));
digest.Replace(_T(","),_T("%2C"));
// new URL
strObject = _T("/team/physicallocation/_Layouts/Ptt.PhysicalLocationWidget/Page.aspx");
// post data to be sent
CString strFormData = _T("__REQUESTDIGEST=" + digest + "&__VIEWSTATE=%2FwEPDwUKLTM0MDQ5MzI3MQ9kFgJmD2QWAgIBDxYCHgVjbGFzcwUtbXMtZGlhbG9nIG1zLWRpYWxvZy1uciBtcy1kaWFsb2cgbXMtZGlhbG9nLW5yFgICAw9kFg4CEQ9kFgRmD2QWBAIBD2QWAgIBDxYCHgdWaXNpYmxlaBYCZg9kFgQCAg9kFgQCAQ8WAh8BaGQCAw8WCB4TQ2xpZW50T25DbGlja1NjcmlwdAWPAWphdmFTY3JpcHQ6Q29yZUludm9rZSgnVGFrZU9mZmxpbmVUb0NsaWVudFJlYWwnLDEsIDEsICdodHRwOlx1MDAyZlx1MDAyZnhjb2xsYWJvcmF0aW9uLnB3Yy5jYVx1MDAyZnRlYW1cdTAwMmZwaHlzaWNhbGxvY2F0aW9uJywgLTEsIC0xLCAnJywgJycpHhhDbGllbnRPbkNsaWNrTmF2aWdhdGVVcmxkHihDbGllbnRPbkNsaWNrU2NyaXB0Q29udGFpbmluZ1ByZWZpeGVkVXJsZB4MSGlkZGVuU2NyaXB0BSFUYWtlT2ZmbGluZURpc2FibGVkKDEsIDEsIC0xLCAtMSlkAgMPDxYKHglBY2Nlc3NLZXkFAS8eD0Fycm93SW1hZ2VXaWR0aAIFHhBBcnJvd0ltYWdlSGVpZ2h0AgMeEUFycm93SW1hZ2VPZmZzZXRYZh4RQXJyb3dJbWFnZU9mZnNldFkC6wNkZAIDD2QWAgIBD2QWAgIDD2QWAgIBDzwrAAUBAA8WAh4PU2l0ZU1hcFByb3ZpZGVyBRFTUFNpdGVNYXBQcm92aWRlcmRkAgEPZBYEAgMPZBYCZg9kFgJmDxQrAANkBSNodHRwOi8vbXlzaXRlLnB3Yy5jYTo4MC9QZXJzb24uYXNweAUYaHR0cDovL215c2l0ZS5wd2MuY2E6ODAvZAIFDw8WBB4EVGV4dAUeTGF1bmNoIHRoZSBEZXZlbG9wZXIgRGFzaGJvYXJkHwFoZGQCFw9kFgICAQ9kFgJmD2QWAgIBDw9kFgYfAAUibXMtc2J0YWJsZSBtcy1zYnRhYmxlLWV4IHM0LXNlYXJjaB4LY2VsbHBhZGRpbmcFATAeC2NlbGxzcGFjaW5nBQEwZAIbD2QWAgIBDxAWAh8BaGQUKwEAZAIhD2QWAmYPZBYCAgEPDxYGHgtOYXZpZ2F0ZVVybAWBAWphdmFzY3JpcHQ6IFNob3dEaWFsb2coJ2h0dHA6Ly94Y29sbGFib3JhdGlvbi5wd2MuY2EvdGVhbS9waHlzaWNhbGxvY2F0aW9uL19MYXlvdXRzL1BXQy5QaHlzaWNhbExvY2F0aW9uV2lkZ2V0L0NvdW50cnlQYWdlLmFzcHgnKR4HVG9vbFRpcAUyQ291bnRyeTogQ2FuYWRhDQpFeHBpcmVzIGluOiAyMyAgSG91ciAgNTcgIE1pbnV0ZXMeCEltYWdlVXJsBTkvX2xheW91dHMvc3RhdGljL1BXQy5QaHlzaWNhbExvY2F0aW9uV2lkZ2V0L2ltYWdlcy9DQS5wbmdkZAInD2QWAmYPZBYCAgEPDxYCHwwFFlRFQ0hOSUNBTCBEQVRBIEFMTE9XRURkZAJFD2QWAgIJD2QWAgIBDw8WAh8BaGQWAgIDD2QWAmYPZBYCAgMPZBYCAgUPDxYEHgZIZWlnaHQbAAAAAAAAeUABAAAAHgRfIVNCAoABZBYCAgEPPCsACQEADxYEHg1QYXRoU2VwYXJhdG9yBAgeDU5ldmVyRXhwYW5kZWRnZGQCWQ9kFgICAQ9kFgYCAQ8PFgIfAWhkFgoCAQ8PFgIfDAUvQ29uZmlybSB5b3VyIGN1cnJlbnQgcGh5c2ljYWwgbG9jYXRpb24gY291bnRyeS5kZAIDDw8WAh8MBRlBcmUgeW91IHN0aWxsIGluIENhbmFkYSA%2FZGQCBQ8PFgIfDAXlAVRvIGRldGVybWluZSBpZiB0ZWNobmljYWwgZGF0YSBpcyBhY2Nlc3NpYmxlLCB5b3VyIGN1cnJlbnQgcGh5c2ljYWwgbG9jYXRpb24gbXVzdCBiZSBwcm92aWRlZC4gPGJyLz48YnIvPg0KQWxsIHZhbHVlcyBwcm92aWRlZCBhcmUgbG9nZ2VkIGFuZCByZXRhaW5lZCBmb3IgYXVkaXRpbmcgcHVycG9zZXMuPGJyLz48YnIvPk1ha2Ugc3VyZSB0byB1cGRhdGUgbG9jYXRpb24gd2hlbiB0cmF2ZWxsaW5nLiBkZAIHDw8WAh8MBQNZZXNkZAIJDw8WAh8MBQJOb2RkAgMPDxYCHwFnZBYUZg8PFgIfDAU1UGxlYXNlIHNlbGVjdCB5b3VyIGN1cnJlbnQgcGh5c2ljYWwgbG9jYXRpb24gY291bnRyeS5kZAIBDw8WAh8MBQhDb3VudHJ5OmRkAgIPDxYCHwwFC1VzZXIgTmFtZSA6ZGQCAw8PFgIfDAUdUmFuYSwgVGFpbW9vciAgICAgICAgICAgICBQV0NkZAIEDw8WAh8MBQZEYXRlIDpkZAIFDw8WAh8MBQtIb3N0IE5hbWUgOmRkAgYPDxYCHwwFGUY1LU1haW5JUC1pbnRlcm5hbC5wd2MuY2FkZAIIDw8WAh8MBeUBVG8gZGV0ZXJtaW5lIGlmIHRlY2huaWNhbCBkYXRhIGlzIGFjY2Vzc2libGUsIHlvdXIgY3VycmVudCBwaHlzaWNhbCBsb2NhdGlvbiBtdXN0IGJlIHByb3ZpZGVkLiA8YnIvPjxici8%2BDQpBbGwgdmFsdWVzIHByb3ZpZGVkIGFyZSBsb2dnZWQgYW5kIHJldGFpbmVkIGZvciBhdWRpdGluZyBwdXJwb3Nlcy48YnIvPjxici8%2BTWFrZSBzdXJlIHRvIHVwZGF0ZSBsb2NhdGlvbiB3aGVuIHRyYXZlbGxpbmcuIGRkAgoPDxYCHwwFB0NvbmZpcm1kZAILDw8WAh8MBQZDYW5jZWxkZAIFD2QWBAIBDw8WAh8MBQdXYXJuaW5nZGQCBQ8PFgIfDAUFQ2xvc2VkZGQShQ6UJsf5SBoFkPM2ZpdvDrHQ0w%3D%3D&__EVENTVALIDATION=%2FwEWBwKO0pUjAqmflsILAs2u9bkDAr36nu0CAo3uhpUDAubLxrYNAtT449AGHxcQwoL9xE%2FsJbw1%2BBKH5DPplNg%3D&ctl00%24PlaceHolderSearchArea%24ctl01%24ctl00=http%3A%2F%2Fxcollaboration.pwc.ca%2Fteam%2Fphysicallocation&InputKeywords=Search+this+site...&ctl00%24PlaceHolderSearchArea%24ctl01%24ctl04=0&countries="+country+"&ctl00%24PlaceHolderMain%24inpHide="+country+"&ctl00%24PlaceHolderMain%24btnSave=Confirm&__spText1=&__spText2=");
// Open connection
pServer = session.GetHttpConnection(strServerName, INTERNET_DEFAULT_HTTP_PORT, _T("user"), _T("pass"));
pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, strObject);
// add 'content-length' header
CString contentLength;
contentLength.Format("Content-Length: %d",strFormData.GetLength());
contentLength+= "\r\n";
headers += _T(contentLength);
szHeaders = _T(headers);
// add the header
pFile->AddRequestHeaders(szHeaders);
//make a new request
bool res = pFile->SendRequest(szHeaders,(LPVOID)(LPCSTR)strFormData ,strFormData.GetLength());
pFile->QueryInfoStatusCode(dwRet);
TRACE("\n%d\n",res);
}catch(CInternetException* e){
TCHAR sz[1024];
e->GetErrorMessage(sz,1024);
_tprintf_s(_T("ERROR! %s\n"), sz);
e->Delete();
}
session.Close();
}
Your code seems fine, make sure you are getting the digest value from the right address.
Code looks good but I'm missing a pFile->EndRequest(); -- but that shouldn't be the cause.
If I were you, I'd check the server responses i.e. have a look at the servers access log. You need to be more specific here so people can help. If you get a 200 response, it might be a server problem as well.

Poco::HttpClientSession.receiveResponse() throws NoMessageException without any apparent reason

I wrote an HTTP server in Java and a client in C++ with Poco. This is a part of the C++ client code:
URI uri("http://127.0.0.1:4444");
HTTPClientSession session(uri.getHost(), uri.getPort());
HTTPRequest req(HTTPRequest::HTTP_POST,
"/pages/page",
HTTPMessage::HTTP_1_1);
session.sendRequest(req);
HTTPResponse res;
std::istream &is = session.receiveResponse(res);
In the last line I get the following error:
terminate called after throwing an instance of 'Poco::Net::NoMessageException'
what(): No message received
But I don't understand why. The connection was established successfully and the page requested exists. I tried the same code with known websites (like Wikipedia) and it works without any exception.
I also tried to make the exact same request with cURL (to my server) in command-line and it shows the response of the server, so the server seems fine.
This is the original response of the server in a string form:
"HTTP/1.1 200 OK\r\n" +
"Server: [server name]\r\n" +
"Content-Type: text/xml; charset=utf-8\r\n" +
"Content-Length:" + bodyBytes.length + "\r\n" +
"Resource: " + job.resId + "\r\n\r\n" +
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><JobRequest><InputRepresentation id=\"0\"/> <effectsList><cvtColor><code> CV_RGB2GRAY </code></cvtColor><resize><scaleFactorX> 0.5 </scaleFactorX><scaleFactorY> 0.5 </scaleFactorY><interpolation> INTER_LINEAR </interpolation></resize><GaussianBlur><kSize> 3 </kSize><sigmaX> 2 </sigmaX><sigmaY> 2 </sigmaY><borderType> BORDER_REPLICATE </borderType></GaussianBlur></effectsList></JobRequest>"
I have written a simple HTTP server which respond with a fixed response for every request, to test what's wrong. this is the code:
public class Test {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(4449);
Socket clientSocket = serverSocket.accept();
String body = "ab";
byte[] bodyBytes = body.getBytes("UTF-8");
String headers = "HTTP/1.1 200 OK\r\n" +
"Server: Foo\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: " + bodyBytes.length + "\r\n\r\n";
byte[] headerBytes = headers.getBytes("UTF-8");
byte[] responseBytes = new byte[headerBytes.length + bodyBytes.length];
/* Fill responseBytes with the header and body bytes */
int i = 0;
for (int j = 0; j < headerBytes.length; ++j) {
responseBytes[i] = headerBytes[j];
++i;
}
for (int j = 0; j < bodyBytes.length; ++j) {
responseBytes[i] = bodyBytes[j];
++i;
}
clientSocket.getOutputStream().write(responseBytes);
} catch (IOException e) {}
}
}
I get the same exception even with this server. So what's wrong here?
Based on experimentation, I've found that it's necessary to include a Content-Length header if you are making an empty POST request. I.e.,
req.add("Content-Length", "0");
I had to use the following sequence in my handler code for a minimal response to be received without a No message received error:
resp.setStatus( Poco::Net::HTTPResponse::HTTP_OK );
resp.setContentType( "text/json" );
ostream &out = resp.send();
out << "[]";
out.flush();

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

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.