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