MythTV  master
mythhttprequest.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QHash>
3 #include <QString>
4 #include <QTcpSocket>
5 
6 // MythTV
7 #include "mythlogging.h"
8 #include "http/mythhttpdata.h"
10 #include "http/mythhttprequest.h"
11 
12 #define LOC QString("HTTPParse: ")
13 
24 MythHTTPRequest::MythHTTPRequest(const MythHTTPConfig& Config, QString Method,
25  HTTPHeaders Headers, HTTPData Content, QTcpSocket* Socket /*=nullptr*/)
26  : m_serverName(Config.m_serverName),
27  m_method(std::move(Method)),
28  m_headers(std::move(Headers)),
29  m_content(std::move(Content)),
30  m_root(Config.m_rootDir),
31  m_timeout(Config.m_timeout)
32 {
33  // TODO is the simplified() call here always safe?
34 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
35  QStringList tokens = m_method.simplified().split(' ', QString::SkipEmptyParts);
36 #else
37  QStringList tokens = m_method.simplified().split(' ', Qt::SkipEmptyParts);
38 #endif
39 
40  // Validation
41  // Must have verb and url and optional version
42  if (tokens.size() < 2 || tokens.size() > 3)
43  {
44  LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to parse HTTP method");
45  return;
46  }
47 
48  // Note
49  // tokens[0] = GET, POST, etc
50  // tokens[1] = rest of URL
51  // tokens[2] = HTTP/1.1
52 
54  m_url = tokens[1];
55 
56  // If no version, assume HTTP/1.1
58  if (tokens.size() > 2)
60 
61  // Unknown HTTP version
63  {
64  LOG(VB_GENERAL, LOG_WARNING, LOC + "Unknown HTTP version");
66  return;
67  }
68 
69  // Unknown request type
70  if (m_type == HTTPUnknown)
71  {
72  LOG(VB_GENERAL, LOG_WARNING, LOC + "Unknown HTTP request");
73  return;
74  }
75 
76  // HTTP/1.1 requires the HOST header - even if empty.
77  bool havehost = m_headers->contains("host");
78  if ((m_version == HTTPOneDotOne) && !havehost)
79  {
80  LOG(VB_GENERAL, LOG_WARNING, LOC + "No host header for HTTP/1.1");
81  return;
82  }
83 
84  // Multiple host headers are also forbidden - assume for any version not just 1/1
85  if (havehost && m_headers->count("host") > 1)
86  {
87  LOG(VB_GENERAL, LOG_WARNING, LOC + "Multiple 'Host' headers forbidden");
88  return;
89  }
90 
91  // If a host is provided, ensure we recognise it. This may be over zealous:)
92  if (havehost)
93  {
94 
95  // Commented this check because people should be able to set up something in their
96  // hosts file that does not match the server. Then the host name in the request
97  // may not match.
98 
99  // N.B. host port is optional - but our host list has both versions
100  // QString host = MythHTTP::GetHeader(m_headers, "host").toLower();
101  // QStringList hostParts = host.split(":");
102  // if (!Config.m_hosts.contains(hostParts[0]))
103  // {
104  // LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Invalid 'Host' header. '%1' not recognised")
105  // .arg(host));
106  // return;
107  // }
108 
109  // TODO Ensure the host address has a port - as below when we add one manually
110  }
111  else if (Socket)
112  {
113  // Use the socket address to add a host address. This just ensures the
114  // response always has a valid address for this thread/socket that can be used
115  // when building a (somewhat dynamic) response.
116  QHostAddress host = Socket->localAddress();
117  m_headers->insert("host", QString("%1:%2").arg(MythHTTP::AddressToString(host)).arg(Socket->localPort()));
118  }
119 
120  // Need a valid URL
121  if (!m_url.isValid())
122  {
123  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Invalid URL: '%1'").arg(m_url.toString()));
124  return;
125  }
126 
127  // Parse the URL into its useful components (path/filename) - queries later
128  m_path = m_url.toString(QUrl::RemoveFilename | QUrl::RemoveFragment | QUrl::RemoveQuery);
129  m_fileName = m_url.fileName();
130 
131  // Parse the connection header
132  // HTTP/1.1 default to KeepAlive, HTTP/1.0 default to close
133  // HTTP/0.9 is unlikely but assume KeepAlive
135  auto connection = MythHTTP::GetHeader(m_headers, "connection").toLower();
136  if (connection.contains(QStringLiteral("keep-alive")))
138  else if (connection.contains(QStringLiteral("close")))
140 
141  // Parse the content type if present - and pull out any form data
142  if (m_content.get() && !m_content->isEmpty() && ((m_type == HTTPPut) || (m_type == HTTPPost)))
144 
145  // Only parse queries if we do not have form data
146  if (m_queries.isEmpty() && m_url.hasQuery())
147  m_queries = ParseQuery(m_url.query());
148 
149  m_status = HTTPOK;
150 }
151 
153 {
154  HTTPQueries result;
155 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
156  QStringList params = Query.split('&', QString::SkipEmptyParts);
157 #else
158  QStringList params = Query.split('&', Qt::SkipEmptyParts);
159 #endif
160  for (const auto & param : qAsConst(params))
161  {
162  QString key = param.section('=', 0, 0);
163  QString value = param.section('=', 1);
164  QByteArray rawvalue = value.toUtf8();
165  value = QUrl::fromPercentEncoding(rawvalue);
166  value.replace("+", " ");
167  if (!key.isEmpty())
168  result.insert(key.trimmed().toLower(), value);
169  }
170  return result;
171 }
HTTPPost
@ HTTPPost
Definition: mythhttptypes.h:94
HTTPOK
@ HTTPOK
Definition: mythhttptypes.h:107
HTTPConnectionKeepAlive
@ HTTPConnectionKeepAlive
Definition: mythhttptypes.h:130
HTTPPut
@ HTTPPut
Definition: mythhttptypes.h:95
MythHTTP::GetHeader
static QString GetHeader(const HTTPHeaders &Headers, const QString &Value, const QString &Default="")
Definition: mythhttptypes.h:317
MythHTTPRequest::m_url
QUrl m_url
Definition: mythhttprequest.h:24
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythHTTPEncoding::GetContentType
static void GetContentType(MythHTTPRequest *Request)
Parse the incoming Content-Type header for POST/PUT content.
Definition: mythhttpencoding.cpp:78
mythhttpdata.h
HTTPData
std::shared_ptr< MythHTTPData > HTTPData
Definition: mythhttptypes.h:36
HTTPHeaders
std::shared_ptr< HTTPMap > HTTPHeaders
Definition: mythhttptypes.h:35
MythHTTPRequest::m_fileName
QString m_fileName
Definition: mythhttprequest.h:27
mythlogging.h
MythHTTP::VersionFromString
static MythHTTPVersion VersionFromString(const QString &Version)
Definition: mythhttptypes.h:195
HTTPQueries
HTTPMap HTTPQueries
Definition: mythhttptypes.h:34
MythHTTPRequest::ParseQuery
static HTTPQueries ParseQuery(const QString &Query)
Definition: mythhttprequest.cpp:152
mythhttpencoding.h
HTTPConnectionClose
@ HTTPConnectionClose
Definition: mythhttptypes.h:129
MythHTTPRequest::m_status
MythHTTPStatus m_status
Definition: mythhttprequest.h:23
MythHTTP::AddressToString
static QString AddressToString(QHostAddress &Address)
Definition: mythhttptypes.h:332
HTTPOneDotOne
@ HTTPOneDotOne
Definition: mythhttptypes.h:86
MythHTTPRequest::m_headers
HTTPHeaders m_headers
Definition: mythhttprequest.h:20
MythHTTPRequest::m_type
MythHTTPRequestType m_type
Definition: mythhttprequest.h:30
MythHTTPRequest::m_version
MythHTTPVersion m_version
Definition: mythhttprequest.h:29
HTTPUnknown
@ HTTPUnknown
Definition: mythhttptypes.h:91
MythHTTPRequest::m_method
QString m_method
Definition: mythhttprequest.h:19
MythHTTPRequest::m_path
QString m_path
Definition: mythhttprequest.h:26
std
Definition: mythchrono.h:23
HTTPOneDotZero
@ HTTPOneDotZero
Definition: mythhttptypes.h:85
mythhttprequest.h
MythHTTP::RequestFromString
static MythHTTPRequestType RequestFromString(const QString &Type)
Definition: mythhttptypes.h:221
MythHTTPRequest::m_content
HTTPData m_content
Definition: mythhttprequest.h:21
MythHTTPRequest::m_queries
HTTPQueries m_queries
Definition: mythhttprequest.h:28
MythHTTPConfig
Definition: mythhttptypes.h:60
Content
Definition: content.h:36
HTTPUnknownVersion
@ HTTPUnknownVersion
Definition: mythhttptypes.h:83
MythHTTPRequest::MythHTTPRequest
MythHTTPRequest(const MythHTTPConfig &Config, QString Method, HTTPHeaders Headers, HTTPData Content, QTcpSocket *Socket=nullptr)
Definition: mythhttprequest.cpp:24
MythHTTPRequest::m_connection
MythHTTPConnection m_connection
Definition: mythhttprequest.h:31
LOC
#define LOC
Definition: mythhttprequest.cpp:12