[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