[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