error handling, debug output, documentation

This commit is contained in:
tastytea 2018-01-13 15:49:46 +01:00
parent 46c67052ab
commit eb3916c84e
Signed by: tastytea
GPG Key ID: 59346E0EA35C67E5
8 changed files with 220 additions and 67 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required (VERSION 3.7) cmake_minimum_required (VERSION 3.7)
project (mastodon-cpp project (mastodon-cpp
VERSION 0.0.3 VERSION 0.1.0
LANGUAGES CXX LANGUAGES CXX
) )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")

View File

@ -3,7 +3,9 @@ The library takes care of the network stuff. You submit a query and get the raw
All versions below 1.0.0 (SOVERSION 0) are considered insecure, unstable and can change any time. All versions below 1.0.0 (SOVERSION 0) are considered insecure, unstable and can change any time.
# Install # Install
## Dependencies ## Dependencies
* Tested OS: Linux * Tested OS: Linux
* C++ compiler (tested: gcc 6.4) * C++ compiler (tested: gcc 6.4)
* [cmake](https://cmake.org/) (tested: 3.9.6) * [cmake](https://cmake.org/) (tested: 3.9.6)
@ -11,34 +13,59 @@ All versions below 1.0.0 (SOVERSION 0) are considered insecure, unstable and can
* Optional: [doxygen](https://www.stack.nl/~dimitri/doxygen/) (tested: 1.8.13) * Optional: [doxygen](https://www.stack.nl/~dimitri/doxygen/) (tested: 1.8.13)
## Get sourcecode ## Get sourcecode
### Development version ### Development version
git clone https://… git clone https://…
## Compile ## Compile
mkdir build mkdir build
cd build/ cd build/
cmake .. cmake ..
make make
If you want to compile a debug build, use `cmake -DCMAKE_BUILD_TYPE=Debug`
instead.
# Usage # Usage
The reference can be generated with `build_doc.sh`, if doxygen is installed. Or just look in `src/mastodon-cpp.hpp`. There is an example in `src/example`. The reference can be generated with `build_doc.sh`, if doxygen is installed. Or just look in `src/mastodon-cpp.hpp`. There is an example in `src/example`.
## Error codes
| Code | Explanation |
| --------: |:------------------------------|
| 0 | No error |
| 1 | Invalid call |
| 2 | Not implemented |
| 16 | Connection failed |
| 17 | TLS error |
| 18 | Invalid response from server |
| 100 - 999 | HTTP status codes |
| 65535 | Unknown exception |
If you use a debug build, you get more verbose error messages.
# TODO # TODO
* Version 0.1.0 * Version 0.1.0
* [x] Implement all GET methods * [x] Implement all GET methods
* [ ] Proper error handling * [x] Usable error handling
* [x] Network stuff * [x] Network stuff
* [ ] Comprehensive example * [x] Comprehensive example
* Version 0.2.0 * Version 0.2.0
* [ ] Implement all PATCH methods * [ ] Implement all PATCH methods
* [ ] Implement all POST methods * [ ] Implement all POST methods
* [ ] Implement all DELETE methods * [ ] Implement all DELETE methods
* Version 0.3.0
* [ ] Handling HTTP statuses 301 & 302
* Later * Later
* [ ] Escape user input * [ ] Escape user input
* [ ] Asynchronous I/O * [ ] Asynchronous I/O
## Status of implementation ## Status of implementation
* [x] GET /api/v1/accounts/:id * [x] GET /api/v1/accounts/:id
* [x] GET /api/v1/accounts/verify_credentials * [x] GET /api/v1/accounts/verify_credentials
* [ ] PATCH /api/v1/accounts/update_credentials * [ ] PATCH /api/v1/accounts/update_credentials

View File

@ -22,14 +22,14 @@
using namespace Mastodon; using namespace Mastodon;
using std::string; using std::string;
using std::cerr; using std::cerr;
const string API::get(const Mastodon::API::v1 &call) const std::uint16_t API::get(const Mastodon::API::v1 &call, string &answer)
{ {
const std::vector<string> v{}; const std::vector<string> v{};
return get(call, v); return get(call, v, answer);
} }
const string API::get(const Mastodon::API::v1 &call, const std::uint16_t API::get(const Mastodon::API::v1 &call,
const std::vector<string> &parameters) const std::vector<string> &parameters, string &answer)
{ {
string strcall = ""; string strcall = "";
switch (call) switch (call)
@ -75,24 +75,35 @@ const string API::get(const Mastodon::API::v1 &call,
break; break;
default: default:
cerr << "ERROR: Invalid call.\n"; cerr << "ERROR: Invalid call.\n";
return ""; return 1;
break; break;
} }
string answer; if (parameters.size() > 0)
_http.request_sync(http::method::GET, strcall, answer); {
return answer; char delim = '?';
for (const string p : parameters)
{
strcall += delim + p;
if (delim == '?')
{
delim = '&';
}
}
}
return _http.request_sync(http::method::GET, strcall, answer);
} }
const string API::get(const Mastodon::API::v1 &call, const std::uint16_t API::get(const Mastodon::API::v1 &call,
const string &argument) const string &argument, string &answer)
{ {
const std::vector<string> v; const std::vector<string> v;
return get(call, argument, v); return get(call, argument, v, answer);
} }
const string API::get(const Mastodon::API::v1 &call, const std::uint16_t API::get(const Mastodon::API::v1 &call,
const string &argument, const string &argument,
const std::vector<string> &parameters) const std::vector<string> &parameters, string &answer)
{ {
string strcall = ""; string strcall = "";
char delim = '?'; char delim = '?';
@ -158,7 +169,7 @@ const string API::get(const Mastodon::API::v1 &call,
break; break;
default: default:
cerr << "ERROR: Invalid call.\n"; cerr << "ERROR: Invalid call.\n";
return ""; return 1;
break; break;
} }
@ -174,12 +185,10 @@ const string API::get(const Mastodon::API::v1 &call,
} }
} }
string answer; return _http.request_sync(http::method::GET, strcall, answer);
_http.request_sync(http::method::GET, strcall, answer);
return answer;
} }
const string API::get(const std::string &call) const std::uint16_t API::get(const std::string &call, string &answer)
{ {
return call; return _http.request_sync(http::method::GET, call, answer);
} }

View File

@ -4,6 +4,7 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <string> #include <string>
#include <cstdint>
#include "../mastodon-cpp.hpp" #include "../mastodon-cpp.hpp"
using Mastodon::API; using Mastodon::API;
@ -17,13 +18,52 @@ int main(int argc, char *argv[])
} }
Mastodon::API masto(argv[1], argv[2]); Mastodon::API masto(argv[1], argv[2]);
std::string answer;
std::uint16_t ret;
std::vector<std::string> parameters = ret = masto.get(API::v1::accounts_verify_credentials, answer);
if (ret == 0)
{ {
"limit=2", std::cout << "Your last toot with media attached:\n";
"only_media=1" std::string uid = answer.substr(7, answer.find("\"", 7) - 7);
}; std::vector<std::string> parameters =
std::cout << {
masto.get(API::v1::accounts_id_statuses, "44897", parameters) << "limit=1",
'\n'; "only_media=1"
};
ret = masto.get(API::v1::accounts_id_statuses, uid,
parameters, answer);
if (ret == 0)
{
std::cout << answer << '\n';
}
else
{
std::cerr << "Error code: " << ret << '\n';
return ret;
}
std::cout << "\nYour last 2 followers:\n";
parameters =
{
"limit=2",
"exclude_types[]=favourite",
"exclude_types[]=reblog",
"exclude_types[]=mention"
};
ret = masto.get(API::v1::notifications, parameters, answer);
if (ret == 0)
{
std::cout << answer << '\n';
return 0;
}
else
{
std::cerr << "Error code: " << ret << '\n';
return ret;
}
}
return 0;
} }

View File

@ -21,6 +21,7 @@
#include <ostream> #include <ostream>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/ssl.hpp> #include <boost/asio/ssl.hpp>
#include "macros.hpp"
#include "mastodon-cpp.hpp" #include "mastodon-cpp.hpp"
using namespace Mastodon; using namespace Mastodon;
@ -64,6 +65,15 @@ const std::uint16_t API::http::request_sync(const method &meth,
tcp::resolver::iterator endpoint_iterator = _resolver.resolve(query); tcp::resolver::iterator endpoint_iterator = _resolver.resolve(query);
boost::asio::connect(_socket.lowest_layer(), endpoint_iterator); boost::asio::connect(_socket.lowest_layer(), endpoint_iterator);
_socket.lowest_layer().set_option(tcp::no_delay(true)); _socket.lowest_layer().set_option(tcp::no_delay(true));
}
catch (const std::exception &e)
{
ttdebug << "ERROR: " << e.what() << "\n";
return 16;
}
try
{
// Server Name Indication (SNI) // Server Name Indication (SNI)
SSL_set_tlsext_host_name(_socket.native_handle(), _instance.c_str()); SSL_set_tlsext_host_name(_socket.native_handle(), _instance.c_str());
@ -71,7 +81,15 @@ const std::uint16_t API::http::request_sync(const method &meth,
_socket.set_verify_callback(ssl::rfc2818_verification(_instance)); _socket.set_verify_callback(ssl::rfc2818_verification(_instance));
_socket.handshake(ssl_socket::client); _socket.handshake(ssl_socket::client);
}
catch (const std::exception &e)
{
ttdebug << "ERROR: " << e.what() << "\n";
return 17;
}
try
{
boost::asio::streambuf request; boost::asio::streambuf request;
std::ostream request_stream(&request); std::ostream request_stream(&request);
switch (meth) switch (meth)
@ -90,8 +108,8 @@ const std::uint16_t API::http::request_sync(const method &meth,
// request_stream << "DELETE"; // request_stream << "DELETE";
// break; // break;
default: default:
cerr << "NOT IMPLEMENTED\n"; ttdebug << "ERROR: Not implemented\n";
return 0xffff; return 2;
} }
request_stream << " HTTP/1.0\r\n"; request_stream << " HTTP/1.0\r\n";
request_stream << "Host: " << _instance << "\r\n"; request_stream << "Host: " << _instance << "\r\n";
@ -115,14 +133,16 @@ const std::uint16_t API::http::request_sync(const method &meth,
std::getline(response_stream, status_message); std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/") if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{ {
cerr << "Invalid response\n"; ttdebug << "ERROR: Invalid response from server\n";
return 0xffff; ttdebug << "Response was: " << http_version << " " << status_code
<< " " << status_message << '\n';
return 18;
} }
if (status_code != 200) if (status_code != 200)
{ {
cerr << "Response returned with status code " << status_code ttdebug << "ERROR: Response returned with status code "
<< ": " << status_message << "\n"; << status_code << ": " << status_message << "\n";
return 0xffff; return status_code;
} }
// Read headers // Read headers
@ -130,7 +150,6 @@ const std::uint16_t API::http::request_sync(const method &meth,
std::string header; std::string header;
while (std::getline(response_stream, header) && header != "\r") while (std::getline(response_stream, header) && header != "\r")
{} {}
//response.consume(response.size());
// Read body // Read body
boost::system::error_code error; boost::system::error_code error;
@ -149,7 +168,7 @@ const std::uint16_t API::http::request_sync(const method &meth,
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
cerr << "Exception: " << e.what() << "\n"; ttdebug << "Exception: " << e.what() << "\n";
return 0xffff; return 0xffff;
} }

28
src/macros.hpp Normal file
View File

@ -0,0 +1,28 @@
/* This file is part of mastodon-cpp.
* Copyright © 2018 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MACROS_HPP
#define MACROS_HPP
#ifdef DEBUG
#define ttdebug std::cerr << "[" << __FILE__ << ":" << __LINE__ << "] DEBUG: "
#else
#define ttdebug false && std::cerr
#endif
#endif // MACROS_HPP

View File

@ -83,21 +83,26 @@ public:
* @brief Make a GET request which doesn't require an argument. * @brief Make a GET request which doesn't require an argument.
* *
* @param call A call defined in Mastodon::API::v1 * @param call A call defined in Mastodon::API::v1
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
* *
* @return The answer from the server. Usually JSON. * @return The HTTP error code, or 0xffff if an other error happens.
*/ */
const std::string get(const Mastodon::API::v1 &call); const std::uint16_t get(const Mastodon::API::v1 &call, std::string &answer);
/*! /*!
* @brief Make a GET request which requires an argument * @brief Make a GET request which requires an argument
* *
* @param call A call defined in Mastodon::API::v1 * @param call A call defined in Mastodon::API::v1
* @param argument The non-optional argument * @param argument The non-optional argument
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
* *
* @return The answer from the server. Usually JSON. * @return The HTTP error code, or 0xffff if an other error happens.
*/ */
const std::string get(const Mastodon::API::v1 &call, const std::uint16_t get(const Mastodon::API::v1 &call,
const std::string &argument); const std::string &argument,
std::string &answer);
/*! /*!
* @brief Make a GET request which doesn't require an argument, pass * @brief Make a GET request which doesn't require an argument, pass
@ -106,11 +111,14 @@ public:
* @param call A call defined in Mastodon::API::v1 * @param call A call defined in Mastodon::API::v1
* @param parameters A std::vector containing optional parameters in the * @param parameters A std::vector containing optional parameters in the
* form `field=value` * form `field=value`
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
* *
* @return The answer from the server. Usually JSON. * @return The HTTP error code, or 0xffff if an other error happens.
*/ */
const std::string get(const Mastodon::API::v1 &call, const std::uint16_t get(const Mastodon::API::v1 &call,
const std::vector<std::string> &parameters); const std::vector<std::string> &parameters,
std::string &answer);
/*! /*!
* @brief Make a GET request which requires an argument, pass optional * @brief Make a GET request which requires an argument, pass optional
@ -119,32 +127,38 @@ public:
* Example: * Example:
* *
* Mastodon::API masto(argv[1], argv[2]); * Mastodon::API masto(argv[1], argv[2]);
* std::vector<std::string> parameters = * std::vector<std::string> parameters =
* { * {
* "limit=2", * "limit=2",
* "only_media=1" * "only_media=1"
* }; * };
* masto.get(Mastodon::API::v1::accounts_id_statuses, "12345", parameters); * masto.get(Mastodon::API::v1::accounts_id_statuses, "12345", parameters);
* *
* @param call A call defined in Mastodon::API::v1 * @param call A call defined in Mastodon::API::v1
* @param argument The non-optional argument * @param argument The non-optional argument
* @param parameters A std::vector containing optional parameters in the * @param parameters A std::vector containing optional parameters in the
* form `field=value` * form `field=value`
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
* *
* @return The answer from the server. Usually JSON. * @return The HTTP error code, or 0xffff if an other error happens.
*/ */
const std::string get(const Mastodon::API::v1 &call, const std::uint16_t get(const Mastodon::API::v1 &call,
const std::string &argument, const std::string &argument,
const std::vector<std::string> &parameters); const std::vector<std::string> &parameters,
std::string &answer);
/*! /*!
* @brief Make a custom GET request. * @brief Make a custom GET request.
* *
* @param call String in the form `/api/v1/example` * @param call String in the form `/api/v1/example`
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
* *
* @return The answer from the server. Usually JSON. * @return The HTTP error code, or 0xffff if an other error happens.
*/ */
const std::string get(const std::string &call); const std::uint16_t get(const std::string &call,
std::string &answer);
/*! /*!
* @brief Sets the useragent. Default is mastodon-cpp/version. * @brief Sets the useragent. Default is mastodon-cpp/version.
@ -181,6 +195,19 @@ private:
const std::uint16_t request_sync(const method &meth, const std::uint16_t request_sync(const method &meth,
const std::string &path, const std::string &path,
std::string &answer); std::string &answer);
/*!
* @brief Blocking request.
*
* @param meth The method defined in http::method
* @param path The api call as string
* @param data The form data for PATCH and POST request. Not
* implemented at the moment. This will likely change
* into a std::vector.
* @param answer The answer from the server
*
* @return The HTTP error code, or 0xffff if an other error happens.
*/
const std::uint16_t request_sync(const method &meth, const std::uint16_t request_sync(const method &meth,
const std::string &path, const std::string &path,
const std::string &data, const std::string &data,

View File

@ -2,19 +2,22 @@
*/ */
#include <iostream> #include <iostream>
#include <cstdint>
#include "../mastodon-cpp.hpp" #include "../mastodon-cpp.hpp"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
Mastodon::API test("soc.ialis.me", ""); Mastodon::API test("soc.ialis.me", "");
std::string answer = test.get(Mastodon::API::v1::instance); std::string answer;
std::uint16_t ret = test.get(Mastodon::API::v1::instance, answer);
if (ret == 0)
{
if (answer.substr(7, 14) == "\"soc.ialis.me\"")
{
return 0;
}
}
if (answer.substr(7, 14) == "\"soc.ialis.me\"") std::cout << ret << ": " << answer.substr(7, 14) << '\n';
{ return 1;
return 0;
}
else
{
return 1;
}
} }