Skip to content

Instantly share code, notes, and snippets.

@tvhung83
Created August 29, 2018 05:25
Show Gist options
  • Save tvhung83/851db220f26913b597327ed72e65af43 to your computer and use it in GitHub Desktop.
Save tvhung83/851db220f26913b597327ed72e65af43 to your computer and use it in GitHub Desktop.
C++ REST + Postgres
//
// Created by Robert Nguyen on 10/28/17.
//
#include <algorithm>
#include <pistache/http.h>
#include <pistache/router.h>
#include <pistache/endpoint.h>
#include <pistache/serializer/rapidjson.h>
#include <tao/postgres/connection_pool.hpp>
using namespace std;
using namespace Pistache;
using namespace rapidjson;
void printCookies(const Http::Request& req) {
auto cookies = req.cookies();
std::cout << "Cookies: [" << std::endl;
const std::string indent(4, ' ');
for (const auto& c: cookies) {
std::cout << indent << c.name << " = " << c.value << std::endl;
}
std::cout << "]" << std::endl;
}
class Movie {
public:
Movie(unsigned int id, unsigned int mid, const std::string &name, const std::string &title, const std::string &description,
const std::string &poster, const std::string &backdrop, const std::string &link, int episode, int sequence, int season_index,
int current_episode, const std::string &trailer) : id(id), mid(mid), name(name), title(title),
description(description), poster(poster), backdrop(backdrop),
link(link), episode(episode), sequence(sequence),
season_index(season_index), current_episode(current_episode),
trailer(trailer) {}
virtual ~Movie() {
}
template <typename Writer>
void Serialize(Writer& writer) const {
writer.StartObject();
writer.String("id"); writer.Uint(id);
writer.String("mid"); writer.Uint(mid);
writer.String("name"); write(writer, name);
writer.String("title"); write(writer, title);
writer.String("description"); write(writer, description);
writer.String("poster"); write(writer, poster);
writer.String("backdrop"); write(writer, backdrop);
writer.String("link"); write(writer, link);
writer.String("episode"); writer.Int(episode);
writer.String("sequence"); writer.Int(sequence);
writer.String("season_index"); writer.Int(season_index);
writer.String("current_episode"); writer.Int(current_episode);
writer.String("trailer"); write(writer, trailer);
writer.EndObject();
}
template <typename Writer>
void write(Writer& writer, string value) const {
#if RAPIDJSON_HAS_STDSTRING
writer.String(value);
#else
writer.String(value.c_str(), static_cast<SizeType>(value.length()));
#endif
}
private:
unsigned id;
unsigned mid;
std::string name;
std::string title;
std::string description;
std::string poster;
std::string backdrop;
std::string link;
int episode;
int sequence;
int season_index;
int current_episode;
std::string trailer;
};
namespace Generic {
void handleReady(const Rest::Request&, Http::ResponseWriter response) {
StringBuffer JSONStrBuffer;
Writer<StringBuffer> writer(JSONStrBuffer);
writer.StartArray();
const auto pool = tao::postgres::connection_pool::create( "user=foo password=bar dbname=fim" );
const auto conn = pool->connection();
const auto res = conn->execute( "SELECT * FROM hdviet.movies" );
for( const auto& row : res ) {
const Movie movie(
row[ 0 ].as< unsigned >(),
row[ 1 ].as< unsigned >(),
row[ 2 ].as< string >(),
row[ 3 ].as< string >(),
row[ 4 ].as< string >(),
row[ 5 ].as< string >(),
row[ 6 ].as< string >(),
row[ 7 ].as< string >(),
row[ 8 ].as< int >(),
row[ 9 ].as< int >(),
row[ 10 ].as< int >(),
row[ 11 ].as< int >(),
row[ 12 ].as< string >()
);
movie.Serialize(writer);
}
writer.EndArray();
auto mediaType = MIME(Application, Json);
mediaType.setParam("charset", "utf-8");
response.headers()
.add<Pistache::Http::Header::ContentType>(mediaType);
response.send(Http::Code::Ok, JSONStrBuffer.GetString());
}
}
class StatsEndpoint {
public:
StatsEndpoint(Address addr)
: httpEndpoint(std::make_shared<Http::Endpoint>(addr))
{ }
void init(size_t thr = 2) {
auto opts = Http::Endpoint::options()
.threads(thr)
.flags(Tcp::Options::InstallSignalHandler);
httpEndpoint->init(opts);
setupRoutes();
}
void start() {
httpEndpoint->setHandler(router.handler());
httpEndpoint->serve();
}
void shutdown() {
httpEndpoint->shutdown();
}
private:
void setupRoutes() {
using namespace Rest;
Routes::Post(router, "/record/:name/:value?", Routes::bind(&StatsEndpoint::doRecordMetric, this));
Routes::Get(router, "/value/:name", Routes::bind(&StatsEndpoint::doGetMetric, this));
Routes::Get(router, "/api/ready", Routes::bind(&Generic::handleReady));
Routes::Get(router, "/auth", Routes::bind(&StatsEndpoint::doAuth, this));
}
void doRecordMetric(const Rest::Request& request, Http::ResponseWriter response) {
auto name = request.param(":name").as<std::string>();
Guard guard(metricsLock);
auto it = std::find_if(metrics.begin(), metrics.end(), [&](const Metric& metric) {
return metric.name() == name;
});
int val = 1;
if (request.hasParam(":value")) {
auto value = request.param(":value");
val = value.as<int>();
}
if (it == std::end(metrics)) {
metrics.push_back(Metric(std::move(name), val));
response.send(Http::Code::Created, std::to_string(val));
}
else {
auto &metric = *it;
metric.incr(val);
response.send(Http::Code::Ok, std::to_string(metric.value()));
}
}
void doGetMetric(const Rest::Request& request, Http::ResponseWriter response) {
auto name = request.param(":name").as<std::string>();
Guard guard(metricsLock);
auto it = std::find_if(metrics.begin(), metrics.end(), [&](const Metric& metric) {
return metric.name() == name;
});
if (it == std::end(metrics)) {
response.send(Http::Code::Not_Found, "Metric does not exist");
} else {
const auto& metric = *it;
response.send(Http::Code::Ok, std::to_string(metric.value()));
}
}
void doAuth(const Rest::Request& request, Http::ResponseWriter response) {
printCookies(request);
response.cookies()
.add(Http::Cookie("lang", "en-US"));
response.send(Http::Code::Ok);
}
class Metric {
public:
Metric(std::string name, int initialValue = 1)
: name_(std::move(name))
, value_(initialValue)
{ }
int incr(int n = 1) {
int old = value_;
value_ += n;
return old;
}
int value() const {
return value_;
}
std::string name() const {
return name_;
}
private:
std::string name_;
int value_;
};
typedef std::mutex Lock;
typedef std::lock_guard<Lock> Guard;
Lock metricsLock;
std::vector<Metric> metrics;
std::shared_ptr<Http::Endpoint> httpEndpoint;
Rest::Router router;
};
int main(int argc, char *argv[]) {
Port port(9080);
int thr = 2;
if (argc >= 2) {
port = std::stoi(argv[1]);
if (argc == 3)
thr = std::stoi(argv[2]);
}
Address addr(Ipv4::any(), port);
cout << "Cores = " << hardware_concurrency() << endl;
cout << "Using " << thr << " threads" << endl;
StatsEndpoint stats(addr);
stats.init(thr);
stats.start();
stats.shutdown();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment