Qt 4.8
qhttpnetworkconnection.cpp
Go to the documentation of this file.
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>
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 
70 
71 #ifdef Q_OS_SYMBIAN
73 #else
75 #endif
76 
77 // The pipeline length. So there will be 4 requests in flight.
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.
82 
83 
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 {
93 }
94 
97  hostName(hostName), port(port), encrypt(encrypt),
98  channelCount(channelCount)
99 #ifndef QT_NO_NETWORKPROXY
100  , networkProxy(QNetworkProxy::NoProxy)
101 #endif
102 {
104 }
105 
106 
107 
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 
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
127 #endif
128  channels[i].init();
129  }
130 }
131 
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
144  }
145 }
146 
148 {
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
158 
159  // Resume pending upload if needed
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 
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 
179 {
180  return reply.d_func()->responseData.byteAmount();
181 }
182 
184 {
185  return reply.d_func()->responseData.sizeNextBlock();
186 }
187 
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
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()) {
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()) {
266  QByteArray host;
267  if(add.setAddress(hostName)) {
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 
293  QHttpNetworkReply *reply,
294  QNetworkReply::NetworkError errorCode)
295 {
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;
310 
311  // send the next request
312  QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
313  }
314 }
315 
316 void 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
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();
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;
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;
393  channels[i].proxyCredentialsSent = false;
394  }
396 #endif
397  }
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)
419  else
421 
422  // authentication is cancelled, send the current contents to the user.
425  QNetworkReply::NetworkError errorCode =
426  isProxy
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 
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)) {
451  if (priv && priv->method != QAuthenticatorPrivate::None) {
452  QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
453  request.setHeaderField("Authorization", response);
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)) {
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 
473 {
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()) {
485  highPriorityQueue.prepend(pair);
486  break;
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.
502 
503 
504  return reply;
505 }
506 
508 {
510 
511  QHttpNetworkRequest request = pair.first;
512  switch (request.priority()) {
514  highPriorityQueue.prepend(pair);
515  break;
518  lowPriorityQueue.prepend(pair);
519  break;
520  }
521 
522  QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
523 }
524 
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 
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
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 
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()
603  return;
604  if (!channels[i].proxyAuthenticator.isNull()
607  return;
608 
609  // must be in ReadingState or WaitingState
612  return;
613 
614  int lengthBefore;
615  while (!highPriorityQueue.isEmpty()) {
616  lengthBefore = channels[i].alreadyPipelinedRequests.length();
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();
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
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 
686  const QString &extraDetail)
687 {
688  Q_ASSERT(socket);
689 
691  switch (errorCode) {
693  errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName());
694  break;
696  errorString = QCoreApplication::translate("QHttp", "Connection refused");
697  break;
699  errorString = QCoreApplication::translate("QHttp", "Connection closed");
700  break;
702  errorString = QCoreApplication::translate("QAbstractSocket", "Socket operation timed out");
703  break;
705  errorString = QCoreApplication::translate("QHttp", "Proxy requires authentication");
706  break;
708  errorString = QCoreApplication::translate("QHttp", "Host requires authentication");
709  break;
711  errorString = QCoreApplication::translate("QHttp", "Data corrupted");
712  break;
714  errorString = QCoreApplication::translate("QHttp", "Unknown protocol specified");
715  break;
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.
730 {
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;
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.
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 
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
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;
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)) {
863  queuedRequest--;
864  }
865  }
866 }
867 
868 
870 {
871  for (int i = 0 ; i < channelCount; ++i) {
872  if (channels[i].reply == reply) {
873  // emulate a readyRead() from the socket
875  return;
876  }
877  }
878 }
879 
880 #ifndef QT_NO_BEARERMANAGEMENT
882  : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
883 {
885  d->networkSession = networkSession;
886  d->init();
887 }
888 
890  : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
891 {
893  d->networkSession = networkSession;
894  d->init();
895 }
896 #else
898  : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
899 {
901  d->init();
902 }
903 
904 QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
905  : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
906 {
908  d->init();
909 }
910 #endif
911 
913 {
914 }
915 
917 {
919  return d->hostName;
920 }
921 
923 {
925  return d->port;
926 }
927 
929 {
931  return d->queueRequest(request);
932 }
933 
935 {
937  return d->encrypt;
938 }
939 
941 {
942  return d_func()->channels;
943 }
944 
945 #ifndef QT_NO_NETWORKPROXY
947 {
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 
960 {
962  return d->networkProxy;
963 }
964 
966 {
968  for (int i = 0; i < d->channelCount; ++i)
969  d->channels[i].socket->setProxy(networkProxy);
970 }
971 
973 {
975  return d->channels[0].socket->proxy();
976 }
977 #endif
978 
979 
980 // SSL support below
981 #ifndef QT_NO_OPENSSL
983 {
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 
994 {
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 
1012 {
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.
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 
1049 
1050 #include "moc_qhttpnetworkconnection_p.cpp"
1051 
1052 #endif // QT_NO_HTTP
QNetworkProxy::ProxyType type() const
Returns the proxy type for this instance.
QSharedDataPointer< QHttpNetworkRequestPrivate > d
double d
Definition: qnumeric_p.h:62
void cacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
QString peerName() const
Returns the name of the peer as specified by connectToHost(), or an empty QString if connectToHost() ...
bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend)
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
static const QSystemLocale * systemLocale()
Definition: qlocale.cpp:471
static QString fromAscii(const char *, int size=-1)
Returns a QString initialized with the first size characters from the string str. ...
Definition: qstring.cpp:4276
QHttpNetworkRequest request() const
#define add(aName)
static void resumeSocketNotifiers(QSslSocket *)
QAuthenticatorPrivate::Method authMethod
void setRequest(const QHttpNetworkRequest &request)
void fillPipeline(QAbstractSocket *socket)
void prepareRequest(HttpMessagePair &request)
QNetworkProxy transparentProxy() const
QString & replace(int i, int len, QChar after)
Definition: qstring.cpp:2005
void parseHttpResponse(const QHttpResponseHeader &, bool isProxy)
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
QList< QPair< QByteArray, QByteArray > > header() const
T1 first
Definition: qpair.h:65
The QSslSocket class provides an SSL encrypted socket for both clients and servers.
Definition: qsslsocket.h:67
static void resumeSocketNotifiers(QAbstractSocket *)
T2 second
Definition: qpair.h:66
NetworkError
Indicates all possible error conditions found during the processing of the request.
Definition: qnetworkreply.h:70
QNetworkProxy cacheProxy() const
QList< HttpMessagePair > highPriorityQueue
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition: qstring.cpp:3734
void setTransparentProxy(const QNetworkProxy &networkProxy)
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
int count(const T &t) const
Returns the number of occurrences of value in the list.
Definition: qlist.h:891
QString errorString() const
QString name() const
Returns the language and country of this locale as a string of the form "language_country", where language is a lowercase, two-letter ISO 639 language code, and country is an uppercase, two- or three-letter ISO 3166 country code.
Definition: qlocale.cpp:963
The QString class provides a Unicode character string.
Definition: qstring.h:83
void setUser(const QString &user)
Sets the user used for authentication.
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
#define Q_D(Class)
Definition: qglobal.h:2482
QHttpNetworkConnectionChannel * channels
void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode)
QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail=QString())
bool isEmpty() const
Returns true if the list contains no items; otherwise returns false.
Definition: qlist.h:152
static QLocale system()
Returns a QLocale object initialized to the system locale.
Definition: qlocale.cpp:1917
static QString translate(const char *context, const char *key, const char *disambiguation=0, Encoding encoding=CodecForTr)
static QAuthenticatorPrivate * getPrivate(QAuthenticator &auth)
void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
#define Q_Q(Class)
Definition: qglobal.h:2483
void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail=QString())
Q_GUI_EXPORT QString errorString(EGLint code=eglGetError())
Definition: qegl.cpp:743
void setCacheProxy(const QNetworkProxy &networkProxy)
int indexOf(QAbstractSocket *socket) const
int port() const
Returns the port of the URL, or -1 if the port is unspecified.
Definition: qurl.cpp:4916
QNonContiguousByteDevice * uploadByteDevice() const
The QNetworkProxy class provides a network layer proxy.
static QByteArray toAce(const QString &)
Returns the ASCII Compatible Encoding of the given domain name domain.
Definition: qurl.cpp:6158
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue=QByteArray()) const
QSharedPointer< QNetworkSession > networkSession
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
QHttpNetworkReply * queueRequest(const QHttpNetworkRequest &request)
QList< HttpMessagePair > lowPriorityQueue
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
#define emit
Definition: qobjectdefs.h:76
QString userInfo() const
Returns the user info of the URL, or an empty string if the user info is undefined.
Definition: qurl.cpp:4631
unsigned short quint16
Definition: qglobal.h:936
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal...
Definition: qstring.h:654
void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
void ignoreSslErrors(int channel=-1)
__int64 qint64
Definition: qglobal.h:942
Operation operation() const
void close()
Closes the I/O device for the socket, disconnects the socket&#39;s connection with the host...
#define QT_NO_NETWORKPROXY
QHttpNetworkConnectionChannel * channels() const
The QAuthenticator class provides an authentication object.
QHttpNetworkConnection * connection()
void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
Q_CORE_EXPORT void qFatal(const char *,...)
QString arg(qlonglong a, int fieldwidth=0, int base=10, const QChar &fillChar=QLatin1Char(' ')) const Q_REQUIRED_RESULT
Definition: qstring.cpp:7186
QAbstractSocket::NetworkLayerProtocol protocol() const
Returns the network layer protocol of the host address.
QSharedPointer< QNetworkSession > networkSession
int compare(const QString &s) const
Definition: qstring.cpp:5037
void setContentLength(qint64 length)
void setAddress(quint32 ip4Addr)
Set the IPv4 address specified by ip4Addr.
QHttpNetworkConnection(const QString &hostName, quint16 port=80, bool encrypt=false, QObject *parent=0, QSharedPointer< QNetworkSession > networkSession=QSharedPointer< QNetworkSession >())
void removeReply(QHttpNetworkReply *reply)
void requeueRequest(const HttpMessagePair &pair)
bool isNull() const
Returns true if the authenticator is null.
Q_INVOKABLE QObject(QObject *parent=0)
Constructs an object with parent object parent.
Definition: qobject.cpp:753
QObject * parent() const
Returns a pointer to the parent object.
Definition: qobject.h:273
QString user() const
returns the user used for authentication.
Q_OUTOFLINE_TEMPLATE QPair< T1, T2 > qMakePair(const T1 &x, const T2 &y)
Definition: qpair.h:102
void readMoreLater(QHttpNetworkReply *reply)
QByteArray toAscii() const Q_REQUIRED_RESULT
Returns an 8-bit representation of the string as a QByteArray.
Definition: qstring.cpp:4014
static const QMetaObjectPrivate * priv(const uint *data)
QByteArray calculateResponse(const QByteArray &method, const QByteArray &path)
T takeAt(int i)
Removes the item at index position i and returns it.
Definition: qlist.h:484
QObject * parent
Definition: qobject.h:92
void setSslConfiguration(const QSslConfiguration &config)
QHttpNetworkReply * sendRequest(const QHttpNetworkRequest &request)
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(0), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
Invokes the member (a signal or a slot name) on the object obj.
QByteArray uri(bool throughProxy) const
bool isEmpty() const
Returns true if the byte array has size 0; otherwise returns false.
Definition: qbytearray.h:421
A QNonContiguousByteDevice is a representation of a file, array or buffer that allows access with a r...
The QSslConfiguration class holds the configuration and state of an SSL connection.
void setHeaderField(const QByteArray &name, const QByteArray &data)
void setPassword(const QString &password)
Sets the password used for authentication.
bool dequeueRequest(QAbstractSocket *socket)
The QHostAddress class provides an IP address.
Definition: qhostaddress.h:70
QString password() const
returns the password used for authentication.
static void pauseSocketNotifiers(QAbstractSocket *)
virtual qint64 size()=0
Returns the size of the complete device or -1 if unknown.
static QChar fromAscii(char c)
Converts the ASCII character c to its equivalent QChar.
Definition: qchar.cpp:1521
QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
The QAbstractSocket class provides the base functionality common to all socket types.
qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
static QByteArray number(int, int base=10)
Returns a byte array containing the string equivalent of the number n to base base (10 by default)...
static void pauseSocketNotifiers(QSslSocket *)
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
void setConnection(QHttpNetworkConnection *c)
SocketState state() const
Returns the state of the socket.
The QList class is a template class that provides lists.
Definition: qdatastream.h:62
void emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator *auth)
QAuthenticatorPrivate::Method proxyAuthMethod
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)