From f9b6009b5750a27e030b8bcea3076d4205f0bc65 Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 25 Feb 2018 23:20:02 +0100 Subject: [PATCH] Enable access to HTTP headers --- CMakeLists.txt | 2 +- README.md | 2 +- src/examples/example8_rate_limiting.cpp | 53 +++++++++++++++++++++++++ src/http_sync.cpp | 13 +++++- src/mastodon-cpp.cpp | 15 +++++++ src/mastodon-cpp.hpp | 13 ++++++ 6 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/examples/example8_rate_limiting.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ce10416..fbafff3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.7) project (mastodon-cpp - VERSION 0.3.2 + VERSION 0.3.3 LANGUAGES CXX ) diff --git a/README.md b/README.md index 45b9159..904ebed 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ If you use a debug build, you get more verbose error messages. * [x] Handle HTTP statuses 301, 302, 307 and 308 * [x] Support registering as an application * Version 0.4.0 - * [ ] Handle X-RateLimit header + * [x] Handle X-RateLimit header * [ ] Streaming API * Later * [ ] Asynchronous I/O diff --git a/src/examples/example8_rate_limiting.cpp b/src/examples/example8_rate_limiting.cpp new file mode 100644 index 0000000..006c24b --- /dev/null +++ b/src/examples/example8_rate_limiting.cpp @@ -0,0 +1,53 @@ +/* This file is part of mastodon-cpp. + * In this example we look at HTTP headers to determine how many calls we are + * allowed to make. + */ + +#include +#include +#include +#include +#include "../mastodon-cpp.hpp" + +using Mastodon::API; + +int main(int argc, char *argv[]) +{ + if (argc < 3) + { + std::cerr << "usage: " << argv[0] << " \n"; + return 1; + } + + Mastodon::API masto(argv[1], argv[2]); + masto.set_useragent("mastodon-cpp-example/1.3.3.7"); + std::string answer; + std::uint16_t ret; + + ret = masto.get(API::v1::accounts_verify_credentials, answer); + if (ret == 0) + { + std::string remaining = masto.get_header("X-RateLimit-Remaining"); + std::string reset = masto.get_header("X-RateLimit-Reset"); + std::cout << "You are allowed to make " << remaining + << " calls until the counter is reset at " << reset << '\n'; + + if (std::stoi(remaining) <= 1) + { + std::cout << "🔥 Please let the server cool off a bit! 🔥\n"; + } + } + else if (ret == 3) + { + std::cerr << "The URL has permanently changed.\n" << + "New URL: " << answer << '\n'; + return ret; + } + else + { + std::cerr << "Error code: " << ret << '\n'; + return ret; + } + + return 0; +} diff --git a/src/http_sync.cpp b/src/http_sync.cpp index cd98d98..b3a4cec 100644 --- a/src/http_sync.cpp +++ b/src/http_sync.cpp @@ -65,6 +65,8 @@ const std::uint16_t API::http::request_sync(const method &meth, "Connection: close", "Authorization: Bearer " + _access_token }); + // Get headers from server + request.setOpt(true); request.setOpt(true); request.setOpt(&oss); if (!formdata.empty()) @@ -93,9 +95,13 @@ const std::uint16_t API::http::request_sync(const method &meth, request.perform(); std::uint16_t ret = curlpp::infos::ResponseCode::get(request); ttdebug << "Response code: " << ret << '\n'; + size_t pos = oss.str().find("\r\n\r\n"); + _headers = oss.str().substr(0, pos); + if (ret == 200 || ret == 302 || ret == 307) { // OK or Found or Temporary Redirect - answer = oss.str(); + // Only return body + answer = oss.str().substr(pos + 4); } else if (ret == 301 || ret == 308) { // Moved Permanently or Permanent Redirect @@ -122,3 +128,8 @@ const std::uint16_t API::http::request_sync(const method &meth, return 0; } + +const void API::http::get_headers(string &headers) const +{ + headers = _headers; +} diff --git a/src/mastodon-cpp.cpp b/src/mastodon-cpp.cpp index d0c0e5b..85123d2 100644 --- a/src/mastodon-cpp.cpp +++ b/src/mastodon-cpp.cpp @@ -216,3 +216,18 @@ const std::uint16_t API::register_app2(const string &instance, return ret; } } + +const string API::get_header(const std::string &header) const +{ + string headers; + _http.get_headers(headers); + size_t startpos = headers.find(header); + if (startpos != std::string::npos) + { + startpos = headers.find(':', startpos) + 2; + size_t endpos = headers.find("\r\n", startpos); + return headers.substr(startpos, endpos - startpos); + } + + return ""; +} diff --git a/src/mastodon-cpp.hpp b/src/mastodon-cpp.hpp index 1b237ea..f2102fd 100644 --- a/src/mastodon-cpp.hpp +++ b/src/mastodon-cpp.hpp @@ -32,6 +32,7 @@ * @example example5_follow_unfollow.cpp * @example example6_toot_delete-toot.cpp * @example example7_register_app.cpp + * @example example8_rate_limiting.cpp */ namespace Mastodon { @@ -472,6 +473,15 @@ public: const parametermap ¶meters, std::string &answer); + /*! + * @brief Gets the header from the last answer. + * + * @param header The header to search + * + * @return The header, or "" on error. + */ + const std::string get_header(const std::string &header) const; + private: const std::string _instance; std::string _access_token; @@ -531,10 +541,13 @@ private: const curlpp::Forms &formdata, std::string &answer); + const void get_headers(std::string &headers) const; + private: const API &parent; const std::string _instance; const std::string _access_token; + std::string _headers; } _http; }; }