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