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)
project (mastodon-cpp
VERSION 0.0.3
VERSION 0.1.0
LANGUAGES CXX
)
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.
# Install
## Dependencies
* Tested OS: Linux
* C++ compiler (tested: gcc 6.4)
* [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)
## Get sourcecode
### Development version
git clone https://…
## Compile
mkdir build
cd build/
cmake ..
make
If you want to compile a debug build, use `cmake -DCMAKE_BUILD_TYPE=Debug`
instead.
# 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`.
## 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
* Version 0.1.0
* [x] Implement all GET methods
* [ ] Proper error handling
* [x] Usable error handling
* [x] Network stuff
* [ ] Comprehensive example
* [x] Comprehensive example
* Version 0.2.0
* [ ] Implement all PATCH methods
* [ ] Implement all POST methods
* [ ] Implement all DELETE methods
* Version 0.3.0
* [ ] Handling HTTP statuses 301 & 302
* Later
* [ ] Escape user input
* [ ] Asynchronous I/O
## Status of implementation
* [x] GET /api/v1/accounts/:id
* [x] GET /api/v1/accounts/verify_credentials
* [ ] PATCH /api/v1/accounts/update_credentials

View File

@ -22,14 +22,14 @@
using namespace Mastodon;
using std::string;
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{};
return get(call, v);
return get(call, v, answer);
}
const string API::get(const Mastodon::API::v1 &call,
const std::vector<string> &parameters)
const std::uint16_t API::get(const Mastodon::API::v1 &call,
const std::vector<string> &parameters, string &answer)
{
string strcall = "";
switch (call)
@ -75,24 +75,35 @@ const string API::get(const Mastodon::API::v1 &call,
break;
default:
cerr << "ERROR: Invalid call.\n";
return "";
return 1;
break;
}
string answer;
_http.request_sync(http::method::GET, strcall, answer);
return answer;
if (parameters.size() > 0)
{
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 string &argument)
const std::uint16_t API::get(const Mastodon::API::v1 &call,
const string &argument, string &answer)
{
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 std::vector<string> &parameters)
const std::vector<string> &parameters, string &answer)
{
string strcall = "";
char delim = '?';
@ -158,7 +169,7 @@ const string API::get(const Mastodon::API::v1 &call,
break;
default:
cerr << "ERROR: Invalid call.\n";
return "";
return 1;
break;
}
@ -174,12 +185,10 @@ const string API::get(const Mastodon::API::v1 &call,
}
}
string answer;
_http.request_sync(http::method::GET, strcall, answer);
return answer;
return _http.request_sync(http::method::GET, strcall, 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 <vector>
#include <string>
#include <cstdint>
#include "../mastodon-cpp.hpp"
using Mastodon::API;
@ -17,13 +18,52 @@ int main(int argc, char *argv[])
}
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",
"only_media=1"
};
std::cout <<
masto.get(API::v1::accounts_id_statuses, "44897", parameters) <<
'\n';
std::cout << "Your last toot with media attached:\n";
std::string uid = answer.substr(7, answer.find("\"", 7) - 7);
std::vector<std::string> parameters =
{
"limit=1",
"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 <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include "macros.hpp"
#include "mastodon-cpp.hpp"
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);
boost::asio::connect(_socket.lowest_layer(), endpoint_iterator);
_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)
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.handshake(ssl_socket::client);
}
catch (const std::exception &e)
{
ttdebug << "ERROR: " << e.what() << "\n";
return 17;
}
try
{
boost::asio::streambuf request;
std::ostream request_stream(&request);
switch (meth)
@ -90,8 +108,8 @@ const std::uint16_t API::http::request_sync(const method &meth,
// request_stream << "DELETE";
// break;
default:
cerr << "NOT IMPLEMENTED\n";
return 0xffff;
ttdebug << "ERROR: Not implemented\n";
return 2;
}
request_stream << " HTTP/1.0\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);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
cerr << "Invalid response\n";
return 0xffff;
ttdebug << "ERROR: Invalid response from server\n";
ttdebug << "Response was: " << http_version << " " << status_code
<< " " << status_message << '\n';
return 18;
}
if (status_code != 200)
{
cerr << "Response returned with status code " << status_code
<< ": " << status_message << "\n";
return 0xffff;
ttdebug << "ERROR: Response returned with status code "
<< status_code << ": " << status_message << "\n";
return status_code;
}
// Read headers
@ -130,7 +150,6 @@ const std::uint16_t API::http::request_sync(const method &meth,
std::string header;
while (std::getline(response_stream, header) && header != "\r")
{}
//response.consume(response.size());
// Read body
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)
{
cerr << "Exception: " << e.what() << "\n";
ttdebug << "Exception: " << e.what() << "\n";
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.
*
* @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
*
* @param call A call defined in Mastodon::API::v1
* @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::string &argument);
const std::uint16_t get(const Mastodon::API::v1 &call,
const std::string &argument,
std::string &answer);
/*!
* @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 parameters A std::vector containing optional parameters in the
* 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::vector<std::string> &parameters);
const std::uint16_t get(const Mastodon::API::v1 &call,
const std::vector<std::string> &parameters,
std::string &answer);
/*!
* @brief Make a GET request which requires an argument, pass optional
@ -119,32 +127,38 @@ public:
* Example:
*
* Mastodon::API masto(argv[1], argv[2]);
* std::vector<std::string> parameters =
* {
* "limit=2",
* "only_media=1"
* };
* masto.get(Mastodon::API::v1::accounts_id_statuses, "12345", parameters);
* std::vector<std::string> parameters =
* {
* "limit=2",
* "only_media=1"
* };
* masto.get(Mastodon::API::v1::accounts_id_statuses, "12345", parameters);
*
* @param call A call defined in Mastodon::API::v1
* @param argument The non-optional argument
* @param parameters A std::vector containing optional parameters in the
* 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::string &argument,
const std::vector<std::string> &parameters);
const std::uint16_t get(const Mastodon::API::v1 &call,
const std::string &argument,
const std::vector<std::string> &parameters,
std::string &answer);
/*!
* @brief Make a custom GET request.
*
* @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.
@ -181,6 +195,19 @@ private:
const std::uint16_t request_sync(const method &meth,
const std::string &path,
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::string &path,
const std::string &data,

View File

@ -2,19 +2,22 @@
*/
#include <iostream>
#include <cstdint>
#include "../mastodon-cpp.hpp"
int main(int argc, char *argv[])
{
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\"")
{
return 0;
}
else
{
return 1;
}
std::cout << ret << ": " << answer.substr(7, 14) << '\n';
return 1;
}