How do I send a POST with boost::asio::ip::tcp::iostream? - c++

From the Boost docs, you can send a GET through an iostream quite easily:
ip::tcp::iostream stream;
stream.expires_from_now(boost::posix_time::seconds(60));
stream.connect("www.boost.org", "http");
stream << "GET /LICENSE_1_0.txt HTTP/1.0\r\n";
stream << "Host: www.boost.org\r\n";
stream << "Accept: */*\r\n";
stream << "Connection: close\r\n\r\n";
stream.flush();
std::cout << stream.rdbuf();
When I modify the above to connect to my IIS server, it works fine. The problem comes in when I try to send a POST to my server. Then I get the error message "HTTP Error 400. The request verb is invalid."
Various online discussions make it seem that the problem is with separator characters in headers, but removing all question marks fixed nothing.
Is there something that I'm missing here? This forum discussion makes it look like POSTing with an iostream should be doable. Google hasn't been much use since post is such an overloaded word online.
Edit - here's an example of my POST. With a GET, the server will pick it up and the handler will complain that it wants a POST (as it should).
boost::asio::ip::tcp::iostream stream;
stream.connect("myurl.com", "http");
stream << "POST /.api/api.svc/objects/723aa707-4978-4062-bcc6-67b05783c4ec/comments/add\r\n";
stream << "Host: myurl.com\r\n";
stream << "Accept: */*\r\n";
stream << "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n";
stream << "Content-Length: 51\r\n";
stream << "Connection: close\r\n\r\n";
stream << "message=%3Cp%3EHello%3C%2Fp%3E";
stream.flush();
std::cout << stream.rdbuf();

As #Omaha spotted, the POST request is invalid. The POST line should look something like:
stream << "POST /.api/api.svc/objects/723aa707-4978-4062-bcc6-67b05783c4ec/comments/add HTTP/1.0\r\n"
to be a valid HTTP request. See HTTP/1.1: Request

Related

HTTP POST REQUEST C++

I have been stuck on a small piece of code trying it out.
char httpRequest[] = "POST http://myhost.com/ HTTP/1.1\r\n"
"Host: myhost.com\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 10\r\n"
"Connection: close\r\n"
"\r\n"
"text1=test";
sock_error2 = send(tcpsock, httpRequest, strlen(httpRequest) + 1, 0);
The request never reaches the destination server, i have packet dump on both sides.
Whenever i change content-length to 0, the packet is received by the server without the body of course. What am i doing in wrong in content-length or carriage returns?

HTTP Response Format incorrect?

I was looking at creating WebServers with C++.
I followed this tutorial by Sloan Kelly, and referenced this answer here.
I wrote (improperly adapted) the following code (showing the sending part only):
std::stringstream make_response;
make_response << "HTTP/1.1 200 OK\r\n";
make_response << "Cache-Control: no-cache, private\r\n";
make_response << "Content-Type: text/html\r\n";
make_response << "Content-Length: 88\r\n";
make_response << "\r\n";
make_response << "Hello!";
std::string finished_response = make_response.str();
send(client, finished_response.c_str(), finished_response.length()+1, 0);
What is the problem here? When I connect to 127.0.0.1:8000, the screen is entirely blank.
I know that the request reaches me, as I have printed it in the console:
C:\Users\Jaideep Shekhar\OneDrive\Documents\Projects\ProjectC++\Library\v0.1\main>webserver.exe
Listening for incoming connections...
Client connected!
Client says: GET / HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.75 Safari/537.36
Sec-Fetch-Mode: navigate
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Sec-Fetch-Site: cross-site
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
#_M
Client disconnected.
You can copy the whole source code from here and open up your terminal:
g++ webserver.cpp -o webserver.exe -lws2_32 && webserver.exe
Note that this code is for Windows OS.
Edit: Thanks to a comment, I solved the problem (see code below). Any improvements or problems in the code are welcome to be pointed out.
Edit2: How would I send images? I am opening the file opened with std::ios::binary, but it is not being recieved by the browser.
Thanks!
Okay, looks like the format and headers were all correct after all. I made a mistake in responding that I am sending 88 bytes of content, while I sent 5.
In fact, to avoid these issues, I have now put the HTML in its separate file.
The relevant part looks like this now:
// Transfer the whole HTML to a std::string
std::fstream grab_content("home.html");
std::stringstream make_content;
make_content << grab_content.rdbuf();
std::string finished_content;
finished_content = make_content.str();
// Create the HTTP Response
std::stringstream make_response;
make_response << "HTTP/1.1 200 OK\r\n";
make_response << "Cache-Control: no-cache, private\r\n";
make_response << "Content-Type: text/html\r\n";
make_response << "Content-Length: " << finished_content.length() << "\r\n";
make_response << "\r\n";
make_response << finished_content;
// Transfer it to a std::string to send
std::string finished_response = make_response.str();
// Send the HTTP Response
send(client, finished_response.c_str(), finished_response.length(), 0); // flag = 0

(Qt) Curl uploading to smtp sends an empty email

I'm using Qt and I recently made a similar application using gmail. Now, I want to send the email from outlook to gmail. EDIT: I just tried sending from outlook to outlook using an app password but still empty email in my outlook inbox... END EDIT Here is my code:
if(file.open(QIODevice::ReadWrite)){ //Writes in the msg.txt
QTextStream stream(&file);
stream << "From: \"Me\" <xxxxxxxxxx#outlook.com>" << endl;
stream << "To: \"Me\" <xxxxxxxxxxxx#gmail.com>" << endl;
stream << "Subject: Subject" << endl;
stream << msg << endl; //msg is just a QString variable
}
QString cmd = "ccurl smtp://smtp-mail.outlook.com:587 -v --mail-from \"xxxxxxxxxxxx#outlook.com\" --mail-rcpt \"xxxxxxxxxxxx#gmail.com\" --ssl -u xxxxxxxxxxxxxx#outlook.com:xxxxxxxxxxxxxx -T \"msg.txt\" -k --anyauth --insecure & pause";
const std::string s = cmd.toStdString();
const char* ccmd = s.c_str();
system(ccmd);
Pause is just used for testing purposes. Also, my .exe is named 'ccurl' and the console that appears doesn't throw any error. I do receive an email but it just says something like (Empty)
---
Email checked by avast....
Thanks for your help!
Ps. Don't tell me to use libcurl instead
You are missing an empty line between the end of the headers and the start of the message body. Without it, the rest of the message is interpreted as if it was still part of the headers.
Also, endl forces a flush in the stream for no good reason, which kills performance when done on files. Just use \n.

Curl vs CPPREST

I am trying to access an URL, using CPPREST http_client :
http://www.20min.ch/rss/rss.tmpl?type=channel&get=68
I am receiving response code 302 for URL- redirection.
But when i try to access the same URL using CURL, I am receiving CURLE_OK.
Below are the 2 piece of code :
using CURL :
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl){
curl_easy_setopt(curl, CURLOPT_URL, "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68");
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
cout<<"failed";
}
else {
cout<<"success";
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
The output is : success
using CPPREST :
std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
try
{
http_client client1(U(url_));
uri_builder builder1(U(""));
client1.request(methods::GET, builder1.to_string()).then([=](http_response response)
{
cout<<"Response code is : "<<response.status_code();
});
}
catch(std::exception& e)
{
cout<<"response :"<<e.what();
}
The output is :: Response code is : 302
I do not understand why the two libs are behaving differently for same URL??
UPDATE :
I have also tried with :
http_client client1(utility::conversions::to_string_t(url_));
and
http_client client1(U("http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"));
and
http_client client1(U("http://www.20min.ch/"));
but the response is same 302 with cpp rest. [ for cross checking bing example
is working fine]
UPDATE 2:
The method as explained by #Matt Weber seems very helpful and legit but i am continuously getting error code : 400 for that, So I tried the below things:
I tried to set the host and port for the URL in uri_builder.
http_client client(U("http://www.20min.ch/rss/"));
uri_builder builder(U("/rss.tmpl"));
builder.append_query(U("type"), U("channel"));
builder.append_query(U("get"), U("68"));
builder.set_host(U("www.20min.ch"));
builder.set_port(U("80"));
client.request(methods::GET, builder.to_string()).then([=](http_response response)
{
cout<<"Received response status code: "<<response.status_code();
});
but still same 302.
The problem with the Rest SDK code is the http_client initialization:
http_client client1(U(url_));
The U macro is for use with string literals to produce something from which a uri can be constructed. If you're on Windows, this shouldn't compile, because the macro expansion results in Lurl_. Apparently whatever this results in on your system leads to a request for something that responds with a 302.
There are a couple of options. One would be to simply use the literal directly:
http_client client1(U("http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"));
If you want to keep the std::string and initialize the client from that, you can convert to a utility::string_t from which the uri can be constructed.
std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
http_client client1(utility::conversions::to_string_t(url_));
Once that is done, you'll likely find that you need to call the wait function on the continuation from request in order to actually see the expected output:
client1.request(methods::GET, builder1.to_string()).then([](http_response response)
{
cout<<"Response code is : "<<response.status_code();
}).wait(); // ensure that the response gets processed
EDIT:
The above is relevant for building on Windows, but has nothing to do with the 302 response.
On Linux, the request results in a 302 consistently. Looking at the request and response on the wire, a request from a Windows host gets a 200 and a request from a Linux host gets a 302. The reason is that in the Linux version, the host header includes a port number, which is what triggers the server to respond with a 302.
Windows request:
GET /rss/rss.tmpl?type=channel&get=68 HTTP/1.1\r\n
Connection: Keep-Alive\r\n
User-Agent: cpprestsdk/2.8.0\r\n
Host: www.20min.ch\r\n
\r\n
Linux request:
GET /rss/rss.tmpl?type=channel&get=68 HTTP/1.1\r\n
Host: www.20min.ch:80\r\n
User-Agent:cpprestsdk/2.8.0\r\n
Connection: Keep-Alive\r\n
\r\n
You can verify that this is the cause with wget:
$ wget --header="Host: www.20min.ch" -S "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"
HTTP/1.1 200 OK
$ wget --header="Host: www.20min.ch:80" -S "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68" --max-redirect 0
HTTP/1.1 302 Found
The difference in the header is due to the different implementations. The WinHTTP client implementation does not add the Host header explicitly, presumably because it relies on WinHTTP to do that internally. The asio client implementation does add it, though.
// Add the Host header if user has not specified it explicitly
if (!ctx->m_request.headers().has(header_names::host))
{
request_stream << "Host: " << host << ":" << port << CRLF;
}
So to get the expected behavior, the header can be set explicitly to avoid adding the port information:
std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
http_client client1(utility::conversions::to_string_t(url_));
http_request request;
request.set_method(methods::GET);
request.headers().add(U("Host"), U("www.20min.ch"));
client1.request(request).then([](http_response response)
{
std::cout<<"Response code is : "<<response.status_code();
}).wait();
With this change, I get a 200 OK on both Windows and Linux.

Qt: Cisco ip phone serivices Authentication URL response

I wrote a program with Qt to work with cisco ip phone services. I'm using QNetworkAccessManager to post XML objects to phones and QTcpServer's socket with QTextStream to respond to authentication requests (simply writing http headers with "AUTHORIZED" to text stream).
QString cTime = currTime.currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss");
QTextStream os(socket); os << "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Date: " + cTime + " GMT\r\n"
"Connection: close\r\n"
"\r\n"
"AUTHORIZED";
The problem is the phones don't accept that response and return <CiscoIPPhoneError Number="4" />.
I used node.js for that before and simply wrote "AUTHORIZED" to http.serverResponse object, but I'm confused now why it doesn't work with Qt
Solved that.
The problem was the "Secure Authentication URL" field was set along with "Authentication url". And what I thought to be GET from phone was "Client hello"...
Cleared "Secure Authentication URL" in CUCM and it works now