[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    KDE/kdenetwork/kopete/protocols/yahoo/libkyahoo
From:       Raphael Kubo da Costa <kubito () gmail ! com>
Date:       2011-01-25 1:05:52
Message-ID: 20110125010552.188B8AC8B9 () svn ! kde ! org
[Download RAW message or body]

SVN commit 1216829 by rkcosta:

libkyahoo: Apply some fixes related to sending and receiving files.

From the ReviewBoard request:

  Receive part:
  a) protocol (as reading from various places) suggests that first a
  HEAD and then GET should be done. Current code was asking them in
  parallel.
  b) fixed the way sending the header was done.

  Sending:
  a) changed to an async method of doing the send
  b) send buffer size is dynamic to try to send as much as possible
  c) ... various - made it work.

  Maybe fixes also #194833 although I'm not very sure what is that one
  about.

Original patch by Cristi Posoiu <cristi.posoiu AT gmail>, thanks!

Cannot backport due to the string additions.

BUG: 242557
FIXED-IN: 4.7.0


 M  +73 -11    receivefiletask.cpp  
 M  +2 -0      receivefiletask.h  
 M  +140 -35   sendfiletask.cpp  
 M  +14 -0     sendfiletask.h  


--- trunk/KDE/kdenetwork/kopete/protocols/yahoo/libkyahoo/receivefiletask.cpp \
#1216828:1216829 @@ -131,6 +131,46 @@
 	
 }
 
+
+void ReceiveFileTask::slotHeadComplete( KJob *job )
+{
+	kDebug(YAHOO_RAW_DEBUG) ;
+
+	KIO::TransferJob *transfer = static_cast< KIO::TransferJob * >(job);
+
+	bool got_error = job->error () || transfer->isErrorPage ();
+
+	/* SEEME:
+	   Following check disabled for now because Yahoo is sending a non http conforming \
answer ('\r\n' and *then* the HTTP response headers. +	   That makes kio slave http \
not want to read more and parse the headers.  +	   So.. leaving this comment here \
until yahoo is sending a correct http response so that we can correctly check if the \
HEAD cmd +	   succeeded.
+        */
+#if 0
+	if (!got_error)
+	{
+		got_error = transfer->queryMetaData(QLatin1String("HTTP-Headers")).length() <= 5; \
// I was getting 2 bytes in such error cases. +	}
+#endif
+	if (got_error)
+	{
+		emit error( m_transferId, KIO::ERR_ABORTED, i18n("An error occurred while \
downloading the file.") ); +		setError();
+	}
+	else
+	{
+		// same URL from the HEAD cmd
+		m_transferJob = KIO::get( transfer->url(), KIO::Reload, KIO::HideProgressInfo );
+
+		QObject::connect( m_transferJob, SIGNAL( result( KJob* ) ), this, SLOT( \
slotComplete( KJob* ) ) ); +		QObject::connect( m_transferJob, SIGNAL( data( \
KIO::Job*, const QByteArray & ) ), this, SLOT( slotData( KIO::Job*, const QByteArray \
& ) ) ); +
+        	setCommonTransferMetaData(m_transferJob);
+	}
+	m_mimetypeJob = 0;
+}
+
+
 void ReceiveFileTask::slotComplete( KJob *job )
 {
 	kDebug(YAHOO_RAW_DEBUG) ;
@@ -149,6 +189,7 @@
 		emit complete( m_transferId );
 		setSuccess();
 	}
+	m_transferJob = 0;
 }
 
 void ReceiveFileTask::parseFileTransfer7Info( YMSGTransfer *transfer )
@@ -188,23 +229,36 @@
 	
 		send( t );
 		// The server expects a HTTP HEAD command prior to the GET
-		m_mimetypeJob = KIO::mimetype(QString::fromLatin1("http://%1/relay?token=%2&sender=%3&recver=%4")
                
-				.arg( QString(transfer->firstParam( 250 )) ).arg( QString(transfer->firstParam( \
251 )) ).arg(m_userId).arg(client()->userId()), false); +		m_mimetypeJob = \
KIO::mimetype( +				QString::fromLatin1("http://%1/relay?token=")
+					.arg( QString(transfer->firstParam( 250 )) ) 
+				+
+				QString(QUrl::toPercentEncoding(QString(transfer->firstParam( 251 )))) +
+				QString::fromLatin1("&sender=%1&recver=%2")
+					.arg(m_userId)
+					.arg(client()->userId())
+				, 
+				KIO::HideProgressInfo);
 		m_mimetypeJob->addMetaData("cookies", "manual");
-		m_mimetypeJob->addMetaData("setcookies", QString::fromLatin1("Cookie: T=%1; \
                path=/; domain=.yahoo.com; Y=%2; C=%3;")
-				.arg(client()->tCookie()).arg(client()->yCookie()).arg(client()->cCookie()) );
+		setCommonTransferMetaData(m_mimetypeJob);
+		QObject::connect( m_mimetypeJob, SIGNAL( result( KJob* ) ), this, SLOT( \
slotHeadComplete( KJob* ) ) );  
+	}
+}
 
-		m_transferJob = KIO::get( \
                QString::fromLatin1("http://%1/relay?token=%2&sender=%3&recver=%4")
-				.arg( QString(transfer->firstParam( 250 )) ).arg( QString(transfer->firstParam( \
251 )) ).arg(m_userId).arg(client()->userId()), KIO::NoReload, KIO::HideProgressInfo \
                );
-		QObject::connect( m_transferJob, SIGNAL( result( KJob* ) ), this, SLOT( \
                slotComplete( KJob* ) ) );
-		QObject::connect( m_transferJob, SIGNAL( data( KIO::Job*, const QByteArray & ) ), \
                this, SLOT( slotData( KIO::Job*, const QByteArray & ) ) );
-		m_transferJob->addMetaData("cookies", "manual");
- 		m_transferJob->addMetaData("setcookies", QString::fromLatin1("Cookie: T=%1; \
path=/; domain=.yahoo.com; Y=%2;  path=/; domain=.yahoo.com;") +
+void ReceiveFileTask::setCommonTransferMetaData(KIO::TransferJob* job)
+{
+	job->addMetaData(QString::fromLatin1("accept"), "*/*"); // Accept header
+	job->addMetaData(QString::fromLatin1("UserAgent"), "Mozilla/5.0");
+	job->addMetaData(QString::fromLatin1("cache"), "reload");
+
+	job->addMetaData("cookies", "manual");
+ 	job->addMetaData("setcookies", QString::fromLatin1("Cookie: T=%1; Y=%2;")
  				.arg(client()->tCookie()).arg(client()->yCookie()) );
 	}
-}
 
+
 void ReceiveFileTask::setRemoteUrl( KUrl url )
 {
 	m_remoteUrl = url;
@@ -235,8 +289,16 @@
 	if( m_transferId != id )
 		return;
 	
+	if( m_mimetypeJob )
+	{
+		m_mimetypeJob->kill();
+		m_mimetypeJob = 0L;
+	}
 	if( m_transferJob )
+	{
 		m_transferJob->kill();
+		m_transferJob = 0L;
+	}
 	
 	setError();
 }
--- trunk/KDE/kdenetwork/kopete/protocols/yahoo/libkyahoo/receivefiletask.h \
#1216828:1216829 @@ -62,11 +62,13 @@
 
 private slots:
 	void slotData( KIO::Job *job, const QByteArray &data );
+	void slotHeadComplete( KJob *job );
 	void slotComplete( KJob *job );
 	void canceled( unsigned int );
 
 private:
 	void parseFileTransfer7Info( YMSGTransfer *transfer );
+	void setCommonTransferMetaData(KIO::TransferJob* job);
 
 	KUrl m_remoteUrl;
 	KUrl m_localUrl;
--- trunk/KDE/kdenetwork/kopete/protocols/yahoo/libkyahoo/sendfiletask.cpp \
#1216828:1216829 @@ -18,6 +18,7 @@
 #include "transfer.h"
 #include "ymsgtransfer.h"
 #include "yahootypes.h"
+#include "libyahoo.h"
 #include "client.h"
 #include <qstring.h>
 #include <qtimer.h>
@@ -30,6 +31,13 @@
 
 using namespace KNetwork;
 
+
+/* Buffer size to send file data. Automatically resized. Won't grow up past \
BUFFER_SIZE_MAX. Code will try +   to send as much data as it can in one shot */
+static const int BUFFER_SIZE_INITIAL = 1024;
+static const int BUFFER_SIZE_MAX = (64 * 1024);
+
+
 SendFileTask::SendFileTask(Task* parent) : Task(parent)
 {
 	kDebug(YAHOO_RAW_DEBUG) ;
@@ -177,9 +185,11 @@
 	kDebug(YAHOO_RAW_DEBUG) << "Token: " << m_token;
 
 	m_socket = new KStreamSocket( m_relayHost, QString::number(80) );
-	m_socket->setBlocking( true );
+	m_socket->setBlocking( false );
+	m_socket->enableWrite(true);
 	connect( m_socket, SIGNAL( connected( const KNetwork::KResolverEntry& ) ), this, \
SLOT( connectSucceeded() ) );  connect( m_socket, SIGNAL( gotError(int) ), this, \
SLOT( connectFailed(int) ) ); +	connect( m_socket, SIGNAL( readyWrite() ), this, \
SLOT( transmitHeader() ) );  
 	m_socket->connect();
 
@@ -197,9 +207,6 @@
 {
 	kDebug(YAHOO_RAW_DEBUG) ;
 
-	QByteArray buffer;
-	QDataStream stream( &buffer, QIODevice::WriteOnly );
-
 	if ( m_file.open(QIODevice::ReadOnly ) )
 	{
 		kDebug(YAHOO_RAW_DEBUG) << "File successfully opened. Reading...";
@@ -208,71 +215,169 @@
 	{
 		kDebug(YAHOO_RAW_DEBUG) << "Error opening file: " << m_file.errorString();
 		client()->notifyError( i18n( "An error occurred while sending the file." ), \
m_file.errorString(), Client::Error ); +		m_socket->close();
+		emit error( m_transferId, m_file.error(), m_file.errorString() );
 		setError();
 		return;
 	}
 
 	kDebug(YAHOO_RAW_DEBUG) << "Sizes: File (" << m_url << "): " << m_file.size();
-	QString header = QString::fromLatin1("POST /relay?token=%1&sender=%2&recver=%3 \
HTTP/1.1\r\n" +	QString header = QString::fromLatin1("POST /relay?token=") + 
+		QUrl::toPercentEncoding(m_token) +
+		QString::fromLatin1("&sender=%1&recver=%2 HTTP/1.1\r\n"
 					     "Cache-Control: no-cache\r\n"
-					     "Cookie: T=%4; Y=%5\r\n"
-					     "Host: %6\r\n"
-					     "Content-Length: %7\r\n"
+					     "Cookie: T=%3; Y=%4\r\n"
+					     "Host: %5\r\n"
+					     "Content-Length: %6\r\n"
 					     "User-Agent: Mozilla/5.0\r\n"
 					     "Connection: Close\r\n\r\n")
-		.arg(m_token).arg(client()->userId()).arg(m_target)
+		.arg(client()->userId()).arg(m_target)
 		.arg(client()->tCookie()).arg(client()->yCookie())
 		.arg(m_relayHost)
 		.arg(QString::number(m_file.size()));
-	kDebug() << header;
-	stream.writeRawData( header.toLocal8Bit(), header.length() );
+	m_buffer = header.toLocal8Bit();
+	m_bufferOutPos = 0;
+	m_bufferInPos = m_buffer.size();
+}
 
-	if( !m_socket->write( buffer ) )
+
+void SendFileTask::transmitHeader()
 	{
+	Q_ASSERT(m_bufferOutPos <= m_buffer.size());
+	const int remaining = m_bufferInPos - m_bufferOutPos;
+
+	if (remaining <= 0)
+	{
+		// Go to next step.
+		disconnect( m_socket, SIGNAL( readyWrite() ), this, SLOT( transmitHeader() ) );
+		connect( m_socket, SIGNAL(readyWrite()), this, SLOT(transmitData()) );
+		m_buffer.clear();
+		m_bufferOutPos = 0;
+		m_bufferInPos = 0;
+		m_buffer.resize(BUFFER_SIZE_INITIAL);
+		transmitData(); // since we're just in the situation of being ready to transmit.
+		return;
+	}
+
+
+	kDebug(YAHOO_RAW_DEBUG) << "Trying to send header part: " << \
m_buffer.mid(m_bufferOutPos); +
+	const qint64 written = m_socket->write(m_buffer.constData() + m_bufferOutPos, \
remaining); +	kDebug(YAHOO_RAW_DEBUG) << "  sent " << written << " bytes";
+
+	if (written <= 0)
+	{
 		emit error( m_transferId, m_socket->error(), m_socket->errorString() );
 		m_socket->close();
+		setError();
+		return;
 	}
-	else
+
+	m_bufferOutPos += written;
+}
+
+
+
+bool SendFileTask::checkTransferEnd()
 	{
-		connect( m_socket, SIGNAL(readyWrite()), this, SLOT(transmitData()) );
-		m_socket->enableWrite( true );
+	if (m_transmitted >= m_file.size())
+	{
+		kDebug(YAHOO_RAW_DEBUG) << "Upload Successful: " << m_transmitted;
+		emit complete( m_transferId );
+		setSuccess();
+		m_socket->close();
+		return true;
 	}
+	return false;
 }
 
+
+
+bool SendFileTask::fillSendBuffer()
+{
+	if (checkTransferEnd())
+		return true;
+
+	Q_ASSERT(m_buffer.size() >= m_bufferOutPos);
+	Q_ASSERT(m_buffer.size() >= m_bufferInPos);
+
+	if (m_bufferOutPos >= m_bufferInPos)
+	{
+		m_bufferOutPos = m_bufferInPos = 0;
+	} else 
+	{
+		// we'd like to go with the maximum speed on the netw interface. reading from \
local file is speedy enough (+ caching at disk block layers) +		m_bufferInPos = \
m_buffer.size() - m_bufferOutPos; +		memmove(m_buffer.data(), m_buffer.constData() + \
m_bufferOutPos, m_bufferInPos); +		m_bufferOutPos = 0;
+	}
+
+
+	const int to_read = m_buffer.size() - m_bufferInPos;
+	if (to_read <= 0)
+	{
+		return false; // buffer full. probably shouldn't happen
+	}
+	const qint64 file_read = m_file.read(m_buffer.data() + m_bufferInPos, to_read);
+
+	// kDebug(YAHOO_RAW_DEBUG) << "reading from file: want" << to_read << "got to read" \
<< file_read << " bytes"; +
+	if (file_read < 0)
+	{
+		kDebug(YAHOO_RAW_DEBUG) << "Upload Failed (reading file)!";
+
+		m_buffer.clear();
+		m_buffer.reserve(0); // free memory until this task is reused or freed.
+
+		emit error( m_transferId, m_file.error(), m_file.errorString() );
+		setError();
+		return true;
+	}
+
+	m_bufferInPos += file_read;
+
+	return false;
+}
+
+
 void SendFileTask::transmitData()
 {
 	kDebug(YAHOO_RAW_DEBUG) ;
-	int read = 0;
-	int written = 0;	
-	char buf[1024];
 
-	m_socket->enableWrite( false );
-	read = m_file.read( buf, 1024 );
-	written = m_socket->write( buf, read );
-	kDebug(YAHOO_RAW_DEBUG) << "read:" << read << " written: " << written;
+	if (fillSendBuffer())
+		return;
 
-	m_transmitted += read;
-	emit bytesProcessed( m_transferId, m_transmitted );
+	Q_ASSERT(m_bufferOutPos <= m_bufferInPos);
 
-	if( written != read )
+	const int remaining = m_bufferInPos - m_bufferOutPos;
+
+	const qint64 written = m_socket->write(m_buffer.constData() + m_bufferOutPos, \
remaining); +	if (written <= 0)
 	{
-		kDebug(YAHOO_RAW_DEBUG) << "Upload Failed!";
+		kDebug(YAHOO_RAW_DEBUG) << "Upload Failed (sending data)! toSend=" << remaining << \
"sent=" << written;  emit error( m_transferId, m_socket->error(), \
m_socket->errorString() );  setError();
 		return;
 	}
-	if( m_transmitted == m_file.size() )
+	// kDebug(YAHOO_RAW_DEBUG) << "sending file content: toSend=" << remaining << \
"sent=" << written; +
+	m_transmitted += written;
+	m_bufferOutPos += written;
+        emit bytesProcessed( m_transferId, m_transmitted );
+	if (checkTransferEnd())
+		return;
+	// see if we should do a bit of buffer resizing
+	if ((m_buffer.size() < BUFFER_SIZE_MAX) &&
+		(written >= remaining) && (written >= m_buffer.size()))
 	{
-		kDebug(YAHOO_RAW_DEBUG) << "Upload Successful: " << m_transmitted;
-		emit complete( m_transferId );
-		setSuccess();
-		m_socket->close();
+		// double its size
+		int oldC = m_buffer.size();
+		m_buffer.resize(MIN(2 * oldC, BUFFER_SIZE_MAX));
+		// kDebug(YAHOO_RAW_DEBUG) << "buffer resized from " << oldC << " to " << \
m_buffer.size();  }
-	else
-	{
-		m_socket->enableWrite( true );
 	}
-}
+
+
 void SendFileTask::setTarget( const QString &to )
 {
 	m_target = to;
--- trunk/KDE/kdenetwork/kopete/protocols/yahoo/libkyahoo/sendfiletask.h \
#1216828:1216829 @@ -60,10 +60,17 @@
 
 	QString newYahooTransferId();
 
+	/** returns true if things need to be stopped due to file transfer error or \
completed */ +	bool fillSendBuffer();
+
+	/** returns true if file transfer completed */ 
+	bool checkTransferEnd();
+
 private slots:
 	void connectSucceeded();
 	void connectFailed( int );
 	void transmitData();
+	void transmitHeader();
 	void canceled( unsigned int );
 
 private:
@@ -78,6 +85,13 @@
 	QString m_relayHost;
 	QString m_token;
 	QString m_yahooTransferId;
+
+	/** buffer containing data to be sent */
+	QByteArray m_buffer;
+	/** position (until m_bufferInPos) of data ready to be sent on the wire */
+	int m_bufferOutPos;
+	/** position where next fills from the file should happen */
+	int m_bufferInPos;
 };
 
 #endif


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic