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