- Improved a lot downloading from url using libcommoncpp2 instead of libcurl

- TODO soon:
  - Updated configure file
	- Add url download error handling
This commit is contained in:
Christophe Dumez 2007-07-20 15:55:50 +00:00
parent 347b03d699
commit 7c8677d787
7 changed files with 61 additions and 93 deletions

View file

@ -24,6 +24,7 @@
- FEATURE: Real torrent share ratio is now displayed in transfer list - FEATURE: Real torrent share ratio is now displayed in transfer list
- FEATURE: Added keyboard shortcuts for main actions (see wiki) - FEATURE: Added keyboard shortcuts for main actions (see wiki)
- FEATURE: Added a popup menu to set priority for multiple fies at once - FEATURE: Added a popup menu to set priority for multiple fies at once
- FEATURE: Improved a lot downloading from urls (using libcommoncpp2 instead of libcurl)
- I18N: Added Hungarian translation - I18N: Added Hungarian translation
- BUGFIX: Progress of paused torrents is now correct on restart - BUGFIX: Progress of paused torrents is now correct on restart
- BUGFIX: Progress column gets sorted on restart it is was during last execution - BUGFIX: Progress column gets sorted on restart it is was during last execution

3
TODO
View file

@ -42,9 +42,8 @@
- update sorting when a new torrent is added? - update sorting when a new torrent is added?
- Allow to hide columns (gtsoul) - Allow to hide columns (gtsoul)
- Complete documentation and english translation - Complete documentation and english translation
- Fix downloader thread (again). Try to use another lib than libcurl:
- libcommoncpp has a URLStream class. This C++ class allow you to download a file using HTTP. See demo/urlfetch.cpp in commoncpp2-1.3.19.tar.gz
* beta2 * beta2
- Fix configure file for libcommoncpp
- Wait for some bug fixes in libtorrent : - Wait for some bug fixes in libtorrent :
- upload/download limit per torrent - upload/download limit per torrent
- ipfilter crash - ipfilter crash

View file

@ -50,7 +50,7 @@ bittorrent::bittorrent(){
ETARefresher.start(6000); ETARefresher.start(6000);
// To download from urls // To download from urls
downloader = new downloadThread(this); downloader = new downloadThread(this);
connect(downloader, SIGNAL(downloadFinished(const QString&, const QString&, int, const QString&)), this, SLOT(processDownloadedFile(const QString&, const QString&, int, const QString&))); connect(downloader, SIGNAL(downloadFinished(const QString&, const QString&)), this, SLOT(processDownloadedFile(const QString&, const QString&)));
} }
// Main destructor // Main destructor
@ -1013,13 +1013,13 @@ void bittorrent::downloadFromUrl(const QString& url){
} }
// Add to bittorrent session the downloaded torrent file // Add to bittorrent session the downloaded torrent file
void bittorrent::processDownloadedFile(const QString& url, const QString& file_path, int return_code, const QString& errorBuffer){ void bittorrent::processDownloadedFile(const QString& url, const QString& file_path){
if(return_code){ // if(return_code){
// Download failed // // Download failed
emit downloadFromUrlFailure(url, errorBuffer); // emit downloadFromUrlFailure(url, errorBuffer);
QFile::remove(file_path); // QFile::remove(file_path);
return; // return;
} // }
// Add file to torrent download list // Add file to torrent download list
emit newDownloadedTorrent(file_path, url); emit newDownloadedTorrent(file_path, url);
} }

View file

@ -136,7 +136,7 @@ class bittorrent : public QObject{
void cleanDeleter(deleteThread* deleter); void cleanDeleter(deleteThread* deleter);
void scanDirectory(); void scanDirectory();
void readAlerts(); void readAlerts();
void processDownloadedFile(const QString&, const QString&, int, const QString&); void processDownloadedFile(const QString&, const QString&);
void resumeUnfinished(); void resumeUnfinished();
bool loadTrackerFile(const QString& hash); bool loadTrackerFile(const QString& hash);
void saveTrackerFile(const QString& hash); void saveTrackerFile(const QString& hash);

View file

@ -28,11 +28,15 @@
#include <QMutex> #include <QMutex>
#include <QMutexLocker> #include <QMutexLocker>
#include <QWaitCondition> #include <QWaitCondition>
#include <curl/curl.h>
#include <iostream> #include <iostream>
#include <cc++/common.h>
#include "misc.h" #include "misc.h"
#ifdef CCXX_NAMESPACES
using namespace std;
using namespace ost;
#endif
class downloadThread : public QThread { class downloadThread : public QThread {
Q_OBJECT Q_OBJECT
@ -41,9 +45,10 @@ class downloadThread : public QThread {
QMutex mutex; QMutex mutex;
QWaitCondition condition; QWaitCondition condition;
bool abort; bool abort;
URLStream url_stream;
signals: signals:
void downloadFinished(const QString& url, const QString& file_path, int return_code, const QString& errorBuffer); void downloadFinished(const QString& url, const QString& file_path);
public: public:
downloadThread(QObject* parent) : QThread(parent){ downloadThread(QObject* parent) : QThread(parent){
@ -84,80 +89,41 @@ class downloadThread : public QThread {
QString url = url_list.takeFirst(); QString url = url_list.takeFirst();
mutex.unlock(); mutex.unlock();
qDebug("In Download thread RUN, mutex unlocked (got url)"); qDebug("In Download thread RUN, mutex unlocked (got url)");
CURL *curl;
QString filePath;
int return_code, response;
// XXX: Trick to get a unique filename // XXX: Trick to get a unique filename
QTemporaryFile *tmpfile = new QTemporaryFile; QString filePath;
QTemporaryFile *tmpfile = new QTemporaryFile();
if (tmpfile->open()) { if (tmpfile->open()) {
filePath = tmpfile->fileName(); filePath = tmpfile->fileName();
} }
delete tmpfile; delete tmpfile;
if(abort) QFile dest_file(filePath);
return; if(!dest_file.open(QIODevice::WriteOnly | QIODevice::Text)){
FILE *file = fopen((const char*)filePath.toUtf8(), "w"); std::cerr << "Error: could't create temporary file: " << (const char*)filePath.toUtf8() << '\n';
if(!file){ continue;
std::cerr << "Error: could not open temporary file...\n";
return;
} }
// Initilization required by libcurl URLStream::Error status = url_stream.get((const char*)url.toUtf8());
curl = curl_easy_init(); if(status){
if(!curl){ // Failure
std::cerr << "Error: Failed to init curl...\n"; //TODO: handle this
fclose(file); QString error_msg = QString(misc::toString(status).c_str());
return; qDebug("Download failed for %s, reason: %s", (const char*)url.toUtf8(), (const char*)error_msg.toUtf8());
url_stream.close();
continue;
} }
// Set url to download qDebug("Downloading %s...", (const char*)url.toUtf8());
curl_easy_setopt(curl, CURLOPT_URL, url.toLocal8Bit().constData()); char cbuf[1024];
qDebug("Url: %s", url.toLocal8Bit().constData()); int len;
// Define our callback to get called when there's data to be written while(!url_stream.eof()) {
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, misc::my_fwrite); url_stream.read(cbuf, sizeof(cbuf));
// Set destination file len = url_stream.gcount();
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); if(len > 0){
// Some SSL mambo jambo dest_file.write(cbuf, len);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); }
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
// Disable progress meter
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
// Any kind of authentication
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
// Auto referrer
curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1);
// Follow redirections
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
// Enable cookies
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
// We want error message:
char errorBuffer[CURL_ERROR_SIZE];
errorBuffer[0]=0; /* prevent junk from being output */
return_code = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
if(return_code){
std::cerr << "Error: failed to set error buffer in curl\n";
fclose(file);
QFile::remove(filePath);
return;
} }
unsigned short retries = 0; dest_file.close();
bool to_many_users = false; url_stream.close();
do{ emit downloadFinished(url, filePath);
// Perform Download qDebug("In Download thread RUN, signal emitted");
return_code = curl_easy_perform(curl);
// We want HTTP response code
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
qDebug("HTTP response code: %d", response);
if(response/100 == 5){
to_many_users = true;
++retries;
SleeperThread::msleep(1000);
}
}while(to_many_users && retries < 10 && response!=0);
// Cleanup
curl_easy_cleanup(curl);
// Close tmp file
fclose(file);
emit downloadFinished(url, filePath, return_code, QString(errorBuffer));
qDebug("In Download thread RUN, signal emitted, ErrorBuffer: %s", errorBuffer);
}else{ }else{
qDebug("In Download thread RUN, mutex still locked (no urls) -> sleeping"); qDebug("In Download thread RUN, mutex still locked (no urls) -> sleeping");
condition.wait(&mutex); condition.wait(&mutex);

View file

@ -137,21 +137,21 @@ class RssStream : public QObject{
public slots : public slots :
// read and store the downloaded rss' informations // read and store the downloaded rss' informations
void processDownloadedFile(const QString&, const QString& file_path, int return_code, const QString&) { void processDownloadedFile(const QString&, const QString& file_path) {
// delete the former file // delete the former file
if(QFile::exists(filePath)) { if(QFile::exists(filePath)) {
QFile::remove(filePath); QFile::remove(filePath);
} }
filePath = file_path; filePath = file_path;
if(return_code){ // if(return_code){
// Download failed // // Download failed
qDebug("(download failure) "+file_path.toUtf8()); // qDebug("(download failure) "+file_path.toUtf8());
if(QFile::exists(filePath)) { // if(QFile::exists(filePath)) {
QFile::remove(filePath); // QFile::remove(filePath);
} // }
emit refreshFinished(url, NEWS); // emit refreshFinished(url, NEWS);
return; // return;
} // }
openRss(); openRss();
emit refreshFinished(url, NEWS); emit refreshFinished(url, NEWS);
} }
@ -189,7 +189,7 @@ class RssStream : public QObject{
read = true; read = true;
downloaderRss = new downloadThread(this); downloaderRss = new downloadThread(this);
downloaderIcon = new downloadThread(this); downloaderIcon = new downloadThread(this);
connect(downloaderRss, SIGNAL(downloadFinished(const QString&, const QString&, int, const QString&)), this, SLOT(processDownloadedFile(const QString&, const QString&, int, const QString&))); connect(downloaderRss, SIGNAL(downloadFinished(const QString&, const QString&)), this, SLOT(processDownloadedFile(const QString&, const QString&)));
downloaderRss->downloadUrl(url); downloaderRss->downloadUrl(url);
// XXX: remove it when gif can be displayed // XXX: remove it when gif can be displayed
iconPath = ":/Icons/rss.png"; iconPath = ":/Icons/rss.png";
@ -216,7 +216,6 @@ class RssStream : public QObject{
} }
void refresh() { void refresh() {
connect(downloaderRss, SIGNAL(downloadFinished(const QString&, const QString&, int, const QString&)), this, SLOT(processDownloadedFile(const QString&, const QString&, int, const QString&)));
downloaderRss->downloadUrl(url); downloaderRss->downloadUrl(url);
lastRefresh.start(); lastRefresh.start();
} }

View file

@ -2,6 +2,9 @@
LANG_PATH = lang LANG_PATH = lang
ICONS_PATH = Icons ICONS_PATH = Icons
LIBS += -lccext2
LIBS += -lccgnu2
#Set the following variable to 1 to enable debug #Set the following variable to 1 to enable debug
DEBUG_MODE = 1 DEBUG_MODE = 1