1 /*
2  * Copyright (C) 2016-2018 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 #ifndef PROTOCOLHTTP_H
19 #define PROTOCOLHTTP_H
20 
21 #include <QObject>
22 
23 #include "protocol.h"
24 #include "socket.h"
25 
26 #include <Cutelyst/Context>
27 
28 namespace Cutelyst {
29 class Server;
30 class Socket;
31 class ProtoRequestHttp final : public ProtocolData, public Cutelyst::EngineRequest
32 {
33     Q_GADGET
34 public:
35     enum WebSocketPhase
36     {
37         WebSocketPhaseHeaders,
38         WebSocketPhaseSize,
39         WebSocketPhaseMask,
40         WebSocketPhasePayload,
41     };
42     Q_ENUM(WebSocketPhase)
43 
44     enum OpCode
45     {
46         OpCodeContinue    = 0x0,
47         OpCodeText        = 0x1,
48         OpCodeBinary      = 0x2,
49         OpCodeReserved3   = 0x3,
50         OpCodeReserved4   = 0x4,
51         OpCodeReserved5   = 0x5,
52         OpCodeReserved6   = 0x6,
53         OpCodeReserved7   = 0x7,
54         OpCodeClose       = 0x8,
55         OpCodePing        = 0x9,
56         OpCodePong        = 0xA,
57         OpCodeReservedB   = 0xB,
58         OpCodeReservedC   = 0xC,
59         OpCodeReservedD   = 0xD,
60         OpCodeReservedE   = 0xE,
61         OpCodeReservedF   = 0xF
62     };
63     Q_ENUM(OpCode)
64 
65     ProtoRequestHttp(Socket *sock, int bufferSize);
66     ~ProtoRequestHttp() override;
67 
68     void setupNewConnection(Socket *sock) override final;
69 
70     bool writeHeaders(quint16 status, const Cutelyst::Headers &headers) override final;
71 
72     qint64 doWrite(const char *data, qint64 len) override final;
doWrite(const QByteArray & data)73     inline qint64 doWrite(const QByteArray &data) {
74         return doWrite(data.constData(), data.size());
75     }
76 
77     void processingFinished() override final;
78 
79     bool webSocketSendTextMessage(const QString &message) override final;
80 
81     bool webSocketSendBinaryMessage(const QByteArray &message) override final;
82 
83     bool webSocketSendPing(const QByteArray &payload) override final;
84 
85     bool webSocketClose(quint16 code, const QString &reason) override final;
86 
resetData()87     inline void resetData() override final {
88         ProtocolData::resetData();
89 
90         // EngineRequest
91         if (status & EngineRequest::Async) {
92             context->deleteLater();
93         } else {
94             delete context;
95         }
96         context = nullptr;
97         body = nullptr;
98 
99         elapsed.invalidate();
100         status = InitialState;
101 
102         websocketUpgraded = false;
103         last = 0;
104         beginLine = 0;
105 
106         serverAddress = sock->serverAddress;
107         remoteAddress = sock->remoteAddress;
108         remotePort = sock->remotePort;
109         isSecure = sock->isSecure;
110     }
111 
112     virtual void socketDisconnected() override final;
113 
114     QByteArray websocket_message;
115     QByteArray websocket_payload;
116     quint64 websocket_payload_size = 0;
117     quint32 websocket_need = 0;
118     quint32 websocket_mask = 0;
119     int last = 0;
120     int beginLine = 0;
121     int websocket_start_of_frame = 0;
122     int websocket_phase = 0;
123     quint8 websocket_continue_opcode = 0;
124     quint8 websocket_finn_opcode = 0;
125     bool websocketUpgraded = false;
126 
127 protected:
128     bool webSocketHandshakeDo(const QString &key, const QString &origin, const QString &protocol) override final;
129 };
130 
131 class ProtocolHttp2;
132 class ProtocolWebSocket;
133 class ProtocolHttp final : public Protocol
134 {
135 public:
136     ProtocolHttp(Server *wsgi, ProtocolHttp2 *upgradeH2c = nullptr);
137     ~ProtocolHttp() override;
138 
139     Type type() const override;
140 
141     void parse(Socket *sock, QIODevice *io) const override final;
142 
143     ProtocolData *createData(Socket *sock) const override final;
144 
145 private:
146     inline bool processRequest(Socket *sock, QIODevice *io) const;
147     inline void parseMethod(const char *ptr, const char *end, Socket *sock) const;
148     inline void parseHeader(const char *ptr, const char *end, Socket *sock) const;
149 
150 protected:
151     friend class ProtoRequestHttp;
152 
153     ProtocolWebSocket *m_websocketProto;
154     ProtocolHttp2 *m_upgradeH2c;
155     bool usingFrontendProxy;
156 };
157 
158 }
159 
160 #endif // PROTOCOLHTTP_H
161