Poco C++ Net Post Request is handled twice - c++

I have a strange issue when sending a Post Request to the Poco HTTP Server.
This is my example Code:
#include <iostream>
#include <Poco/Util/ServerApplication.h>
#include <Poco/Runnable.h>
#include <Poco/Random.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Net/HTTPRequestHandlerFactory.h>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Environment.h>
#include <Poco/Net/ServerSocket.h>
#include <Poco/Net/HTTPServer.h>
class ErrorEndpoint : public Poco::Net::HTTPRequestHandler {
public:
ErrorEndpoint() {}
void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) {
response.setStatus(status_);
std::string answer{ R"({"error":"failed"})" };
response.setContentLength(answer.length());
response.setContentType("application/json");
auto& istream = response.send();
istream << answer;
}
private:
std::string errorMessage_{};
Poco::Net::HTTPResponse::HTTPStatus status_{};
};
class MyRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory {
public:
MyRequestHandlerFactory() {}
Poco::Net::HTTPRequestHandler*
createRequestHandler(const Poco::Net::HTTPServerRequest& request) override {
std::cout << request.getURI() << '\n';
return new ErrorEndpoint;
}
};
class MetricsSampleApp : public Poco::Util::ServerApplication {
protected:
void initialize(Poco::Util::Application& app) override {
loadConfiguration();
Poco::Util::ServerApplication::initialize(app);
}
int main(const std::vector<std::string>& args) override {
std::string ver = Poco::format("%d.%d.%d", static_cast<int>(Poco::Environment::libraryVersion() >> 24),
static_cast<int>((Poco::Environment::libraryVersion() >> 16) & 0xFF),
static_cast<int>((Poco::Environment::libraryVersion() >> 8) & 0xFF));
std::cout << ver << '\n';
Poco::UInt16 port = 8080;
Poco::Net::ServerSocket svs(port);
Poco::Net::HTTPServer srv(new MyRequestHandlerFactory(), svs, new Poco::Net::HTTPServerParams());
srv.start();
waitForTerminationRequest();
srv.stop();
return EXIT_OK;
}
};
POCO_SERVER_MAIN(MetricsSampleApp)
If i'm now running the following command:
Invoke-RestMethod -Uri "http://localhost:8080/dummy" -H #{"Content-Type" = "application/json"} -Method Post -Body '{"format": "error","node id": "1"}'
i get the following output:
1.12.3
/dummy
"error","node
The first line is the Poco Version which is printed after the start of the example Program.
The second is the URI to which i send the POST Request.
The third is printed together with the second and contains parts of the POST Body.
I'm using Poco in Version 1.12.3 on Windows.
Changing Invoke-RestMethod with curl leads to the excat same error.

Related

Serving static files in uWebSockets HTTP server (C++)

I am setting up an HTTP server in C++ using the uWebSockets library and I would like to add a middleware to serve static files, similar to what app.use(express.static(path.join(__dirname, 'public'))); does in Express.js.
The static files reside in the public folder. The middleware should make the server load files under the path http://localhost:8087/css/bootstrap.min.css, not http://localhost:8087/public/css/bootstrap.min.css, thus re-routing root to public.
How could one do this in C++ using the uWebSockets library? I already inspected the uWS::App struct, however I find there nothing related to the path or to serving static files.
Here is an example an HTTP server:
#include <uWebSockets/App.h>
#include <rapidjson/rapidjson.h>
#include "util/AsyncFileReader.h"
#include "util/AsyncFileStreamer.h"
#include <iostream>
#include <string>
void get_home(uWS::HttpResponse<false> *res, uWS::HttpRequest *req) {
res->writeHeader("Content-Type", "text/html; charset=utf8");
// res->writeStatus(uWS::HTTP_200_OK);
// res->end("Hello! This is <b>Sergei's C++ web server</b>.");
AsyncFileReader page_contents("./public/home.html");
res->end(page_contents.peek(0));
}
int main() {
int port{8087};
// HTTP
uWS::App app = uWS::App();
app.get("/", get_home);
app.listen(port, [&port](auto *token) {
if (token) {
std::cout << "Listening on port " << port << std::endl;
}
})
.run();
return 0;
}
There is an example with this exactly
I eventually ended up adding a directory watch and updating the html files if saved (a few changes in codebase) but i guess thats a different thing
#include "helpers/AsyncFileReader.h"
#include "helpers/AsyncFileStreamer.h"
#include "helpers/Middleware.h"
AsyncFileStreamer asyncFileStreamer("htmls"); // htmls is a relative folder path to static files
app.get("/*", gethome); // note the *
void get_home(auto *res, auto *req) {
//void get_home(uWS::HttpResponse<false> *res, uWS::HttpRequest *req) {
serveFile(res, req); // essentially res->writeStatus(uWS::HTTP_200_OK);
asyncFileStreamer.streamFile(res, req->getUrl());
}
Please note serveFile() function also needs to take care of different Content-Type header setting for images
example mentioned:
https://github.com/uNetworking/uWebSockets/blob/master/examples/HttpServer.cpp
To expand the answer of #amit.user105387, here is how a working solution could look like:
#include <uWebSockets/App.h>
#include <rapidjson/rapidjson.h>
#include "util/AsyncFileReader.h"
#include "util/AsyncFileStreamer.h"
#include "util/Middleware.h"
#include <iostream>
#include <string>
void get_home(uWS::HttpResponse<false> *res, uWS::HttpRequest *req) {
// res->writeHeader("Alt-Svc", "h2=\"localhost:8087\"public\"");
res->writeHeader("Content-Type", "text/html; charset=utf8");
AsyncFileReader page_contents("./public/html/home.html");
res->end(page_contents.peek(0));
}
void PrintCachedFiles(std::map<std::string_view, AsyncFileReader *> dict) {
for (auto it = dict.begin(); it != dict.end(); it++) {
std::cout << it->first << std::endl;
}
}
int main() {
const int port{8087};
const std::string public_folder{"./public"};
AsyncFileStreamer public_files("./public");
// HTTP
uWS::App app = uWS::App();
app.get("/", [&public_files](auto res, auto req){
std::cout << "root folder = "<< public_files.root << std::endl;
PrintCachedFiles(public_files.asyncFileReaders);
res->writeHeader("Content-Type", "text/html; charset=utf8");
public_files.streamFile(res, "/html/home.html");
res->writeStatus(uWS::HTTP_200_OK)->end();
});
app.get("/css/:path", [&public_files](auto res, auto req){
public_files.streamFile(res, req->getUrl());
res->writeStatus(uWS::HTTP_200_OK)->end();
});
app.get("/js/:path", [&public_files](auto res, auto req){
public_files.streamFile(res, req->getUrl());
res->writeStatus(uWS::HTTP_200_OK)->end();
});
app.get("/img/:path/*", [&public_files](auto res, auto req){
serveFile(res, req);
public_files.streamFile(res, req->getUrl());
res->writeStatus(uWS::HTTP_200_OK)->end();
});
app.get("/*", [&public_files](auto res, auto req){
res->writeStatus(uWS::HTTP_200_OK)->end("404: Page not found.");
});
app.listen(port, [&port](auto *token) {
if (token) {
std::cout << "Listening on port " << port << std::endl;
}
})
.run();
return 0;
}

vk api on boost c++ doesn't work correctly

I wrote a some code that should send GET request and get response.
It works for ip-api.com and returns me json file.
But for api.vk.com it returns html as that:
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>kittenx</center>
</body>
</html>
The most interesting thing is that the program returns the correct link, after opening which the desired GET request will be executed.
main.cpp:
#include <iostream>
#include "client.hpp"
#include "json.hpp"
std::string get_token(const std::string &);
int main()
{
std::string token = get_token("data/token1");
std::string query = "https://api.vk.com/method/groups.getMembers?access_token=" + token + "&v=5.13&group_id=klubauto";
std::cout << query << "\n\n\n";
Client client(url);
client.send_request(query);
std::string response = client.get_response();
std::cout << response << std::endl;
return 0;
}
client.hpp:
#pragma once
#include <string>
#include <boost/beast.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
namespace http = boost::beast::http;
class Client
{
public:
Client();
Client(const std::string &api);
~Client();
void send_request(const std::string &arguments);
std::string get_response();
private:
boost::asio::io_context io;
boost::asio::ip::tcp::resolver resolver;
boost::asio::ip::tcp::socket socket;
std::string url;
};
client.cpp
#include "client.hpp"
/*
* Constructors
*/
Client::Client() : url("google.com"), resolver(io), socket(io)
{
boost::asio::connect(socket, resolver.resolve(url, "80"));
}
Client::Client(const std::string &api) : url(api), resolver(io), socket(io)
{
boost::asio::connect(socket, resolver.resolve(url, "80"));
}
/*
* Destructor
*/
Client::~Client()
{
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
}
/*
* Send request
*/
void Client::send_request(const std::string &arguments)
{
http::request<http::string_body> req(http::verb::get, arguments, 11);
req.set(http::field::host, url);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::write(socket, req);
}
/*
* Get response
*/
std::string Client::get_response()
{
std::string response;
{
boost::beast::flat_buffer buffer;
http::response<http::dynamic_body> res;
http::read(socket, buffer, res);
response = boost::beast::buffers_to_string(res.body().data());
}
return response;
}
I would like to receive a json file in the response variable, please tell me how to achieve this?
Like I commented, that's how HTTP works: Servers can redirect to a better/new location.
I assume the prime reason for this is because your connection is not HTTPS, and that's what the end-points require. So, fix that first.
Next, your query includes the base URL, which is another error.
Live Demo
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <string>
namespace http = boost::beast::http;
namespace ssl = boost::asio::ssl;
using boost::asio::ip::tcp;
class Client {
public:
Client(const std::string& host = "google.com") : _host(host) {
_ctx.set_default_verify_paths();
connect(_socket.lowest_layer(),
tcp::resolver{_io}.resolve(_host, "https"));
_socket.handshake(ssl::stream_base::client);
}
void send_request(const std::string& query)
{
http::request<http::string_body> req(http::verb::get, query, 11);
req.set(http::field::host, _host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::write(_socket, req);
}
std::string get_response() {
http::response<http::string_body> res;
boost::beast::flat_buffer buffer;
http::read(_socket, buffer, res);
return std::move(res.body());
}
private:
boost::asio::io_context _io;
ssl::context _ctx{ssl::context::sslv23_client};
ssl::stream<tcp::socket> _socket{_io, _ctx};
std::string _host;
};
#include <iostream>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for COLIRU header-only
namespace json = boost::json;
std::string get_token(const std::string&) { return ""; }
int main()
{
Client client("api.vk.com");
client.send_request("/method/groups.getMembers?access_token=" +
get_token("data/token1") + "&v=5.13&group_id=klubauto");
std::cout << json::parse(client.get_response()) << std::endl;
}
Coliru doesn't allow public network access:
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
what(): resolve: Service not found
But on my machine it correctly says:
{"error":{"error_code":5,"error_msg":"User authorization failed: no access_token passed.","request_params":[{"key":"v","value":"5.13"},{"key":"group_id","value":"klubauto"},{"key":"method","value":"groups.getMembers"},{"key":"oauth","value":"1"}]}}
Note I included quite a number of simplifications along the way.

Unix domain sockets with Poco::Net::TCPServer

I created a TCPServer using the Poco::Net::TCPServer framework, that uses a unix domain socket and it seems to work. However if I close the server and start it again I get this error:
Net Exception: Address already in use: /tmp/app.SocketTest
What is the right way to deal with this error?
Are the TCPServerConnections, TCPServerConnectionFactory and sockets
automatically cleaned-up or do I need to implement their destructors or destroy them manually?
EDIT
I have two questions here. The first is answered by using remove() on the socket-file. The other question is, if the clean-up in the Poco::Net::TCPServer framework is automatic or if it has to be manually implemented to prevent memory-leak?
Here is the code for the TCPServer:
#include "Poco/Util/ServerApplication.h"
#include "Poco/Net/TCPServer.h"
#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/TCPServerConnectionFactory.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/File.h"
#include <fstream>
#include <iostream>
using Poco::Net::ServerSocket;
using Poco::Net::StreamSocket;
using Poco::Net::TCPServer;
using Poco::Net::TCPServerConnection;
using Poco::Net::TCPServerConnectionFactory;
using Poco::Net::SocketAddress;
using Poco::Util::ServerApplication;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
class UnixSocketServerConnection: public TCPServerConnection
/// This class handles all client connections.
{
public:
UnixSocketServerConnection(const StreamSocket& s):
TCPServerConnection(s)
{
}
void run()
{
try
{
/*char buffer[1024];
int n = 1;
while (n > 0)
{
n = socket().receiveBytes(buffer, sizeof(buffer));
EchoBack(buffer);
}*/
std::string message;
char buffer[1024];
int n = 1;
while (n > 0)
{
n = socket().receiveBytes(buffer, sizeof(buffer));
buffer[n] = '\0';
message += buffer;
if(sizeof(buffer) > n && message != "")
{
EchoBack(message);
message = "";
}
}
}
catch (Poco::Exception& exc)
{
std::cerr << "Error: " << exc.displayText() << std::endl;
}
std::cout << "Disconnected." << std::endl;
}
private:
inline void EchoBack(std::string message)
{
std::cout << "Message: " << message << std::endl;
socket().sendBytes(message.data(), message.length());
}
};
class UnixSocketServerConnectionFactory: public TCPServerConnectionFactory
/// A factory
{
public:
UnixSocketServerConnectionFactory()
{
}
TCPServerConnection* createConnection(const StreamSocket& socket)
{
std::cout << "Got new connection." << std::endl;
return new UnixSocketServerConnection(socket);
}
private:
};
class UnixSocketServer: public Poco::Util::ServerApplication
/// The main application class.
{
public:
UnixSocketServer(): _helpRequested(false)
{
}
~UnixSocketServer()
{
}
protected:
void initialize(Application& self)
{
loadConfiguration(); // load default configuration files, if present
ServerApplication::initialize(self);
}
void uninitialize()
{
ServerApplication::uninitialize();
}
void defineOptions(OptionSet& options)
{
ServerApplication::defineOptions(options);
options.addOption(
Option("help", "h", "display help information on command line arguments")
.required(false)
.repeatable(false));
}
void handleOption(const std::string& name, const std::string& value)
{
ServerApplication::handleOption(name, value);
if (name == "help")
_helpRequested = true;
}
void displayHelp()
{
HelpFormatter helpFormatter(options());
helpFormatter.setCommand(commandName());
helpFormatter.setUsage("OPTIONS");
helpFormatter.setHeader("A server application to test unix domain sockets.");
helpFormatter.format(std::cout);
}
int main(const std::vector<std::string>& args)
{
if (_helpRequested)
{
displayHelp();
}
else
{
// set-up unix domain socket
Poco::File socketFile("/tmp/app.SocketTest");
SocketAddress unixSocket(SocketAddress::UNIX_LOCAL, socketFile.path());
// set-up a server socket
ServerSocket svs(unixSocket);
// set-up a TCPServer instance
TCPServer srv(new UnixSocketServerConnectionFactory, svs);
// start the TCPServer
srv.start();
// wait for CTRL-C or kill
waitForTerminationRequest();
// Stop the TCPServer
srv.stop();
}
return Application::EXIT_OK;
}
private:
bool _helpRequested;
};
int main(int argc, char **argv) {
UnixSocketServer app;
return app.run(argc, argv);
}
You don't need to worry about deallocating memory. All is done by library.
TCPServer srv(new UnixSocketServerConnectionFactory, svs);
^^^
Instance of UnixSocketServerConnectionFactory is deleted by TCPServer according to poco ref
The server takes ownership of the TCPServerConnectionFactory and
deletes it when it's no longer needed.
TCPServerConnection* createConnection(const StreamSocket& socket)
{
std::cout << "Got new connection." << std::endl;
return new UnixSocketServerConnection(socket);
^^^
}
instances of UnixSocketServerConnection are deleted by Poco library code as well:
As soon as the run() method returns, the server connection object is
destroyed and the connection is automatically closed.
The problem with Poco::File was that the destructor of Poco::File cannot remove file, you have to do it explicitly by remove method.

RPC failure code 14 in our simple GRPC example program

We've made good progress in getting GRPC running under RHEL 7.
Our application has one rather complicated structure with three levels of nesting with the outer level implementing a "oneof" keyword.
We find that all our other structures run fine, but this one gives us an RPC failure with code=14.
We've simplified this part of the application as much as possible so it can hopefully be recompiled and run easily.
Here's the .proto file, updated to accommodate Uli's question:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.debug";
option java_outer_classname = "DebugProto";
option objc_class_prefix = "DEBUG";
package DEBUGpackage;
service DEBUGservice {
rpc DEBUG_val_container_get (input_int32_request) returns (outer_container) {}
}
message input_int32_request {
int32 ival = 1;
}
message inner_container {
repeated uint32 val_array = 1;
}
message middle_container {
inner_container vac = 1;
}
message other_container {
int32 other_val = 1;
}
message outer_container {
oneof reply {
middle_container r1 = 1;
other_container r2 = 2;
}
}
(Please note that the java lines in this prototype code are just in there because they are in the GRPC website examples. Our code is entirely C++, with no java. Don't know if that means we can do without some of these "option java..." lines).
Here's our client source code:
#include <iostream>
#include <memory>
#include <string>
#include <grpc++/grpc++.h>
#include <grpc/support/log.h>
#include <thread>
#include <unistd.h>
#include "debug.grpc.pb.h"
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using DEBUGpackage::input_int32_request;
using DEBUGpackage::inner_container;
using DEBUGpackage::middle_container;
using DEBUGpackage::outer_container;
using DEBUGpackage::DEBUGservice;
class DEBUGClient {
public:
explicit DEBUGClient(std::shared_ptr<Channel> channel)
: stub_(DEBUGservice::NewStub(channel)) {}
void DEBUG_val_container_get() {
std::cout << "in DEBUG_val_container_get" << std::endl;
// Data we are sending to the server
input_int32_request val;
val.set_ival(0);
AsyncClientCall* call = new AsyncClientCall;
call->response_reader = stub_->AsyncDEBUG_val_container_get(&call->context, val, &cq_);
call->response_reader->Finish(&call->reply_, &call->status, (void*)call);
}
void AsyncCompleteRpc() {
void* got_tag;
bool ok = false;
while (cq_.Next(&got_tag, &ok)) {
AsyncClientCall* call = static_cast<AsyncClientCall*>(got_tag);
GPR_ASSERT(ok);
if (call->status.ok()) {
if (call->reply_.has_r1()) {
std::cout << call << " DEBUG received: "
<< call->reply_.r1().vac().val_array(0) << std::endl;
}
}
else {
std::cout << call << " RPC failed" << std::endl;
std::cout << " RPC failure code = " << call->status.error_code() << std::endl;
std::cout << " RPC failure message = " << call->status.error_message() << std::endl;
}
delete call;
}
}
private:
struct AsyncClientCall {
outer_container reply_;
ClientContext context;
Status status;
std::unique_ptr<ClientAsyncResponseReader<outer_container>> response_reader;
};
std::unique_ptr<DEBUGservice::Stub> stub_;
CompletionQueue cq_;
};
int main(int argc, char** argv) {
DEBUGClient DEBUG0(grpc::CreateChannel("172.16.17.46:50050", grpc::InsecureChannelCredentials()));
std::thread thread0_ = std::thread(&DEBUGClient::AsyncCompleteRpc, &DEBUG0);
DEBUG0.DEBUG_val_container_get();
sleep(1);
std::cout << "Press control-c to quit" << std::endl << std::endl;
thread0_.join(); //blocks forever
return 0;
}
And, here's our server source code:
#include <memory>
#include <iostream>
#include <string>
#include <thread>
#include <grpc++/grpc++.h>
#include <grpc/support/log.h>
#include "debug.grpc.pb.h"
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCompletionQueue;
using grpc::Status;
using DEBUGpackage::inner_container;
using DEBUGpackage::input_int32_request;
using DEBUGpackage::middle_container;
using DEBUGpackage::outer_container;
using DEBUGpackage::DEBUGservice;
std::string save_server_address;
class ServerImpl final {
public:
~ServerImpl() {
server_->Shutdown();
cq_->Shutdown();
}
void Run() {
std::string server_address("0.0.0.0:50050");
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service_);
cq_ = builder.AddCompletionQueue();
server_ = builder.BuildAndStart();
std::cout << "Server listening on " << server_address << std::endl;
save_server_address = server_address;
HandleRpcs();
}
private:
class CallData {
public:
virtual void Proceed() = 0;
};
class DebugGetCallData final : public CallData{
public:
DebugGetCallData(DEBUGservice::AsyncService* service, ServerCompletionQueue* cq)
: service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) {
Proceed();
}
void Proceed() {
if (status_ == CREATE) {
status_ = PROCESS;
service_->RequestDEBUG_val_container_get(&ctx_, &request_, &responder_, cq_, cq_, this);
} else if (status_ == PROCESS) {
new DebugGetCallData(service_, cq_);
char *portchar;
portchar = (char *) save_server_address.c_str();
long cq_addr = (long) cq_;
int cq_addr32 = (int) (cq_addr & 0xfffffff);
srand(cq_addr32);
fprintf(stderr, "%s task started\n", portchar); fflush(stderr);
unsigned int return_val = 10;
inner_container ic;
ic.add_val_array(return_val);
middle_container reply_temp;
reply_temp.set_allocated_vac(&ic);
reply_.set_allocated_r1(&reply_temp);
fprintf(stderr, "%s %s task done\n", portchar, "val_container_get"); fflush(stderr);
status_ = FINISH;
responder_.Finish(reply_, Status::OK, this);
} else {
GPR_ASSERT(status_ == FINISH);
}
}
private:
DEBUGservice::AsyncService* service_;
ServerCompletionQueue* cq_;
ServerContext ctx_;
input_int32_request request_;
outer_container reply_;
ServerAsyncResponseWriter<outer_container> responder_;
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_;
};
void HandleRpcs() {
new DebugGetCallData(&service_, cq_.get());
void* tag;
bool ok;
while (true) {
GPR_ASSERT(cq_->Next(&tag, &ok));
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Proceed();
}
}
std::unique_ptr<ServerCompletionQueue> cq_;
DEBUGservice::AsyncService service_;
std::unique_ptr<Server> server_;
};
int main() {
ServerImpl server;
server.Run();
return 0;
}
The output when I run it looks like this:
[fossum#netsres46 debug]$ DEBUG_client2
in DEBUG_val_container_get
0xb73ff0 RPC failed
RPC failure code = 14
RPC failure message = Endpoint read failed
Press control-c to quit
We ran the server under gdb, and found a place in the generated
file "debug.pb.cc" where if we just comment out one line, it all starts working.
Here's the pertinent piece of the generated file "debug.pb.cc":
middle_container::~middle_container() {
// ##protoc_insertion_point(destructor:DEBUGpackage.middle_container)
SharedDtor();
}
void middle_container::SharedDtor() {
if (this != internal_default_instance()) {
delete vac_; // comment out this one line, to make the problem go away
}
}
The "delete vac_" line appears to be an attempt to delete storage that either has already been deleted, or is about to be deleted somewhere else. Please, can someone look into this? [The files below are still the files we use to generate this code, and to debug the problem to this point]
I have no idea whether I've uncovered a bug in GRPC, or whether I've coded something wrong.
The issue is that you are allocated middle_container reply_tmp on the stack in your server. As a result it gets destructed as soon as you pass out of the scope. At that time, you have called Finish but not yet waited for its result. Since this is an async server, the data must remain alive until you've received the tag back for it. This is why manually editing your destructor works in your case; you're basically nullifying the destructor (and leaking memory as a result).

Cannot get simple boost networking example to work, merely initializing server causes errors immediately

I'm running Xubuntu 14.04 and Boost 1.54.0. I've modified the chapter 4 example from this book to be able to send arbitrary strings between client and server, but now the program just fails immediately.
This is the server:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <string>
using namespace boost::asio;
io_service service;
size_t read_complete(
char* buff, const boost::system::error_code& err, size_t bytes)
{
if (err) {
return 0;
}
bool found = std::find(buff, buff + bytes, '\n') < buff + bytes;
return found? 0 : 1;
}
void handle_connections() {
ip::tcp::acceptor acceptor(
service, ip::tcp::endpoint(ip::tcp::v4(), 8001));
std::string buff;
while (true) {
ip::tcp::socket sock(service);
acceptor.accept(sock);
int bytes = read(sock, buffer(buff.c_str()), bind(read_complete, buff, _1, _2));
std::string msg(buff, bytes);
sock.write_some(buffer(msg));
sock.close();
}
}
int main(int argc, char* argv[]) {
handle_connections();
}
It's copied directly from the book and has been verified to work correctly.
This is the minimal client necessary to reproduce the problem:
#include <memory>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
using namespace boost::asio;
class Client
{
public:
Client(const std::string& server, const std::string& port):
server(server), port(port)
{
service = std::unique_ptr<io_service>(new io_service);
endpoint = std::unique_ptr<ip::tcp::endpoint>(new ip::tcp::endpoint(
ip::address::from_string(server), atoi(port.c_str())));
boostSocket = std::unique_ptr<ip::tcp::socket>(
new ip::tcp::socket(*service));
std::cout << std::boolalpha << boostSocket.is_open(); // "false"
}
private:
std::string server;
std::string port;
std::unique_ptr<io_service> service;
std::unique_ptr<ip::tcp::socket> boostSocket;
std::unique_ptr<ip::tcp::endpoint> endpoint;
};
int main()
{
Client client("127.0.0.1", "8001");
return 0;
}
Running this program tells me that the socket is never opened. So I dived into GDB and found this. For convenience, I have omitted the (gdb) s debugging commands I used between every set of lines.
Client::Client (this=0x7fffffffd7f8, server="127.0.0.1", port="8001")
at DIRECTORY WHERE I STORED THE CPP FILE:14
14 service = std::unique_ptr<io_service>(new io_service);
boost::asio::io_service::io_service (this=0x622130) at /usr/include/boost/asio/impl/io_service.ipp:41
41 {
boost::asio::detail::noncopyable::noncopyable (this=0x622130) at /usr/include/boost/asio/detail/noncopyable.hpp:29
29 noncopyable() {}
boost::asio::io_service::io_service (this=0x622130) at /usr/include/boost/asio/impl/io_service.ipp:39
39 (std::numeric_limits<std::size_t>::max)())),
std::numeric_limits<unsigned long>::max () at /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/limits:1196
1196 max() _GLIBCXX_USE_NOEXCEPT { return __LONG_MAX__ * 2UL + 1; }
boost::asio::io_service::io_service (this=0x622130) at /usr/include/boost/asio/impl/io_service.ipp:41
41 {
boost::asio::detail::service_registry::service_registry<boost::asio::detail::task_io_service, unsigned long> (this=0x622150, o=..., arg=18446744073709551615)
at /usr/include/boost/asio/detail/impl/service_registry.hpp:29
29 {
boost::asio::detail::noncopyable::noncopyable (this=0x622150) at /usr/include/boost/asio/detail/noncopyable.hpp:29
29 noncopyable() {}
boost::asio::detail::posix_mutex::posix_mutex (this=0x622158) at /usr/include/boost/asio/detail/impl/posix_mutex.ipp:33
33 {
boost::asio::detail::noncopyable::noncopyable (this=0x622158) at /usr/include/boost/asio/detail/noncopyable.hpp:29
29 noncopyable() {}
boost::asio::detail::posix_mutex::posix_mutex (this=0x622158) at /usr/include/boost/asio/detail/impl/posix_mutex.ipp:34
34 int error = ::pthread_mutex_init(&mutex_, 0);
The relevant lines are the first (because it's part of my code) and the last (it goes straight to error handling). Subsequent GDB commands only show that it delves further into Boost's error handling systems. Why? All I'm doing is creating an io_service.
I have to fix errors in both the client and the server programs in order to even compile.
Here's the fixed server:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <string>
using namespace boost::asio;
io_service service;
size_t read_complete(std::vector<char>const& buff, const boost::system::error_code &err, size_t bytes) {
if (err) {
return 0;
}
auto last = buff.begin()+bytes;
bool found = (last != std::find(buff.begin(), last, '\n'));
return found;
}
void handle_connections() {
ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(), 8001));
std::vector<char> buff(1024);
while (true) {
ip::tcp::socket sock(service);
acceptor.accept(sock);
int bytes = read(sock, buffer(buff), bind(read_complete, boost::ref(buff), _1, _2));
sock.send(buffer(buff, bytes));
sock.close();
}
}
int main() { handle_connections(); }
There were quite a number of changes, most notably the one fixing the buffer.
Here's the fixed client. I junked all the superstitious use of new:
#include <memory>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
using namespace boost::asio;
class Client {
public:
Client(const std::string &server, const std::string &port)
: server(server),
port(port),
service(),
endpoint(ip::address::from_string(server), atoi(port.c_str())),
boostSocket(service)
{
std::cout << std::boolalpha << boostSocket.is_open() << "\n"; // of course, "false"
}
private:
std::string server;
std::string port;
io_service service;
ip::tcp::endpoint endpoint;
ip::tcp::socket boostSocket;
};
int main() {
Client client("127.0.0.1", "8001");
return 0;
}
Notes:
you should likely use the resolver to resolve address/port instead of just hardcoding the conversions (it might not be IP or port numbers)
you never connect, so of course the socket is not open
boostSocket.connect(endpoint);
std::cout << std::boolalpha << boostSocket.is_open() << "\n"; // of connected, "true"
/*
*std::string message("hellow world\n\n\n\n\n");
*boostSocket.send(buffer(message));
*streambuf sb;
*read(boostSocket, sb, boost::asio::transfer_all());
*std::cout << "RESPONSE: '" << &sb << "'\n";
*/
Happy debugging