1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#ifndef HTTP2SRV_H
30#define HTTP2SRV_H
31
32#include <QtNetwork/private/qhttpnetworkrequest_p.h>
33#include <QtNetwork/private/qhttpnetworkreply_p.h>
34#include <QtNetwork/private/http2protocol_p.h>
35#include <QtNetwork/private/http2frames_p.h>
36#include <QtNetwork/private/hpack_p.h>
37
38#include <QtNetwork/qabstractsocket.h>
39#include <QtCore/qsharedpointer.h>
40#include <QtCore/qscopedpointer.h>
41#include <QtNetwork/qtcpserver.h>
42#include <QtCore/qbytearray.h>
43#include <QtCore/qatomic.h>
44#include <QtCore/qglobal.h>
45#include <QtCore/qmap.h>
46
47#include <vector>
48#include <map>
49#include <set>
50
51QT_BEGIN_NAMESPACE
52
53// At the moment we do not have any public API parsing HTTP headers. Even worse -
54// the code that can do this exists only in QHttpNetworkReplyPrivate class.
55// To be able to access reply's d_func() we have these classes:
56class Http11ReplyPrivate : public QHttpNetworkReplyPrivate
57{
58};
59
60class Http11Reply : public QHttpNetworkReply
61{
62public:
63 Q_DECLARE_PRIVATE(Http11Reply)
64};
65
66enum class H2Type {
67 h2Alpn, // Secure connection, ALPN to negotiate h2.
68 h2c, // Clear text with protocol upgrade.
69 h2Direct, // Secure connection, ALPN not supported.
70 h2cDirect, // Clear text direct
71};
72
73using RawSettings = QMap<Http2::Settings, quint32>;
74
75class Http2Server : public QTcpServer
76{
77 Q_OBJECT
78public:
79
80 Http2Server(H2Type type, const RawSettings &serverSettings,
81 const RawSettings &clientSettings);
82
83 ~Http2Server();
84
85
86 // To be called before server started:
87 void enablePushPromise(bool enabled, const QByteArray &path = QByteArray());
88 void setResponseBody(const QByteArray &body);
89 // No authentication data is generated for the method, the full header value must be set
90 void setAuthenticationHeader(const QByteArray &authentication);
91 // Set the redirect URL and count. The server will return a redirect response with the url
92 // 'count' amount of times
93 void setRedirect(const QByteArray &redirectUrl, int count);
94 void emulateGOAWAY(int timeout);
95 void redirectOpenStream(quint16 targetPort);
96
97 bool isClearText() const;
98
99 QByteArray requestAuthorizationHeader();
100
101 // Invokables, since we can call them from the main thread,
102 // but server (can) work on its own thread.
103 Q_INVOKABLE void startServer();
104 bool sendProtocolSwitchReply();
105 Q_INVOKABLE void sendServerSettings();
106 Q_INVOKABLE void sendGOAWAY(quint32 streamID, quint32 error,
107 quint32 lastStreamID);
108 Q_INVOKABLE void sendRST_STREAM(quint32 streamID, quint32 error);
109 Q_INVOKABLE void sendDATA(quint32 streamID, quint32 windowSize);
110 Q_INVOKABLE void sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
111
112 Q_INVOKABLE void handleProtocolUpgrade();
113 Q_INVOKABLE void handleConnectionPreface();
114 Q_INVOKABLE void handleIncomingFrame();
115 Q_INVOKABLE void handleSETTINGS();
116 Q_INVOKABLE void handleDATA();
117 Q_INVOKABLE void handleWINDOW_UPDATE();
118
119 Q_INVOKABLE void sendResponse(quint32 streamID, bool emptyBody);
120
121 void stopSendingDATAFrames();
122
123private:
124 void processRequest();
125
126Q_SIGNALS:
127 void serverStarted(quint16 port);
128 // Error/success notifications:
129 void clientPrefaceOK();
130 void clientPrefaceError();
131 void serverSettingsAcked();
132 void invalidFrame();
133 void invalidRequest(quint32 streamID);
134 void decompressionFailed(quint32 streamID);
135 void receivedRequest(quint32 streamID);
136 void receivedData(quint32 streamID);
137 // Emitted for every DATA frame. Includes the content of the frame as \a body.
138 void receivedDATAFrame(quint32 streamID, const QByteArray &body);
139 void windowUpdate(quint32 streamID);
140 void sendingData();
141
142private slots:
143 void connectionEstablished();
144 void readReady();
145
146private:
147 void incomingConnection(qintptr socketDescriptor) override;
148
149 quint32 clientSetting(Http2::Settings identifier, quint32 defaultValue);
150 bool readMethodLine();
151 bool verifyProtocolUpgradeRequest();
152 void triggerGOAWAYEmulation();
153
154 QScopedPointer<QAbstractSocket> socket;
155
156 H2Type connectionType = H2Type::h2Alpn;
157 // Connection preface:
158 bool waitingClientPreface = false;
159 bool waitingClientSettings = false;
160 bool settingsSent = false;
161 bool waitingClientAck = false;
162
163 RawSettings serverSettings;
164 RawSettings expectedClientSettings;
165
166 bool connectionError = false;
167
168 Http2::FrameReader reader;
169 Http2::Frame inboundFrame;
170 Http2::FrameWriter writer;
171
172 using FrameSequence = std::vector<Http2::Frame>;
173 FrameSequence continuedRequest;
174
175 std::map<quint32, quint32> streamWindows;
176
177 HPack::Decoder decoder{HPack::FieldLookupTable::DefaultSize};
178 HPack::Encoder encoder{HPack::FieldLookupTable::DefaultSize, true};
179
180 using Http2Requests = std::map<quint32, HPack::HttpHeader>;
181 Http2Requests activeRequests;
182 // 'remote half-closed' streams to keep
183 // track of streams with END_STREAM set:
184 std::set<quint32> closedStreams;
185 // streamID + offset in response body to send.
186 std::map<quint32, quint32> suspendedStreams;
187
188 // We potentially reset this once (see sendServerSettings)
189 // and do not change later:
190 quint32 sessionRecvWindowSize = Http2::defaultSessionWindowSize;
191 // This changes in the range [0, sessionRecvWindowSize]
192 // while handling DATA frames:
193 quint32 sessionCurrRecvWindow = sessionRecvWindowSize;
194 // This we potentially update only once (sendServerSettings).
195 quint32 streamRecvWindowSize = Http2::defaultSessionWindowSize;
196
197 QByteArray responseBody;
198 bool pushPromiseEnabled = false;
199 quint32 lastPromisedStream = 0;
200 QByteArray pushPath;
201
202 bool testingGOAWAY = false;
203 int goawayTimeout = 0;
204
205 // Clear text HTTP/2, we have to deal with the protocol upgrade request
206 // from the initial HTTP/1.1 request.
207 bool upgradeProtocol = false;
208 QByteArray requestLine;
209 QHttpNetworkRequest::Operation requestType;
210 // We need QHttpNetworkReply (actually its private d-object) to handle the
211 // first HTTP/1.1 request. QHttpNetworkReplyPrivate does parsing + in case
212 // of POST it is also reading the body for us.
213 QScopedPointer<Http11Reply> protocolUpgradeHandler;
214 // We need it for PUSH_PROMISE, with the correct port number appended,
215 // when replying to essentially 1.1 request.
216 QByteArray authority;
217 // Redirect, with status code 308, as soon as we've seen headers, while client
218 // may still be sending DATA frames. See tst_Http2::earlyResponse().
219 bool redirectWhileReading = false;
220 bool redirectSent = false;
221 quint16 targetPort = 0;
222 QAtomicInt interrupted;
223
224 QByteArray authenticationHeader;
225
226 QByteArray redirectUrl;
227 int redirectCount = 0;
228protected slots:
229 void ignoreErrorSlot();
230};
231
232QT_END_NAMESPACE
233
234#endif
235
236

source code of qtbase/tests/auto/network/access/http2/http2srv.h