summaryrefslogtreecommitdiffstats
path: root/src/network/access/qnetworkreplywasmimpl_p.h
blob: 4b00bb09eaa29256c87fd4c7161eb286739e9403 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QNETWORKREPLYWASMIMPL_H
#define QNETWORKREPLYWASMIMPL_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists for the convenience
// of the Network Access API.  This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//

#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "qnetworkaccessmanager.h"

#include <QtCore/qfile.h>

#include <private/qtnetworkglobal_p.h>
#include <private/qabstractfileengine_p.h>

#include <emscripten.h>
#include <emscripten/fetch.h>

#include <memory>
#include <mutex>

QT_BEGIN_NAMESPACE

class QIODevice;

class QNetworkReplyWasmImplPrivate;
class QNetworkReplyWasmImpl: public QNetworkReply
{
    Q_OBJECT
public:
    QNetworkReplyWasmImpl(QObject *parent = nullptr);
    ~QNetworkReplyWasmImpl();
    virtual void abort() override;

    // reimplemented from QNetworkReply
    virtual void close() override;
    virtual qint64 bytesAvailable() const override;
    virtual bool isSequential () const override;
    qint64 size() const override;

    virtual qint64 readData(char *data, qint64 maxlen) override;

    void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
               QIODevice *outgoingData);

    Q_DECLARE_PRIVATE(QNetworkReplyWasmImpl)

    Q_PRIVATE_SLOT(d_func(), void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString))
    Q_PRIVATE_SLOT(d_func(), void emitDataReadProgress(qint64 done, qint64 total))
    Q_PRIVATE_SLOT(d_func(), void dataReceived(const QByteArray &buffer))

private:
    QByteArray methodName() const;
};

class QNetworkReplyWasmImplPrivate;

/*!
    The FetchContext class ensures the requestData object remains valid
    while a fetch operation is pending. Since Emscripten fetch is asynchronous,
    requestData must persist until one of the final callbacks is invoked.
    Additionally, there's a potential race condition between the thread
    scheduling the fetch operation and the one executing it. Since fetch must
    occur on the main thread due to browser limitations,
    a mutex safeguards the FetchContext to ensure atomic state transitions.
*/
struct FetchContext
{
    enum class State { SCHEDULED, SENT, FINISHED, CANCELED, TO_BE_DESTROYED };

    FetchContext(QNetworkReplyWasmImplPrivate *networkReply) : reply(networkReply) { }

    QNetworkReplyWasmImplPrivate *reply{ nullptr };
    std::mutex mutex;
    QByteArray requestData;
    State state{ State::SCHEDULED };
};

class QNetworkReplyWasmImplPrivate: public QNetworkReplyPrivate
{
public:
    QNetworkReplyWasmImplPrivate();
    ~QNetworkReplyWasmImplPrivate();

    QNetworkAccessManagerPrivate *managerPrivate;
    void doSendRequest();
    static void setReplyAttributes(quintptr data, int statusCode, const QString &statusReason);

    void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &);
    void emitDataReadProgress(qint64 done, qint64 total);
    void dataReceived(const QByteArray &buffer);
    void headersReceived(const QByteArray &buffer);

    void setStatusCode(int status, const QByteArray &statusText);

    void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
               QIODevice *outgoingData);

    State state;
    void _q_bufferOutgoingData();
    void _q_bufferOutgoingDataFinished();

    std::shared_ptr<QAtomicInt> pendingDownloadData;
    std::shared_ptr<QAtomicInt> pendingDownloadProgress;

    qint64 bytesDownloaded;
    qint64 bytesBuffered;

    qint64 downloadBufferReadPosition;
    qint64 downloadBufferCurrentSize;
    qint64 totalDownloadSize;
    qint64 percentFinished;
    QByteArray downloadBuffer;

    QIODevice *outgoingData;
    std::shared_ptr<QRingBuffer> outgoingDataBuffer;

    static void downloadProgress(emscripten_fetch_t *fetch);
    static void downloadFailed(emscripten_fetch_t *fetch);
    static void downloadSucceeded(emscripten_fetch_t *fetch);
    static void stateChange(emscripten_fetch_t *fetch);

    static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url);

    emscripten_fetch_t *m_fetch;
    FetchContext *m_fetchContext;
    void setReplyFinished();
    void setCanceled();

    Q_DECLARE_PUBLIC(QNetworkReplyWasmImpl)
};

QT_END_NAMESPACE

#endif // QNETWORKREPLYWASMIMPL_H