The http loader should only ask for http cookies. (not, for example, data: cookies)
[WebKit-https.git] / WebKitQt / Api / qwebnetworkinterface.cpp
1 /*
2   Copyright (C) 2006 Enrico Ros <enrico.ros@m31engineering.it>
3   Copyright (C) 2007 Trolltech ASA
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Library General Public
7   License as published by the Free Software Foundation; either
8   version 2 of the License, or (at your option) any later version.
9   
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Library General Public License for more details.
14   
15   You should have received a copy of the GNU Library General Public License
16   along with this library; see the file COPYING.LIB.  If not, write to
17   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18   Boston, MA 02111-1307, USA.
19   
20   This class provides all functionality needed for loading images, style sheets and html
21   pages from the web. It has a memory cache for these objects.
22 */
23 #include <qglobal.h>
24 #include "qwebnetworkinterface.h"
25 #include "qwebnetworkinterface_p.h"
26 #include <qdebug.h>
27 #include <qfile.h>
28
29 #include "ResourceHandle.h"
30 #include "ResourceHandleClient.h"
31 #include "ResourceHandleInternal.h"
32 #include "MimeTypeRegistry.h"
33 #include "CookieJar.h"
34
35 #define notImplemented() qDebug("FIXME: UNIMPLEMENTED: %s:%d (%s)", __FILE__, __LINE__, __FUNCTION__)
36
37 #if 0
38 #define DEBUG qDebug
39 #else
40 #define DEBUG if (1) {} else qDebug
41 #endif
42
43 static QWebNetworkInterface *default_interface = 0;
44 static QWebNetworkManager *manager = 0;
45
46 using namespace WebCore;
47
48 uint qHash(const HostInfo &info)
49 {
50     return qHash(info.host) + info.port;
51 }
52
53 static bool operator==(const HostInfo &i1, const HostInfo &i2)
54 {
55     return i1.port == i2.port && i1.host == i2.host;
56 }
57
58 void QWebNetworkJobPrivate::setURL(const QUrl &u)
59 {
60     url = u;
61     int port = url.port();
62     if (port > 0 && port != 80)
63         request.setValue(QLatin1String("Host"), url.host() + QLatin1Char(':') + QString::number(port));
64     else
65         request.setValue(QLatin1String("Host"), url.host());
66 }
67
68 /*!
69   \class QWebNetworkJob
70
71   The QWebNetworkJob class represents a network job, that needs to be
72   processed by the QWebNetworkInterface.
73
74   This class is only required when implementing a new network layer (or
75   support for a special protocol) using QWebNetworkInterface.
76
77   QWebNetworkJob objects are created and owned by the QtWebKit library.
78   Most of it's properties are read-only.
79
80   The job is reference counted. This can be used to ensure that the job doesn't
81   get deleted while it's still stored in some data structure.
82 */
83
84 /*!
85   \internal
86 */
87 QWebNetworkJob::QWebNetworkJob()
88     : d(new QWebNetworkJobPrivate)
89 {
90     d->ref = 1;
91     d->redirected = false;
92     d->interface = 0;
93 }
94
95 /*!
96   \internal
97 */
98 QWebNetworkJob::~QWebNetworkJob()
99 {
100     delete d;
101 }
102
103 /*!
104   The requested URL
105 */
106 QUrl QWebNetworkJob::url() const
107 {
108     return d->url;
109 }
110
111 /*!
112   Post data associated with the job
113 */
114 QByteArray QWebNetworkJob::postData() const
115 {
116     return d->postData;
117 }
118
119 /*!
120   The HTTP request header that should be used to download the job.
121 */
122 QHttpRequestHeader QWebNetworkJob::request() const
123 {
124     return d->request;
125 }
126
127 /*!
128   The HTTP response header received from the network.
129 */
130 QHttpResponseHeader QWebNetworkJob::response() const
131 {
132     return d->response;
133 }
134
135 /*!
136   Sets the HTTP reponse header. The response header has to be called before
137   emitting QWebNetworkInterface::started.
138 */
139 void QWebNetworkJob::setResponse(const QHttpResponseHeader &response)
140 {
141     d->response = response;
142 }
143
144 /*!
145   returns true if the job has been cancelled by the WebKit framework
146 */
147 bool QWebNetworkJob::cancelled() const
148 {
149     return !d->resourceHandle;
150 }
151
152 /*!
153   reference the job.
154 */
155 void QWebNetworkJob::ref()
156 {
157     ++d->ref;
158 }
159
160 /*!
161   derefence the job.
162
163   If the reference count drops to 0 this method also deletes the job.
164
165   Returns false if the reference count has dropped to 0.
166 */
167 bool QWebNetworkJob::deref()
168 {
169     if (!--d->ref) {
170         delete this;
171         return false;
172     }
173     return true;
174 }
175
176 /*!
177    Returns the network interface that is associated with this job.
178 */
179 QWebNetworkInterface *QWebNetworkJob::networkInterface() const
180 {
181     return d->interface;
182 }
183
184 /*!
185   \class QWebNetworkManager
186   \internal
187 */
188 QWebNetworkManager::QWebNetworkManager()
189     : QObject(0)
190 {
191 }
192
193 QWebNetworkManager *QWebNetworkManager::self()
194 {
195     // ensure everything's constructed and connected
196     QWebNetworkInterface::defaultInterface();
197
198     return manager;
199 }
200
201 bool QWebNetworkManager::add(ResourceHandle *handle, QWebNetworkInterface *interface)
202 {
203     ASSERT(resource);
204
205     if (!interface)
206         interface = default_interface;
207
208     ASSERT(interface);
209
210     QWebNetworkJob *job = new QWebNetworkJob();
211     handle->getInternal()->m_job = job;
212     job->d->resourceHandle = handle;
213     job->d->interface = interface;
214
215     KURL url = handle->url();
216     QUrl qurl = QString(url.url());
217     //qDebug() << QString(url.path() + url.query());
218     job->d->request = QHttpRequestHeader(handle->method(), url.path() + url.query());
219     job->d->request.setValue(QLatin1String("User-Agent"),
220                              QLatin1String("Mozilla/5.0 (PC; U; Intel; Linux; en) AppleWebKit/420+ (KHTML, like Gecko)"));
221     job->d->request.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
222
223     job->d->setURL(qurl);
224
225     const QString scheme = qurl.scheme().toLower();
226     if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
227         QString cookies = WebCore::cookies(handle->url());
228         if (!cookies.isEmpty())
229             job->d->request.setValue(QLatin1String("Cookie"), cookies);
230     }
231
232     const HTTPHeaderMap& loaderHeaders = handle->requestHeaders();
233     HTTPHeaderMap::const_iterator end = loaderHeaders.end();
234     for (HTTPHeaderMap::const_iterator it = loaderHeaders.begin(); it != end; ++it)
235         job->d->request.setValue(it->first, it->second);
236
237     int id;
238     // handle and perform a 'POST' request
239     if (handle->method() == "POST") {
240         DeprecatedString pd = handle->postData()->flattenToString().deprecatedString();
241         job->d->postData = QByteArray(pd.ascii(), pd.length());
242         job->d->request.setValue(QLatin1String("content-length"), QString::number(job->d->postData.size()));
243     } else if (handle->method() != "GET") {
244         // or.. don't know what to do! (probably a request error!!)
245         // but treat it like a 'GET' request
246         qWarning("REQUEST: [%s]\n", qPrintable(job->d->request.toString()));
247     }
248
249     DEBUG() << "QWebNetworkManager::add:" <<  job->d->request.toString();
250
251     interface->addJob(job);
252
253     return true;
254 }
255
256 void QWebNetworkManager::cancel(ResourceHandle *handle)
257 {
258     QWebNetworkJob *job = handle->getInternal()->m_job;
259     if (!job)
260         return;
261     job->d->resourceHandle = 0;
262     job->d->interface->cancelJob(job);
263     handle->getInternal()->m_job = 0;
264 }
265
266 void QWebNetworkManager::started(QWebNetworkJob *job)
267 {
268     if (!job->d->resourceHandle)
269         return;
270
271     DEBUG() << "ResourceHandleManager::receivedResponse:";
272     DEBUG() << job->d->response.toString();
273
274     ResourceHandleClient* client = job->d->resourceHandle->client();
275     if (!client)
276         return;
277
278     QStringList cookies = job->d->response.allValues("Set-Cookie");
279     KURL url = job->d->resourceHandle->url();
280     foreach (QString c, cookies) {
281         setCookies(url, url, c);
282     }
283     QString contentType = job->d->response.value("Content-Type");
284     QString encoding;
285     int idx = contentType.indexOf(QLatin1Char(';'));
286     if (idx > 0) {
287         QString remainder = contentType.mid(idx + 1).toLower();
288         contentType = contentType.left(idx).trimmed();
289
290         idx = remainder.indexOf("charset");
291         if (idx >= 0) {
292             idx = remainder.indexOf(QLatin1Char('='), idx);
293             if (idx >= 0)
294                 encoding = remainder.mid(idx + 1).trimmed();
295         }
296     }
297     if (contentType.isEmpty()) {
298         // let's try to guess from the extension
299         QString extension = job->d->url.path();
300         int index = extension.lastIndexOf(QLatin1Char('.'));
301         if (index > 0) {
302             extension = extension.mid(index + 1);
303             contentType = MimeTypeRegistry::getMIMETypeForExtension(extension);
304         }
305     }
306 //     qDebug() << "Content-Type=" << contentType;
307 //     qDebug() << "Encoding=" << encoding;
308
309
310     ResourceResponse response(url, contentType,
311                               0 /* FIXME */,
312                               encoding,
313                               String() /* FIXME */);
314
315     int statusCode = job->d->response.statusCode();
316     response.setHTTPStatusCode(statusCode);
317     /* Fill in the other fields */
318
319     if (statusCode >= 300 && statusCode < 400) {
320         // we're on a redirect page! if the 'Location:' field is valid, we redirect
321         QString location = job->d->response.value("location");
322         DEBUG() << "Redirection";
323         if (!location.isEmpty()) {
324             ResourceRequest newRequest = job->d->resourceHandle->request();
325             newRequest.setURL(KURL(newRequest.url(), DeprecatedString(location)));
326             client->willSendRequest(job->d->resourceHandle, newRequest, response);
327             job->d->request.setRequest(job->d->request.method(), newRequest.url().path() + newRequest.url().query());
328             job->d->setURL(QString(newRequest.url().url()));
329             job->d->redirected = true;
330             return;
331         }
332     }
333
334     client->didReceiveResponse(job->d->resourceHandle, response);
335     
336 }
337
338 void QWebNetworkManager::data(QWebNetworkJob *job, const QByteArray &data)
339 {
340     if (!job->d->resourceHandle)
341         return;
342
343     ResourceHandleClient* client = job->d->resourceHandle->client();
344     if (!client)
345         return;
346
347     if (job->d->redirected)
348         return; // don't emit the "Document has moved here" type of HTML
349
350     DEBUG() << "receivedData" << job->d->url.path();
351     client->didReceiveData(job->d->resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/);
352     
353 }
354
355 void QWebNetworkManager::finished(QWebNetworkJob *job, int errorCode)
356 {
357     if (!job->d->resourceHandle) {
358         job->deref();
359         return;
360     }
361
362     DEBUG() << "receivedFinished" << errorCode << job->url();
363
364     if (job->d->redirected) {
365         job->d->redirected = false;
366         job->d->interface->addJob(job);
367         return;
368     }
369     job->d->resourceHandle->getInternal()->m_job = 0;
370
371     ResourceHandleClient* client = job->d->resourceHandle->client();
372     if (!client)
373         return;
374
375     if (errorCode) {
376         //FIXME: error setting error was removed from ResourceHandle
377         client->didFail(job->d->resourceHandle,
378                         ResourceError(job->d->url.host(), job->d->response.statusCode(),
379                                       job->d->url.toString(), String()));
380     } else {
381         client->didFinishLoading(job->d->resourceHandle);
382     }
383     DEBUG() << "receivedFinished done" << job->d->url;
384
385     job->deref();
386 }
387
388 void QWebNetworkManager::addHttpJob(QWebNetworkJob *job)
389 {
390     HostInfo hostInfo(job->url());
391     WebCoreHttp *httpConnection = m_hostMapping.value(hostInfo);
392     if (!httpConnection) {
393         // #### fix custom ports
394         DEBUG() << "   new connection to" << hostInfo.host << hostInfo.port;
395         httpConnection = new WebCoreHttp(this, hostInfo);
396         QObject::connect(httpConnection, SIGNAL(connectionClosed(const WebCore::HostInfo&)),
397                          this, SLOT(httpConnectionClosed(const WebCore::HostInfo&)));
398
399         m_hostMapping[hostInfo] = httpConnection;
400     }
401     httpConnection->request(job);
402 }
403
404 void QWebNetworkManager::cancelHttpJob(QWebNetworkJob *job)
405 {
406     WebCoreHttp *httpConnection = m_hostMapping.value(job->url());
407     if (httpConnection)
408         httpConnection->cancel(job);
409 }
410
411 void QWebNetworkManager::httpConnectionClosed(const WebCore::HostInfo &info)
412 {
413     WebCoreHttp *connection = m_hostMapping.take(info);
414     delete connection;
415 }
416
417 void QWebNetworkInterfacePrivate::sendFileData(QWebNetworkJob* job, int statusCode, const QByteArray &data)
418 {
419     int error = statusCode >= 400 ? 1 : 0;
420     if (!job->cancelled()) {
421         QHttpResponseHeader response;
422         response.setStatusLine(statusCode);
423         job->setResponse(response);
424         emit q->started(job);
425         if (!data.isEmpty())
426             emit q->data(job, data);
427     }
428     emit q->finished(job, error);
429 }
430
431 void QWebNetworkInterfacePrivate::parseDataUrl(QWebNetworkJob* job)
432 {
433     QByteArray data = job->url().toString().toLatin1();
434     //qDebug() << "handling data url:" << data; 
435
436     ASSERT(data.startsWith("data:"));
437
438     // Here's the syntax of data URLs:
439     // dataurl    := "data:" [ mediatype ] [ ";base64" ] "," data
440     // mediatype  := [ type "/" subtype ] *( ";" parameter )
441     // data       := *urlchar
442     // parameter  := attribute "=" value
443     QByteArray header;
444     bool base64 = false;
445
446     int index = data.indexOf(',');
447     if (index != -1) {
448         header = data.mid(5, index - 5);
449         header = header.toLower();
450         //qDebug() << "header=" << header;
451         data = data.mid(index+1);
452         //qDebug() << "data=" << data;
453
454         if (header.endsWith(";base64")) {
455             //qDebug() << "base64";
456             base64 = true;
457             header = header.left(header.length() - 7);
458             //qDebug() << "mime=" << header;
459         }        
460     } else {
461         data = QByteArray();
462     }
463     if (base64) {
464         data = QByteArray::fromBase64(data);
465     } else {
466         data = QUrl::fromPercentEncoding(data).toLatin1();
467     }
468
469     if (header.isEmpty()) 
470         header = "text/plain;charset=US-ASCII";
471     int statusCode = data.isEmpty() ? 404 : 200;
472     QHttpResponseHeader response;
473     response.setContentType(header);
474     response.setContentLength(data.size());
475     job->setResponse(response);
476
477     sendFileData(job, statusCode, data);
478 }
479
480 /*!
481   \class QWebNetworkInterface
482
483   The QWebNetworkInterface class provides an abstraction layer for
484   WebKit's network interface.  It allows to completely replace or
485   extend the builtin network layer.
486
487   QWebNetworkInterface contains two virtual methods, addJob and
488   cancelJob that have to be reimplemented when implementing your own
489   networking layer.
490
491   QWebNetworkInterface can by default handle the http, https, file and
492   data URI protocols.
493   
494 */
495
496 /*!
497   Sets a new default interface that will be used by all of WebKit
498   for downloading data from the internet.
499 */
500 void QWebNetworkInterface::setDefaultInterface(QWebNetworkInterface *defaultInterface)
501 {
502     if (default_interface == defaultInterface)
503         return;
504     if (default_interface)
505         delete default_interface;
506     default_interface = defaultInterface;
507 }
508
509 /*!
510   Returns the default interface that will be used by WebKit. If no
511   default interface has been set, QtWebkit will create an instance of
512   QWebNetworkInterface to do the work.
513 */
514 QWebNetworkInterface *QWebNetworkInterface::defaultInterface()
515 {
516     if (!default_interface)
517         setDefaultInterface(new QWebNetworkInterface);
518     return default_interface;
519 }
520
521
522 /*!
523   Constructs a QWebNetworkInterface object.
524 */
525 QWebNetworkInterface::QWebNetworkInterface(QObject *parent)
526     : QObject(parent)
527 {
528     d = new QWebNetworkInterfacePrivate;
529     d->q = this;
530
531     if (!manager)
532         manager = new QWebNetworkManager;
533
534     QObject::connect(this, SIGNAL(started(QWebNetworkJob*)),
535                      manager, SLOT(started(QWebNetworkJob*)), Qt::QueuedConnection);
536     QObject::connect(this, SIGNAL(data(QWebNetworkJob*, const QByteArray &)),
537                      manager, SLOT(data(QWebNetworkJob*, const QByteArray &)), Qt::QueuedConnection);
538     QObject::connect(this, SIGNAL(finished(QWebNetworkJob*, int)),
539                      manager, SLOT(finished(QWebNetworkJob*, int)), Qt::QueuedConnection);
540 }
541
542 /*!
543   Destructs the QWebNetworkInterface object.
544 */
545 QWebNetworkInterface::~QWebNetworkInterface()
546 {
547     delete d;
548 }
549
550 /*!
551   This virtual method gets called whenever QtWebkit needs to add a
552   new job to download.
553
554   The QWebNetworkInterface should process this job, by first emitting
555   the started signal, then emitting data repeatedly as new data for
556   the Job is available, and finally ending the job with emitting a
557   finished signal.
558
559   After the finished signal has been emitted, the QWebNetworkInterface
560   is not allowed to access the job anymore.
561 */
562 void QWebNetworkInterface::addJob(QWebNetworkJob *job)
563 {
564     QString protocol = job->url().scheme();
565     if (protocol == QLatin1String("http")) {
566         QWebNetworkManager::self()->addHttpJob(job);
567         return;
568     }
569
570     // "file", "data" and all unhandled stuff go through here
571     //DEBUG() << "fileRequest";
572     DEBUG() << "FileLoader::request" << job->url();
573
574     if (job->cancelled()) {
575         d->sendFileData(job, 400, QByteArray());
576         return;
577     }
578
579     QUrl url = job->url();
580     if (protocol == QLatin1String("data")) {
581         d->parseDataUrl(job);
582         return;
583     }
584
585     int statusCode = 200;
586     QByteArray data;
587     if (!(protocol.isEmpty() || protocol == QLatin1String("file"))) {
588         statusCode = 404;
589     } else if (job->postData().isEmpty()) {
590         QFile f(url.path());
591         DEBUG() << "opening" << QString(url.path());
592
593         if (f.open(QIODevice::ReadOnly)) {
594             QHttpResponseHeader response;
595             response.setStatusLine(200);
596             job->setResponse(response);
597             data = f.readAll();
598         } else {
599             statusCode = 404;
600         }
601     } else {
602         statusCode = 404;
603     }
604     d->sendFileData(job, statusCode, data);
605 }
606
607 /*!
608   This virtual method gets called whenever QtWebkit needs to cancel a
609   new job.
610
611   The QWebNetworkInterface acknowledge the canceling of the job, by
612   emitting the finished signal with an error code of 1. After emitting
613   the finished signal, the interface should not access the job
614   anymore.
615 */
616 void QWebNetworkInterface::cancelJob(QWebNetworkJob *job)
617 {
618     QString protocol = job->url().scheme();
619     if (protocol == QLatin1String("http"))
620         QWebNetworkManager::self()->cancelHttpJob(job);
621 }
622
623 /////////////////////////////////////////////////////////////////////////////
624 WebCoreHttp::WebCoreHttp(QObject* parent, const HostInfo &hi)
625     : QObject(parent), info(hi),
626       m_inCancel(false)
627 {
628     for (int i = 0; i < 2; ++i) {
629         connection[i].http = new QHttp(info.host, info.port);
630         connection[i].current = 0;
631         connect(connection[i].http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)),
632                 this, SLOT(onResponseHeaderReceived(const QHttpResponseHeader&)));
633         connect(connection[i].http, SIGNAL(readyRead(const QHttpResponseHeader&)),
634                 this, SLOT(onReadyRead()));
635         connect(connection[i].http, SIGNAL(requestFinished(int, bool)),
636                 this, SLOT(onRequestFinished(int, bool)));
637         connect(connection[i].http, SIGNAL(stateChanged(int)),
638                 this, SLOT(onStateChanged(int)));
639     }
640 }
641
642 WebCoreHttp::~WebCoreHttp()
643 {
644 }
645
646 void WebCoreHttp::request(QWebNetworkJob *job)
647 {
648     DEBUG() << ">>>>>>>>>>>>>> WebCoreHttp::request";
649     DEBUG() << job->request().toString() << "\n";
650     m_pendingRequests.append(job);
651
652     scheduleNextRequest();
653 }
654
655 void WebCoreHttp::scheduleNextRequest()
656 {
657     int c = 0;
658     for (; c < 2; ++c) {
659         if (!connection[c].current)
660             break;
661     }
662     if (c >= 2)
663         return;
664
665     QWebNetworkJob *job = 0;
666     while (!job && !m_pendingRequests.isEmpty()) {
667         job = m_pendingRequests.takeFirst();
668         if (job->cancelled()) {
669             emit job->networkInterface()->finished(job, 1);
670             job = 0;
671         }
672     }
673     if (!job)
674         return;
675     
676     QHttp *http = connection[c].http;
677     QByteArray postData = job->postData();
678     if (!postData.isEmpty())
679         http->request(job->request(), postData);
680     else
681         http->request(job->request());
682     connection[c].current = job;
683
684     DEBUG() << "WebCoreHttp::scheduleNextRequest: using connection" << c;
685 //     DEBUG() << job->request.toString();
686 }
687
688 int WebCoreHttp::getConnection()
689 {
690     QObject *o = sender();
691     int c;
692     if (o == connection[0].http) {
693         c = 0;
694     } else {
695         Q_ASSERT(o == connection[1].http);
696         c = 1;
697     }
698     //Q_ASSERT(connection[c].current);
699     return c;
700 }
701
702 void WebCoreHttp::onResponseHeaderReceived(const QHttpResponseHeader &resp)
703 {
704     int c = getConnection();
705     QWebNetworkJob *job = connection[c].current;
706     DEBUG() << "WebCoreHttp::slotResponseHeaderReceived connection=" << c;
707
708     job->setResponse(resp);
709
710     emit job->networkInterface()->started(job);
711 }
712
713 void WebCoreHttp::onReadyRead()
714 {
715     int c = getConnection();
716     QWebNetworkJob *req = connection[c].current;
717     QHttp *http = connection[c].http;
718     DEBUG() << "WebCoreHttp::slotReadyRead connection=" << c;
719
720     QByteArray data;
721     data.resize(http->bytesAvailable());
722     http->read(data.data(), data.length());
723     emit req->networkInterface()->data(req, data);
724 }
725
726 void WebCoreHttp::onRequestFinished(int, bool error)
727 {
728     int c = getConnection();
729     QWebNetworkJob *req = connection[c].current;
730     if (!req) {
731         scheduleNextRequest();
732         return;
733     }
734     QHttp *http = connection[c].http;
735     DEBUG() << "WebCoreHttp::slotFinished connection=" << c << error << req;
736
737     if (error)
738         DEBUG() << "   error: " << http->errorString();
739
740     if (!error && http->bytesAvailable()) {
741         QByteArray data;
742         data.resize(http->bytesAvailable());
743         http->read(data.data(), data.length());
744         emit req->networkInterface()->data(req, data);
745     }
746     emit req->networkInterface()->finished(req, error ? 1 : 0);
747
748     connection[c].current = 0;
749     scheduleNextRequest();
750 }
751
752 void WebCoreHttp::onStateChanged(int state)
753 {
754     if (state == QHttp::Closing || state == QHttp::Unconnected) {
755         if (!m_inCancel && m_pendingRequests.isEmpty()
756             && !connection[0].current && !connection[1].current)
757             emit connectionClosed(info);
758     }
759 }
760
761 void WebCoreHttp::cancel(QWebNetworkJob* request)
762 {
763     bool doEmit = true;
764     m_inCancel = true;
765     for (int i = 0; i < 2; ++i) {
766         if (request == connection[i].current) {
767             connection[i].http->abort();
768             doEmit = false;
769         }
770     }
771     if (!m_pendingRequests.removeAll(request))
772         doEmit = false;
773     m_inCancel = false;
774
775     if (doEmit)
776         emit request->networkInterface()->finished(request, 1);
777
778     if (m_pendingRequests.isEmpty()
779         && !connection[0].current && !connection[1].current)
780         emit connectionClosed(info);
781 }
782
783 HostInfo::HostInfo(const QUrl& url)
784     : protocol(url.scheme())
785     , host(url.host())
786     , port(url.port())
787 {
788     if (port < 0) {
789         if (protocol == QLatin1String("http"))
790             port = 80;
791         else if (protocol == QLatin1String("https"))
792             port = 443;
793     }
794 }
795
796 #include "qwebnetworkinterface_p.moc"
797 #include "qwebnetworkinterface.moc"
798