Read Response of POCO HTTPClientRequest - c++

Hi I am uploading a file using multipart upload in POCO (by sending a the content of file appended with boundaries through a normal POST request). But when I try to read the response as:
std::ostream& myOStream = session.sendRequest(req);
// sends the body
myOStream << reqBody;
Poco::Net::HTTPResponse res;
// get the response body from server
std::istream& inStream = session.receiveResponse(res);
std::ostringstream outStringStream;
outStringStream << inStream.rdbuf();
std::cout<< outStringStream.str();
I get this exception "SSL Connection Unexpectedly Closed" on this line:
outStringStream << inStream.rdbuf();
Also if i instead try to read the response as:
Poco::StreamCopier::copyStream( inStream, outStringStream );
std::cout<< outStringStream.str();
I get empty response (No exception in this case).
The server returns a plain text in response. And i am getting it as response if using qt, but not through POCO. Please help me to read the response body. What could be the reason of this exception??
Also, one more thing if I read the response status as res.getStatus() and res.getReason() it returns 200Ok. I dont understand what is the issue with response body.

This code is working for me you can try.
std::istream& rs = _httpClientSession.receiveResponse(response);
memset(_response, 0, RESPONSE_BUFF_SIZE);
rs.read(_response, RESPONSE_BUFF_SIZE-1);
//below code is just to clear the istream
std::streamsize nRead = rs.gcount();
while( !rs.eof() || nRead != 0)
{
rs.read(_response, RESPONSE_BUFF_SIZE);
nRead = rs.gcount();
}
One more thing, in case of any exception you have to reset http session by invoking _httpClientSession.reset();

Related

How to detect when stream has been closed in POCO HTTPRequestHandler?

In my application I want to stream jpg images through a HTTP web server using the POCO library, and for this I am using multipart/x-mixed-replace content type in my response body. This means when a GET request arrives to the HTTP server, it is continously sending images to the client in parts in a single http response.
The stream should close and the request handler should return when the client closes the window. But my problem with the HTTPServerResponse.send() stream is that it only gets destroyed, when the response object is destroyed, so I do not know when the client has left, and when do I have to stop sending the images.
That is how the code looks like:
#pragma once
#include <fstream>
#include <sstream>
#include <string>
#include "Poco/Net/HTTPRequestHandler.h"
class StreamHandler : public Poco::Net::HTTPRequestHandler {
public:
void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) override {
std::ifstream imgFile("../../assets/imgs/random.jpg");
std::stringstream ss{};
ss << imgFile.rdbuf();
std::string buf = ss.str();
std::string boundary = "--BOUNDARY--";
response.setVersion(request.getVersion());
response.setStatus(Poco::Net::HTTPServerResponse::HTTP_OK);
response.setChunkedTransferEncoding(false);
response.setKeepAlive(false);
response.setContentType("multipart/x-mixed-replace; boundary=" + boundary);
response.set("Access-Control-Allow-Origin", "*");
response.set("Connection", "Close");
response.set("Cache-Control",
"no-cache, no-store, must-revalidate, pre-check=0, post-check=0, max-age=0, false");
response.set("Pragma", "no-cache");
std::ostream& ostr = response.send();
while (true) { // <-- What is the stop condition?
ostr << boundary << "\r\n";
ostr << "Content-Type: image/jpeg\r\n"
"Content-Length: " +
std::to_string(buf.length()) + "\r\n\r\n";
ostr << buf;
ostr << "\r\n";
}
}
};
Is there a way to detect whether the client has left?
PS: Possibly I would like a solution within the boundaries of Poco::Net::HTTPRequestHandler. I do not want any solution that requires opening another port, or using another third party library only for the streaming. For example, I am already using nadjieb/cpp-mjpeg-streamer which is working great, but I want to simplify my code and depend only on POCO. In lower level libraries I saw good implementations that are using the following strategy:
while (true) {
if (res) {
res = http_res_send_chunk(req, part_buf, part_len);
} else {
break;
}
}
The send command returns with some positive integer in case of success, and with 0 if the send function failed. However, I cannot replicate this with POCO unfortunately. Any ideas how to get around this?
I finally found a solution and posted it here. If my answer gets accepted, I will flag this question as a duplicate.

How to assign JSON body to Boost Library 1.70 http::request as POST request?

I am new to the Boost library. I try to create an Rest HTTP request using the Boost::http library.
My question is how can i simply assign the JSON payload to the http request.
the following code snippet shows my current try which connects successfully but the payload is not assigned.
http::request<http::string_body> req{ http::verb::post, LOGIN_PATH, 10 };
req.set(beast::http::field::content_type, "application/json");
req.body() = std::move(serviceUser);
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::dynamic_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
return "OK";
The following snippet shows the JSON message.
std::stringstream strStream;
strStream << "{\"userName\" : lena, \"password\" : liebe }";
serviceUser = strStream.str();
Can you give me a simple example please. Importent to mention is that i use the Boost library version 1.70.

Poco HTTPClientSession.recieveResponse skips next lines in program C++

I am using Poco to send HTTP requests, as the name implies. Right now, I'm just trying to send a GET request to google.com and store the resulting HTML in a string to test out Poco and see if it fits what I need. However, I'm having a little trouble with this. Here's my code:
try
{
Poco::URI uri("https://www.google.com");
std::string path(uri.getPathAndQuery());
if (path.empty()) path = "/";
Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort());
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1);
request.add("Content-Length", "0");
Poco::Net::HTTPResponse response;
doRequest(session, request, response);
}
catch (Poco::Exception& exc)
{
OutputDebugStringA(exc.displayText().c_str());
}
void doRequest(Poco::Net::HTTPSClientSession& session, Poco::Net::HTTPRequest& request, Poco::Net::HTTPResponse& response)
{
session.sendRequest(request);
std::istream& is = session.receiveResponse(response);
std::string body(std::istreambuf_iterator<char>(is), { });
message = (LPCWSTR)response.getStatus();
}
I have put breakpoints on every line of doRequest, but the only ones that are triggered are the first two. After clicking continue on the second, the program simply continues as normal. I'm not getting any exceptions or anything. I am adding Content-Length: 0 to avoid a NoMessageException from Poco.

rest post request with chilkat lib

a working post request with postman interface is not working when trying to implement in c++ with chilkat library, with postman I get response 200, with the program i get 401 (unauthorized).
I'm using chilkat's example for rest post request.
wherever you see username and password, valid values are filled.
CkRest rest;
// Make the initial connection (without sending a request yet).
bool bTls = true;
int port = 443;
bool bAutoReconnect = true;
success = rest.Connect("https://dev-182721.oktapreview.com",port,bTls,bAutoReconnect);
if (success != true) {
std::cout << rest.lastErrorText() << "\r\n";
return;
}
rest.put_Authorization("username:password");
rest.authorization();
rest.AddHeader("Content-Type","application/x-www-form-urlencoded");
// Provide query params.
rest.AddQueryParam("grant_type","password");
rest.AddQueryParam("redirect_uri","http://localhost");
rest.AddQueryParam("username","username");
rest.AddQueryParam("password","password");
rest.AddQueryParam("scope","openid");
const char *responseStr=
rest.fullRequestFormUrlEncoded("POST","/oauth2/default/v1/token");
if (rest.get_LastMethodSuccess() != true) {
std::cout << rest.lastErrorText() << "\r\n";
return;
}
// When successful, the response status code will equal 200.
can anyone please help me figure out what's missing
Okta access token examples are available here: https://www.example-code.com/cpp/okta_oauth_oidc.asp

boost::asio file sending

There is a server that response a .png file over http:
#include "server.h"
string Server::header(int contentLength)
{
string h =
"HTTP/1.1 200 OK\n"
"Content-Length: " + boost::lexical_cast<string>(contentLength) + "\n"
"Content-Type: image/png;\n"
"Connection: close\n"
"\n";
return h;
}
string Server::readMap(const string &filename)
{
ifstream file (filename.c_str(), ios::in|ios::binary);
string reply;
char buf[512];
while (file.read(buf, sizeof(buf)).gcount() > 0)
reply.append(buf, file.gcount());
return reply;
}
void Server::run(const string &filename, int port)
{
string data = readMap(filename);
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
boost::asio::write(socket, boost::asio::buffer(header( data.size() )));
boost::asio::write(socket, boost::asio::buffer(data));
}
}
catch (std::exception& e)
{
cerr << "exception: " << e.what() << endl;
}
}
Every time an error happens:
exception: Connection reset by peer
I can see some part of an image in my browser, and sometimes the image is almost complete, but it never works without the error.
If I use wget it looks like
wget http://localhost:8089
--2012-03-07 12:07:19-- http://localhost:8089/
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'
62% [========================================================> ] 475,136 --.-K/s in 0.002s
2012-03-07 12:07:19 (287 MB/s) - Read error at byte 475136/760032 (Connection reset by peer). Retrying.
--2012-03-07 12:07:20-- (try: 2) http://localhost:8089/
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'
73% [==================================================================> ] 557,056 --.-K/s in 0.001s
... many failes and finally
--2012-03-07 12:09:01-- (try: 9) http://localhost:8089/
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'
100%[===========================================================================================>] 760,032 --.-K/s in 0.001s
Any ideas how to fix it?
There are several more complete HTTP-implementations in the ASIO-docs, including static file serving. One way to go would be to reuse some of that sample code for your application.
In this particular case, there's an example of how to correctly open and buffer a file at http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/example/http/server/request_handler.cpp
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
...
char buf[512];
while (is.read(buf, sizeof(buf)).gcount() > 0)
rep.content.append(buf, is.gcount());
The docs also has examples for actual asynchronous HTTP-implementations. (I assume you're using boost::asio to eventually make it asynchronous?)
You should receive and decode the HTTP request first, and only send the content if that was what was requested. Browsers sometimes request other resources as well; they may get upset if you send something unexpected, or if you send it before they've sent the request.
You also seem to have an off-by-one error in the data size - you put data.size()-1 in the header, and then send all of data. Perhaps this is a partial workaround for the bug in readMap, where you push an extra character after reaching EOF. You would be better off fixing that, by checking for eof() after reading but before pushing the character; or by reading in a less error-prone (and more efficient) manner, such as:
std::copy(std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>(),
std::back_inserter(data));
Also, I don't see any reason to copy the vector into a string. vector can also be converted to an asio::buffer.
Your way of reading the file is incorrect for a start.
Not simply that reading one character at a time isn't a great idea, but the loop is wrong. You could use istreambuf_iterator<char> to input or read() with a number of characters with gcount() determining when the read is complete.