1/*
2    Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18*/
19#ifndef QNetworkReplyHandler_h
20#define QNetworkReplyHandler_h
21
22#include <QObject>
23
24#include <QNetworkAccessManager>
25#include <QNetworkReply>
26#include <QNetworkRequest>
27#include <QBasicTimer>
28
29#include "FormData.h"
30#include "QtMIMETypeSniffer.h"
31
32QT_BEGIN_NAMESPACE
33class QFile;
34class QNetworkReply;
35QT_END_NAMESPACE
36
37namespace WebCore {
38
39class FormDataIODevice;
40class ResourceError;
41class ResourceHandle;
42class ResourceRequest;
43class ResourceResponse;
44class QNetworkReplyHandler;
45
46class QNetworkReplyHandlerCallQueue : public QObject {
47    Q_OBJECT
48public:
49    QNetworkReplyHandlerCallQueue(QNetworkReplyHandler*, bool deferSignals);
50    bool deferSignals() const { return m_deferSignals; }
51    void setDeferSignals(bool, bool sync = false);
52
53    typedef void (QNetworkReplyHandler::*EnqueuedCall)();
54    void push(EnqueuedCall method);
55    void clear() { m_enqueuedCalls.clear(); }
56
57    void lock();
58    void unlock();
59private:
60    QNetworkReplyHandler* m_replyHandler;
61    int m_locks;
62    bool m_deferSignals;
63    bool m_flushing;
64    QList<EnqueuedCall> m_enqueuedCalls;
65
66    Q_INVOKABLE void flush();
67};
68
69class QNetworkReplyWrapper : public QObject {
70    Q_OBJECT
71public:
72    QNetworkReplyWrapper(QNetworkReplyHandlerCallQueue*, QNetworkReply*, bool sniffMIMETypes, QObject* parent = 0);
73    ~QNetworkReplyWrapper();
74
75    QNetworkReply* reply() const { return m_reply; }
76    QNetworkReply* release();
77
78    void synchronousLoad();
79
80    QUrl redirectionTargetUrl() const { return m_redirectionTargetUrl; }
81    QString encoding() const { return m_encoding; }
82    QString advertisedMIMEType() const { return m_advertisedMIMEType; }
83    QString mimeType() const { return m_sniffedMIMEType.isEmpty() ? m_advertisedMIMEType : m_sniffedMIMEType; }
84
85    bool responseContainsData() const { return m_responseContainsData; }
86    bool wasRedirected() const { return m_redirectionTargetUrl.isValid(); }
87
88    // See setFinished().
89    bool isFinished() const { return m_reply->property("_q_isFinished").toBool(); }
90
91private Q_SLOTS:
92    void receiveMetaData();
93    void didReceiveFinished();
94    void didReceiveReadyRead();
95    void receiveSniffedMIMEType();
96    void setFinished();
97    void replyDestroyed();
98
99private:
100    void stopForwarding();
101    void emitMetaDataChanged();
102
103    QNetworkReply* m_reply;
104    QUrl m_redirectionTargetUrl;
105
106    QString m_encoding;
107    QNetworkReplyHandlerCallQueue* m_queue;
108    bool m_responseContainsData;
109
110    QString m_advertisedMIMEType;
111
112    QString m_sniffedMIMEType;
113    OwnPtr<QtMIMETypeSniffer> m_sniffer;
114    bool m_sniffMIMETypes;
115};
116
117class QNetworkReplyHandler : public QObject
118{
119    Q_OBJECT
120public:
121    enum LoadType {
122        AsynchronousLoad,
123        SynchronousLoad
124    };
125
126    QNetworkReplyHandler(ResourceHandle*, LoadType, bool deferred = false);
127    void setLoadingDeferred(bool deferred) { m_queue.setDeferSignals(deferred, m_loadType == SynchronousLoad); }
128
129    QNetworkReply* reply() const { return m_replyWrapper ? m_replyWrapper->reply() : 0; }
130
131    void abort();
132
133    QNetworkReply* release();
134
135    void finish();
136    void forwardData();
137    void sendResponseIfNeeded();
138
139    static ResourceError errorForReply(QNetworkReply*);
140
141private Q_SLOTS:
142    void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
143
144private:
145    void start();
146    String httpMethod() const;
147    void redirect(ResourceResponse&, const QUrl&);
148    bool wasAborted() const { return !m_resourceHandle; }
149    QNetworkReply* sendNetworkRequest(QNetworkAccessManager*, const ResourceRequest&);
150    FormDataIODevice* getIODevice(const ResourceRequest&);
151    void clearContentHeaders();
152    virtual void timerEvent(QTimerEvent*) OVERRIDE;
153    void timeout();
154
155    OwnPtr<QNetworkReplyWrapper> m_replyWrapper;
156    ResourceHandle* m_resourceHandle;
157    LoadType m_loadType;
158    QNetworkAccessManager::Operation m_method;
159    QNetworkRequest m_request;
160    QBasicTimer m_timeoutTimer;
161
162    // defer state holding
163    int m_redirectionTries;
164
165    QNetworkReplyHandlerCallQueue m_queue;
166};
167
168// Self destructing QIODevice for FormData
169//  For QNetworkAccessManager::put we will have to gurantee that the
170//  QIODevice is valid as long finished() of the QNetworkReply has not
171//  been emitted. With the presence of QNetworkReplyHandler::release I do
172//  not want to gurantee this.
173class FormDataIODevice : public QIODevice {
174    Q_OBJECT
175public:
176    FormDataIODevice(FormData*);
177    ~FormDataIODevice();
178
179    bool isSequential() const;
180    qint64 getFormDataSize() const { return m_fileSize + m_dataSize; }
181
182protected:
183    qint64 readData(char*, qint64);
184    qint64 writeData(const char*, qint64);
185
186private:
187    void prepareFormElements(FormData*);
188    void moveToNextElement();
189    qint64 computeSize();
190    void openFileForCurrentElement();
191    void prepareCurrentElement();
192
193private:
194    Vector<FormDataElement> m_formElements;
195    QFile* m_currentFile;
196    qint64 m_currentDelta;
197    qint64 m_fileSize;
198    qint64 m_dataSize;
199};
200
201}
202
203#endif // QNetworkReplyHandler_h
204