Torc  0.1
torchttpreader.cpp
Go to the documentation of this file.
1 /* Class TorcHTTPReader
2 *
3 * This file is part of the Torc project.
4 *
5 * Copyright (C) Mark Kendall 2012-18
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 * USA.
21 */
22 
23 // Torc
24 #include "torclogging.h"
25 #include "torchttpreader.h"
26 
34  : m_ready(false),
35  m_requestStarted(false),
36  m_headersComplete(false),
37  m_headersRead(0),
38  m_contentLength(0),
39  m_contentReceived(0),
40  m_method(QStringLiteral()),
41  m_content(),
42  m_headers()
43 {
44 }
45 
47 void TorcHTTPReader::TakeRequest(QByteArray& Content, QMap<QString,QString>& Headers)
48 {
49  Content = m_content;
50  Headers = m_headers;
51  m_content = QByteArray();
52  m_headers = QMap<QString,QString>();
53 }
54 
55 bool TorcHTTPReader::IsReady(void) const
56 {
57  return m_ready;
58 }
59 
60 QString TorcHTTPReader::GetMethod(void) const
61 {
62  return m_method;
63 }
64 
66 {
67  return m_headersComplete;
68 }
69 
72 {
73  m_ready = false;
74  m_requestStarted = false;
75  m_headersComplete = false;
76  m_headersRead = 0;
77  m_contentLength = 0;
78  m_contentReceived = 0;
79  m_method = QStringLiteral();
80  m_content = QByteArray();
81  m_headers = QMap<QString,QString>();
82 }
83 
89 bool TorcHTTPReader::Read(QTcpSocket *Socket)
90 {
91  if (!Socket)
92  return false;
93 
94  if (Socket->state() != QAbstractSocket::ConnectedState)
95  return false;
96 
97  // sanity check
98  if (m_headersRead >= 200)
99  {
100  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Read 200 lines of headers - aborting"));
101  return false;
102  }
103 
104  // read headers
105  if (!m_headersComplete)
106  {
107  // filter out invalid HTTP early
108  if (!m_requestStarted && Socket->bytesAvailable() >= 7 /*OPTIONS is longest valid type*/)
109  {
110  QByteArray buf(7, ' ');
111  (void)Socket->peek(buf.data(), 7);
112  if (!buf.startsWith("HTTP"))
113  if (!buf.startsWith("GET"))
114  if (!buf.startsWith("PUT"))
115  if(!buf.startsWith("POST"))
116  if(!buf.startsWith("OPTIONS"))
117  if (!buf.startsWith("HEAD"))
118  if (!buf.startsWith("DELETE"))
119  {
120  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Invalid HTTP start ('%1')- aborting").arg(buf.constData()));
121  return false;
122  }
123  }
124 
125  while (Socket->canReadLine() && m_headersRead < 200)
126  {
127  QByteArray line = Socket->readLine().trimmed();
128 
129  // an unusually long header is likely to mean this is not a valid HTTP message
130  if (line.size() > 1000)
131  {
132  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Header is too long - aborting"));
133  return false;
134  }
135 
136  if (line.isEmpty())
137  {
138  m_headersRead = 0;
139  m_headersComplete = true;
140  break;
141  }
142 
143  if (!m_requestStarted)
144  {
145  LOG(VB_NETWORK, LOG_DEBUG, QString(line));
146  m_method = line;
147  }
148  else
149  {
150  m_headersRead++;
151  int index = line.indexOf(":");
152 
153  if (index > 0)
154  {
155  QByteArray key = line.left(index).trimmed();
156  QByteArray value = line.mid(index + 1).trimmed();
157 
158  if (key == "Content-Length")
159  m_contentLength = value.toULongLong();
160 
161  LOG(VB_NETWORK, LOG_DEBUG, QStringLiteral("%1: %2").arg(key.data(), value.data()));
162 
163  m_headers.insert(key, value);
164  }
165  }
166 
167  m_requestStarted = true;
168  }
169  }
170 
171  // loop if we need more header data
172  if (!m_headersComplete)
173  return true;
174 
175  // abort early if needed
176  if (Socket->state() != QAbstractSocket::ConnectedState)
177  return false;
178 
179  // read content
180  while ((m_contentReceived < m_contentLength) && Socket->bytesAvailable() &&
181  Socket->state() == QAbstractSocket::ConnectedState)
182  {
183  static quint64 MAX_CHUNK = 32 * 1024;
184  m_content.append(Socket->read(qMax(m_contentLength - m_contentReceived, qMax(MAX_CHUNK, (quint64)Socket->bytesAvailable()))));
185  m_contentReceived = m_content.size();
186  }
187 
188  // loop if we need more data
189  if (m_contentReceived < m_contentLength)
190  return true;
191 
192  // abort early if needed
193  if (Socket->state() != QAbstractSocket::ConnectedState)
194  return false;
195 
196  m_ready = true;
197  return true;
198 }
QString GetMethod(void) const
bool Read(QTcpSocket *Socket)
Read and parse data from the given socket.
bool IsReady(void) const
bool HeadersComplete(void) const
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
void TakeRequest(QByteArray &Content, QMap< QString, QString > &Headers)
Take ownership of the contents and headers. New owner is responsible for deleting.
void Reset(void)
Reset the read state.