Qt Network in Qt 6
October 05, 2020 by Mårten Nordheim, Timur Pocheptsov | Comments
In this blog post we want to tell you about some of the recent updates and changes that Qt Network module received in Qt 6, and also about some potential future developments.
QNetworkAccessBackend
QNetworkAccessBackend is an abstract base class used to interface with our cache, file and ftp backends. We have had QNetworkAccessBackend in Qt for a while, but it was not possible to use outside Qt Network in any reasonable way. Now it is! With Qt 6 we planned to move the ftp backend out of Qt Network and to distribute it separately as a plugin. To do this, we made QNetworkAccessBackend more friendly to use from the outside and gave QNetworkAccessManager the ability to load these plugins at runtime. As with some of our other plugin interfaces, it does not have the strict backwards-compatibility promises that Qt itself has, and it requires linking to QtNetworkPrivate to be able to use it.
We wanted to be able to specialize some of the behavior of various backends. For example, a non-network backend doesn't need us to prepare a list of proxies for the target URL. For this we added three different enumerations with a few values in each:
- TargetType
- SecurityFeatures
- IOFeatures
Of these, only 'TargetType' is required, currently only specifying if the backend's supported targets are networked and/or local.
Due to this approach some functions may not do anything if certain features aren’t enabled. One such example is the IO feature 'ZeroCopy' which enables the functions readPointer() and advanceReadPointer(). If it is not enabled these two functions will not be called. The main idea of having this feature system is that we can make these kinds of improvements without breaking any existing code. New code would have to opt in to the new feature. Although, to reiterate: we cannot guarantee the same level of backwards-compatibility for network access backend plugins as for applications made with Qt.
To implement a network access backend plugin you'll need to implement a class inheriting from QNetworkAccessBackendFactory. This class will do the work of creating the backend if the requested scheme is supported. You will also need to create a class inheriting from QNetworkAccessBackend, and override all pure-virtual functions, as well as 'read()', or 'readPointer()' and 'advanceReadPointer()' if your class supports the 'ZeroCopy' feature. As for existing uses, both the QNetworkAccessCacheBackend and the QNetworkAccessFileBackend are updated to use the new interface, although those are directly compiled into Qt rather than being distributed as plugins at the moment.
Protocols
In Qt 6 we dropped SPDY support. SPDY was an experimental protocol with an open specification, and it was developed primarily at Google. SPDY became the predecessor and prototype for the HTTP/2 protocol; the introduction of HTTP/2 obsoleted SPDY, and today SPDY is deprecated.
While maintaining compatibility with HTTP/1.1 (for example with methods, status codes, URIs, and most header fields) , HTTP/2 also adds:
- data compression of HTTP headers
- multiplexing multiple requests over a single TCP connection
- HTTP/2 Server Push
- Negotiation mechanisms that allow client and server to select HTTP/2 or HTTP/1.1
Prior to Qt 6, HTTP/2 protocol had to be manually enabled by setting one of these attributes:
- QNetworkRequest::Http2AllowedAttribute
- QNetworkRequest::Http2DirectAttribute,
E.g.:
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
For 'Http2AllowedAttribute', QNetworkAccessManager uses Application Layer Protocol Negotiation for 'https' scheme and protocol upgrade header 'h2c' for 'http' scheme. The second attribute is useful if you know in advance that the server supports HTTP/2 in so-called 'direct' mode, without protocol negotiation.
In Qt 6, HTTP/2 support is enabled by default: this means the attribute 'Http2AllowedAttribute' is set to 'true'. If HTTP/2 cannot be negotiated, QNetworkAccessManager will fall back to HTTP/1.1. If your application can only use HTTP/1.1, you can set the attribute 'Http2AllowedAttribute' to 'false' for a new network request:
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
There is an exception to this 'enabled by default' rule:
QNetworkAccessManager::connectToHostEncrypted(host, port, configuration);
Using this function an application can pre-connect to a server without sending any requests. If we were to enable HTTP/2 on such a connection, this connection would be useless for an application that wants to work with HTTP/1.1 and disables HTTP/2 by using request attributes. HTTP/2 can be enabled explicitly by setting the desired list of protocol in the 'configuration' parameter, e.g.:
auto tlsConfig = QSslConfiguration::defaultConfiguration();
tlsConfig.setAllowedNextProtocols({QSslConfiguration::ALPNProtocolHTTP2});
manager.connectToHostEncrypted(host, port, tlsConfig);
Default redirect policy changed
Another change that may affect your networking code is the default redirect policy that QNetworkAccessManager uses in Qt 6. The policy API was first introduced in Qt 5, with the idea in mind of switching to default automatic redirect handling in Qt 6.
QNetworkAccessManager supports several redirect policies, described by the enumeration QNetworkRequest::RedirectPolicy. These policies are:
- ManualRedirectPolicy,
- NoLessSafeRedirectPolicy
- SameOriginRedirectPolicy
- UserVerifiedRedirectPolicy
Starting from Qt 6 the default redirect policy that QNetworkAccessManager will use when making requests is QNetworkRequest::NoLessSafeRedirectPolicy (the policy that prohibits redirects from 'https' to 'http').
In case you have an application, that relies on a slot connected to QNetworkReply::redirected() to handle redirects, you will have to set the policy to QNetworkRequest::ManualRedirectPolicy with Qt 6:
req.setAttribute(RedirectPolicyAttribute, ManualRedirectPolicy);
Bearer management was removed
Bearer management was the name given to the machinery that was historically needed to support roaming and manage connectivity on certain devices. It could also select which of the device's network interfaces to use when connecting to the internet. Today this is no longer the case since operating systems typically handle roaming on their own and will automatically choose the best network interface to send data over, often providing no way (or no convenient way) for an application to influence these choices.
Although QNetworkManager provided APIs to chose which interface to use or to configure the interface in use, this new reality meant that no currently supported platforms provided a reliable way for Qt to do as requested. At the same time, the attempt to keep track of network status triggered various issues, such as heavy Wifi traffic on Windows causing high ping-times, or, after a brief network outage, this could keep Qt from recognising that the network was back, causing all subsequent requests to fail.
Given that the new reality also makes bearer management largely redundant, we decided that it was time to retire this part of Qt Network.
One feature that was still useful was the ability to check connectivity and get notified of connectivity changes. So far in Qt 6.0 it has not seen a replacement, although we've gathered up some use-cases and hope to have a replacement for that functionality in the coming minor releases. If you have specific use-cases you would like to see covered, or you want to track this task's progress, then please head over to QTBUG-86966.
QSslSocket
In Qt 6 QSslSocket received an additional API, providing information about alert messages which were received or sent as a part of the TLS protocol. We have two new enumerations:
- QSsl::AlertLevel describes how serious problem is
- QSsl::AlertType indicates the type of problem
There are two new signals in QSslSocket:
- QSslSocket::alertSent()
- QSslSocket::alertReceived()
When possible, these signals also report the textual description of the problem (if provided by the TLS library).
Another change, related to TLS handshake, is the ability to report errors encountered during handshake early, while the handshake is still in progress. These errors are reported directly from the verification callback. QSslConfiguration has a new getter and setter:
- bool QSslConfiguration::handshakeMustInterruptOnError() const;
- void QSslConfiguration::setHandshakeMustInterruptOnError() const;
And QSslSocket has a new signal:
- void handshakeInterruptedOnError();
and a function to say the error must ignored:
- void continueInterruptedHandshake();
If errors are not ignored, the underlying TLS library will send alert message to the peer.
This API is a little low-level and not of much interest for an application that uses QNetworkAccessManager. But it can be quite handy for debugging purposes or when implementing some error or warning handling logic, if you work with QSslSocket directly. This change at the moment is working with our OpenSSL backend; other TLS libraries we use do not have required API or do not provide the complete information we need.
TLS 1.3 and above
Well, this part is not exactly about what Qt 6 already has. It's also about possible future developments. As of now, Qt 6 supports the latest version of TLS protocol, which is 1.3, thanks to our OpenSSL back-end (Qt 5 also supports TLS 1.3). We are working on enabling TLS 1.3 in the Schannel backend too. Unfortunately, SecureTransport was deprecated by Apple with no available TLS library as a replacement, only some higher-level frameworks built on top of BoringSSL (note the irony of this!). This means that for our Darwin users we will provide an alternative in future. And in case of QNetworkAccessManager, a new QNetworkAccessBackend implemented on top of Apple's classes like NSUrlSession can be quite handy (with TLS 1.3 and experimental HTTP/3 support as a bonus included!).
Thank you for reading this blog post. Do not hesitate and share your opinion, comments or suggest an improvement!
Blog Topics:
Comments
Subscribe to our newsletter
Subscribe Newsletter
Try Qt 6.9 Now!
Download the latest release here: www.qt.io/download.
Qt 6.9 is now available, with new features and improvements for application developers and device creators.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.
Commenting for this post has ended.
Awesome :] Any news about potential changes in QWebSockets module? :P
Hey, thanks! For 6.0 it was decided that we want to have a more limited scope to focus on the core/base components, so QWebSockets is not 'officially' supported in 6.0. But it will be brought back shortly after! I don't know anything more about the plans beyond that I'm afraid.
Could you remove the limit of 6 instances of QNetworkAccessManager? And I can't understand why you do this limit for Qt5?
There's no limit on how many instances of QNetworkAccessManager you can have (although our documentation recommends only having one instance). Are you perhaps talking about the limit of parallel connections QNetworkAccessManager will make in the background to a single host? That limit only counts for HTTP 1(.1) and was/is fairly standard. For HTTP/2 it's actually limited to only 1 connection, but thanks to how the protocol is made up it can actually send multiple requests per connection, so it doesn't need any more.
Yes, I am talking about the parallel connections. But I still hope you to remove this limit because QNetworkAccessManager is a very useful library et everywhere needs it, even in some urgent cases. For example, in the Security Industry with alarms, we hope continuously post the images immediately. If you still won't change ideas, it doesn't matter. But if we buy a commercial license, could you help us to remove this limit?? Thanks.
PS, the Http protocol is the best way to communicate with other languages et system, doesn't it? So I hope you can seriously think about it. Thanks again.
If you compile Qt on your own then as far as I'm aware (never tried) this is the only line that needs to be changed to increase it: https://github.com/qt/qtbase/blob/dev/src/network/access/qhttpnetworkconnection.cpp#L68
Thanks, I will try immediately. But I want to know what's your philosophy to set the value 6 ? I know a book named "the philosophy of Unix" which explains many things at the top level. And I want to get the same philosophy from you.
I wasn't around when the choice was made so I don't know the definitive source, but it was chosen to be 6 since that matched what major browsers used at the time (and largely still do for HTTP 1) and was considered a reasonable fit for Qt as well.
https://stackoverflow.com/a/30064610/2493610
You are so kind and I now know everything about it. In fact, the browsers are the applications for the client, so they can set the value 6 because peoples havn't 20 or 1000 eyes (another reason is the work of rendering is heavy). But Qt is a neutral library, the developers should do what they want, only limited by OS or physical. So removing the limit is still a good idea, this is only my suggestion, you can do as you like.
Last question, if I modify it myself, set defaultHttpChannelCount = 1000 even 60000, that's enough? Should I modify the other things, by example, which are the best values for defaultPipelineLength and defaultRePipelineLength when HttpChannelCount is 1000 or 60000? Sorry I have troubled you so much. But this is really an interesting thing.
Right, as I mentioned earlier I haven't tried it myself, but in theory changing defaultHttpChannelCount is enough. Pipelining isn't enabled by default so unless you enable it it's not going to make a difference what you set those to. Do keep in mind that if you set it to 60000 then it will allocate 60000 channels as well, even if for some connections you only end up using 10. There might also be limits on the server on how many connections you, or perhaps anyone in total, can establish before they start getting dropped.
I recommend to vote and subscribe to and maybe comment on https://bugreports.qt.io/browse/QTBUG-25280 :-)
good to see that there's some work on QTBUG-86966 to provide infos on Network Connection State. really need this for nearly all of my apps.
Hi to all What I can use insteed of QNetworkConfiguration Thank you
QNetworkConfiguration has a few things available. There's no replacement for it in 6.0 though. What about QNC are you currently using?
Can be the linking between Qt Network and QML optional? I don't see why should I deploy Qt Network in an application that does not uses network capabilities at all.
Redirect not happening automatically and I can't seem to get it to work with any policy.
Where is the FTP plugin in Qt 6.1.2?
Still no FTP plugin or support in Qt 6.2?