source: trunk/src/network/access/qhttpnetworkconnection.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 35.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <private/qabstractsocket_p.h>
43#include "qhttpnetworkconnection_p.h"
44#include "qhttpnetworkconnectionchannel_p.h"
45#include "private/qnoncontiguousbytedevice_p.h"
46#include <private/qnetworkrequest_p.h>
47#include <private/qobject_p.h>
48#include <private/qauthenticator_p.h>
49#include <qnetworkproxy.h>
50#include <qauthenticator.h>
51
52#include <qbuffer.h>
53#include <qpair.h>
54#include <qhttp.h>
55#include <qdebug.h>
56
57#ifndef QT_NO_HTTP
58
59#ifndef QT_NO_OPENSSL
60# include <private/qsslsocket_p.h>
61# include <QtNetwork/qsslkey.h>
62# include <QtNetwork/qsslcipher.h>
63# include <QtNetwork/qsslconfiguration.h>
64#endif
65
66
67
68QT_BEGIN_NAMESPACE
69
70#ifdef Q_OS_SYMBIAN
71const int QHttpNetworkConnectionPrivate::defaultChannelCount = 3;
72#else
73const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6;
74#endif
75
76// The pipeline length. So there will be 4 requests in flight.
77const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
78// Only re-fill the pipeline if there's defaultRePipelineLength slots free in the pipeline.
79// This means that there are 2 requests in flight and 2 slots free that will be re-filled.
80const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
81
82
83QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
84: state(RunningState),
85 hostName(hostName), port(port), encrypt(encrypt),
86 channelCount(defaultChannelCount)
87#ifndef QT_NO_NETWORKPROXY
88 , networkProxy(QNetworkProxy::NoProxy)
89#endif
90{
91 channels = new QHttpNetworkConnectionChannel[channelCount];
92}
93
94QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
95: state(RunningState),
96 hostName(hostName), port(port), encrypt(encrypt),
97 channelCount(channelCount)
98#ifndef QT_NO_NETWORKPROXY
99 , networkProxy(QNetworkProxy::NoProxy)
100#endif
101{
102 channels = new QHttpNetworkConnectionChannel[channelCount];
103}
104
105
106
107QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
108{
109 for (int i = 0; i < channelCount; ++i) {
110 if (channels[i].socket) {
111 channels[i].socket->close();
112 delete channels[i].socket;
113 }
114 }
115 delete []channels;
116}
117
118void QHttpNetworkConnectionPrivate::init()
119{
120 for (int i = 0; i < channelCount; i++) {
121 channels[i].setConnection(this->q_func());
122 channels[i].init();
123 }
124}
125
126void QHttpNetworkConnectionPrivate::pauseConnection()
127{
128 state = PausedState;
129
130 // Disable all socket notifiers
131 for (int i = 0; i < channelCount; i++) {
132#ifndef QT_NO_OPENSSL
133 if (encrypt)
134 QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
135 else
136#endif
137 QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket);
138 }
139}
140
141void QHttpNetworkConnectionPrivate::resumeConnection()
142{
143 state = RunningState;
144 // Enable all socket notifiers
145 for (int i = 0; i < channelCount; i++) {
146#ifndef QT_NO_OPENSSL
147 if (encrypt)
148 QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
149 else
150#endif
151 QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket);
152
153 // Resume pending upload if needed
154 if (channels[i].state == QHttpNetworkConnectionChannel::WritingState)
155 QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
156 }
157
158 // queue _q_startNextRequest
159 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
160}
161
162int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
163{
164 for (int i = 0; i < channelCount; ++i)
165 if (channels[i].socket == socket)
166 return i;
167
168 qFatal("Called with unknown socket object.");
169 return 0;
170}
171
172qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
173{
174 return reply.d_func()->responseData.byteAmount();
175}
176
177qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
178{
179 return reply.d_func()->responseData.sizeNextBlock();
180}
181
182void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
183{
184 QHttpNetworkRequest &request = messagePair.first;
185 QHttpNetworkReply *reply = messagePair.second;
186
187 // add missing fields for the request
188 QByteArray value;
189 // check if Content-Length is provided
190 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
191 if (uploadByteDevice) {
192 if (request.contentLength() != -1 && uploadByteDevice->size() != -1) {
193 // both values known, take the smaller one.
194 request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength()));
195 } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) {
196 // content length not supplied by user, but the upload device knows it
197 request.setContentLength(uploadByteDevice->size());
198 } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) {
199 // everything OK, the user supplied us the contentLength
200 } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) {
201 qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
202 }
203 }
204 // set the Connection/Proxy-Connection: Keep-Alive headers
205#ifndef QT_NO_NETWORKPROXY
206 if (networkProxy.type() == QNetworkProxy::HttpCachingProxy) {
207 value = request.headerField("proxy-connection");
208 if (value.isEmpty())
209 request.setHeaderField("Proxy-Connection", "Keep-Alive");
210 } else {
211#endif
212 value = request.headerField("connection");
213 if (value.isEmpty())
214 request.setHeaderField("Connection", "Keep-Alive");
215#ifndef QT_NO_NETWORKPROXY
216 }
217#endif
218
219 // If the request had a accept-encoding set, we better not mess
220 // with it. If it was not set, we announce that we understand gzip
221 // and remember this fact in request.d->autoDecompress so that
222 // we can later decompress the HTTP reply if it has such an
223 // encoding.
224 value = request.headerField("accept-encoding");
225 if (value.isEmpty()) {
226#ifndef QT_NO_COMPRESS
227 request.setHeaderField("Accept-Encoding", "gzip");
228 request.d->autoDecompress = true;
229#else
230 // if zlib is not available set this to false always
231 request.d->autoDecompress = false;
232#endif
233 }
234
235 // some websites mandate an accept-language header and fail
236 // if it is not sent. This is a problem with the website and
237 // not with us, but we work around this by setting
238 // one always.
239 value = request.headerField("accept-language");
240 if (value.isEmpty()) {
241 QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-'));
242 QString acceptLanguage;
243 if (systemLocale == QLatin1String("C"))
244 acceptLanguage = QString::fromAscii("en,*");
245 else if (systemLocale.startsWith(QLatin1String("en-")))
246 acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale);
247 else
248 acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale);
249 request.setHeaderField("Accept-Language", acceptLanguage.toAscii());
250 }
251
252 // set the User Agent
253 value = request.headerField("user-agent");
254 if (value.isEmpty())
255 request.setHeaderField("User-Agent", "Mozilla/5.0");
256 // set the host
257 value = request.headerField("host");
258 if (value.isEmpty()) {
259 QByteArray host = QUrl::toAce(hostName);
260
261 int port = request.url().port();
262 if (port != -1) {
263 host += ':';
264 host += QByteArray::number(port);
265 }
266
267 request.setHeaderField("Host", host);
268 }
269
270 reply->d_func()->requestIsPrepared = true;
271}
272
273
274
275
276void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
277 QHttpNetworkReply *reply,
278 QNetworkReply::NetworkError errorCode)
279{
280 Q_Q(QHttpNetworkConnection);
281 if (socket && reply) {
282 // this error matters only to this reply
283 reply->d_func()->errorString = errorDetail(errorCode, socket);
284 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
285 int i = indexOf(socket);
286 // remove the corrupt data if any
287 reply->d_func()->eraseData();
288 channels[i].close();
289 // send the next request
290 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
291 }
292}
293
294void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
295{
296 Q_ASSERT(auth);
297
298 // NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up.
299 if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm)
300 return;
301 if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm)
302 return;
303
304
305 // select another channel
306 QAuthenticator* otherAuth = 0;
307 for (int i = 0; i < channelCount; ++i) {
308 if (i == fromChannel)
309 continue;
310 if (isProxy)
311 otherAuth = &channels[i].proxyAuthenticator;
312 else
313 otherAuth = &channels[i].authenticator;
314 // if the credentials are different, copy them
315 if (otherAuth->user().compare(auth->user()))
316 otherAuth->setUser(auth->user());
317 if (otherAuth->password().compare(auth->password()))
318 otherAuth->setPassword(auth->password());
319 }
320}
321
322
323// handles the authentication for one channel and eventually re-starts the other channels
324bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
325 bool isProxy, bool &resend)
326{
327 Q_ASSERT(socket);
328 Q_ASSERT(reply);
329
330 resend = false;
331 //create the response header to be used with QAuthenticatorPrivate.
332 QList<QPair<QByteArray, QByteArray> > fields = reply->header();
333
334 //find out the type of authentication protocol requested.
335 QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy);
336 if (authMethod != QAuthenticatorPrivate::None) {
337 int i = indexOf(socket);
338 //Use a single authenticator for all domains. ### change later to use domain/realm
339 QAuthenticator* auth = 0;
340 if (isProxy) {
341 auth = &channels[i].proxyAuthenticator;
342 channels[i].proxyAuthMethod = authMethod;
343 } else {
344 auth = &channels[i].authenticator;
345 channels[i].authMethod = authMethod;
346 }
347 //proceed with the authentication.
348 if (auth->isNull())
349 auth->detach();
350 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
351 priv->parseHttpResponse(fields, isProxy);
352
353 if (priv->phase == QAuthenticatorPrivate::Done) {
354 pauseConnection();
355 if (!isProxy) {
356 emit reply->authenticationRequired(reply->request(), auth);
357#ifndef QT_NO_NETWORKPROXY
358 } else {
359 emit reply->proxyAuthenticationRequired(networkProxy, auth);
360#endif
361 }
362 resumeConnection();
363
364 if (priv->phase != QAuthenticatorPrivate::Done) {
365 // send any pending requests
366 copyCredentials(i, auth, isProxy);
367 }
368 }
369 // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
370 // then nothing was filled in by the user or the cache
371 // - If withCredentials has been set to false (e.g. by QtWebKit for a cross-origin XMLHttpRequest) then
372 // we need to bail out if authentication is required.
373 if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
374 // Reset authenticator so the next request on that channel does not get messed up
375 auth = 0;
376 if (isProxy)
377 channels[i].proxyAuthenticator = QAuthenticator();
378 else
379 channels[i].authenticator = QAuthenticator();
380
381 // authentication is cancelled, send the current contents to the user.
382 emit channels[i].reply->headerChanged();
383 emit channels[i].reply->readyRead();
384 QNetworkReply::NetworkError errorCode =
385 isProxy
386 ? QNetworkReply::ProxyAuthenticationRequiredError
387 : QNetworkReply::AuthenticationRequiredError;
388 reply->d_func()->errorString = errorDetail(errorCode, socket);
389 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
390 // ### at this point the reply could be deleted
391 socket->close();
392 return true;
393 }
394 //resend the request
395 resend = true;
396 return true;
397 }
398 return false;
399}
400
401void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
402{
403 Q_ASSERT(socket);
404
405 int i = indexOf(socket);
406
407 // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
408 if (channels[i].authMethod != QAuthenticatorPrivate::None) {
409 if (!(channels[i].authMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) {
410 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
411 if (priv && priv->method != QAuthenticatorPrivate::None) {
412 QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
413 request.setHeaderField("Authorization", response);
414 }
415 }
416 }
417
418 // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
419 if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) {
420 if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
421 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
422 if (priv && priv->method != QAuthenticatorPrivate::None) {
423 QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
424 request.setHeaderField("Proxy-Authorization", response);
425 }
426 }
427 }
428}
429
430QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
431{
432 Q_Q(QHttpNetworkConnection);
433
434 // The reply component of the pair is created initially.
435 QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());
436 reply->setRequest(request);
437 reply->d_func()->connection = q;
438 reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
439 HttpMessagePair pair = qMakePair(request, reply);
440
441 switch (request.priority()) {
442 case QHttpNetworkRequest::HighPriority:
443 highPriorityQueue.prepend(pair);
444 break;
445 case QHttpNetworkRequest::NormalPriority:
446 case QHttpNetworkRequest::LowPriority:
447 lowPriorityQueue.prepend(pair);
448 break;
449 }
450
451 // this used to be called via invokeMethod and a QueuedConnection
452 // It is the only place _q_startNextRequest is called directly without going
453 // through the event loop using a QueuedConnection.
454 // This is dangerous because of recursion that might occur when emitting
455 // signals as DirectConnection from this code path. Therefore all signal
456 // emissions that can come out from this code path need to
457 // be QueuedConnection.
458 // We are currently trying to fine-tune this.
459 _q_startNextRequest();
460
461
462 return reply;
463}
464
465void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
466{
467 Q_Q(QHttpNetworkConnection);
468
469 QHttpNetworkRequest request = pair.first;
470 switch (request.priority()) {
471 case QHttpNetworkRequest::HighPriority:
472 highPriorityQueue.prepend(pair);
473 break;
474 case QHttpNetworkRequest::NormalPriority:
475 case QHttpNetworkRequest::LowPriority:
476 lowPriorityQueue.prepend(pair);
477 break;
478 }
479
480 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
481}
482
483void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socket)
484{
485 Q_ASSERT(socket);
486
487 int i = indexOf(socket);
488
489 if (!highPriorityQueue.isEmpty()) {
490 // remove from queue before sendRequest! else we might pipeline the same request again
491 HttpMessagePair messagePair = highPriorityQueue.takeLast();
492 if (!messagePair.second->d_func()->requestIsPrepared)
493 prepareRequest(messagePair);
494 channels[i].request = messagePair.first;
495 channels[i].reply = messagePair.second;
496 channels[i].sendRequest();
497 return;
498 }
499
500 if (!lowPriorityQueue.isEmpty()) {
501 // remove from queue before sendRequest! else we might pipeline the same request again
502 HttpMessagePair messagePair = lowPriorityQueue.takeLast();
503 if (!messagePair.second->d_func()->requestIsPrepared)
504 prepareRequest(messagePair);
505 channels[i].request = messagePair.first;
506 channels[i].reply = messagePair.second;
507 channels[i].sendRequest();
508 return;
509 }
510}
511
512// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
513void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
514{
515 // return fast if there is nothing to pipeline
516 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
517 return;
518
519 int i = indexOf(socket);
520
521 // return fast if there was no reply right now processed
522 if (channels[i].reply == 0)
523 return;
524
525 if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
526 return;
527 }
528
529 if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
530 return;
531
532 // the current request that is in must already support pipelining
533 if (!channels[i].request.isPipeliningAllowed())
534 return;
535
536 // the current request must be a idempotent (right now we only check GET)
537 if (channels[i].request.operation() != QHttpNetworkRequest::Get)
538 return;
539
540 // check if socket is connected
541 if (socket->state() != QAbstractSocket::ConnectedState)
542 return;
543
544 // check for resendCurrent
545 if (channels[i].resendCurrent)
546 return;
547
548 // we do not like authentication stuff
549 // ### make sure to be OK with this in later releases
550 if (!channels[i].authenticator.isNull() || !channels[i].authenticator.user().isEmpty())
551 return;
552 if (!channels[i].proxyAuthenticator.isNull() || !channels[i].proxyAuthenticator.user().isEmpty())
553 return;
554
555 // must be in ReadingState or WaitingState
556 if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
557 || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
558 return;
559
560
561 //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing highPriorityQueue, size=" << highPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length();
562 int lengthBefore;
563 while (!highPriorityQueue.isEmpty()) {
564 lengthBefore = channels[i].alreadyPipelinedRequests.length();
565 fillPipeline(highPriorityQueue, channels[i]);
566
567 if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
568 return;
569
570 if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
571 break; // did not process anything, now do the low prio queue
572 }
573
574 //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing lowPriorityQueue, size=" << lowPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length();
575 while (!lowPriorityQueue.isEmpty()) {
576 lengthBefore = channels[i].alreadyPipelinedRequests.length();
577 fillPipeline(lowPriorityQueue, channels[i]);
578
579 if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
580 return;
581
582 if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
583 break; // did not process anything
584 }
585
586
587}
588
589// returns true when the processing of a queue has been done
590bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
591{
592 if (queue.isEmpty())
593 return true;
594
595 for (int i = queue.count() - 1; i >= 0; --i) {
596 HttpMessagePair messagePair = queue.at(i);
597 const QHttpNetworkRequest &request = messagePair.first;
598
599 // we currently do not support pipelining if HTTP authentication is used
600 if (!request.url().userInfo().isEmpty())
601 continue;
602
603 // take only GET requests
604 if (request.operation() != QHttpNetworkRequest::Get)
605 continue;
606
607 if (!request.isPipeliningAllowed())
608 continue;
609
610 // remove it from the queue
611 queue.takeAt(i);
612 // we modify the queue we iterate over here, but since we return from the function
613 // afterwards this is fine.
614
615 // actually send it
616 if (!messagePair.second->d_func()->requestIsPrepared)
617 prepareRequest(messagePair);
618 channel.pipelineInto(messagePair);
619
620 // return false because we processed something and need to process again
621 return false;
622 }
623
624 // return true, the queue has been processed and not changed
625 return true;
626}
627
628
629QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket,
630 const QString &extraDetail)
631{
632 Q_ASSERT(socket);
633
634 QString errorString;
635 switch (errorCode) {
636 case QNetworkReply::HostNotFoundError:
637 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QHttp", "Host %1 not found"))
638 .arg(socket->peerName());
639 break;
640 case QNetworkReply::ConnectionRefusedError:
641 errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection refused"));
642 break;
643 case QNetworkReply::RemoteHostClosedError:
644 errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection closed"));
645 break;
646 case QNetworkReply::TimeoutError:
647 errorString = QLatin1String(QT_TRANSLATE_NOOP("QAbstractSocket", "Socket operation timed out"));
648 break;
649 case QNetworkReply::ProxyAuthenticationRequiredError:
650 errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Proxy requires authentication"));
651 break;
652 case QNetworkReply::AuthenticationRequiredError:
653 errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Host requires authentication"));
654 break;
655 case QNetworkReply::ProtocolFailure:
656 errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Data corrupted"));
657 break;
658 case QNetworkReply::ProtocolUnknownError:
659 errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown protocol specified"));
660 break;
661 case QNetworkReply::SslHandshakeFailedError:
662 errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "SSL handshake failed"));
663 break;
664 default:
665 // all other errors are treated as QNetworkReply::UnknownNetworkError
666 errorString = extraDetail;
667 break;
668 }
669 return errorString;
670}
671
672// this is called from the destructor of QHttpNetworkReply. It is called when
673// the reply was finished correctly or when it was aborted.
674void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
675{
676 Q_Q(QHttpNetworkConnection);
677
678 // check if the reply is currently being processed or it is pipelined in
679 for (int i = 0; i < channelCount; ++i) {
680 // is the reply associated the currently processing of this channel?
681 if (channels[i].reply == reply) {
682 channels[i].reply = 0;
683 channels[i].request = QHttpNetworkRequest();
684 channels[i].resendCurrent = false;
685
686 if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
687 // the reply had to be prematurely removed, e.g. it was not finished
688 // therefore we have to requeue the already pipelined requests.
689 channels[i].requeueCurrentlyPipelinedRequests();
690 }
691
692 // if HTTP mandates we should close
693 // or the reply is not finished yet, e.g. it was aborted
694 // we have to close that connection
695 if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished())
696 channels[i].close();
697
698 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
699 return;
700 }
701
702 // is the reply inside the pipeline of this channel already?
703 for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
704 if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
705 // Remove that HttpMessagePair
706 channels[i].alreadyPipelinedRequests.removeAt(j);
707
708 channels[i].requeueCurrentlyPipelinedRequests();
709
710 // Since some requests had already been pipelined, but we removed
711 // one and re-queued the others
712 // we must force a connection close after the request that is
713 // currently in processing has been finished.
714 if (channels[i].reply)
715 channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
716
717 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
718 return;
719 }
720 }
721 }
722 // remove from the high priority queue
723 if (!highPriorityQueue.isEmpty()) {
724 for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
725 HttpMessagePair messagePair = highPriorityQueue.at(j);
726 if (messagePair.second == reply) {
727 highPriorityQueue.removeAt(j);
728 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
729 return;
730 }
731 }
732 }
733 // remove from the low priority queue
734 if (!lowPriorityQueue.isEmpty()) {
735 for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
736 HttpMessagePair messagePair = lowPriorityQueue.at(j);
737 if (messagePair.second == reply) {
738 lowPriorityQueue.removeAt(j);
739 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
740 return;
741 }
742 }
743 }
744}
745
746
747
748// This function must be called from the event loop. The only
749// exception is documented in QHttpNetworkConnectionPrivate::queueRequest
750// although it is called _q_startNextRequest, it will actually start multiple requests when possible
751void QHttpNetworkConnectionPrivate::_q_startNextRequest()
752{
753 // If the QHttpNetworkConnection is currently paused then bail out immediately
754 if (state == PausedState)
755 return;
756
757 //resend the necessary ones.
758 for (int i = 0; i < channelCount; ++i) {
759 if (channels[i].resendCurrent) {
760 channels[i].resendCurrent = false;
761 channels[i].state = QHttpNetworkConnectionChannel::IdleState;
762
763 // if this is not possible, error will be emitted and connection terminated
764 if (!channels[i].resetUploadData())
765 continue;
766 channels[i].sendRequest();
767 }
768 }
769
770 // dequeue new ones
771
772 // return fast if there is nothing to do
773 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
774 return;
775 // try to get a free AND connected socket
776 for (int i = 0; i < channelCount; ++i) {
777 if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
778 dequeueAndSendRequest(channels[i].socket);
779 }
780 }
781
782 // return fast if there is nothing to do
783 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
784 return;
785 // try to get a free unconnected socket
786 for (int i = 0; i < channelCount; ++i) {
787 if (!channels[i].reply && !channels[i].isSocketBusy()) {
788 dequeueAndSendRequest(channels[i].socket);
789 }
790 }
791
792 // try to push more into all sockets
793 // ### FIXME we should move this to the beginning of the function
794 // as soon as QtWebkit is properly using the pipelining
795 // (e.g. not for XMLHttpRequest or the first page load)
796 // ### FIXME we should also divide the requests more even
797 // on the connected sockets
798 //tryToFillPipeline(socket);
799 // return fast if there is nothing to pipeline
800 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
801 return;
802 for (int i = 0; i < channelCount; i++)
803 if (channels[i].socket->state() == QAbstractSocket::ConnectedState)
804 fillPipeline(channels[i].socket);
805}
806
807
808void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
809{
810 for (int i = 0 ; i < channelCount; ++i) {
811 if (channels[i].reply == reply) {
812 // emulate a readyRead() from the socket
813 QMetaObject::invokeMethod(&channels[i], "_q_readyRead", Qt::QueuedConnection);
814 return;
815 }
816 }
817}
818
819QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
820 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
821{
822 Q_D(QHttpNetworkConnection);
823 d->init();
824}
825
826QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
827 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
828{
829 Q_D(QHttpNetworkConnection);
830 d->init();
831}
832
833QHttpNetworkConnection::~QHttpNetworkConnection()
834{
835}
836
837QString QHttpNetworkConnection::hostName() const
838{
839 Q_D(const QHttpNetworkConnection);
840 return d->hostName;
841}
842
843quint16 QHttpNetworkConnection::port() const
844{
845 Q_D(const QHttpNetworkConnection);
846 return d->port;
847}
848
849QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
850{
851 Q_D(QHttpNetworkConnection);
852 return d->queueRequest(request);
853}
854
855bool QHttpNetworkConnection::isSsl() const
856{
857 Q_D(const QHttpNetworkConnection);
858 return d->encrypt;
859}
860
861QHttpNetworkConnectionChannel *QHttpNetworkConnection::channels() const
862{
863 return d_func()->channels;
864}
865
866#ifndef QT_NO_NETWORKPROXY
867void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
868{
869 Q_D(QHttpNetworkConnection);
870 d->networkProxy = networkProxy;
871 // update the authenticator
872 if (!d->networkProxy.user().isEmpty()) {
873 for (int i = 0; i < d->channelCount; ++i) {
874 d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
875 d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
876 }
877 }
878}
879
880QNetworkProxy QHttpNetworkConnection::cacheProxy() const
881{
882 Q_D(const QHttpNetworkConnection);
883 return d->networkProxy;
884}
885
886void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
887{
888 Q_D(QHttpNetworkConnection);
889 for (int i = 0; i < d->channelCount; ++i)
890 d->channels[i].socket->setProxy(networkProxy);
891}
892
893QNetworkProxy QHttpNetworkConnection::transparentProxy() const
894{
895 Q_D(const QHttpNetworkConnection);
896 return d->channels[0].socket->proxy();
897}
898#endif
899
900
901// SSL support below
902#ifndef QT_NO_OPENSSL
903void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
904{
905 Q_D(QHttpNetworkConnection);
906 if (!d->encrypt)
907 return;
908
909 // set the config on all channels
910 for (int i = 0; i < d->channelCount; ++i)
911 static_cast<QSslSocket *>(d->channels[i].socket)->setSslConfiguration(config);
912}
913
914void QHttpNetworkConnection::ignoreSslErrors(int channel)
915{
916 Q_D(QHttpNetworkConnection);
917 if (!d->encrypt)
918 return;
919
920 if (channel == -1) { // ignore for all channels
921 for (int i = 0; i < d->channelCount; ++i) {
922 static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors();
923 d->channels[i].ignoreAllSslErrors = true;
924 }
925
926 } else {
927 static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors();
928 d->channels[channel].ignoreAllSslErrors = true;
929 }
930}
931
932void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
933{
934 Q_D(QHttpNetworkConnection);
935 if (!d->encrypt)
936 return;
937
938 if (channel == -1) { // ignore for all channels
939 for (int i = 0; i < d->channelCount; ++i) {
940 static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors);
941 d->channels[i].ignoreSslErrorsList = errors;
942 }
943
944 } else {
945 static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(errors);
946 d->channels[channel].ignoreSslErrorsList = errors;
947 }
948}
949
950#endif //QT_NO_OPENSSL
951
952#ifndef QT_NO_NETWORKPROXY
953// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
954// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
955// e.g. it is for SOCKS proxies which require authentication.
956void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
957{
958 // Also pause the connection because socket notifiers may fire while an user
959 // dialog is displaying
960 pauseConnection();
961 emit chan->reply->proxyAuthenticationRequired(proxy, auth);
962 resumeConnection();
963 int i = indexOf(chan->socket);
964 copyCredentials(i, auth, true);
965}
966#endif
967
968
969QT_END_NAMESPACE
970
971#include "moc_qhttpnetworkconnection_p.cpp"
972
973#endif // QT_NO_HTTP
Note: See TracBrowser for help on using the repository browser.