GitHub Viewer
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "StarWarsData.h"
#include "graphqlservice/JSONResponse.h"
#ifdef _MSC_VER
#include
#endif // _MSC_VER
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std::literals;
using namespace graphql;
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = boost::asio::ip::tcp;
using tcp_stream = typename beast::tcp_stream::rebind_executor<
net::use_awaitable_t::executor_with_default>::other;
constexpr net::string_view c_host { "127.0.0.1" };
constexpr unsigned short c_port = 8080;
constexpr beast::string_view c_target { "/graphql" };
// Based on:
// https://www.boost.org/doc/libs/1_82_0/libs/beast/example/http/server/awaitable/http_server_awaitable.cpp
int main()
{
auto service = star_wars::GetService();
std::cout net::awaitable {
// This buffer is required to persist across reads
beast::flat_buffer buffer;
bool keepAlive = false;
// This lambda is used to send messages
try
{
do
{
// Set the timeout.
stream.expires_after(std::chrono::seconds(30));
// Read a request
http::request req;
co_await http::async_read(stream, buffer, req);
// Handle the request
http::response msg;
try
{
if (req.method() != http::verb::post
|| req.target() != c_target)
{
throw std::runtime_error(
"Only POST requests to /graphql are supported.");
}
else
{
std::ostringstream oss;
oss second.type() != response::Type::String)
{
throw std::runtime_error("Invalid request!");
}
peg::ast query;
query = peg::parseString(
queryItr->second.get());
if (!query.root)
{
throw std::runtime_error("Unknown error!");
}
const auto operationNameItr =
payload.find("operationName"sv);
const auto operationName =
(operationNameItr != payload.end()
&& operationNameItr->second.type()
== response::Type::String)
? std::string_view { operationNameItr->second
.get() }
: std::string_view {};
const auto variablesItr = payload.find("variables"sv);
auto variables = (variablesItr != payload.end()
&& variablesItr->second.type()
== response::Type::String)
? response::parseJSON(
variablesItr->second.get())
: response::Value {};
msg = http::response { http::status::ok,
req.version() };
msg.set(http::field::server, BOOST_BEAST_VERSION_STRING);
msg.set(http::field::content_type, "application/json");
msg.keep_alive(req.keep_alive());
msg.body() = response::toJSON(
service
->resolve(
{ query, operationName, std::move(variables) })
.get());
msg.prepare_payload();
}
}
catch (const std::runtime_error& ex)
{
msg = http::response {
http::status::bad_request,
req.version()
};
msg.set(http::field::server, BOOST_BEAST_VERSION_STRING);
msg.set(http::field::content_type, "text/plain");
msg.keep_alive(req.keep_alive());
msg.body() = "Error: "s + ex.what();
msg.prepare_payload();
}
// Determine if we should close the connection
keepAlive = msg.keep_alive();
// Send the response
co_await beast::async_write(stream,
http::message_generator { std::move(msg) },
net::use_awaitable);
} while (keepAlive);
}
catch (boost::system::system_error& se)
{
if (se.code() != http::error::end_of_stream)
throw;
}
// Send a TCP shutdown
beast::error_code ec;
stream.socket().shutdown(tcp::socket::shutdown_send, ec);
// At this point the connection is closed gracefully
// we ignore the error because the client might have
// dropped the connection already.
}(tcp_stream(co_await acceptor.async_accept()), service),
[](std::exception_ptr exp) {
if (exp)
{
try
{
std::rethrow_exception(exp);
}
catch (const std::exception& ex)
{
std::cerr