webengine/osswebengine/WebKit/qt/Api/qwebnetworkinterface.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2   Copyright (C) 2006 Enrico Ros <enrico.ros@m31engineering.it>
       
     3   Copyright (C) 2007 Trolltech ASA
       
     4   Copyright (C) 2007 Staikos Computing Services Inc.  <info@staikos.net>
       
     5 
       
     6   This library is free software; you can redistribute it and/or
       
     7   modify it under the terms of the GNU Library General Public
       
     8   License as published by the Free Software Foundation; either
       
     9   version 2 of the License, or (at your option) any later version.
       
    10 
       
    11   This library is distributed in the hope that it will be useful,
       
    12   but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14   Library General Public License for more details.
       
    15 
       
    16   You should have received a copy of the GNU Library General Public License
       
    17   along with this library; see the file COPYING.LIB.  If not, write to
       
    18   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    19   Boston, MA 02110-1301, USA.
       
    20 
       
    21   This class provides all functionality needed for loading images, style sheets and html
       
    22   pages from the web. It has a memory cache for these objects.
       
    23 */
       
    24 #include <qglobal.h>
       
    25 #include "qwebframe.h"
       
    26 #include "qwebnetworkinterface.h"
       
    27 #include "qwebnetworkinterface_p.h"
       
    28 #include "qwebobjectpluginconnector.h"
       
    29 #include "qwebpage.h"
       
    30 #include <qdebug.h>
       
    31 #include <qfile.h>
       
    32 #include <qnetworkproxy.h>
       
    33 #include <qurl.h>
       
    34 #include <QAuthenticator>
       
    35 #include <QCoreApplication>
       
    36 #include <QSslError>
       
    37 
       
    38 #include "ResourceHandle.h"
       
    39 #include "ResourceHandleClient.h"
       
    40 #include "ResourceHandleInternal.h"
       
    41 #include "MIMETypeRegistry.h"
       
    42 #include "CookieJar.h"
       
    43 
       
    44 #define notImplemented() qDebug("FIXME: UNIMPLEMENTED: %s:%d (%s)", __FILE__, __LINE__, __FUNCTION__)
       
    45 
       
    46 #if 0
       
    47 #define DEBUG qDebug
       
    48 #else
       
    49 #define DEBUG if (1) {} else qDebug
       
    50 #endif
       
    51 
       
    52 static QWebNetworkInterface *default_interface = 0;
       
    53 static QWebNetworkManager *manager = 0;
       
    54 
       
    55 using namespace WebCore;
       
    56 
       
    57 uint qHash(const HostInfo &info)
       
    58 {
       
    59     return qHash(info.host) + info.port;
       
    60 }
       
    61 
       
    62 static bool operator==(const HostInfo &i1, const HostInfo &i2)
       
    63 {
       
    64     return i1.port == i2.port && i1.host == i2.host;
       
    65 }
       
    66 
       
    67 void QWebNetworkRequestPrivate::init(const WebCore::ResourceRequest &resourceRequest)
       
    68 {
       
    69     KURL url = resourceRequest.url();
       
    70     QUrl qurl = QString(url.url());
       
    71     init(resourceRequest.httpMethod(), qurl, &resourceRequest);
       
    72 }
       
    73 
       
    74 void QWebNetworkRequestPrivate::init(const QString &method, const QUrl &url, const WebCore::ResourceRequest *resourceRequest)
       
    75 {
       
    76     httpHeader = QHttpRequestHeader(method, url.toString(QUrl::RemoveScheme|QUrl::RemoveAuthority));
       
    77     httpHeader.setValue(QLatin1String("User-Agent"),
       
    78                          QLatin1String("Mozilla/5.0 (PC; U; Intel; Linux; en) AppleWebKit/420+ (KHTML, like Gecko)"));
       
    79     httpHeader.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
       
    80     setURL(url);
       
    81 
       
    82     if (resourceRequest) {
       
    83         const QString scheme = url.scheme().toLower();
       
    84         if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
       
    85             QString cookies = WebCore::cookies(resourceRequest->url());
       
    86             if (!cookies.isEmpty())
       
    87                 httpHeader.setValue(QLatin1String("Cookie"), cookies);
       
    88         }
       
    89 
       
    90 
       
    91         const HTTPHeaderMap& loaderHeaders = resourceRequest->httpHeaderFields();
       
    92         HTTPHeaderMap::const_iterator end = loaderHeaders.end();
       
    93         for (HTTPHeaderMap::const_iterator it = loaderHeaders.begin(); it != end; ++it)
       
    94             httpHeader.setValue(it->first, it->second);
       
    95 
       
    96         // handle and perform a 'POST' request
       
    97         if (method == "POST") {
       
    98             DeprecatedString pd = resourceRequest->httpBody()->flattenToString().deprecatedString();
       
    99             postData = QByteArray(pd.ascii(), pd.length());
       
   100             httpHeader.setValue(QLatin1String("content-length"), QString::number(postData.size()));
       
   101         }
       
   102     }
       
   103 }
       
   104 
       
   105 void QWebNetworkRequestPrivate::setURL(const QUrl &u)
       
   106 {
       
   107     url = u;
       
   108     int port = url.port();
       
   109     const QString scheme = u.scheme();
       
   110     if (port > 0 && (port != 80 || scheme != "http") && (port != 443 || scheme != "https"))
       
   111         httpHeader.setValue(QLatin1String("Host"), url.host() + QLatin1Char(':') + QString::number(port));
       
   112     else
       
   113         httpHeader.setValue(QLatin1String("Host"), url.host());
       
   114 }
       
   115 
       
   116 /*!
       
   117   \class QWebNetworkRequest
       
   118 
       
   119   The QWebNetworkRequest class represents a request for data from the network with all the
       
   120   necessary information needed for retrieval. This includes the url, extra HTTP header fields
       
   121   as well as data for a HTTP POST request.
       
   122 */
       
   123 
       
   124 QWebNetworkRequest::QWebNetworkRequest()
       
   125     : d(new QWebNetworkRequestPrivate)
       
   126 {
       
   127 }
       
   128 
       
   129 QWebNetworkRequest::QWebNetworkRequest(const QUrl &url, Method method, const QByteArray &postData)
       
   130     : d(new QWebNetworkRequestPrivate)
       
   131 {
       
   132     d->init(method == Get ? "GET" : "POST", url);
       
   133     d->postData = postData;
       
   134 }
       
   135 
       
   136 QWebNetworkRequest::QWebNetworkRequest(const QWebNetworkRequest &other)
       
   137     : d(new QWebNetworkRequestPrivate(*other.d))
       
   138 {
       
   139 }
       
   140 
       
   141 QWebNetworkRequest &QWebNetworkRequest::operator=(const QWebNetworkRequest &other)
       
   142 {
       
   143     *d = *other.d;
       
   144     return *this;
       
   145 }
       
   146 
       
   147 /*!
       
   148   \internal
       
   149 */
       
   150 QWebNetworkRequest::QWebNetworkRequest(const QWebNetworkRequestPrivate &priv)
       
   151     : d(new QWebNetworkRequestPrivate(priv))
       
   152 {
       
   153 }
       
   154 
       
   155 /*!
       
   156   \internal
       
   157 */
       
   158 QWebNetworkRequest::QWebNetworkRequest(const WebCore::ResourceRequest &request)
       
   159     : d(new QWebNetworkRequestPrivate)
       
   160 {
       
   161     d->init(request);
       
   162 }
       
   163 
       
   164 QWebNetworkRequest::~QWebNetworkRequest()
       
   165 {
       
   166     delete d;
       
   167 }
       
   168 
       
   169 /*!
       
   170   The requested URL
       
   171 */
       
   172 QUrl QWebNetworkRequest::url() const
       
   173 {
       
   174     return d->url;
       
   175 }
       
   176 
       
   177 /*!
       
   178    Sets the URL to request.
       
   179 
       
   180    Note that setting the URL also sets the "Host" field in the HTTP header.
       
   181 */
       
   182 void QWebNetworkRequest::setUrl(const QUrl &url)
       
   183 {
       
   184     d->setURL(url);
       
   185 }
       
   186 
       
   187 /*!
       
   188    The http request header information.
       
   189 */
       
   190 QHttpRequestHeader QWebNetworkRequest::httpHeader() const
       
   191 {
       
   192     return d->httpHeader;
       
   193 }
       
   194 
       
   195 void QWebNetworkRequest::setHttpHeader(const QHttpRequestHeader &header) const
       
   196 {
       
   197     d->httpHeader = header;
       
   198 }
       
   199 
       
   200 QString QWebNetworkRequest::httpHeaderField(const QString &key) const
       
   201 {
       
   202     return d->httpHeader.value(key);
       
   203 }
       
   204 
       
   205 void QWebNetworkRequest::setHttpHeaderField(const QString &key, const QString &value)
       
   206 {
       
   207     d->httpHeader.setValue(key, value);
       
   208 }
       
   209 
       
   210 /*!
       
   211     Post data sent with HTTP POST requests.
       
   212 */
       
   213 QByteArray QWebNetworkRequest::postData() const
       
   214 {
       
   215     return d->postData;
       
   216 }
       
   217 
       
   218 void QWebNetworkRequest::setPostData(const QByteArray &data)
       
   219 {
       
   220     d->postData = data;
       
   221 }
       
   222 
       
   223 /*!
       
   224   \class QWebNetworkJob
       
   225 
       
   226   The QWebNetworkJob class represents a network job, that needs to be
       
   227   processed by the QWebNetworkInterface.
       
   228 
       
   229   This class is only required when implementing a new network layer (or
       
   230   support for a special protocol) using QWebNetworkInterface.
       
   231 
       
   232   QWebNetworkJob objects are created and owned by the QtWebKit library.
       
   233   Most of it's properties are read-only.
       
   234 
       
   235   The job is reference counted. This can be used to ensure that the job doesn't
       
   236   get deleted while it's still stored in some data structure.
       
   237 */
       
   238 
       
   239 /*!
       
   240   \internal
       
   241 */
       
   242 QWebNetworkJob::QWebNetworkJob()
       
   243     : d(new QWebNetworkJobPrivate)
       
   244 {
       
   245     d->ref = 1;
       
   246     d->redirected = false;
       
   247     d->interface = 0;
       
   248 }
       
   249 
       
   250 /*!
       
   251   \internal
       
   252 */
       
   253 QWebNetworkJob::~QWebNetworkJob()
       
   254 {
       
   255     delete d;
       
   256 }
       
   257 
       
   258 /*!
       
   259   The requested URL
       
   260 */
       
   261 QUrl QWebNetworkJob::url() const
       
   262 {
       
   263     return d->request.url;
       
   264 }
       
   265 
       
   266 /*!
       
   267   Post data associated with the job
       
   268 */
       
   269 QByteArray QWebNetworkJob::postData() const
       
   270 {
       
   271     return d->request.postData;
       
   272 }
       
   273 
       
   274 /*!
       
   275   The HTTP request header that should be used to download the job.
       
   276 */
       
   277 QHttpRequestHeader QWebNetworkJob::httpHeader() const
       
   278 {
       
   279     return d->request.httpHeader;
       
   280 }
       
   281 
       
   282 /*!
       
   283   The complete network request that should be used to download the job.
       
   284 */
       
   285 QWebNetworkRequest QWebNetworkJob::request() const
       
   286 {
       
   287     return QWebNetworkRequest(d->request);
       
   288 }
       
   289 
       
   290 /*!
       
   291   The HTTP response header received from the network.
       
   292 */
       
   293 QHttpResponseHeader QWebNetworkJob::response() const
       
   294 {
       
   295     return d->response;
       
   296 }
       
   297 
       
   298 /*!
       
   299   Sets the HTTP reponse header. The response header has to be called before
       
   300   emitting QWebNetworkInterface::started.
       
   301 */
       
   302 void QWebNetworkJob::setResponse(const QHttpResponseHeader &response)
       
   303 {
       
   304     d->response = response;
       
   305 }
       
   306 
       
   307 /*!
       
   308   returns true if the job has been cancelled by the WebKit framework
       
   309 */
       
   310 bool QWebNetworkJob::cancelled() const
       
   311 {
       
   312     return !d->resourceHandle && !d->connector;
       
   313 }
       
   314 
       
   315 /*!
       
   316   reference the job.
       
   317 */
       
   318 void QWebNetworkJob::ref()
       
   319 {
       
   320     ++d->ref;
       
   321 }
       
   322 
       
   323 /*!
       
   324   derefence the job.
       
   325 
       
   326   If the reference count drops to 0 this method also deletes the job.
       
   327 
       
   328   Returns false if the reference count has dropped to 0.
       
   329 */
       
   330 bool QWebNetworkJob::deref()
       
   331 {
       
   332     if (!--d->ref) {
       
   333         delete this;
       
   334         return false;
       
   335     }
       
   336     return true;
       
   337 }
       
   338 
       
   339 /*!
       
   340    Returns the network interface that is associated with this job.
       
   341 */
       
   342 QWebNetworkInterface *QWebNetworkJob::networkInterface() const
       
   343 {
       
   344     return d->interface;
       
   345 }
       
   346 
       
   347 /*!
       
   348    Returns the network interface that is associated with this job.
       
   349 */
       
   350 QWebFrame *QWebNetworkJob::frame() const
       
   351 {
       
   352     if (d->resourceHandle) {
       
   353         ResourceHandleInternal *rhi = d->resourceHandle->getInternal();
       
   354         if (rhi) {
       
   355             return rhi->m_frame;
       
   356         }
       
   357     }
       
   358     return 0;
       
   359 }
       
   360 
       
   361 /*!
       
   362   \class QWebNetworkManager
       
   363   \internal
       
   364 */
       
   365 QWebNetworkManager::QWebNetworkManager()
       
   366     : QObject(0)
       
   367 {
       
   368 }
       
   369 
       
   370 QWebNetworkManager *QWebNetworkManager::self()
       
   371 {
       
   372     // ensure everything's constructed and connected
       
   373     QWebNetworkInterface::defaultInterface();
       
   374 
       
   375     return manager;
       
   376 }
       
   377 
       
   378 bool QWebNetworkManager::add(ResourceHandle *handle, QWebNetworkInterface *interface)
       
   379 {
       
   380     if (!interface)
       
   381         interface = default_interface;
       
   382 
       
   383     ASSERT(interface);
       
   384 
       
   385     QWebNetworkJob *job = new QWebNetworkJob();
       
   386     handle->getInternal()->m_job = job;
       
   387     job->d->resourceHandle = handle;
       
   388     job->d->interface = interface;
       
   389     job->d->connector = 0;
       
   390 
       
   391     job->d->request.init(handle->request());
       
   392 
       
   393     const QString method = handle->getInternal()->m_request.httpMethod();
       
   394     if (method != "POST" && method != "GET") {
       
   395         // don't know what to do! (probably a request error!!)
       
   396         // but treat it like a 'GET' request
       
   397         qWarning("REQUEST: [%s]\n", qPrintable(job->d->request.httpHeader.toString()));
       
   398     }
       
   399 
       
   400     DEBUG() << "QWebNetworkManager::add:" <<  job->d->request.httpHeader.toString();
       
   401 
       
   402     interface->addJob(job);
       
   403 
       
   404     return true;
       
   405 }
       
   406 
       
   407 void QWebNetworkManager::cancel(ResourceHandle *handle)
       
   408 {
       
   409     QWebNetworkJob *job = handle->getInternal()->m_job;
       
   410     if (!job)
       
   411         return;
       
   412     DEBUG() << "QWebNetworkManager::cancel:" <<  job->d->request.httpHeader.toString();
       
   413     job->d->resourceHandle = 0;
       
   414     job->d->connector = 0;
       
   415     job->d->interface->cancelJob(job);
       
   416     handle->getInternal()->m_job = 0;
       
   417 }
       
   418 
       
   419 void QWebNetworkManager::started(QWebNetworkJob *job)
       
   420 {
       
   421     ResourceHandleClient* client = 0;
       
   422     if (job->d->resourceHandle) {
       
   423         client = job->d->resourceHandle->client();
       
   424         if (!client)
       
   425             return;
       
   426     } else if (!job->d->connector) {
       
   427         return;
       
   428     }
       
   429 
       
   430     DEBUG() << "ResourceHandleManager::receivedResponse:";
       
   431     DEBUG() << job->d->response.toString();
       
   432 
       
   433     QStringList cookies = job->d->response.allValues("Set-Cookie");
       
   434     KURL url(job->url().toString());
       
   435     foreach (QString c, cookies) {
       
   436         setCookies(url, url, c);
       
   437     }
       
   438     QString contentType = job->d->response.value("Content-Type");
       
   439     QString encoding;
       
   440     int idx = contentType.indexOf(QLatin1Char(';'));
       
   441     if (idx > 0) {
       
   442         QString remainder = contentType.mid(idx + 1).toLower();
       
   443         contentType = contentType.left(idx).trimmed();
       
   444 
       
   445         idx = remainder.indexOf("charset");
       
   446         if (idx >= 0) {
       
   447             idx = remainder.indexOf(QLatin1Char('='), idx);
       
   448             if (idx >= 0)
       
   449                 encoding = remainder.mid(idx + 1).trimmed();
       
   450         }
       
   451     }
       
   452     if (contentType.isEmpty()) {
       
   453         // let's try to guess from the extension
       
   454         QString extension = job->d->request.url.path();
       
   455         int index = extension.lastIndexOf(QLatin1Char('.'));
       
   456         if (index > 0) {
       
   457             extension = extension.mid(index + 1);
       
   458             contentType = MIMETypeRegistry::getMIMETypeForExtension(extension);
       
   459         }
       
   460     }
       
   461 //     qDebug() << "Content-Type=" << contentType;
       
   462 //     qDebug() << "Encoding=" << encoding;
       
   463 
       
   464 
       
   465     ResourceResponse response(url, contentType,
       
   466                               0 /* FIXME */,
       
   467                               encoding,
       
   468                               String() /* FIXME */);
       
   469 
       
   470     int statusCode = job->d->response.statusCode();
       
   471     response.setHTTPStatusCode(statusCode);
       
   472     /* Fill in the other fields */
       
   473 
       
   474     if (statusCode >= 300 && statusCode < 400) {
       
   475         // we're on a redirect page! if the 'Location:' field is valid, we redirect
       
   476         QString location = job->d->response.value("location");
       
   477         DEBUG() << "Redirection";
       
   478         if (!location.isEmpty()) {
       
   479             QUrl newUrl = job->d->request.url.resolved(location);
       
   480             if (job->d->resourceHandle) {
       
   481                 ResourceRequest newRequest = job->d->resourceHandle->request();
       
   482                 newRequest.setURL(KURL(newUrl.toString()));
       
   483                 if (client)
       
   484                     client->willSendRequest(job->d->resourceHandle, newRequest, response);
       
   485             }
       
   486 
       
   487             job->d->request.httpHeader.setRequest(job->d->request.httpHeader.method(),
       
   488                                                   newUrl.toString(QUrl::RemoveScheme|QUrl::RemoveAuthority));
       
   489             job->d->request.setURL(newUrl);
       
   490             job->d->redirected = true;
       
   491             return;
       
   492         }
       
   493     }
       
   494 
       
   495     if (client)
       
   496         client->didReceiveResponse(job->d->resourceHandle, response);
       
   497     if (job->d->connector)
       
   498         emit job->d->connector->started(job);
       
   499 
       
   500 }
       
   501 
       
   502 void QWebNetworkManager::data(QWebNetworkJob *job, const QByteArray &data)
       
   503 {
       
   504     ResourceHandleClient* client = 0;
       
   505     if (job->d->resourceHandle) {
       
   506         client = job->d->resourceHandle->client();
       
   507         if (!client)
       
   508             return;
       
   509     } else if (!job->d->connector) {
       
   510         return;
       
   511     }
       
   512 
       
   513     if (job->d->redirected)
       
   514         return; // don't emit the "Document has moved here" type of HTML
       
   515 
       
   516     DEBUG() << "receivedData" << job->d->request.url.path();
       
   517     if (client)
       
   518         client->didReceiveData(job->d->resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/);
       
   519     if (job->d->connector)
       
   520         emit job->d->connector->data(job, data);
       
   521 
       
   522 }
       
   523 
       
   524 void QWebNetworkManager::finished(QWebNetworkJob *job, int errorCode)
       
   525 {
       
   526     ResourceHandleClient* client = 0;
       
   527     if (job->d->resourceHandle) {
       
   528         client = job->d->resourceHandle->client();
       
   529         if (!client)
       
   530             return;
       
   531     } else if (!job->d->connector) {
       
   532         job->deref();
       
   533         return;
       
   534     }
       
   535 
       
   536     DEBUG() << "receivedFinished" << errorCode << job->url();
       
   537 
       
   538     if (job->d->redirected) {
       
   539         job->d->redirected = false;
       
   540         job->d->interface->addJob(job);
       
   541         return;
       
   542     }
       
   543 
       
   544     if (job->d->resourceHandle)
       
   545         job->d->resourceHandle->getInternal()->m_job = 0;
       
   546 
       
   547     if (client) {
       
   548         if (errorCode) {
       
   549             //FIXME: error setting error was removed from ResourceHandle
       
   550             client->didFail(job->d->resourceHandle,
       
   551                             ResourceError(job->d->request.url.host(), job->d->response.statusCode(),
       
   552                                           job->d->request.url.toString(), String()));
       
   553         } else {
       
   554             client->didFinishLoading(job->d->resourceHandle);
       
   555         }
       
   556     }
       
   557 
       
   558     if (job->d->connector)
       
   559         emit job->d->connector->finished(job, errorCode);
       
   560 
       
   561     DEBUG() << "receivedFinished done" << job->d->request.url;
       
   562 
       
   563     job->deref();
       
   564 }
       
   565 
       
   566 void QWebNetworkManager::addHttpJob(QWebNetworkJob *job)
       
   567 {
       
   568     HostInfo hostInfo(job->url());
       
   569     WebCoreHttp *httpConnection = m_hostMapping.value(hostInfo);
       
   570     if (!httpConnection) {
       
   571         // #### fix custom ports
       
   572         DEBUG() << "   new connection to" << hostInfo.host << hostInfo.port;
       
   573         httpConnection = new WebCoreHttp(this, hostInfo);
       
   574         QObject::connect(httpConnection, SIGNAL(connectionClosed(const WebCore::HostInfo&)),
       
   575                          this, SLOT(httpConnectionClosed(const WebCore::HostInfo&)));
       
   576 
       
   577         m_hostMapping[hostInfo] = httpConnection;
       
   578     }
       
   579     httpConnection->request(job);
       
   580 }
       
   581 
       
   582 void QWebNetworkManager::cancelHttpJob(QWebNetworkJob *job)
       
   583 {
       
   584     WebCoreHttp *httpConnection = m_hostMapping.value(job->url());
       
   585     if (httpConnection)
       
   586         httpConnection->cancel(job);
       
   587 }
       
   588 
       
   589 void QWebNetworkManager::httpConnectionClosed(const WebCore::HostInfo &info)
       
   590 {
       
   591     WebCoreHttp *connection = m_hostMapping.take(info);
       
   592     connection->deleteLater();
       
   593 }
       
   594 
       
   595 void QWebNetworkInterfacePrivate::sendFileData(QWebNetworkJob* job, int statusCode, const QByteArray &data)
       
   596 {
       
   597     int error = statusCode >= 400 ? 1 : 0;
       
   598     if (!job->cancelled()) {
       
   599         QHttpResponseHeader response;
       
   600         response.setStatusLine(statusCode);
       
   601         response.setContentLength(data.length());
       
   602         job->setResponse(response);
       
   603         emit q->started(job);
       
   604         if (!data.isEmpty())
       
   605             emit q->data(job, data);
       
   606     }
       
   607     emit q->finished(job, error);
       
   608 }
       
   609 
       
   610 void QWebNetworkInterfacePrivate::parseDataUrl(QWebNetworkJob* job)
       
   611 {
       
   612     QByteArray data = job->url().toString().toLatin1();
       
   613     //qDebug() << "handling data url:" << data;
       
   614 
       
   615     ASSERT(data.startsWith("data:"));
       
   616 
       
   617     // Here's the syntax of data URLs:
       
   618     // dataurl    := "data:" [ mediatype ] [ ";base64" ] "," data
       
   619     // mediatype  := [ type "/" subtype ] *( ";" parameter )
       
   620     // data       := *urlchar
       
   621     // parameter  := attribute "=" value
       
   622     QByteArray header;
       
   623     bool base64 = false;
       
   624 
       
   625     int index = data.indexOf(',');
       
   626     if (index != -1) {
       
   627         header = data.mid(5, index - 5).toLower();
       
   628         //qDebug() << "header=" << header;
       
   629         data = data.mid(index+1);
       
   630         //qDebug() << "data=" << data;
       
   631 
       
   632         if (header.endsWith(";base64")) {
       
   633             //qDebug() << "base64";
       
   634             base64 = true;
       
   635             header = header.left(header.length() - 7);
       
   636             //qDebug() << "mime=" << header;
       
   637         }
       
   638     } else {
       
   639         data = QByteArray();
       
   640     }
       
   641     data = QUrl::fromPercentEncoding(data).toLatin1();
       
   642     if (base64) {
       
   643         data = QByteArray::fromBase64(data);
       
   644     }
       
   645 
       
   646     if (header.isEmpty())
       
   647         header = "text/plain;charset=US-ASCII";
       
   648     int statusCode = 200;
       
   649     QHttpResponseHeader response;
       
   650     response.setContentType(header);
       
   651     response.setContentLength(data.size());
       
   652     job->setResponse(response);
       
   653 
       
   654     int error = statusCode >= 400 ? 1 : 0;
       
   655     emit q->started(job);
       
   656     if (!data.isEmpty())
       
   657         emit q->data(job, data);
       
   658     emit q->finished(job, error);
       
   659 }
       
   660 
       
   661 /*!
       
   662   \class QWebNetworkInterface
       
   663 
       
   664   The QWebNetworkInterface class provides an abstraction layer for
       
   665   WebKit's network interface.  It allows to completely replace or
       
   666   extend the builtin network layer.
       
   667 
       
   668   QWebNetworkInterface contains two virtual methods, addJob and
       
   669   cancelJob that have to be reimplemented when implementing your own
       
   670   networking layer.
       
   671 
       
   672   QWebNetworkInterface can by default handle the http, https, file and
       
   673   data URI protocols.
       
   674 
       
   675 */
       
   676 
       
   677 static bool gRoutineAdded = false;
       
   678 
       
   679 static void gCleanupInterface()
       
   680 {
       
   681     delete default_interface;
       
   682     default_interface = 0;
       
   683 }
       
   684 
       
   685 /*!
       
   686   Sets a new default interface that will be used by all of WebKit
       
   687   for downloading data from the internet.
       
   688 */
       
   689 void QWebNetworkInterface::setDefaultInterface(QWebNetworkInterface *defaultInterface)
       
   690 {
       
   691     if (default_interface == defaultInterface)
       
   692         return;
       
   693     if (default_interface)
       
   694         delete default_interface;
       
   695     default_interface = defaultInterface;
       
   696     if (!gRoutineAdded) {
       
   697         qAddPostRoutine(gCleanupInterface);
       
   698         gRoutineAdded = true;
       
   699     }
       
   700 }
       
   701 
       
   702 /*!
       
   703   Returns the default interface that will be used by WebKit. If no
       
   704   default interface has been set, QtWebkit will create an instance of
       
   705   QWebNetworkInterface to do the work.
       
   706 */
       
   707 QWebNetworkInterface *QWebNetworkInterface::defaultInterface()
       
   708 {
       
   709     if (!default_interface) {
       
   710         setDefaultInterface(new QWebNetworkInterface);
       
   711     }
       
   712     return default_interface;
       
   713 }
       
   714 
       
   715 
       
   716 /*!
       
   717   Constructs a QWebNetworkInterface object.
       
   718 */
       
   719 QWebNetworkInterface::QWebNetworkInterface(QObject *parent)
       
   720     : QObject(parent)
       
   721 {
       
   722     d = new QWebNetworkInterfacePrivate;
       
   723     d->q = this;
       
   724 
       
   725     if (!manager)
       
   726         manager = new QWebNetworkManager;
       
   727 
       
   728     QObject::connect(this, SIGNAL(started(QWebNetworkJob*)),
       
   729                      manager, SLOT(started(QWebNetworkJob*)), Qt::QueuedConnection);
       
   730     QObject::connect(this, SIGNAL(data(QWebNetworkJob*, const QByteArray &)),
       
   731                      manager, SLOT(data(QWebNetworkJob*, const QByteArray &)), Qt::QueuedConnection);
       
   732     QObject::connect(this, SIGNAL(finished(QWebNetworkJob*, int)),
       
   733                      manager, SLOT(finished(QWebNetworkJob*, int)), Qt::QueuedConnection);
       
   734 }
       
   735 
       
   736 /*!
       
   737   Destructs the QWebNetworkInterface object.
       
   738 */
       
   739 QWebNetworkInterface::~QWebNetworkInterface()
       
   740 {
       
   741     delete d;
       
   742 }
       
   743 
       
   744 /*!
       
   745   This virtual method gets called whenever QtWebkit needs to add a
       
   746   new job to download.
       
   747 
       
   748   The QWebNetworkInterface should process this job, by first emitting
       
   749   the started signal, then emitting data repeatedly as new data for
       
   750   the Job is available, and finally ending the job with emitting a
       
   751   finished signal.
       
   752 
       
   753   After the finished signal has been emitted, the QWebNetworkInterface
       
   754   is not allowed to access the job anymore.
       
   755 */
       
   756 void QWebNetworkInterface::addJob(QWebNetworkJob *job)
       
   757 {
       
   758     QString protocol = job->url().scheme();
       
   759     if (protocol == QLatin1String("http") || protocol == QLatin1String("https")) {
       
   760         QWebNetworkManager::self()->addHttpJob(job);
       
   761         return;
       
   762     }
       
   763 
       
   764     // "file", "data" and all unhandled stuff go through here
       
   765     //DEBUG() << "fileRequest";
       
   766     DEBUG() << "FileLoader::request" << job->url();
       
   767 
       
   768     if (job->cancelled()) {
       
   769         d->sendFileData(job, 400, QByteArray());
       
   770         return;
       
   771     }
       
   772 
       
   773     QUrl url = job->url();
       
   774     if (protocol == QLatin1String("data")) {
       
   775         d->parseDataUrl(job);
       
   776         return;
       
   777     }
       
   778 
       
   779     int statusCode = 200;
       
   780     QByteArray data;
       
   781     QString path = url.path();
       
   782     if (protocol == QLatin1String("qrc")) {
       
   783         protocol = "file";
       
   784         path.prepend(QLatin1Char(':'));
       
   785     }
       
   786 
       
   787     if (!(protocol.isEmpty() || protocol == QLatin1String("file"))) {
       
   788         statusCode = 404;
       
   789     } else {
       
   790         // we simply ignore post data here.
       
   791         QFile f(path);
       
   792         DEBUG() << "opening" << QString(url.path());
       
   793 
       
   794         if (f.open(QIODevice::ReadOnly)) {
       
   795             QHttpResponseHeader response;
       
   796             response.setStatusLine(200);
       
   797             job->setResponse(response);
       
   798             data = f.readAll();
       
   799         } else {
       
   800             statusCode = 404;
       
   801         }
       
   802     }
       
   803     d->sendFileData(job, statusCode, data);
       
   804 }
       
   805 
       
   806 /*!
       
   807   This virtual method gets called whenever QtWebkit needs to cancel a
       
   808   new job.
       
   809 
       
   810   The QWebNetworkInterface acknowledge the canceling of the job, by
       
   811   emitting the finished signal with an error code of 1. After emitting
       
   812   the finished signal, the interface should not access the job
       
   813   anymore.
       
   814 */
       
   815 void QWebNetworkInterface::cancelJob(QWebNetworkJob *job)
       
   816 {
       
   817     QString protocol = job->url().scheme();
       
   818     if (protocol == QLatin1String("http") || protocol == QLatin1String("https"))
       
   819         QWebNetworkManager::self()->cancelHttpJob(job);
       
   820 }
       
   821 
       
   822 /////////////////////////////////////////////////////////////////////////////
       
   823 WebCoreHttp::WebCoreHttp(QObject* parent, const HostInfo &hi)
       
   824     : QObject(parent), info(hi),
       
   825       m_inCancel(false)
       
   826 {
       
   827     for (int i = 0; i < 2; ++i) {
       
   828         connection[i].http = new QHttp(info.host, (hi.protocol == QLatin1String("https")) ? QHttp::ConnectionModeHttps : QHttp::ConnectionModeHttp, info.port);
       
   829         connect(connection[i].http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)),
       
   830                 this, SLOT(onResponseHeaderReceived(const QHttpResponseHeader&)));
       
   831         connect(connection[i].http, SIGNAL(readyRead(const QHttpResponseHeader&)),
       
   832                 this, SLOT(onReadyRead()));
       
   833         connect(connection[i].http, SIGNAL(requestFinished(int, bool)),
       
   834                 this, SLOT(onRequestFinished(int, bool)));
       
   835         connect(connection[i].http, SIGNAL(done(bool)),
       
   836                 this, SLOT(onDone(bool)));
       
   837         connect(connection[i].http, SIGNAL(stateChanged(int)),
       
   838                 this, SLOT(onStateChanged(int)));
       
   839         connect(connection[i].http, SIGNAL(authenticationRequired(const QString&, quint16, QAuthenticator*)),
       
   840                 this, SLOT(onAuthenticationRequired(const QString&, quint16, QAuthenticator*)));
       
   841         connect(connection[i].http, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
       
   842                 this, SLOT(onProxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)));
       
   843         connect(connection[i].http, SIGNAL(sslErrors(const QList<QSslError>&)),
       
   844                 this, SLOT(onSslErrors(const QList<QSslError>&)));
       
   845     }
       
   846 }
       
   847 
       
   848 WebCoreHttp::~WebCoreHttp()
       
   849 {
       
   850     connection[0].http->deleteLater();
       
   851     connection[1].http->deleteLater();
       
   852 }
       
   853 
       
   854 void WebCoreHttp::request(QWebNetworkJob *job)
       
   855 {
       
   856     m_pendingRequests.append(job);
       
   857     scheduleNextRequest();
       
   858 }
       
   859 
       
   860 void WebCoreHttp::scheduleNextRequest()
       
   861 {
       
   862     int c = 0;
       
   863     for (; c < 2; ++c) {
       
   864         if (!connection[c].current)
       
   865             break;
       
   866     }
       
   867     if (c >= 2)
       
   868         return;
       
   869 
       
   870     QWebNetworkJob *job = 0;
       
   871     while (!job && !m_pendingRequests.isEmpty()) {
       
   872         job = m_pendingRequests.takeFirst();
       
   873         if (job->cancelled()) {
       
   874             emit job->networkInterface()->finished(job, 1);
       
   875             job = 0;
       
   876         }
       
   877     }
       
   878     if (!job)
       
   879         return;
       
   880 
       
   881     QHttp *http = connection[c].http;
       
   882 
       
   883     connection[c].current = job;
       
   884     connection[c].id = -1;
       
   885 #ifndef QT_NO_NETWORKPROXY
       
   886     int proxyId = http->setProxy(job->frame()->page()->networkProxy());
       
   887 #endif
       
   888 
       
   889     QByteArray postData = job->postData();
       
   890     if (!postData.isEmpty())
       
   891         connection[c].id = http->request(job->httpHeader(), postData);
       
   892     else
       
   893         connection[c].id = http->request(job->httpHeader());
       
   894 
       
   895     DEBUG() << "WebCoreHttp::scheduleNextRequest: using connection" << c;
       
   896     DEBUG() << job->httpHeader().toString();
       
   897 //     DEBUG() << job->request.toString();
       
   898 }
       
   899 
       
   900 int WebCoreHttp::getConnection()
       
   901 {
       
   902     QObject *o = sender();
       
   903     int c;
       
   904     if (o == connection[0].http) {
       
   905         c = 0;
       
   906     } else {
       
   907         Q_ASSERT(o == connection[1].http);
       
   908         c = 1;
       
   909     }
       
   910     Q_ASSERT(connection[c].current);
       
   911     return c;
       
   912 }
       
   913 
       
   914 void WebCoreHttp::onResponseHeaderReceived(const QHttpResponseHeader &resp)
       
   915 {
       
   916     QHttp *http = qobject_cast<QHttp*>(sender());
       
   917     if (http->currentId() == 0) {
       
   918         qDebug() << "ERROR!  Invalid job id.  Why?"; // foxnews.com triggers this
       
   919         return;
       
   920     }
       
   921     int c = getConnection();
       
   922     QWebNetworkJob *job = connection[c].current;
       
   923     DEBUG() << "WebCoreHttp::slotResponseHeaderReceived connection=" << c;
       
   924     DEBUG() << resp.toString();
       
   925 
       
   926     job->setResponse(resp);
       
   927 
       
   928     emit job->networkInterface()->started(job);
       
   929 }
       
   930 
       
   931 void WebCoreHttp::onReadyRead()
       
   932 {
       
   933     QHttp *http = qobject_cast<QHttp*>(sender());
       
   934     if (http->currentId() == 0) {
       
   935         qDebug() << "ERROR!  Invalid job id.  Why?"; // foxnews.com triggers this
       
   936         return;
       
   937     }
       
   938     int c = getConnection();
       
   939     QWebNetworkJob *req = connection[c].current;
       
   940     Q_ASSERT(http == connection[c].http);
       
   941     //DEBUG() << "WebCoreHttp::slotReadyRead connection=" << c;
       
   942 
       
   943     QByteArray data;
       
   944     data.resize(http->bytesAvailable());
       
   945     http->read(data.data(), data.length());
       
   946     emit req->networkInterface()->data(req, data);
       
   947 }
       
   948 
       
   949 void WebCoreHttp::onRequestFinished(int id, bool error)
       
   950 {
       
   951     int c = getConnection();
       
   952     if (connection[c].id != id) {
       
   953         return;
       
   954     }
       
   955 
       
   956     QWebNetworkJob *req = connection[c].current;
       
   957     if (!req) {
       
   958         scheduleNextRequest();
       
   959         return;
       
   960     }
       
   961 
       
   962     QHttp *http = connection[c].http;
       
   963     DEBUG() << "WebCoreHttp::slotFinished connection=" << c << error << req;
       
   964     if (error)
       
   965         DEBUG() << "   error: " << http->errorString();
       
   966 
       
   967     if (!error && http->bytesAvailable()) {
       
   968         QByteArray data;
       
   969         data.resize(http->bytesAvailable());
       
   970         http->read(data.data(), data.length());
       
   971         emit req->networkInterface()->data(req, data);
       
   972     }
       
   973     emit req->networkInterface()->finished(req, error ? 1 : 0);
       
   974     connection[c].current = 0;
       
   975     connection[c].id = -1;
       
   976     scheduleNextRequest();
       
   977 }
       
   978 
       
   979 void WebCoreHttp::onDone(bool error)
       
   980 {
       
   981     DEBUG() << "WebCoreHttp::onDone" << error;
       
   982 }
       
   983 
       
   984 void WebCoreHttp::onStateChanged(int state)
       
   985 {
       
   986     DEBUG() << "State changed to" << state << "and connections are" << connection[0].current << connection[1].current;
       
   987     if (state == QHttp::Closing || state == QHttp::Unconnected) {
       
   988         if (!m_inCancel && m_pendingRequests.isEmpty()
       
   989             && !connection[0].current && !connection[1].current)
       
   990             emit connectionClosed(info);
       
   991     }
       
   992 }
       
   993 
       
   994 void WebCoreHttp::cancel(QWebNetworkJob* request)
       
   995 {
       
   996     bool doEmit = true;
       
   997     m_inCancel = true;
       
   998     for (int i = 0; i < 2; ++i) {
       
   999         if (request == connection[i].current) {
       
  1000             connection[i].http->abort();
       
  1001             doEmit = false;
       
  1002         }
       
  1003     }
       
  1004     if (!m_pendingRequests.removeAll(request))
       
  1005         doEmit = false;
       
  1006     m_inCancel = false;
       
  1007 
       
  1008     if (doEmit)
       
  1009         emit request->networkInterface()->finished(request, 1);
       
  1010 
       
  1011     if (m_pendingRequests.isEmpty()
       
  1012         && !connection[0].current && !connection[1].current)
       
  1013         emit connectionClosed(info);
       
  1014 }
       
  1015 
       
  1016 void WebCoreHttp::onSslErrors(const QList<QSslError>& errors)
       
  1017 {
       
  1018     int c = getConnection();
       
  1019     QWebNetworkJob *req = connection[c].current;
       
  1020     if (req) {
       
  1021         bool continueAnyway = false;
       
  1022         emit req->networkInterface()->sslErrors(req->frame(), req->url(), errors, &continueAnyway);
       
  1023 #ifndef QT_NO_OPENSSL
       
  1024         if (continueAnyway)
       
  1025             connection[c].http->ignoreSslErrors();
       
  1026 #endif
       
  1027     }
       
  1028 }
       
  1029 
       
  1030 void WebCoreHttp::onAuthenticationRequired(const QString& hostname, quint16 port, QAuthenticator *auth)
       
  1031 {
       
  1032     int c = getConnection();
       
  1033     QWebNetworkJob *req = connection[c].current;
       
  1034     if (req) {
       
  1035         emit req->networkInterface()->authenticate(req->frame(), req->url(), hostname, port, auth);
       
  1036         if (auth->isNull())
       
  1037             connection[c].http->abort();
       
  1038     }
       
  1039 }
       
  1040 
       
  1041 void WebCoreHttp::onProxyAuthenticationRequired(const QNetworkProxy& proxy, QAuthenticator *auth)
       
  1042 {
       
  1043     int c = getConnection();
       
  1044     QWebNetworkJob *req = connection[c].current;
       
  1045     if (req) {
       
  1046         emit req->networkInterface()->authenticateProxy(req->frame(), req->url(), proxy, auth);
       
  1047         if (auth->isNull())
       
  1048             connection[c].http->abort();
       
  1049     }
       
  1050 }
       
  1051 
       
  1052 HostInfo::HostInfo(const QUrl& url)
       
  1053     : protocol(url.scheme())
       
  1054     , host(url.host())
       
  1055     , port(url.port())
       
  1056 {
       
  1057     if (port < 0) {
       
  1058         if (protocol == QLatin1String("http"))
       
  1059             port = 80;
       
  1060         else if (protocol == QLatin1String("https"))
       
  1061             port = 443;
       
  1062     }
       
  1063 }
       
  1064