1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qhttp2configuration.h"
41 
42 #include "private/http2protocol_p.h"
43 #include "private/hpack_p.h"
44 
45 #include "qdebug.h"
46 
47 QT_BEGIN_NAMESPACE
48 
49 /*!
50     \class QHttp2Configuration
51     \brief The QHttp2Configuration class controls HTTP/2 parameters and settings.
52     \since 5.14
53 
54     \reentrant
55     \inmodule QtNetwork
56     \ingroup network
57     \ingroup shared
58 
59     QHttp2Configuration controls HTTP/2 parameters and settings that
60     QNetworkAccessManager will use to send requests and process responses
61     when the HTTP/2 protocol is enabled.
62 
63     The HTTP/2 parameters that QHttp2Configuration currently supports include:
64 
65     \list
66       \li The session window size for connection-level flow control.
67          Will be sent to a remote peer when needed as 'WINDOW_UPDATE'
68          frames on the stream with an identifier 0.
69       \li The stream receiving window size for stream-level flow control.
70          Sent as 'SETTINGS_INITIAL_WINDOW_SIZE' parameter in the initial
71          SETTINGS frame and, when needed, 'WINDOW_UPDATE' frames will be
72          sent on streams that QNetworkAccessManager opens.
73       \li The maximum frame size. This parameter limits the maximum payload
74          a frame coming from the remote peer can have. Sent by QNetworkAccessManager
75          as 'SETTINGS_MAX_FRAME_SIZE' parameter in the initial 'SETTINGS'
76          frame.
77       \li The server push. Allows to enable or disable server push. Sent
78          as 'SETTINGS_ENABLE_PUSH' parameter in the initial 'SETTINGS'
79          frame.
80     \endlist
81 
82     The QHttp2Configuration class also controls if the header compression
83     algorithm (HPACK) is additionally using Huffman coding for string
84     compression.
85 
86     \note The configuration must be set before the first request
87     was sent to a given host (and thus an HTTP/2 session established).
88 
89     \note Details about flow control, server push and 'SETTINGS'
90     can be found in \l {https://httpwg.org/specs/rfc7540.html}{RFC 7540}.
91     Different modes and parameters of the HPACK compression algorithm
92     are described in \l {https://httpwg.org/specs/rfc7541.html}{RFC 7541}.
93 
94     \sa QNetworkRequest::setHttp2Configuration(), QNetworkRequest::http2Configuration(), QNetworkAccessManager
95 */
96 
97 class QHttp2ConfigurationPrivate : public QSharedData
98 {
99 public:
100     unsigned sessionWindowSize = Http2::defaultSessionWindowSize;
101     // The size below is quite a limiting default value, QNetworkRequest
102     // by default sets a larger number, an application can change this using
103     // QNetworkRequest::setHttp2Configuration.
104     unsigned streamWindowSize = Http2::defaultSessionWindowSize;
105 
106     unsigned maxFrameSize = Http2::minPayloadLimit; // Initial (default) value of 16Kb.
107 
108     bool pushEnabled = false;
109     // TODO: for now those two below are noop.
110     bool huffmanCompressionEnabled = true;
111 };
112 
113 /*!
114     Default constructs a QHttp2Configuration object.
115 
116     Such a configuration has the following values:
117     \list
118         \li Server push is disabled
119         \li Huffman string compression is enabled
120         \li Window size for connection-level flow control is 65535 octets
121         \li Window size for stream-level flow control is 65535 octets
122         \li Frame size is 16384 octets
123     \endlist
124 */
QHttp2Configuration()125 QHttp2Configuration::QHttp2Configuration()
126     : d(new QHttp2ConfigurationPrivate)
127 {
128 }
129 
130 /*!
131     Copy-constructs this QHttp2Configuration.
132 */
133 QHttp2Configuration::QHttp2Configuration(const QHttp2Configuration &) = default;
134 
135 /*!
136     Move-constructs this QHttp2Configuration from \a other
137 */
QHttp2Configuration(QHttp2Configuration && other)138 QHttp2Configuration::QHttp2Configuration(QHttp2Configuration &&other) noexcept
139 {
140     swap(other);
141 }
142 
143 /*!
144     Copy-assigns \a other to this QHttp2Configuration.
145 */
146 QHttp2Configuration &QHttp2Configuration::operator=(const QHttp2Configuration &) = default;
147 
148 /*!
149     Move-assigns \a other to this QHttp2Configuration.
150 */
151 QHttp2Configuration &QHttp2Configuration::operator=(QHttp2Configuration &&) noexcept = default;
152 
153 /*!
154     Destructor.
155 */
~QHttp2Configuration()156 QHttp2Configuration::~QHttp2Configuration()
157 {
158 }
159 
160 /*!
161     If \a enable is \c true, a remote server can potentially
162     use server push to send reponses in advance.
163 
164     \sa serverPushEnabled
165 */
setServerPushEnabled(bool enable)166 void QHttp2Configuration::setServerPushEnabled(bool enable)
167 {
168     d->pushEnabled = enable;
169 }
170 
171 /*!
172     Returns true if server push was enabled.
173 
174     \note By default, QNetworkAccessManager disables server
175     push via the 'SETTINGS' frame.
176 
177     \sa setServerPushEnabled
178 */
serverPushEnabled() const179 bool QHttp2Configuration::serverPushEnabled() const
180 {
181     return d->pushEnabled;
182 }
183 
184 /*!
185     If \a enable is \c true, HPACK compression will additionally
186     compress string using the Huffman coding. Enabled by default.
187 
188     \note This parameter only affects 'HEADERS' frames that
189     QNetworkAccessManager is sending.
190 
191     \sa huffmanCompressionEnabled
192 */
setHuffmanCompressionEnabled(bool enable)193 void QHttp2Configuration::setHuffmanCompressionEnabled(bool enable)
194 {
195     d->huffmanCompressionEnabled = enable;
196 }
197 
198 /*!
199     Returns \c true if the Huffman coding in HPACK is enabled.
200 
201     \sa setHuffmanCompressionEnabled
202 */
huffmanCompressionEnabled() const203 bool QHttp2Configuration::huffmanCompressionEnabled() const
204 {
205     return d->huffmanCompressionEnabled;
206 }
207 
208 /*!
209     Sets the window size for connection-level flow control.
210     \a size cannot be 0 and must not exceed 2147483647 octets.
211 
212     Returns \c true on success, \c false otherwise.
213 
214     \sa sessionReceiveWindowSize
215 */
setSessionReceiveWindowSize(unsigned size)216 bool QHttp2Configuration::setSessionReceiveWindowSize(unsigned size)
217 {
218     if (!size || size > Http2::maxSessionReceiveWindowSize) { // RFC-7540, 6.9
219         qCWarning(QT_HTTP2) << "Invalid session window size";
220         return false;
221     }
222 
223     d->sessionWindowSize = size;
224     return true;
225 }
226 
227 /*!
228     Returns the window size for connection-level flow control.
229     The default value QNetworkAccessManager will be using is
230     2147483647 octets.
231 */
sessionReceiveWindowSize() const232 unsigned QHttp2Configuration::sessionReceiveWindowSize() const
233 {
234     return d->sessionWindowSize;
235 }
236 
237 /*!
238     Sets the window size for stream-level flow control.
239     \a size cannot be 0 and must not exceed 2147483647 octets.
240 
241     Returns \c true on success, \c false otherwise.
242 
243     \sa streamReceiveWindowSize
244  */
setStreamReceiveWindowSize(unsigned size)245 bool QHttp2Configuration::setStreamReceiveWindowSize(unsigned size)
246 {
247     if (!size || size > Http2::maxSessionReceiveWindowSize) { // RFC-7540, 6.9
248         qCWarning(QT_HTTP2) << "Invalid stream window size";
249         return false;
250     }
251 
252     d->streamWindowSize = size;
253     return true;
254 }
255 
256 /*!
257     Returns the window size for stream-level flow control.
258     The default value QNetworkAccessManager will be using is
259     21474836 octets.
260 */
streamReceiveWindowSize() const261 unsigned QHttp2Configuration::streamReceiveWindowSize() const
262 {
263     return d->streamWindowSize;
264 }
265 
266 /*!
267     Sets the maximum frame size that QNetworkAccessManager
268     will advertise to the server when sending its initial SETTINGS frame.
269     \note While this \a size is required to be within a range between
270     16384 and 16777215 inclusive, the actual payload size in frames
271     that carry payload maybe be less than 16384.
272 
273     Returns \c true on success, \c false otherwise.
274 */
setMaxFrameSize(unsigned size)275 bool QHttp2Configuration::setMaxFrameSize(unsigned size)
276 {
277     if (size < Http2::minPayloadLimit || size > Http2::maxPayloadSize) {
278         qCWarning(QT_HTTP2) << "Maximum frame size to advertise is invalid";
279         return false;
280     }
281 
282     d->maxFrameSize = size;
283     return true;
284 }
285 
286 /*!
287     Returns the maximum payload size that HTTP/2 frames can
288     have. The default (initial) value is 16384 octets.
289 */
maxFrameSize() const290 unsigned QHttp2Configuration::maxFrameSize() const
291 {
292     return d->maxFrameSize;
293 }
294 
295 /*!
296     Swaps this configuration with the \a other configuration.
297 */
swap(QHttp2Configuration & other)298 void QHttp2Configuration::swap(QHttp2Configuration &other) noexcept
299 {
300     d.swap(other.d);
301 }
302 
303 /*!
304     Returns \c true if \a lhs and \a rhs have the same set of HTTP/2
305     parameters.
306 */
operator ==(const QHttp2Configuration & lhs,const QHttp2Configuration & rhs)307 bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs)
308 {
309     if (lhs.d == rhs.d)
310         return true;
311 
312     return lhs.d->pushEnabled == rhs.d->pushEnabled
313            && lhs.d->huffmanCompressionEnabled == rhs.d->huffmanCompressionEnabled
314            && lhs.d->sessionWindowSize == rhs.d->sessionWindowSize
315            && lhs.d->streamWindowSize == rhs.d->streamWindowSize;
316 }
317 
318 QT_END_NAMESPACE
319