Torc  0.1
torcnetworkrequest.cpp
Go to the documentation of this file.
1 /* Class TorcNetworkRequest
2 *
3 * This file is part of the Torc project.
4 *
5 * Copyright (C) Mark Kendall 2013-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 "torcadminthread.h"
26 #include "http/torchttprequest.h"
27 #include "torccoreutils.h"
28 #include "torcnetworkrequest.h"
29 
30 #include <errno.h>
31 
51 TorcNetworkRequest::TorcNetworkRequest(const QNetworkRequest &Request, QNetworkAccessManager::Operation Type, int BufferSize, int *Abort)
52  : m_type(Type),
53  m_abort(Abort),
54  m_started(false),
55  m_positionInFile(0),
56  m_rewindPositionInFile(0),
57  m_readPosition(0),
58  m_writePosition(0),
59  m_availableToRead(0),
60  m_bufferSize(BufferSize),
61  m_reserveBufferSize(BufferSize >> 3),
62  m_writeBufferSize(BufferSize - m_reserveBufferSize),
63  m_buffer(QByteArray(BufferSize, 0)),
64  m_readSize(DEFAULT_STREAMED_READ_SIZE),
65  m_redirectionCount(0),
66  m_readTimer(),
67  m_writeTimer(),
68  m_postData(),
69  m_replyFinished(false),
70  m_replyBytesAvailable(0),
71  m_bytesReceived(0),
72  m_bytesTotal(0),
73  m_rawHeaders(),
74  m_replyError(QNetworkReply::NoError),
75  m_request(Request),
76  m_rangeStart(0),
77  m_rangeEnd(0),
78  m_httpStatus(HTTP_BadRequest),
79  m_contentLength(0),
80  m_contentType(),
81  m_byteServingAvailable(false)
82 {
83  // reserve some space for seeking backwards in the stream
84  if (m_bufferSize)
85  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Request buffer size %1bytes (%2 reserved)").arg(m_bufferSize).arg(m_bufferSize - m_writeBufferSize));
86 }
87 
88 TorcNetworkRequest::TorcNetworkRequest(const QNetworkRequest &Request, const QByteArray &PostData, int *Abort)
89  : m_type(QNetworkAccessManager::PostOperation),
90  m_abort(Abort),
91  m_started(false),
94  m_readPosition(0),
95  m_writePosition(0),
97  m_bufferSize(0),
100  m_buffer(),
103  m_readTimer(),
104  m_writeTimer(),
105  m_postData(PostData),
106  m_replyFinished(false),
108  m_bytesReceived(0),
109  m_bytesTotal(0),
110  m_rawHeaders(),
111  m_replyError(QNetworkReply::NoError),
112  m_request(Request),
113  m_rangeStart(0),
114  m_rangeEnd(0),
116  m_contentLength(0),
117  m_contentType(),
119 {
120 }
121 
123 {
124  if (!m_bufferSize)
125  return m_buffer.size();
126 
127  return m_availableToRead.fetchAndAddOrdered(0);
128 }
129 
130 qint64 TorcNetworkRequest::GetSize(void) const
131 {
132  return m_contentLength;
133 }
134 
136 {
137  if (!m_bufferSize)
138  return -1;
139 
140  return m_positionInFile;
141 }
142 
144 {
146 
147  while (!m_started && !m_replyFinished && !(*m_abort) && (m_readTimer.Elapsed() < Timeout))
148  QThread::usleep(50000);
149 
150  return m_started || m_replyFinished;
151 }
152 
153 int TorcNetworkRequest::Peek(char *Buffer, qint32 BufferSize, int Timeout)
154 {
155  return Read(Buffer, BufferSize, Timeout, true);
156 }
157 
162 qint64 TorcNetworkRequest::Seek(qint64 Offset)
163 {
164  // unbuffered
165  if (!m_bufferSize)
166  return -1;
167 
168  // beyond currently downloaded
169  // TODO wait if within a 'reasonable' distance
170  if (Offset > (m_positionInFile + BytesAvailable()))
171  return -1;
172 
173  // seek backwards
174  if (Offset < m_positionInFile)
175  if (Offset < m_rewindPositionInFile)
176  return -1;
177 
178  // seek within the currently available buffer
179  int seek = Offset - m_positionInFile;
180  m_readPosition += seek;
183  else if (m_readPosition < 0)
185 
186  m_positionInFile = Offset;
187  m_availableToRead.fetchAndAddOrdered(-seek);
188 
189  return Offset;
190 }
191 
192 int TorcNetworkRequest::Read(char *Buffer, qint32 BufferSize, int Timeout, bool Peek)
193 {
194  if (!Buffer || !m_bufferSize)
195  return -1;
196 
198 
199  while ((BytesAvailable() < m_readSize) && !(*m_abort) && (m_readTimer.Elapsed() < Timeout) && !(m_replyFinished && m_replyBytesAvailable == 0))
200  {
202  gNetwork->Poke(this);
203  QThread::usleep(50000);
204  }
205 
206  if (*m_abort)
207  return -ECONNABORTED;
208 
209  int available = BytesAvailable();
210  if (available < 1)
211  {
212  if (m_replyFinished && m_replyBytesAvailable == 0 && available == 0)
213  {
214  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Download complete"));
215  return 0;
216  }
217 
218  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Waited %1ms for data. Aborting").arg(Timeout));
219  return -ECONNABORTED;
220  }
221 
222  available = qMin(available, BufferSize);
223 
224  if (m_readPosition + available > m_bufferSize)
225  {
226  int rest = m_bufferSize - m_readPosition;
227  memcpy(Buffer, (void*)(m_buffer.constData() + m_readPosition), rest);
228  memcpy(Buffer + rest, (void*)m_buffer.constData(), available - rest);
229 
230  if (!Peek)
231  m_readPosition = available - rest;
232  }
233  else
234  {
235  memcpy(Buffer, (void*)(m_buffer.constData() + m_readPosition), available);
236  if (!Peek)
237  m_readPosition += available;
238  }
239 
240  if (!Peek)
241  {
244 
245  m_positionInFile += available;
246 
248 
249  m_availableToRead.fetchAndAddOrdered(-available);
250  }
251 
252  return available;
253 }
254 
255 bool TorcNetworkRequest::WritePriv(QNetworkReply *Reply, const char *Buffer, int Size)
256 {
257  if (Reply && Buffer && Size)
258  {
259  char *buffer = const_cast<char *>(Buffer);
260  int read = Reply->read(buffer, Size);
261 
262  if (read < 0)
263  {
264  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Error reading '%1'").arg(Reply->errorString()));
265  return false;
266  }
267 
268  m_writePosition += read;
271  m_availableToRead.fetchAndAddOrdered(read);
272 
273  m_replyBytesAvailable = Reply->bytesAvailable();
274 
275  if (read == Size)
276  return true;
277 
278  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Expected %1 bytes, got %2").arg(Size).arg(read));
279  }
280 
281  return false;
282 }
283 
284 void TorcNetworkRequest::Write(QNetworkReply *Reply)
285 {
286  if (!Reply)
287  return;
288 
290 
291  if (!m_bufferSize)
292  {
293  m_buffer += Reply->readAll();
294  return;
295  }
296 
297  qint64 available = qMin(m_writeBufferSize - (qint64)m_availableToRead.fetchAndAddOrdered(0), Reply->bytesAvailable());
298 
299  if (m_writePosition + available > m_bufferSize)
300  {
301  int rest = m_bufferSize - m_writePosition;
302 
303  if (!WritePriv(Reply, m_buffer.constData() + m_writePosition, rest))
304  return;
305  if (!WritePriv(Reply, m_buffer.constData(), available - rest))
306  return;
307  }
308  else
309  {
310  WritePriv(Reply, m_buffer.constData() + m_writePosition, available);
311  }
312 }
313 
315 {
316  m_readSize = Size;
317 }
318 
319 void TorcNetworkRequest::SetRange(int Start, int End)
320 {
321  if (m_rangeStart != 0 || m_rangeEnd != 0)
322  {
323  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Byte ranges already set - ignoring"));
324  return;
325  }
326 
327  if (Start < 0)
328  {
329  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Invalid range start"));
330  return;
331  }
332 
333  m_rangeStart = Start;
334  m_rangeEnd = End;
335  if (m_rangeEnd <= Start)
336  m_rangeEnd = -1;
337 
338  // adjust known position in file
341 
342  // and update the request
343  m_request.setRawHeader("Range", QStringLiteral("bytes=%1-%2").arg(m_rangeStart).arg(m_rangeEnd != -1 ? QString::number(m_rangeEnd) : QStringLiteral("")).toLatin1());
344 }
345 
346 void TorcNetworkRequest::DownloadProgress(qint64 Received, qint64 Total)
347 {
348  m_bytesReceived = Received;
349  m_bytesTotal = Total;
350 }
351 
353 {
354  return m_byteServingAvailable;
355 }
356 
358 {
359  return m_request.url();
360 }
361 
363 {
364  return m_contentType;
365 }
366 
368 {
369  return m_httpStatus;
370 }
371 
376 QByteArray TorcNetworkRequest::GetHeader(const QByteArray &Header) const
377 {
378  foreach (QNetworkReply::RawHeaderPair header, m_rawHeaders)
379  if (header.first == Header)
380  return header.second;
381  return QByteArray();
382 }
383 
384 QNetworkReply::NetworkError TorcNetworkRequest::GetReplyError(void) const
385 {
386  return m_replyError;
387 }
388 
389 void TorcNetworkRequest::SetReplyError(QNetworkReply::NetworkError Error)
390 {
391  m_replyError = Error;
392 }
393 
395 {
396  return m_buffer;
397 }
398 
399 QByteArray TorcNetworkRequest::ReadAll(int Timeout)
400 {
401  if (m_bufferSize)
402  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("ReadAll called for a streamed buffer"));
403 
405 
406  while (!(*m_abort) && (m_readTimer.Elapsed() < Timeout) && !m_replyFinished)
407  QThread::usleep(50000);
408 
409  if (*m_abort)
410  return QByteArray();
411 
412  if (!m_replyFinished)
413  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Download timed out - probably incomplete"));
414 
415  return m_buffer;
416 }
TorcNetworkRequest(const QNetworkRequest &Request, QNetworkAccessManager::Operation Type, int BufferSize, int *Abort)
int Read(char *Buffer, qint32 BufferSize, int Timeout, bool Peek=false)
void SetReadSize(int Size)
void SetReplyError(QNetworkReply::NetworkError Error)
TorcNetwork * gNetwork
Definition: torcnetwork.cpp:31
qint64 GetPosition(void) const
QNetworkReply::NetworkError GetReplyError(void) const
bool WaitForStart(int Timeout)
QNetworkAccessManager::Operation m_type
const QByteArray m_postData
QByteArray & GetBuffer(void)
int Peek(char *Buffer, qint32 BufferSize, int Timeout)
#define DEFAULT_STREAMED_READ_SIZE
Definition: torcnetwork.h:16
QString GetContentType(void) const
QUrl GetFinalURL(void) const
bool CanByteServe(void) const
QList< QNetworkReply::RawHeaderPair > m_rawHeaders
QNetworkRequest m_request
qint64 Seek(qint64 Offset)
void DownloadProgress(qint64 Received, qint64 Total)
void Write(QNetworkReply *Reply)
static void Poke(TorcNetworkRequest *Request)
Definition: torcnetwork.cpp:80
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
QNetworkReply::NetworkError m_replyError
QByteArray ReadAll(int Timeout)
QAtomicInt m_availableToRead
int GetStatus(void) const
int Restart(void)
Definition: torctimer.cpp:15
QByteArray GetHeader(const QByteArray &Header) const
Return the value of the given header, if present.
void SetRange(int Start, int End=0)
qint64 GetSize(void) const
int Elapsed(void)
Definition: torctimer.cpp:22