MythTV master
mythhttpparser.cpp
Go to the documentation of this file.
1// C++
2#include <algorithm>
3
4// Qt
5#include <QTcpSocket>
6
7// MythTV
8#include "mythlogging.h"
9#include "http/mythhttpdata.h"
11#include "http/mythhttpparser.h"
12
13#define LOC QString("HTTPParser: ")
14
15HTTPRequest2 MythHTTPParser::GetRequest(const MythHTTPConfig& Config, QTcpSocket* Socket)
16{
17 // Debug
18 if (VERBOSE_LEVEL_CHECK(VB_HTTP, LOG_DEBUG))
19 {
20 LOG(VB_HTTP, LOG_DEBUG, m_method);
21 for (auto it = m_headers->cbegin(); it != m_headers->cend(); ++it)
22 LOG(VB_HTTP, LOG_DEBUG, it.key() + ": " + it.value());
23 if (m_content.get())
24 LOG(VB_HTTP, LOG_DEBUG, QString("Content:\r\n") + m_content->constData());
25 }
26
27 // Build the request
28 auto result = std::make_shared<MythHTTPRequest>(Config, m_method, m_headers, m_content, Socket);
29
30 // Reset our internal state
31 m_started = false;
32 m_linesRead = 0;
33 m_headersComplete = false;
34 m_method.clear();
36 m_headers = std::make_shared<HTTPMap>();
37 m_content = nullptr;
38
39 return result;
40}
41
42bool MythHTTPParser::Read(QTcpSocket* Socket, bool& Ready)
43{
44 Ready = false;
45
46 // Fail early
47 if (!Socket || (Socket->state() != QAbstractSocket::ConnectedState))
48 return false;
49
50 // Sanity check the number of headers
51 if (!m_headersComplete && m_linesRead > 200)
52 {
53 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Read %1 headers - aborting").arg(m_linesRead));
54 return false;
55 }
56
57 // Filter out non HTTP content quickly. This assumes server side only.
58 if (!m_started && Socket->bytesAvailable() > 2)
59 {
60 QByteArray buf(3, '\0');
61 if (Socket->peek(buf.data(), 3) == 3)
62 {
63 static const std::vector<const char *> s_starters = { "GET", "PUT", "POS", "OPT", "HEA", "DEL" };
64#ifdef __cpp_lib_ranges_contains
65 if (!std::ranges::contains(s_starters, buf.data()))
66#else
67 if (std::ranges::none_of(s_starters, [&](const char * Starter)
68 { return strcmp(Starter, buf.data()) == 0; }))
69#endif
70 {
71 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Invalid HTTP request start '%1' - quitting").arg(buf.constData()));
72 return false;
73 }
74 }
75 }
76
77 // Read headers
78 while (!m_headersComplete && Socket->canReadLine())
79 {
80 QByteArray line = Socket->readLine().trimmed();
82
83 // A large header suggests an error
84 if (line.size() > 2048)
85 {
86 LOG(VB_GENERAL, LOG_WARNING, LOC + "Unusually long header - quitting");
87 return false;
88 }
89
90 if (line.isEmpty())
91 {
92 m_headersComplete = true;
93 break;
94 }
95
96 if (m_started)
97 {
98 int index = line.indexOf(":");
99 if (index > 0)
100 {
101 QByteArray key = line.left(index).trimmed().toLower();
102 QByteArray value = line.mid(index + 1).trimmed();
103 if (key == "content-length")
104 m_contentLength = value.toLongLong();
105 m_headers->insert(key, value);
106 }
107 else
108 {
109 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Invalid header: '%1'").arg(line.constData()));
110 return false;
111 }
112 }
113 else
114 {
115 m_started = true;
116 m_method = line;
117 }
118 }
119
120 // Need more data...
122 return true;
123
124 // No body?
125 if (m_contentLength < 1)
126 {
127 Ready = true;
128 return true;
129 }
130
131 // Create a buffer for the content if needed
132 if (!m_content)
134
135 // Read contents
136 while ((Socket->state() == QAbstractSocket::ConnectedState) && Socket->bytesAvailable() &&
137 (static_cast<int64_t>(m_content->size()) < m_contentLength))
138 {
139 int64_t want = m_contentLength - m_content->size();
140 int64_t have = Socket->bytesAvailable();
141 m_content->append(Socket->read(std::max({want, HTTP_CHUNKSIZE, have})));
142 }
143
144 // Need more data...
145 if (static_cast<int64_t>(m_content->size()) < m_contentLength)
146 return true;
147
148 Ready = true;
149 return true;
150}
static HTTPData Create()
Definition: mythhttpdata.cpp:4
int64_t m_contentLength
bool Read(QTcpSocket *Socket, bool &Ready)
HTTPRequest2 GetRequest(const MythHTTPConfig &Config, QTcpSocket *Socket)
HTTPData m_content
HTTPHeaders m_headers
#define LOC
std::shared_ptr< MythHTTPRequest > HTTPRequest2
Definition: mythhttptypes.h:39
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39