Browse Source

Merge pull request #11060 from Chocobo1/tracker

Improve embedded tracker
adaptive-webui-19844
Mike Tzou 5 years ago committed by GitHub
parent
commit
5c7f9530ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      src/base/bittorrent/session.cpp
  2. 472
      src/base/bittorrent/tracker.cpp
  3. 62
      src/base/bittorrent/tracker.h
  4. 2
      src/base/exceptions.h
  5. 19
      src/base/http/httperror.cpp
  6. 28
      src/base/http/httperror.h
  7. 2
      src/base/http/responsebuilder.cpp
  8. 7
      src/base/http/types.h
  9. 14
      src/base/tristatebool.h
  10. 2
      src/gui/advancedsettings.cpp
  11. 4
      src/webui/api/appcontroller.cpp

14
src/base/bittorrent/session.cpp

@ -909,10 +909,12 @@ bool Session::isTrackerEnabled() const @@ -909,10 +909,12 @@ bool Session::isTrackerEnabled() const
void Session::setTrackerEnabled(const bool enabled)
{
if (isTrackerEnabled() != enabled) {
enableTracker(enabled);
if (m_isTrackerEnabled != enabled)
m_isTrackerEnabled = enabled;
}
// call enableTracker() unconditionally, otherwise port change won't trigger
// tracker restart
enableTracker(enabled);
}
qreal Session::globalMaxRatio() const
@ -1480,13 +1482,9 @@ void Session::enableTracker(const bool enable) @@ -1480,13 +1482,9 @@ void Session::enableTracker(const bool enable)
if (!m_tracker)
m_tracker = new Tracker(this);
if (m_tracker->start())
LogMsg(tr("Embedded Tracker [ON]"), Log::INFO);
else
LogMsg(tr("Failed to start the embedded tracker!"), Log::CRITICAL);
m_tracker->start();
}
else {
LogMsg(tr("Embedded Tracker [OFF]"), Log::INFO);
if (m_tracker)
delete m_tracker;
}

472
src/base/bittorrent/tracker.cpp

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 Mike Tzou (Chocobo1)
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
@ -32,235 +33,410 @@ @@ -32,235 +33,410 @@
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include <QHostAddress>
#include "base/exceptions.h"
#include "base/global.h"
#include "base/http/httperror.h"
#include "base/http/server.h"
#include "base/http/types.h"
#include "base/logger.h"
#include "base/preferences.h"
// static limits
static const int MAX_TORRENTS = 100;
static const int MAX_PEERS_PER_TORRENT = 1000;
static const int ANNOUNCE_INTERVAL = 1800; // 30min
using namespace BitTorrent;
// Peer
bool Peer::operator!=(const Peer &other) const
namespace
{
return uid() != other.uid();
// static limits
const int MAX_TORRENTS = 10000;
const int MAX_PEERS_PER_TORRENT = 200;
const int ANNOUNCE_INTERVAL = 1800; // 30min
// constants
const int PEER_ID_SIZE = 20;
const char ANNOUNCE_REQUEST_PATH[] = "/announce";
const char ANNOUNCE_REQUEST_COMPACT[] = "compact";
const char ANNOUNCE_REQUEST_INFO_HASH[] = "info_hash";
const char ANNOUNCE_REQUEST_IP[] = "ip";
const char ANNOUNCE_REQUEST_LEFT[] = "left";
const char ANNOUNCE_REQUEST_NO_PEER_ID[] = "no_peer_id";
const char ANNOUNCE_REQUEST_NUM_WANT[] = "numwant";
const char ANNOUNCE_REQUEST_PEER_ID[] = "peer_id";
const char ANNOUNCE_REQUEST_PORT[] = "port";
const char ANNOUNCE_REQUEST_EVENT[] = "event";
const char ANNOUNCE_REQUEST_EVENT_COMPLETED[] = "completed";
const char ANNOUNCE_REQUEST_EVENT_EMPTY[] = "empty";
const char ANNOUNCE_REQUEST_EVENT_STARTED[] = "started";
const char ANNOUNCE_REQUEST_EVENT_STOPPED[] = "stopped";
const char ANNOUNCE_REQUEST_EVENT_PAUSED[] = "paused";
const char ANNOUNCE_RESPONSE_COMPLETE[] = "complete";
const char ANNOUNCE_RESPONSE_EXTERNAL_IP[] = "external ip";
const char ANNOUNCE_RESPONSE_FAILURE_REASON[] = "failure reason";
const char ANNOUNCE_RESPONSE_INCOMPLETE[] = "incomplete";
const char ANNOUNCE_RESPONSE_INTERVAL[] = "interval";
const char ANNOUNCE_RESPONSE_PEERS6[] = "peers6";
const char ANNOUNCE_RESPONSE_PEERS[] = "peers";
const char ANNOUNCE_RESPONSE_PEERS_IP[] = "ip";
const char ANNOUNCE_RESPONSE_PEERS_PEER_ID[] = "peer id";
const char ANNOUNCE_RESPONSE_PEERS_PORT[] = "port";
class TrackerError : public RuntimeError
{
public:
using RuntimeError::RuntimeError;
};
QByteArray toBigEndianByteArray(const QHostAddress &addr)
{
// translate IP address to a sequence of bytes in big-endian order
switch (addr.protocol()) {
case QAbstractSocket::IPv4Protocol:
case QAbstractSocket::AnyIPProtocol: {
const quint32 ipv4 = addr.toIPv4Address();
QByteArray ret;
ret.append(static_cast<char>((ipv4 >> 24) & 0xFF))
.append(static_cast<char>((ipv4 >> 16) & 0xFF))
.append(static_cast<char>((ipv4 >> 8) & 0xFF))
.append(static_cast<char>(ipv4 & 0xFF));
return ret;
}
case QAbstractSocket::IPv6Protocol: {
const Q_IPV6ADDR ipv6 = addr.toIPv6Address();
QByteArray ret;
for (int i = (sizeof(ipv6.c) - 1); i >= 0; --i)
ret.append(static_cast<char>(ipv6.c[i]));
return ret;
}
case QAbstractSocket::UnknownNetworkLayerProtocol:
default:
return {};
};
}
}
bool Peer::operator==(const Peer &other) const
namespace BitTorrent
{
return uid() == other.uid();
// Peer
QByteArray Peer::uniqueID() const
{
return (QByteArray::fromStdString(address) + ':' + QByteArray::number(port));
}
bool operator==(const Peer &left, const Peer &right)
{
return (left.uniqueID() == right.uniqueID());
}
bool operator!=(const Peer &left, const Peer &right)
{
return !(left == right);
}
uint qHash(const Peer &key, const uint seed)
{
return qHash(key.uniqueID(), seed);
}
}
QString Peer::uid() const
using namespace BitTorrent;
// TrackerAnnounceRequest
struct Tracker::TrackerAnnounceRequest
{
return ip.toString() + ':' + QString::number(port);
QHostAddress socketAddress;
QByteArray claimedAddress; // self claimed by peer
InfoHash infoHash;
QString event;
Peer peer;
int numwant = 50;
bool compact = true;
bool noPeerId = false;
};
// Tracker::TorrentStats
void Tracker::TorrentStats::setPeer(const Peer &peer)
{
// always replace existing peer
if (!removePeer(peer)) {
// Too many peers, remove a random one
if (peers.size() >= MAX_PEERS_PER_TORRENT)
removePeer(*peers.begin());
}
// add peer
if (peer.isSeeder)
++seeders;
peers.insert(peer);
}
lt::entry Peer::toEntry(const bool noPeerId) const
bool Tracker::TorrentStats::removePeer(const Peer &peer)
{
lt::entry::dictionary_type peerMap;
if (!noPeerId)
peerMap["id"] = lt::entry(peerId.toStdString());
peerMap["ip"] = lt::entry(ip.toString().toStdString());
peerMap["port"] = lt::entry(port);
return lt::entry(peerMap);
const auto iter = peers.find(peer);
if (iter == peers.end())
return false;
if (iter->isSeeder)
--seeders;
peers.remove(*iter);
return true;
}
// Tracker
Tracker::Tracker(QObject *parent)
: QObject(parent)
, m_server(new Http::Server(this, this))
{
}
Tracker::~Tracker()
{
if (m_server->isListening())
qDebug("Shutting down the embedded tracker...");
// TODO: Store the torrent list
}
bool Tracker::start()
{
const int listenPort = Preferences::instance()->getTrackerPort();
const QHostAddress ip = QHostAddress::Any;
const int port = Preferences::instance()->getTrackerPort();
if (m_server->isListening()) {
if (m_server->serverPort() == listenPort) {
if (m_server->serverPort() == port) {
// Already listening on the right port, just return
return true;
}
// Wrong port, closing the server
m_server->close();
}
qDebug("Starting the embedded tracker...");
// Listen on the predefined port
return m_server->listen(QHostAddress::Any, listenPort);
const bool listenSuccess = m_server->listen(ip, port);
if (listenSuccess) {
LogMsg(tr("Embedded Tracker: Now listening on IP: %1, port: %2")
.arg(ip.toString(), QString::number(port)), Log::INFO);
}
else {
LogMsg(tr("Embedded Tracker: Unable to bind to IP: %1, port: %2. Reason: %3")
.arg(ip.toString(), QString::number(port), m_server->errorString())
, Log::WARNING);
}
return listenSuccess;
}
Http::Response Tracker::processRequest(const Http::Request &request, const Http::Environment &env)
{
clear(); // clear response
clear(); // clear response
m_request = request;
m_env = env;
status(200);
try {
// Is it a GET request?
if (request.method != Http::HEADER_REQUEST_METHOD_GET)
throw MethodNotAllowedHTTPError();
//qDebug("Tracker received the following request:\n%s", qUtf8Printable(parser.toString()));
// Is request a GET request?
if (request.method != "GET") {
qDebug("Tracker: Unsupported HTTP request: %s", qUtf8Printable(request.method));
status(100, "Invalid request type");
if (request.path.toLower().startsWith(ANNOUNCE_REQUEST_PATH))
processAnnounceRequest();
else
throw NotFoundHTTPError();
}
else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) {
qDebug("Tracker: Unrecognized path: %s", qUtf8Printable(request.path));
status(100, "Invalid request type");
catch (const HTTPError &error) {
status(error.statusCode(), error.statusText());
if (!error.message().isEmpty())
print(error.message(), Http::CONTENT_TYPE_TXT);
}
else {
// OK, this is a GET request
m_request = request;
m_env = env;
respondToAnnounceRequest();
catch (const TrackerError &error) {
clear(); // clear response
status(200);
const lt::entry::dictionary_type bencodedEntry = {
{ANNOUNCE_RESPONSE_FAILURE_REASON, {error.what()}}
};
QByteArray reply;
lt::bencode(std::back_inserter(reply), bencodedEntry);
print(reply, Http::CONTENT_TYPE_TXT);
}
return response();
}
void Tracker::respondToAnnounceRequest()
void Tracker::processAnnounceRequest()
{
const QHash<QString, QByteArray> &queryParams = m_request.query;
TrackerAnnounceRequest announceReq;
// IP
// Use the "ip" parameter provided from tracker request first, then fall back to client IP if invalid
const QHostAddress paramIP {QString::fromLatin1(queryParams.value("ip"))};
announceReq.peer.ip = paramIP.isNull() ? m_env.clientAddress : paramIP;
// ip address
announceReq.socketAddress = m_env.clientAddress;
announceReq.claimedAddress = queryParams.value(ANNOUNCE_REQUEST_IP);
// 1. Get info_hash
if (!queryParams.contains("info_hash")) {
qDebug("Tracker: Missing info_hash");
status(101, "Missing info_hash");
return;
}
announceReq.infoHash = queryParams.value("info_hash");
// info_hash cannot be longer than 20 bytes
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qUtf8Printable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
status(150, "Invalid infohash");
return;
}*/
// 1. info_hash
const auto infoHashIter = queryParams.find(ANNOUNCE_REQUEST_INFO_HASH);
if (infoHashIter == queryParams.end())
throw TrackerError("Missing \"info_hash\" parameter");
// 2. Get peer ID
if (!queryParams.contains("peer_id")) {
qDebug("Tracker: Missing peer_id");
status(102, "Missing peer_id");
return;
}
announceReq.peer.peerId = queryParams.value("peer_id");
// peer_id cannot be longer than 20 bytes
/*if (annonce_req.peer.peer_id.length() > 20) {
qDebug("Tracker: peer_id is not 20 byte long: %s", qUtf8Printable(annonce_req.peer.peer_id));
status(151, "Invalid peerid");
return;
}*/
const InfoHash infoHash(infoHashIter->toHex());
if (!infoHash.isValid())
throw TrackerError("Invalid \"info_hash\" parameter");
// 3. Get port
if (!queryParams.contains("port")) {
qDebug("Tracker: Missing port");
status(103, "Missing port");
return;
}
bool ok = false;
announceReq.peer.port = queryParams.value("port").toInt(&ok);
if (!ok || (announceReq.peer.port < 0) || (announceReq.peer.port > 65535)) {
qDebug("Tracker: Invalid port number (%d)", announceReq.peer.port);
status(103, "Missing port");
return;
}
announceReq.infoHash = infoHash;
// 4. Get event
announceReq.event = "";
if (queryParams.contains("event")) {
announceReq.event = queryParams.value("event");
qDebug("Tracker: event is %s", qUtf8Printable(announceReq.event));
}
// 2. peer_id
const auto peerIdIter = queryParams.find(ANNOUNCE_REQUEST_PEER_ID);
if (peerIdIter == queryParams.end())
throw TrackerError("Missing \"peer_id\" parameter");
// 5. Get numwant
announceReq.numwant = 50;
if (queryParams.contains("numwant")) {
int tmp = queryParams.value("numwant").toInt();
if (tmp > 0) {
qDebug("Tracker: numwant = %d", tmp);
announceReq.numwant = tmp;
}
}
if (peerIdIter->size() > PEER_ID_SIZE)
throw TrackerError("Invalid \"peer_id\" parameter");
announceReq.peer.peerId = *peerIdIter;
// 3. port
const auto portIter = queryParams.find(ANNOUNCE_REQUEST_PORT);
if (portIter == queryParams.end())
throw TrackerError("Missing \"port\" parameter");
const ushort portNum = portIter->toUShort();
if (portNum == 0)
throw TrackerError("Invalid \"port\" parameter");
// 6. no_peer_id (extension)
announceReq.noPeerId = false;
if (queryParams.contains("no_peer_id"))
announceReq.noPeerId = true;
announceReq.peer.port = portNum;
// 7. TODO: support "compact" extension
// 4. numwant
const auto numWantIter = queryParams.find(ANNOUNCE_REQUEST_NUM_WANT);
if (numWantIter != queryParams.end()) {
const int num = numWantIter->toInt();
if (num < 0)
throw TrackerError("Invalid \"numwant\" parameter");
announceReq.numwant = num;
}
// Done parsing, now let's reply
if (announceReq.event == "stopped") {
// 5. no_peer_id
// non-formal extension
announceReq.noPeerId = (queryParams.value(ANNOUNCE_REQUEST_NO_PEER_ID) == "1");
// 6. left
announceReq.peer.isSeeder = (queryParams.value(ANNOUNCE_REQUEST_LEFT) == "0");
// 7. compact
announceReq.compact = (queryParams.value(ANNOUNCE_REQUEST_COMPACT) != "0");
// 8. cache `peers` field so we don't recompute when sending response
const QHostAddress claimedIPAddress {QString::fromLatin1(announceReq.claimedAddress)};
announceReq.peer.endpoint = toBigEndianByteArray(!claimedIPAddress.isNull() ? claimedIPAddress : announceReq.socketAddress)
.append(static_cast<char>((announceReq.peer.port >> 8) & 0xFF))
.append(static_cast<char>(announceReq.peer.port & 0xFF))
.toStdString();
// 9. cache `address` field so we don't recompute when sending response
announceReq.peer.address = !announceReq.claimedAddress.isEmpty()
? announceReq.claimedAddress.constData()
: announceReq.socketAddress.toString().toLatin1().constData(),
// 10. event
announceReq.event = queryParams.value(ANNOUNCE_REQUEST_EVENT);
if (announceReq.event.isEmpty()
|| (announceReq.event == ANNOUNCE_REQUEST_EVENT_EMPTY)
|| (announceReq.event == ANNOUNCE_REQUEST_EVENT_COMPLETED)
|| (announceReq.event == ANNOUNCE_REQUEST_EVENT_STARTED)
|| (announceReq.event == ANNOUNCE_REQUEST_EVENT_PAUSED)) {
// [BEP-21] Extension for partial seeds (partial support)
registerPeer(announceReq);
prepareAnnounceResponse(announceReq);
}
else if (announceReq.event == ANNOUNCE_REQUEST_EVENT_STOPPED) {
unregisterPeer(announceReq);
}
else {
registerPeer(announceReq);
replyWithPeerList(announceReq);
throw TrackerError("Invalid \"event\" parameter");
}
}
void Tracker::registerPeer(const TrackerAnnounceRequest &announceReq)
{
if (announceReq.peer.port == 0) return;
if (!m_torrents.contains(announceReq.infoHash)) {
// Unknown torrent
if (m_torrents.size() == MAX_TORRENTS) {
// Reached max size, remove a random torrent
// Reached max size, remove a random torrent
if (m_torrents.size() >= MAX_TORRENTS)
m_torrents.erase(m_torrents.begin());
}
}
// Register the user
PeerList &peers = m_torrents[announceReq.infoHash];
if (!peers.contains(announceReq.peer.uid())) {
// Unknown peer
if (peers.size() == MAX_PEERS_PER_TORRENT) {
// Too many peers, remove a random one
peers.erase(peers.begin());
}
}
peers[announceReq.peer.uid()] = announceReq.peer;
m_torrents[announceReq.infoHash].setPeer(announceReq.peer);
}
void Tracker::unregisterPeer(const TrackerAnnounceRequest &announceReq)
{
if (announceReq.peer.port == 0) return;
const auto torrentStatsIter = m_torrents.find(announceReq.infoHash);
if (torrentStatsIter == m_torrents.end())
return;
if (m_torrents[announceReq.infoHash].remove(announceReq.peer.uid()) > 0)
qDebug("Tracker: Peer stopped downloading, deleting it from the list");
torrentStatsIter->removePeer(announceReq.peer);
if (torrentStatsIter->peers.isEmpty())
m_torrents.erase(torrentStatsIter);
}
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &announceReq)
void Tracker::prepareAnnounceResponse(const TrackerAnnounceRequest &announceReq)
{
// Prepare the entry for bencoding
lt::entry::dictionary_type replyDict;
replyDict["interval"] = lt::entry(ANNOUNCE_INTERVAL);
const TorrentStats &torrentStats = m_torrents[announceReq.infoHash];
lt::entry::dictionary_type replyDict {
{ANNOUNCE_RESPONSE_INTERVAL, ANNOUNCE_INTERVAL},
{ANNOUNCE_RESPONSE_COMPLETE, torrentStats.seeders},
{ANNOUNCE_RESPONSE_INCOMPLETE, (torrentStats.peers.size() - torrentStats.seeders)},
// [BEP-24] Tracker Returns External IP
{ANNOUNCE_RESPONSE_EXTERNAL_IP, toBigEndianByteArray(announceReq.socketAddress).toStdString()}
};
// peer list
// [BEP-7] IPv6 Tracker Extension (partial support)
// [BEP-23] Tracker Returns Compact Peer Lists
if (announceReq.compact) {
lt::entry::list_type peerList;
lt::entry::list_type peer6List;
int counter = 0;
for (const Peer &peer : asConst(torrentStats.peers)) {
if (counter++ >= announceReq.numwant)
break;
if (peer.endpoint.size() == 6) // IPv4
peerList.emplace_back(peer.endpoint);
else if (peer.endpoint.size() == 18) // IPv6
peer6List.emplace_back(peer.endpoint);
}
lt::entry::list_type peerList;
for (const Peer &p : m_torrents.value(announceReq.infoHash))
peerList.push_back(p.toEntry(announceReq.noPeerId));
replyDict["peers"] = lt::entry(peerList);
replyDict[ANNOUNCE_RESPONSE_PEERS] = peerList; // required, even it's empty
if (!peer6List.empty())
replyDict[ANNOUNCE_RESPONSE_PEERS6] = peer6List;
}
else {
lt::entry::list_type peerList;
int counter = 0;
for (const Peer &peer : torrentStats.peers) {
if (counter++ >= announceReq.numwant)
break;
lt::entry::dictionary_type peerDict = {
{ANNOUNCE_RESPONSE_PEERS_IP, peer.address},
{ANNOUNCE_RESPONSE_PEERS_PORT, peer.port}
};
if (!announceReq.noPeerId)
peerDict[ANNOUNCE_RESPONSE_PEERS_PEER_ID] = peer.peerId.constData();
peerList.emplace_back(peerDict);
}
replyDict[ANNOUNCE_RESPONSE_PEERS] = peerList;
}
const lt::entry replyEntry(replyDict);
// bencode
QByteArray reply;
lt::bencode(std::back_inserter(reply), replyEntry);
qDebug("Tracker: reply with the following bencoded data:\n %s", reply.constData());
// HTTP reply
lt::bencode(std::back_inserter(reply), replyDict);
print(reply, Http::CONTENT_TYPE_TXT);
}

62
src/base/bittorrent/tracker.h

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 Mike Tzou (Chocobo1)
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
*
@ -30,12 +31,15 @@ @@ -30,12 +31,15 @@
#ifndef BITTORRENT_TRACKER_H
#define BITTORRENT_TRACKER_H
#include <libtorrent/fwd.hpp>
#include <string>
#include <libtorrent/entry.hpp>
#include <QHash>
#include <QObject>
#include <QHostAddress>
#include <QSet>
#include "base/bittorrent/infohash.h"
#include "base/http/irequesthandler.h"
#include "base/http/responsebuilder.h"
@ -48,54 +52,58 @@ namespace BitTorrent @@ -48,54 +52,58 @@ namespace BitTorrent
{
struct Peer
{
QHostAddress ip;
QByteArray peerId;
int port;
ushort port = 0; // self-claimed by peer, might not be the same as socket port
bool isSeeder = false;
bool operator!=(const Peer &other) const;
bool operator==(const Peer &other) const;
QString uid() const;
lt::entry toEntry(bool noPeerId) const;
};
// caching precomputed values
lt::entry::string_type address;
lt::entry::string_type endpoint;
struct TrackerAnnounceRequest
{
QByteArray infoHash;
QString event;
int numwant;
Peer peer;
// Extensions
bool noPeerId;
QByteArray uniqueID() const;
};
typedef QHash<QString, Peer> PeerList;
typedef QHash<QByteArray, PeerList> TorrentList;
bool operator==(const Peer &left, const Peer &right);
bool operator!=(const Peer &left, const Peer &right);
uint qHash(const Peer &key, uint seed);
/* Basic Bittorrent tracker implementation in Qt */
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
// *Basic* Bittorrent tracker implementation
// [BEP-3] The BitTorrent Protocol Specification
// also see: https://wiki.theory.org/index.php/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol
class Tracker : public QObject, public Http::IRequestHandler, private Http::ResponseBuilder
{
Q_OBJECT
Q_DISABLE_COPY(Tracker)
struct TrackerAnnounceRequest;
struct TorrentStats
{
qint64 seeders = 0;
QSet<Peer> peers;
void setPeer(const Peer &peer);
bool removePeer(const Peer &peer);
};
public:
explicit Tracker(QObject *parent = nullptr);
~Tracker();
bool start();
Http::Response processRequest(const Http::Request &request, const Http::Environment &env) override;
private:
void respondToAnnounceRequest();
Http::Response processRequest(const Http::Request &request, const Http::Environment &env) override;
void processAnnounceRequest();
void registerPeer(const TrackerAnnounceRequest &announceReq);
void unregisterPeer(const TrackerAnnounceRequest &announceReq);
void replyWithPeerList(const TrackerAnnounceRequest &announceReq);
void prepareAnnounceResponse(const TrackerAnnounceRequest &announceReq);
Http::Server *m_server;
TorrentList m_torrents;
Http::Request m_request;
Http::Environment m_env;
QHash<InfoHash, TorrentStats> m_torrents;
};
}

2
src/base/exceptions.h

@ -34,6 +34,6 @@ @@ -34,6 +34,6 @@
class RuntimeError : public std::runtime_error
{
public:
explicit RuntimeError(const QString &message = "");
explicit RuntimeError(const QString &message = {});
QString message() const;
};

19
src/base/http/httperror.cpp

@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
#include "httperror.h"
HTTPError::HTTPError(int statusCode, const QString &statusText, const QString &message)
HTTPError::HTTPError(const int statusCode, const QString &statusText, const QString &message)
: RuntimeError {message}
, m_statusCode {statusCode}
, m_statusText {statusText}
@ -50,8 +50,8 @@ BadRequestHTTPError::BadRequestHTTPError(const QString &message) @@ -50,8 +50,8 @@ BadRequestHTTPError::BadRequestHTTPError(const QString &message)
{
}
ConflictHTTPError::ConflictHTTPError(const QString &message)
: HTTPError(409, QLatin1String("Conflict"), message)
UnauthorizedHTTPError::UnauthorizedHTTPError(const QString &message)
: HTTPError(401, QLatin1String("Unauthorized"), message)
{
}
@ -65,13 +65,18 @@ NotFoundHTTPError::NotFoundHTTPError(const QString &message) @@ -65,13 +65,18 @@ NotFoundHTTPError::NotFoundHTTPError(const QString &message)
{
}
UnsupportedMediaTypeHTTPError::UnsupportedMediaTypeHTTPError(const QString &message)
: HTTPError(415, QLatin1String("Unsupported Media Type"), message)
MethodNotAllowedHTTPError::MethodNotAllowedHTTPError(const QString &message)
: HTTPError(405, QLatin1String("Method Not Allowed"), message)
{
}
UnauthorizedHTTPError::UnauthorizedHTTPError(const QString &message)
: HTTPError(401, QLatin1String("Unauthorized"), message)
ConflictHTTPError::ConflictHTTPError(const QString &message)
: HTTPError(409, QLatin1String("Conflict"), message)
{
}
UnsupportedMediaTypeHTTPError::UnsupportedMediaTypeHTTPError(const QString &message)
: HTTPError(415, QLatin1String("Unsupported Media Type"), message)
{
}

28
src/base/http/httperror.h

@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
class HTTPError : public RuntimeError
{
public:
HTTPError(int statusCode, const QString &statusText, const QString &message = "");
HTTPError(int statusCode, const QString &statusText, const QString &message = {});
int statusCode() const;
QString statusText() const;
@ -46,41 +46,47 @@ private: @@ -46,41 +46,47 @@ private:
class BadRequestHTTPError : public HTTPError
{
public:
explicit BadRequestHTTPError(const QString &message = "");
explicit BadRequestHTTPError(const QString &message = {});
};
class UnauthorizedHTTPError : public HTTPError
{
public:
explicit UnauthorizedHTTPError(const QString &message = {});
};
class ForbiddenHTTPError : public HTTPError
{
public:
explicit ForbiddenHTTPError(const QString &message = "");
explicit ForbiddenHTTPError(const QString &message = {});
};
class NotFoundHTTPError : public HTTPError
{
public:
explicit NotFoundHTTPError(const QString &message = "");
explicit NotFoundHTTPError(const QString &message = {});
};
class ConflictHTTPError : public HTTPError
class MethodNotAllowedHTTPError : public HTTPError
{
public:
explicit ConflictHTTPError(const QString &message = "");
explicit MethodNotAllowedHTTPError(const QString &message = {});
};
class UnsupportedMediaTypeHTTPError : public HTTPError
class ConflictHTTPError : public HTTPError
{
public:
explicit UnsupportedMediaTypeHTTPError(const QString &message = "");
explicit ConflictHTTPError(const QString &message = {});
};
class UnauthorizedHTTPError : public HTTPError
class UnsupportedMediaTypeHTTPError : public HTTPError
{
public:
explicit UnauthorizedHTTPError(const QString &message = "");
explicit UnsupportedMediaTypeHTTPError(const QString &message = {});
};
class InternalServerErrorHTTPError : public HTTPError
{
public:
explicit InternalServerErrorHTTPError(const QString &message = "");
explicit InternalServerErrorHTTPError(const QString &message = {});
};

2
src/base/http/responsebuilder.cpp

@ -32,7 +32,7 @@ using namespace Http; @@ -32,7 +32,7 @@ using namespace Http;
void ResponseBuilder::status(const uint code, const QString &text)
{
m_response.status = ResponseStatus(code, text);
m_response.status = {code, text};
}
void ResponseBuilder::header(const QString &name, const QString &value)

7
src/base/http/types.h

@ -107,8 +107,6 @@ namespace Http @@ -107,8 +107,6 @@ namespace Http
{
uint code;
QString text;
ResponseStatus(uint code = 200, const QString &text = "OK"): code(code), text(text) {}
};
struct Response
@ -117,7 +115,10 @@ namespace Http @@ -117,7 +115,10 @@ namespace Http
QStringMap headers;
QByteArray content;
Response(uint code = 200, const QString &text = "OK"): status(code, text) {}
Response(uint code = 200, const QString &text = "OK")
: status {code, text}
{
}
};
}

14
src/base/tristatebool.h

@ -50,18 +50,18 @@ public: @@ -50,18 +50,18 @@ public:
TriStateBool &operator=(const TriStateBool &other) = default; // add constexpr when using C++17
constexpr bool operator==(const TriStateBool &other) const
constexpr friend bool operator==(const TriStateBool &left, const TriStateBool &right)
{
return (m_value == other.m_value);
}
constexpr bool operator!=(const TriStateBool &other) const
{
return !operator==(other);
return (left.m_value == right.m_value);
}
private:
signed char m_value = -1; // Undefined by default
};
constexpr bool operator!=(const TriStateBool &left, const TriStateBool &right)
{
return !(left == right);
}
#endif // TRISTATEBOOL_H

2
src/gui/advancedsettings.cpp

@ -228,8 +228,8 @@ void AdvancedSettings::saveAdvancedSettings() @@ -228,8 +228,8 @@ void AdvancedSettings::saveAdvancedSettings()
pref->setSpeedWidgetEnabled(m_checkBoxSpeedWidgetEnabled.isChecked());
// Tracker
session->setTrackerEnabled(m_checkBoxTrackerStatus.isChecked());
pref->setTrackerPort(m_spinBoxTrackerPort.value());
session->setTrackerEnabled(m_checkBoxTrackerStatus.isChecked());
// Choking algorithm
session->setChokingAlgorithm(static_cast<BitTorrent::ChokingAlgorithm>(m_comboBoxChokingAlgorithm.currentIndex()));
// Seed choking algorithm

4
src/webui/api/appcontroller.cpp

@ -713,10 +713,10 @@ void AppController::setPreferencesAction() @@ -713,10 +713,10 @@ void AppController::setPreferencesAction()
if (hasKey("enable_multi_connections_from_same_ip"))
session->setMultiConnectionsPerIpEnabled(it.value().toBool());
// Embedded tracker
if (hasKey("enable_embedded_tracker"))
session->setTrackerEnabled(it.value().toBool());
if (hasKey("embedded_tracker_port"))
pref->setTrackerPort(it.value().toInt());
if (hasKey("enable_embedded_tracker"))
session->setTrackerEnabled(it.value().toBool());
// Choking algorithm
if (hasKey("upload_slots_behavior"))
session->setChokingAlgorithm(static_cast<BitTorrent::ChokingAlgorithm>(it.value().toInt()));

Loading…
Cancel
Save