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  QStringList tokens = m_method.simplified().split(' ', Qt::SkipEmptyParts);
35 
36  // Validation
37  // Must have verb and url and optional version
38  if (tokens.size() < 2 || tokens.size() > 3)
39  {
40  LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to parse HTTP method");
41  return;
42  }
43 
44  // Note
45  // tokens[0] = GET, POST, etc
46  // tokens[1] = rest of URL
47  // tokens[2] = HTTP/1.1
48 
50  m_url = tokens[1];
51 
52  // If no version, assume HTTP/1.1
54  if (tokens.size() > 2)
56 
57  // Unknown HTTP version
59  {
60  LOG(VB_GENERAL, LOG_WARNING, LOC + "Unknown HTTP version");
62  return;
63  }
64 
65  // Unknown request type
66  if (m_type == HTTPUnknown)
67  {
68  LOG(VB_GENERAL, LOG_WARNING, LOC + "Unknown HTTP request");
69  return;
70  }
71 
72  // HTTP/1.1 requires the HOST header - even if empty.
73  bool havehost = m_headers->contains("host");
74  if ((m_version == HTTPOneDotOne) && !havehost)
75  {
76  LOG(VB_GENERAL, LOG_WARNING, LOC + "No host header for HTTP/1.1");
77  return;
78  }
79 
80  // Multiple host headers are also forbidden - assume for any version not just 1/1
81  if (havehost && m_headers->count("host") > 1)
82  {
83  LOG(VB_GENERAL, LOG_WARNING, LOC + "Multiple 'Host' headers forbidden");
84  return;
85  }
86 
87  // If a host is provided, ensure we recognise it. This may be over zealous:)
88  if (havehost)
89  {
90 
91  // Commented this check because people should be able to set up something in their
92  // hosts file that does not match the server. Then the host name in the request
93  // may not match.
94 
95  // N.B. host port is optional - but our host list has both versions
96  // QString host = MythHTTP::GetHeader(m_headers, "host").toLower();
97  // QStringList hostParts = host.split(":");
98  // if (!Config.m_hosts.contains(hostParts[0]))
99  // {
100  // LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Invalid 'Host' header. '%1' not recognised")
101  // .arg(host));
102  // return;
103  // }
104 
105  // TODO Ensure the host address has a port - as below when we add one manually
106  }
107  else if (Socket)
108  {
109  // Use the socket address to add a host address. This just ensures the
110  // response always has a valid address for this thread/socket that can be used
111  // when building a (somewhat dynamic) response.
112  QHostAddress host = Socket->localAddress();
113  m_headers->insert("host", QString("%1:%2").arg(MythHTTP::AddressToString(host)).arg(Socket->localPort()));
114  }
115 
116  // Need a valid URL
117  if (!m_url.isValid())
118  {
119  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Invalid URL: '%1'").arg(m_url.toString()));
120  return;
121  }
122 
123  // Parse the URL into its useful components (path/filename) - queries later
124  m_path = m_url.toString(QUrl::RemoveFilename | QUrl::RemoveFragment | QUrl::RemoveQuery);
125  m_fileName = m_url.fileName();
126 
127  // Parse the connection header
128  // HTTP/1.1 default to KeepAlive, HTTP/1.0 default to close
129  // HTTP/0.9 is unlikely but assume KeepAlive
131  auto connection = MythHTTP::GetHeader(m_headers, "connection").toLower();
132  if (connection.contains(QStringLiteral("keep-alive")))
134  else if (connection.contains(QStringLiteral("close")))
136 
137  // Parse the content type if present - and pull out any form data
138  if (m_content.get() && !m_content->isEmpty() && ((m_type == HTTPPut) || (m_type == HTTPPost)))
140 
141  // Only parse queries if we do not have form data
142  if (m_queries.isEmpty() && m_url.hasQuery())
143  m_queries = ParseQuery(m_url.query());
144 
145  m_status = HTTPOK;
146 }
147 
149 {
150  HTTPQueries result;
151  QStringList params = Query.split('&', Qt::SkipEmptyParts);
152  for (const auto & param : std::as_const(params))
153  {
154  QString key = param.section('=', 0, 0);
155  QString value = param.section('=', 1);
156  QByteArray rawvalue = value.toUtf8();
157  value = QUrl::fromPercentEncoding(rawvalue);
158  value.replace("+", " ");
159  if (!key.isEmpty())
160  result.insert(key.trimmed().toLower(), value);
161  }
162  return result;
163 }
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:74
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:148
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
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