Skip to content

Instantly share code, notes, and snippets.

@MikaelSmith
Last active February 23, 2016 23:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MikaelSmith/f255a807a39f4fd172f1 to your computer and use it in GitHub Desktop.
Save MikaelSmith/f255a807a39f4fd172f1 to your computer and use it in GitHub Desktop.
C++ SSL JSON Prettifier (Boost.asio)
#include <iostream>
#include <cstdint>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <rapidjson/reader.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/filewritestream.h>
using namespace std;
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using ssl_stream = ssl::stream<tcp::socket>;
struct InputStream
{
using Ch = char;
boost::asio::streambuf &response;
ssl_stream &sock;
size_t count = 0;
boost::system::error_code error;
InputStream(ssl_stream &s, boost::asio::streambuf &r) : sock(s), response(r) {}
void buffer() {
if (response.in_avail() <= 0) {
auto success = boost::asio::read(sock, response, boost::asio::transfer_at_least(1), error);
if (!success && error != boost::asio::error::eof)
throw boost::system::system_error(error);
}
}
Ch Peek() const {
if (error == boost::asio::error::eof)
return '\0';
const_cast<InputStream*>(this)->buffer();
return response.sgetc();
}
Ch Take() {
if (error == boost::asio::error::eof)
return '\0';
buffer();
++count;
return response.sbumpc();
}
size_t Tell() {
return count;
}
Ch* PutBegin() { throw runtime_error("called PutBegin"); }
void Put(Ch c) { throw runtime_error("called Put"); }
void Flush() { throw runtime_error("called Flush"); }
size_t PutEnd(Ch* begin) { throw runtime_error("called PutEnd"); }
};
int main(int argc, char const* argv[])
{
if (argc != 2) {
cout << "usage: pretty-json <url>" << endl;
return EXIT_SUCCESS;
}
string url = argv[1];
auto scheme_end = url.find("://");
string scheme = scheme_end != string::npos ? url.substr(0, scheme_end) : "http";
auto host_start = scheme_end != string::npos ? scheme_end+3 : 0;
auto host_end = url.find("/", host_start);
string host = url.substr(host_start, host_end-host_start);
string path = host_end != string::npos ? url.substr(host_end) : "/";
cout << "Retrieving " << scheme << "://" << host << path << endl;
try {
// Open a socket and connect it to the remote host.
boost::asio::io_service io_service;
ssl::context ctx(ssl::context::sslv23);
ssl_stream sock(io_service, ctx);
tcp::resolver resolver(io_service);
tcp::resolver::query query(host, scheme);
boost::asio::connect(sock.lowest_layer(), resolver.resolve(query));
sock.handshake(ssl_stream::client);
boost::asio::streambuf request;
ostream request_stream(&request);
request_stream << "GET " << path << " HTTP/1.1\r\n";
request_stream << "Host: " << host << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "User-Agent: pretty-json/1.0.0\r\n";
request_stream << "Connection: close\r\n\r\n";
boost::asio::write(sock, request);
boost::asio::streambuf response;
istream response_stream(&response);
string line;
boost::asio::read_until(sock, response, "\r\n\r\n");
while (getline(response_stream, line) && line != "\r")
cout << line << endl;
cout << endl;
#if 0
// TODO: check Content-Type and branch appropriately
// Read until EOF, writing data to output as we go.
boost::system::error_code error;
while (boost::asio::read(sock, response, boost::asio::transfer_at_least(1), error))
std::cout << &response;
if (error != boost::asio::error::eof)
throw boost::system::system_error(error);
#else
InputStream input {sock, response};
char buffer[BUFSIZ];
rapidjson::FileWriteStream output{ stdout, buffer, sizeof(buffer) };
rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer{ output };
rapidjson::Reader reader;
if (!reader.Parse<rapidjson::kParseValidateEncodingFlag>(input, writer)) {
cout << "error: response was not valid JSON." << endl;
return EXIT_FAILURE;
}
cout << endl;
#endif
return EXIT_SUCCESS;
} catch (exception const& ex) {
cout << "error: " << ex.what() << endl;
}
return EXIT_FAILURE;
}
@MikaelSmith
Copy link
Author

Compiled on Mac OS X with g++ -std=c++14 main.cc -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lcrypto -lssl -lboost_system -o pretty-json.

Testing performed on api.github.com via ./pretty-json https://api.github.com.

@MikaelSmith
Copy link
Author

http://stackoverflow.com/questions/3668128/how-to-create-a-boost-ssl-iostream is interesting as a way to create an ssl-based iostream. Wasn't useful for interacting with rapidjson, since they create their own stream concept.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment