Holger Hans Peter Freyther <zecke@selfish.org>
[WebKit-https.git] / WebCore / platform / network / qt / QNetworkReplyHandler.cpp
index 1709966..5e3d943 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2007 Trolltech ASA
+    Copyright (C) 2007-2008 Trolltech ASA
     Copyright (C) 2007 Staikos Computing Services Inc.  <info@staikos.net>
 
     This library is free software; you can redistribute it and/or
 #include "ResourceHandleInternal.h"
 #include "ResourceResponse.h"
 #include "ResourceRequest.h"
+#include <QDateTime>
+#include <QFile>
 #include <QNetworkReply>
 #include <QNetworkCookie>
 #include <qwebframe.h>
 #include <qwebpage.h>
 
+#include <QDebug>
+
 namespace WebCore {
 
+// Take a deep copy of the FormDataElement
+FormDataIODevice::FormDataIODevice(FormData* data)
+    : m_formElements(data ? data->elements() : Vector<FormDataElement>())
+    , m_currentFile(0)
+    , m_currentDelta(0)
+{
+    setOpenMode(FormDataIODevice::ReadOnly);
+}
+
+FormDataIODevice::~FormDataIODevice()
+{
+    delete m_currentFile;
+}
+
+void FormDataIODevice::moveToNextElement()
+{
+    if (m_currentFile)
+        m_currentFile->close();
+    m_currentDelta = 0;
+
+    m_formElements.remove(0);
+
+    if (m_formElements.isEmpty() || m_formElements[0].m_type == FormDataElement::data)
+        return;
+
+    if (!m_currentFile)
+        m_currentFile = new QFile;
+
+    m_currentFile->setFileName(m_formElements[0].m_filename);
+    m_currentFile->open(QFile::ReadOnly);
+}
+
+// m_formElements[0] is the current item. If the destination buffer is
+// big enough we are going to read from more than one FormDataElement
+qint64 FormDataIODevice::readData(char* destination, qint64 size)
+{
+    if (m_formElements.isEmpty())
+        return -1;
+
+    qint64 copied = 0;
+    while (copied < size && !m_formElements.isEmpty()) {
+        const FormDataElement& element = m_formElements[0];
+        const qint64 available = size-copied;
+
+        if (element.m_type == FormDataElement::data) {
+            const qint64 toCopy = qMin<qint64>(available, element.m_data.size() - m_currentDelta);
+            memcpy(destination+copied, element.m_data.data()+m_currentDelta, toCopy); 
+            m_currentDelta += toCopy;
+            copied += toCopy;
+
+            if (m_currentDelta == element.m_data.size())
+                moveToNextElement();
+        } else {
+            const QByteArray data = m_currentFile->read(available);
+            memcpy(destination+copied, data.constData(), data.size());
+            copied += data.size();
+
+            if (m_currentFile->atEnd() || !m_currentFile->isOpen())
+                moveToNextElement();
+        }
+    }
+
+    return copied;
+}
+
+qint64 FormDataIODevice::writeData(const char*, qint64)
+{
+    return -1;
+}
+
+void FormDataIODevice::setParent(QNetworkReply* reply)
+{
+    QIODevice::setParent(reply);
+
+    connect(reply, SIGNAL(finished()), SLOT(slotFinished()));
+}
+
+bool FormDataIODevice::isSequential() const
+{
+    return true;
+}
+
+void FormDataIODevice::slotFinished()
+{
+    deleteLater();
+}
+
 QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle *handle)
     : QObject(0)
-      , m_resourceHandle(handle)
-      , m_reply(0)
-      , m_redirected(false)
-      , m_responseSent(false)
+    , m_resourceHandle(handle)
+    , m_reply(0)
+    , m_redirected(false)
+    , m_responseSent(false)
+    , m_startTime(0)
 {
     const ResourceRequest &r = m_resourceHandle->request();
 
@@ -68,7 +160,18 @@ void QNetworkReplyHandler::abort()
         disconnect(m_reply, 0, this, 0);
         m_reply->abort();
         deleteLater();
+        m_reply = 0;
+    }
+}
+
+QNetworkReply *QNetworkReplyHandler::release()
+{
+    QNetworkReply *reply = m_reply;
+    if (m_reply) {
+        disconnect(m_reply, 0, this, 0);
+        m_reply = 0;
     }
+    return reply;
 }
 
 void QNetworkReplyHandler::finish()
@@ -83,6 +186,7 @@ void QNetworkReplyHandler::finish()
         return;
     if (m_redirected) {
         m_redirected = false;
+        m_responseSent = false;
         start();
     } else if (m_reply->error() != QNetworkReply::NoError) {
         QUrl url = m_reply->url();
@@ -119,37 +223,54 @@ void QNetworkReplyHandler::sendResponseIfNeeded()
     }
 
     KURL url(m_reply->url());
-    String contentDisposition = QString::fromAscii(m_reply->rawHeader("Content-Disposition"));
+    String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromAscii(m_reply->rawHeader("Content-Disposition")));
+
+    if (suggestedFilename.isEmpty())
+        suggestedFilename = url.lastPathComponent();
 
     ResourceResponse response(url, mimeType,
                               m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(),
                               encoding,
-                              filenameFromHTTPContentDisposition(contentDisposition));
+                              suggestedFilename);
 
+    const bool isLocalFileReply = (m_reply->url().scheme() == QLatin1String("file"));
     int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-    if (m_reply->url().scheme() != QLatin1String("file"))
+    if (!isLocalFileReply)
         response.setHTTPStatusCode(statusCode);
     else if (m_reply->error() == QNetworkReply::ContentNotFoundError)
         response.setHTTPStatusCode(404);
 
 
-    /* Fill in the other fields */
-    foreach (QByteArray headerName, m_reply->rawHeaderList())
+    /* Fill in the other fields
+     * For local file requests remove the content length and the last-modified
+     * headers as required by fast/dom/xmlhttprequest-get.xhtml
+     */
+    foreach (QByteArray headerName, m_reply->rawHeaderList()) {
+
+        if (isLocalFileReply
+            && (headerName == "Content-Length" || headerName == "Last-Modified"))
+            continue;
+
         response.setHTTPHeaderField(QString::fromAscii(headerName), QString::fromAscii(m_reply->rawHeader(headerName)));
+    }
+
+    if (isLocalFileReply)
+        response.setExpirationDate(m_startTime);
 
     QUrl redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
     if (redirection.isValid()) {
         QUrl newUrl = m_reply->url().resolved(redirection);
         ResourceRequest newRequest = m_resourceHandle->request();
         newRequest.setURL(newUrl);
-        client->willSendRequest(m_resourceHandle, newRequest, response);
 
-        if (statusCode >= 301 && statusCode <= 303 && m_method == QNetworkAccessManager::PostOperation)
+        if (((statusCode >= 301 && statusCode <= 303) || statusCode == 307) && m_method == QNetworkAccessManager::PostOperation) {
             m_method = QNetworkAccessManager::GetOperation;
-        m_redirected = true;
-        m_responseSent = false;
+            newRequest.setHTTPMethod("GET");
+        }
 
-        m_request.setUrl(newUrl);
+        client->willSendRequest(m_resourceHandle, newRequest, response);
+        m_redirected = true;
+        m_request = newRequest.toNetworkRequest();
     } else {
         client->didReceiveResponse(m_resourceHandle, response);
     }
@@ -182,24 +303,34 @@ void QNetworkReplyHandler::start()
 
     QNetworkAccessManager* manager = d->m_frame->page()->networkAccessManager();
 
+    const QUrl url = m_request.url();
+    const QString scheme = url.scheme();
+    // Post requests on files and data don't really make sense, but for
+    // fast/forms/form-post-urlencoded.html and for fast/forms/button-state-restore.html
+    // we still need to retrieve the file/data, which means we map it to a Get instead.
+    if (m_method == QNetworkAccessManager::PostOperation
+        && (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data")))
+        m_method = QNetworkAccessManager::GetOperation;
+
+    m_startTime = QDateTime::currentDateTime().toTime_t();
+
     switch (m_method) {
         case QNetworkAccessManager::GetOperation:
             m_reply = manager->get(m_request);
             break;
         case QNetworkAccessManager::PostOperation: {
-            Vector<char> bytes;
-            d->m_request.httpBody()->flatten(bytes);
-            m_reply = manager->post(m_request, QByteArray(bytes.data(), bytes.size()));
+            FormDataIODevice* postDevice = new FormDataIODevice(d->m_request.httpBody()); 
+            m_reply = manager->post(m_request, postDevice);
+            postDevice->setParent(m_reply);
             break;
         }
         case QNetworkAccessManager::HeadOperation:
             m_reply = manager->head(m_request);
             break;
         case QNetworkAccessManager::PutOperation: {
-            // ### data?
-            Vector<char> bytes;
-            d->m_request.httpBody()->flatten(bytes);
-            m_reply = manager->put(m_request, QByteArray(bytes.data(), bytes.size()));
+            FormDataIODevice* putDevice = new FormDataIODevice(d->m_request.httpBody()); 
+            m_reply = manager->put(m_request, putDevice);
+            putDevice->setParent(m_reply);
             break;
         }
         case QNetworkAccessManager::UnknownOperation:
@@ -213,7 +344,6 @@ void QNetworkReplyHandler::start()
 
     // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we
     // can send the response as early as possible
-    QString scheme = m_request.url().scheme();
     if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
         connect(m_reply, SIGNAL(metaDataChanged()),
                 this, SLOT(sendResponseIfNeeded()));