[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: extragear/network/ktorrent/libbtcore
From: Joris Guisson <joris.guisson () gmail ! com>
Date: 2008-08-19 19:20:47
Message-ID: 1219173647.690885.1217.nullmailer () svn ! kde ! org
[Download RAW message or body]
SVN commit 849574 by guisson:
Handle HTTP redirects properly when webseeding
BUG: 165740
M +1 -1 download/downloader.h
M +112 -29 download/httpconnection.cpp
M +13 -1 download/httpconnection.h
M +59 -30 download/webseed.cpp
M +3 -1 download/webseed.h
M +1 -1 interfaces/piecedownloader.h
M +18 -0 net/socket.cpp
M +3 -0 net/socket.h
M +1 -1 net/socketmonitor.cpp
--- trunk/extragear/network/ktorrent/libbtcore/download/downloader.h #849573:849574
@@ -92,7 +92,7 @@
Uint32 getNumWebSeeds() const {return webseeds.count();}
/// Get a webseed
- const WebSeed* getWebSeed(Uint32 i) const {return i < webseeds.count() ? \
webseeds[i] : 0;} + const WebSeed* getWebSeed(Uint32 i) const {return i < \
(Uint32)webseeds.count() ? webseeds[i] : 0;}
/// Add a webseed
WebSeed* addWebSeed(const KUrl & url);
--- trunk/extragear/network/ktorrent/libbtcore/download/httpconnection.cpp \
#849573:849574 @@ -25,18 +25,20 @@
#include <klocale.h>
#include <net/socketmonitor.h>
#include <util/log.h>
+#include <util/functions.h>
#include "btversion.h"
namespace bt
{
- HttpConnection::HttpConnection() : \
sock(0),state(IDLE),mutex(QMutex::Recursive),using_proxy(false) \
+ HttpConnection::HttpConnection() : \
sock(0),state(IDLE),mutex(QMutex::Recursive),request(0),using_proxy(false) {
status = i18n("Not connected");
connect(&reply_timer,SIGNAL(timeout()),this,SLOT(replyTimeout()));
connect(&connect_timer,SIGNAL(timeout()),this,SLOT(connectTimeout()));
up_gid = down_gid = 0;
+ close_when_finished = false;
}
@@ -48,7 +50,8 @@
delete sock;
}
- qDeleteAll(requests);
+ if (request)
+ delete request;
}
void HttpConnection::setGroupIDs(Uint32 up,Uint32 down)
@@ -86,6 +89,12 @@
return state == CLOSED || (sock && !sock->ok());
}
+ bool HttpConnection::ready() const
+ {
+ QMutexLocker locker(&mutex);
+ return !request;
+ }
+
void HttpConnection::connectToProxy(const QString & proxy,Uint16 proxy_port)
{
using_proxy = true;
@@ -108,7 +117,7 @@
{
QMutexLocker locker(&mutex);
- if (state != ERROR && requests.count() > 0)
+ if (state != ERROR && request)
{
if (size == 0)
{
@@ -118,13 +127,12 @@
}
else
{
- HttpGet* g = requests.front();
- if (!g->onDataReady(buf,size))
+ if (!request->onDataReady(buf,size))
{
state = ERROR;
- status = i18n("Error: request failed: %1",g->failure_reason);
+ status = i18n("Error: request failed: %1",request->failure_reason);
}
- else if (g->response_header_received)
+ else if (request->response_header_received)
reply_timer.stop();
}
}
@@ -151,7 +159,7 @@
}
else if (state == ACTIVE)
{
- HttpGet* g = requests.front();
+ HttpGet* g = request;
if (g->request_sent)
return 0;
@@ -180,11 +188,10 @@
if (state == CONNECTING)
return true;
- if (state == ERROR || requests.count() == 0)
+ if (state == ERROR || !request)
return false;
- HttpGet* g = requests.front();
- return !g->request_sent;
+ return !request->request_sent;
}
void HttpConnection::hostResolved(KNetwork::KResolverResults res)
@@ -192,25 +199,27 @@
if (res.count() > 0)
{
KNetwork::KInetSocketAddress addr = res.front().address();
- sock = new net::BufferedSocket(true,addr.ipVersion());
- sock->setNonBlocking();
- sock->setReader(this);
- sock->setWriter(this);
- sock->setGroupID(up_gid,true);
- sock->setGroupID(down_gid,false);
+ if (!sock)
+ {
+ sock = new net::BufferedSocket(true,addr.ipVersion());
+ sock->setNonBlocking();
+ sock->setReader(this);
+ sock->setWriter(this);
+ sock->setGroupID(up_gid,true);
+ sock->setGroupID(down_gid,false);
+ net::SocketMonitor::instance().add(sock);
+ }
if (sock->connectTo(addr))
{
status = i18n("Connected");
state = ACTIVE;
- net::SocketMonitor::instance().add(sock);
net::SocketMonitor::instance().signalPacketReady();
}
else if (sock->state() == net::Socket::CONNECTING)
{
status = i18n("Connecting");
state = CONNECTING;
- net::SocketMonitor::instance().add(sock);
net::SocketMonitor::instance().signalPacketReady();
// 60 second connect timeout
connect_timer.start(60000);
@@ -233,11 +242,10 @@
bool HttpConnection::get(const QString & host,const QString & path,bt::Uint64 \
start,bt::Uint64 len) {
QMutexLocker locker(&mutex);
- if (state == ERROR)
+ if (state == ERROR || request)
return false;
- HttpGet* g = new HttpGet(host,path,start,len,using_proxy);
- requests.append(g);
+ request = new HttpGet(host,path,start,len,using_proxy);
net::SocketMonitor::instance().signalPacketReady();
return true;
}
@@ -245,14 +253,27 @@
bool HttpConnection::getData(QByteArray & data)
{
QMutexLocker locker(&mutex);
- if (requests.count() == 0)
+ if (!request)
return false;
- HttpGet* g = requests.front();
+ HttpGet* g = request;
+ if (g->redirected)
+ {
+ // wait until we have the entire content if we are redirected
+ if (g->data_received < g->content_length)
+ return false;
+
+ // we have the content so we can redirect the connection
+ KUrl u = g->redirected_to;
+ redirected(u);
+ return false;
+ }
+
if (g->piece_data.size() == 0)
{
if (!g->request_sent)
net::SocketMonitor::instance().signalPacketReady();
+
return false;
}
@@ -264,9 +285,14 @@
if (g->piece_data.size() == 0 && g->finished())
{
delete g;
- requests.pop_front();
- if (requests.size() > 0)
- net::SocketMonitor::instance().signalPacketReady();
+ request = 0;
+ if (close_when_finished)
+ {
+ state = CLOSED;
+ Out(SYS_CON|LOG_DEBUG) << "HttpConnection: closing connection due to \
redirection" << endl; + // reset connection
+ sock->reset();
+ }
}
return true;
@@ -276,7 +302,10 @@
{
QMutexLocker locker(&mutex);
if (sock)
+ {
+ sock->updateSpeeds(bt::GetCurrentTime());
return sock->getDownloadRate();
+ }
else
return 0;
}
@@ -300,9 +329,41 @@
reply_timer.stop();
}
+ void HttpConnection::redirected(const KUrl & url)
+ {
+ // Note: mutex is locked in onDataReady
+ bt::Uint64 start = request->start;
+ bt::Uint64 len = request->len;
+ QString host = request->host;
+
+ // if we are using a proxy or the host hasn't changed, just send another request \
with the new path + if (using_proxy || url.host() == host)
+ {
+ delete request;
+ request = new HttpGet(url.host(),url.path(),start,len,using_proxy);
+ net::SocketMonitor::instance().signalPacketReady();
+ }
+ else
+ {
+ // reset socket for new connection
+ sock->reset();
+ // delete request
+ delete request;
+ request = 0;
+ // reset state to IDLE
+ state = IDLE;
+ reply_timer.stop();
+ // connect new location
+ connectTo(url);
+ // do a new get
+ get(url.host(),url.path(),start,len);
+ close_when_finished = true; // we have been redirected to a new host, so close \
connectin when finished + }
+ }
+
////////////////////////////////////////////
- HttpConnection::HttpGet::HttpGet(const QString & host,const QString & \
path,bt::Uint64 start,bt::Uint64 len,bool using_proxy) : \
path(path),start(start),len(len),data_received(0),bytes_sent(0),response_header_received(false),request_sent(false)
+ HttpConnection::HttpGet::HttpGet(const QString & host,const QString & \
path,bt::Uint64 start,bt::Uint64 len,bool using_proxy) : \
host(host),path(path),start(start),len(len),data_received(0),bytes_sent(0),response_header_received(false),request_sent(false)
{
QHttpRequestHeader request("GET",!using_proxy ? path : \
QString("http://%1/%2").arg(host).arg(path)); request.setValue("Host",host);
@@ -319,6 +380,8 @@
else
request.setValue("Connection","Keep-Alive");
buffer = request.toString().toLocal8Bit();
+ redirected = false;
+ content_length = 0;
// Out(SYS_CON|LOG_DEBUG) << "HttpConnection: sending http request:" << endl;
// Out(SYS_CON|LOG_DEBUG) << request.toString() << endl;
}
@@ -340,10 +403,30 @@
response_header_received = true;
QHttpResponseHeader hdr(QString::fromLocal8Bit(buffer.mid(0,idx + 4)));
+ if (hdr.hasKey("Content-Length"))
+ content_length = hdr.value("Content-Length").toInt();
+ else
+ content_length = 0;
+
// Out(SYS_CON|LOG_DEBUG) << "HttpConnection: http reply header received" << endl;
// Out(SYS_CON|LOG_DEBUG) << hdr.toString() << endl;
- if (! (hdr.statusCode() == 200 || hdr.statusCode() == 206))
+ if ((hdr.statusCode() >= 300 && hdr.statusCode() <= 303) || hdr.statusCode() == \
307) {
+ // we got redirected to somewhere else
+ if (!hdr.hasKey("Location"))
+ {
+ failure_reason = i18n("Redirected without a new location !");
+ return false;
+ }
+ else
+ {
+ Out(SYS_CON|LOG_DEBUG) << "Redirected to " << hdr.value("Location") << endl;
+ redirected = true;
+ redirected_to = KUrl(hdr.value("Location"));
+ }
+ }
+ else if (! (hdr.statusCode() == 200 || hdr.statusCode() == 206))
+ {
failure_reason = hdr.reasonPhrase();
return false;
}
--- trunk/extragear/network/ktorrent/libbtcore/download/httpconnection.h \
#849573:849574 @@ -25,6 +25,7 @@
#include <QTimer>
#include <QHttpRequestHeader>
#include <k3resolver.h>
+#include <kurl.h>
#include <net/bufferedsocket.h>
class KUrl;
@@ -49,6 +50,7 @@
struct HttpGet
{
+ QString host;
QString path;
bt::Uint64 start;
bt::Uint64 len;
@@ -59,6 +61,9 @@
bool response_header_received;
bool request_sent;
QString failure_reason;
+ bool redirected;
+ KUrl redirected_to;
+ bt::Uint64 content_length;
HttpGet(const QString & host,const QString & path,bt::Uint64 start,bt::Uint64 \
len,bool using_proxy); virtual ~HttpGet();
@@ -70,12 +75,13 @@
net::BufferedSocket* sock;
State state;
mutable QMutex mutex;
- QList<HttpGet*> requests;
+ HttpGet* request;
bool using_proxy;
QString status;
QTimer connect_timer;
QTimer reply_timer;
Uint32 up_gid,down_gid;
+ bool close_when_finished;
public:
HttpConnection();
virtual ~HttpConnection();
@@ -109,6 +115,9 @@
/// Has the connection been closed
bool closed() const;
+ /// Ready to do another request
+ bool ready() const;
+
/**
* Do a HTTP GET request
* @param path The path of the file
@@ -138,6 +147,9 @@
void hostResolved(KNetwork::KResolverResults res);
void connectTimeout();
void replyTimeout();
+
+ private:
+ void redirected(const KUrl & url);
};
}
--- trunk/extragear/network/ktorrent/libbtcore/download/webseed.cpp #849573:849574
@@ -126,6 +126,29 @@
else
return 0;
}
+
+ void WebSeed::connectToServer()
+ {
+ if (!proxy_enabled)
+ {
+ QString proxy = KProtocolManager::proxyForUrl(url); // Use KDE settings
+ if (proxy.isNull() || proxy == "DIRECT")
+ conn->connectTo(url); // direct connection
+ else
+ {
+ KUrl proxy_url(proxy);
+ conn->connectToProxy(proxy_url.host(),proxy_url.port() <= 0 ? 80 : \
proxy_url.port()); + }
+ }
+ else
+ {
+ if (proxy_host.isNull())
+ conn->connectTo(url); // direct connection
+ else
+ conn->connectToProxy(proxy_host,proxy_port); // via a proxy
+ }
+ status = conn->getStatusString();
+ }
void WebSeed::download(Uint32 first,Uint32 last)
{
@@ -148,39 +171,22 @@
if (!conn->connected())
{
- if (!proxy_enabled)
- {
- QString proxy = KProtocolManager::proxyForUrl(url); // Use KDE settings
- if (proxy.isNull() || proxy == "DIRECT")
- conn->connectTo(url); // direct connection
- else
- {
- KUrl proxy_url(proxy);
- conn->connectToProxy(proxy_url.host(),proxy_url.port() <= 0 ? 80 : \
proxy_url.port());
- }
- }
- else
- {
- if (proxy_host.isNull())
- conn->connectTo(url); // direct connection
- else
- conn->connectToProxy(proxy_host,proxy_port); // via a proxy
- }
- status = conn->getStatusString();
+ connectToServer();
}
if (tor.isMultiFile())
{
// make the list of ranges to download
- QList<Range> ranges;
- for (Uint32 i = first_chunk;i != last_chunk;i++)
+ for (Uint32 i = first_chunk;i <= last_chunk;i++)
{
- doChunk(i,ranges);
+ fillRangeList(i);
}
- // add a request for each range
- foreach (const Range & r,ranges)
+ if (range_queue.count() > 0)
{
+ // send the first request
+ Range r = range_queue[0];
+ range_queue.pop_front();
const TorrentFile & tf = tor.getFile(r.file);
conn->get(url.host(),path + '/' + tf.getPath(),r.off,r.len);
}
@@ -282,6 +288,29 @@
num_failures = 0;
finished();
}
+
+ if (range_queue.count() > 0 && conn->ready())
+ {
+ if (conn->closed())
+ {
+ // after a redirect it is possible that the connection is closed
+ // so we need to reconnect to the old url
+ delete conn;
+ conn = new HttpConnection();
+ conn->setGroupIDs(up_gid,down_gid);
+ connectToServer();
+ }
+
+ QString path = url.path();
+ if (path.endsWith('/'))
+ path += tor.getNameSuggestion();
+
+ // ask for the next range
+ Range r = range_queue[0];
+ range_queue.pop_front();
+ const TorrentFile & tf = tor.getFile(r.file);
+ conn->get(url.host(),path + '/' + tf.getPath(),r.off,r.len);
+ }
status = conn->getStatusString();
}
@@ -326,7 +355,7 @@
}
}
- void WebSeed::doChunk(Uint32 chunk,QList<Range> & ranges)
+ void WebSeed::fillRangeList(Uint32 chunk)
{
QList<Uint32> tflist;
tor.calcChunkPos(chunk,tflist);
@@ -350,15 +379,15 @@
r.len = tf.getSize();
// add the range
- if (ranges.count() == 0)
- ranges.append(r);
- else if (ranges.back().file != r.file)
- ranges.append(r);
+ if (range_queue.count() == 0)
+ range_queue.append(r);
+ else if (range_queue.back().file != r.file)
+ range_queue.append(r);
else
{
// the last range and this one are in the same file
// so expand it
- Range & l = ranges.back();
+ Range & l = range_queue.back();
l.len += r.len;
}
--- trunk/extragear/network/ktorrent/libbtcore/download/webseed.h #849573:849574
@@ -138,10 +138,11 @@
Uint64 len;
};
- void doChunk(Uint32 chunk,QList<Range> & ranges);
+ void fillRangeList(Uint32 chunk);
void handleData(const QByteArray & data);
void chunkStarted(Uint32 chunk);
void chunkStopped();
+ void connectToServer();
private:
const Torrent & tor;
@@ -156,6 +157,7 @@
Uint32 downloaded;
WebSeedChunkDownload* current;
Uint32 up_gid,down_gid;
+ QList<Range> range_queue;
static QString proxy_host;
static Uint16 proxy_port;
--- trunk/extragear/network/ktorrent/libbtcore/interfaces/piecedownloader.h \
#849573:849574 @@ -115,7 +115,7 @@
* overridden by subclasses.
* @param idx The Chunk's index
*/
- virtual bool hasChunk(bt::Uint32 idx) const {return true;}
+ virtual bool hasChunk(bt::Uint32 /*idx*/) const {return true;}
/**
* Check if requests have timedout
--- trunk/extragear/network/ktorrent/libbtcore/net/socket.cpp #849573:849574
@@ -108,6 +108,24 @@
}
}
+ void Socket::reset()
+ {
+ close();
+ int fd = socket(m_ip_version == 4 ? PF_INET : PF_INET6,SOCK_STREAM,0);
+ if (fd < 0)
+ Out(SYS_GEN|LOG_IMPORTANT) << QString("Cannot create socket : \
%1").arg(strerror(errno)) << endl; + m_fd = fd;
+
+#if defined(Q_OS_MACX) || defined(Q_OS_DARWIN) || (defined(Q_OS_FREEBSD) && \
__FreeBSD_version < 600020 && !defined(__DragonFly__)) + int val = 1;
+ if (setsockopt(m_fd,SOL_SOCKET,SO_NOSIGPIPE,&val,sizeof(int)) < 0)
+ {
+ Out(SYS_CON|LOG_NOTICE) << QString("Failed to set the NOSIGPIPE option : \
%1").arg(strerror(errno)) << endl; + }
+#endif
+ m_state = IDLE;
+ }
+
void Socket::close()
{
if (m_fd >= 0)
--- trunk/extragear/network/ktorrent/libbtcore/net/socket.h #849573:849574
@@ -65,7 +65,10 @@
bool isIPv4() const {return m_ip_version == 4;}
bool isIPv6() const {return m_ip_version == 6;}
+ /// reset the socket (i.e. close it and create a new one)
+ void reset();
+
Uint32 bytesAvailable() const;
/**
--- trunk/extragear/network/ktorrent/libbtcore/net/socketmonitor.cpp #849573:849574
@@ -33,7 +33,7 @@
{
SocketMonitor SocketMonitor::self;
- SocketMonitor::SocketMonitor() : ut(0),dt(0),next_group_id(1)
+ SocketMonitor::SocketMonitor() : \
mutex(QMutex::Recursive),ut(0),dt(0),next_group_id(1) {
dt = new DownloadThread(this);
ut = new UploadThread(this);
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic