From 53357f01561d7c2b50e0a656ca250f5e3c1af923 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 27 Jul 2017 14:34:39 +0200 Subject: HTTP/2 - implement the proper 'h2c' (protocol upgrade) Without TLS (and thus ALPN/NPN negotiation) HTTP/2 requires a protocol upgrade procedure, as described in RFC 7540, 3.2. We start as HTTP/1.1 (and thus we create QHttpProtocolHandler first), augmenting the headers we send with 'Upgrade: h2c'. In case we receive HTTP/1.1 response with status code 101 ('Switching Protocols'), we continue as HTTP/2 session, creating QHttp2ProtocolHandler and pretending the first request we sent was HTTP/2 request on a real HTTP/2 stream. If the first response is something different from 101, we continue as HTTP/1.1. This change also required auto-test update: our toy-server now has to respond to the initial HTTP/1.1 request on a platform without ALPN/NPN. As a bonus a subtle flakyness in 'goaway' auto-test went away (well, it was fixed). [ChangeLog][QtNetwork][HTTP/2] In case of clear text HTTP/2 we now initiate a required protocol upgrade procedure instead of 'H2Direct' connection. Task-number: QTBUG-61397 Change-Id: I573fa304fdaf661490159037dc47775d97c8ea5b Reviewed-by: Edward Welbourne Reviewed-by: Timur Pocheptsov --- tests/auto/network/access/http2/http2srv.h | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'tests/auto/network/access/http2/http2srv.h') diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h index 63a4a4c8e9..10d0e86736 100644 --- a/tests/auto/network/access/http2/http2srv.h +++ b/tests/auto/network/access/http2/http2srv.h @@ -29,11 +29,14 @@ #ifndef HTTP2SRV_H #define HTTP2SRV_H +#include +#include #include #include #include #include +#include #include #include #include @@ -58,6 +61,19 @@ struct Http2Setting using Http2Settings = std::vector; +// At the moment we do not have any public API parsing HTTP headers. Even worse - +// the code that can do this exists only in QHttpNetworkReplyPrivate class. +// To be able to access reply's d_func() we have these classes: +class Http11ReplyPrivate : public QHttpNetworkReplyPrivate +{ +}; + +class Http11Reply : public QHttpNetworkReply +{ +public: + Q_DECLARE_PRIVATE(Http11Reply) +}; + class Http2Server : public QTcpServer { Q_OBJECT @@ -75,6 +91,7 @@ public: // Invokables, since we can call them from the main thread, // but server (can) work on its own thread. Q_INVOKABLE void startServer(); + bool sendProtocolSwitchReply(); Q_INVOKABLE void sendServerSettings(); Q_INVOKABLE void sendGOAWAY(quint32 streamID, quint32 error, quint32 lastStreamID); @@ -82,6 +99,7 @@ public: Q_INVOKABLE void sendDATA(quint32 streamID, quint32 windowSize); Q_INVOKABLE void sendWINDOW_UPDATE(quint32 streamID, quint32 delta); + Q_INVOKABLE void handleProtocolUpgrade(); Q_INVOKABLE void handleConnectionPreface(); Q_INVOKABLE void handleIncomingFrame(); Q_INVOKABLE void handleSETTINGS(); @@ -114,6 +132,9 @@ private: void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE; quint32 clientSetting(Http2::Settings identifier, quint32 defaultValue); + bool readMethodLine(); + bool verifyProtocolUpgradeRequest(); + void triggerGOAWAYEmulation(); QScopedPointer socket; @@ -166,6 +187,18 @@ private: bool testingGOAWAY = false; int goawayTimeout = 0; + // Clear text HTTP/2, we have to deal with the protocol upgrade request + // from the initial HTTP/1.1 request. + bool upgradeProtocol = false; + QByteArray requestLine; + QHttpNetworkRequest::Operation requestType; + // We need QHttpNetworkReply (actually its private d-object) to handle the + // first HTTP/1.1 request. QHttpNetworkReplyPrivate does parsing + in case + // of POST it is also reading the body for us. + QScopedPointer protocolUpgradeHandler; + // We need it for PUSH_PROMISE, with the correct port number appended, + // when replying to essentially 1.1 request. + QByteArray authority; protected slots: void ignoreErrorSlot(); }; -- cgit v1.2.3