SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
httprequest.cpp
Go to the documentation of this file.
1 
6 #include "httprequest.h"
7 #include "httplistenersettings.h"
8 
9 #include <QList>
10 #include <QDir>
11 #include "httpcookie.h"
12 
13 using namespace qtwebapp;
14 
15 HttpRequest::HttpRequest(QSettings* settings) :
16  useQtSettings(true)
17 {
18  Q_ASSERT(settings != 0);
20  currentSize=0;
22  maxSize=settings->value("maxRequestSize","16000").toInt();
23  maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt();
24  tempFile=0;
25 }
26 
28  useQtSettings(false)
29 {
30  Q_ASSERT(settings != 0);
32  currentSize=0;
34  maxSize=settings->maxRequestSize;
36  tempFile=0;
37 }
38 
39 
40 void HttpRequest::readRequest(QTcpSocket* socket)
41 {
42  #ifdef SUPERVERBOSE
43  qDebug("HttpRequest::readRequest: read request");
44  #endif
45  int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow
46  lineBuffer.append(socket->readLine(toRead));
47  currentSize+=lineBuffer.size();
48  if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n'))
49  {
50  #ifdef SUPERVERBOSE
51  qDebug("HttpRequest::readRequest: collecting more parts until line break");
52  #endif
53  return;
54  }
55  QByteArray newData=lineBuffer.trimmed();
56  lineBuffer.clear();
57  if (!newData.isEmpty())
58  {
59  QList<QByteArray> list=newData.split(' ');
60  if (list.count()!=3 || !list.at(2).contains("HTTP"))
61  {
62  qWarning("HttpRequest::readRequest: received broken HTTP request, invalid first line");
63  status=abort;
64  }
65  else {
66  method=list.at(0).trimmed();
67  path=list.at(1);
68  version=list.at(2);
69  peerAddress = socket->peerAddress();
71  }
72  }
73 }
74 
75 void HttpRequest::readHeader(QTcpSocket* socket)
76 {
77  #ifdef SUPERVERBOSE
78  qDebug("HttpRequest::readHeader");
79  #endif
80  int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow
81  lineBuffer.append(socket->readLine(toRead));
82  currentSize+=lineBuffer.size();
83  if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n'))
84  {
85  #ifdef SUPERVERBOSE
86  qDebug("HttpRequest::readHeader: collecting more parts until line break");
87  #endif
88  return;
89  }
90  QByteArray newData=lineBuffer.trimmed();
91  lineBuffer.clear();
92  int colon=newData.indexOf(':');
93  if (colon>0)
94  {
95  // Received a line with a colon - a header
96  currentHeader=newData.left(colon).toLower();
97  QByteArray value=newData.mid(colon+1).trimmed();
98  headers.insert(currentHeader,value);
99  #ifdef SUPERVERBOSE
100  qDebug("HttpRequest::readHeader: received header %s: %s",currentHeader.data(),value.data());
101  #endif
102  }
103  else if (!newData.isEmpty())
104  {
105  // received another line - belongs to the previous header
106  #ifdef SUPERVERBOSE
107  qDebug("HttpRequest::readHeader: read additional line of header");
108  #endif
109  // Received additional line of previous header
110  if (headers.contains(currentHeader)) {
111  headers.insert(currentHeader,headers.value(currentHeader)+" "+newData);
112  }
113  }
114  else
115  {
116  // received an empty line - end of headers reached
117  #ifdef SUPERVERBOSE
118  qDebug("HttpRequest::readHeader: headers completed");
119  #endif
120  // Empty line received, that means all headers have been received
121  // Check for multipart/form-data
122  QByteArray contentType=headers.value("content-type");
123  if (contentType.startsWith("multipart/form-data"))
124  {
125  int posi=contentType.indexOf("boundary=");
126  if (posi>=0) {
127  boundary=contentType.mid(posi+9);
128  if (boundary.startsWith('"') && boundary.endsWith('"'))
129  {
130  boundary = boundary.mid(1,boundary.length()-2);
131  }
132  }
133  }
134  QByteArray contentLength=headers.value("content-length");
135  if (!contentLength.isEmpty())
136  {
137  expectedBodySize=contentLength.toInt();
138  }
139  if (expectedBodySize==0)
140  {
141  #ifdef SUPERVERBOSE
142  qDebug("HttpRequest::readHeader: expect no body");
143  #endif
145  }
146  else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize)
147  {
148  qWarning("HttpRequest::readHeader: expected body is too large");
149  status=abort;
150  }
151  else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize)
152  {
153  qWarning("HttpRequest::readHeader: expected multipart body is too large");
154  status=abort;
155  }
156  else {
157  #ifdef SUPERVERBOSE
158  qDebug("HttpRequest::readHeader: expect %i bytes body",expectedBodySize);
159  #endif
161  }
162  }
163 }
164 
165 void HttpRequest::readBody(QTcpSocket* socket)
166 {
167  Q_ASSERT(expectedBodySize!=0);
168  if (boundary.isEmpty())
169  {
170  // normal body, no multipart
171  #ifdef SUPERVERBOSE
172  qDebug("HttpRequest::readBody: receive body");
173  #endif
174  int toRead=expectedBodySize-bodyData.size();
175  QByteArray newData=socket->read(toRead);
176  currentSize+=newData.size();
177  bodyData.append(newData);
178  if (bodyData.size()>=expectedBodySize)
179  {
181  }
182  }
183  else
184  {
185  // multipart body, store into temp file
186  #ifdef SUPERVERBOSE
187  qDebug("HttpRequest::readBody: receiving multipart body");
188  #endif
189  // Create an object for the temporary file, if not already present
190  if (tempFile == NULL)
191  {
192  tempFile = new QTemporaryFile;
193  }
194  if (!tempFile->isOpen())
195  {
196  tempFile->open();
197  }
198  // Transfer data in 64kb blocks
199  int fileSize=tempFile->size();
200  int toRead=expectedBodySize-fileSize;
201  if (toRead>65536)
202  {
203  toRead=65536;
204  }
205  fileSize+=tempFile->write(socket->read(toRead));
206  if (fileSize>=maxMultiPartSize)
207  {
208  qWarning("HttpRequest::readBody: received too many multipart bytes");
209  status=abort;
210  }
211  else if (fileSize>=expectedBodySize)
212  {
213  #ifdef SUPERVERBOSE
214  qDebug("HttpRequest::readBody: received whole multipart body");
215  #endif
216  tempFile->flush();
217  if (tempFile->error())
218  {
219  qCritical("HttpRequest::readBody: Error writing temp file for multipart body");
220  }
222  tempFile->close();
224  }
225  }
226 }
227 
229 {
230  #ifdef SUPERVERBOSE
231  qDebug("HttpRequest::decodeRequestParams: extract and decode request parameters");
232  #endif
233  // Get URL parameters
234  QByteArray rawParameters;
235  int questionMark=path.indexOf('?');
236  if (questionMark>=0)
237  {
238  rawParameters=path.mid(questionMark+1);
239  path=path.left(questionMark);
240  }
241  // Get request body parameters
242  QByteArray contentType=headers.value("content-type");
243  if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith("application/x-www-form-urlencoded")))
244  {
245  if (!rawParameters.isEmpty())
246  {
247  rawParameters.append('&');
248  rawParameters.append(bodyData);
249  }
250  else
251  {
252  rawParameters=bodyData;
253  }
254  }
255  // Split the parameters into pairs of value and name
256  QList<QByteArray> list=rawParameters.split('&');
257  foreach (QByteArray part, list)
258  {
259  int equalsChar=part.indexOf('=');
260  if (equalsChar>=0)
261  {
262  QByteArray name=part.left(equalsChar).trimmed();
263  QByteArray value=part.mid(equalsChar+1).trimmed();
264  parameters.insert(urlDecode(name),urlDecode(value));
265  }
266  else if (!part.isEmpty())
267  {
268  // Name without value
269  parameters.insert(urlDecode(part),"");
270  }
271  }
272 }
273 
275 {
276  #ifdef SUPERVERBOSE
277  qDebug("HttpRequest::extractCookies");
278  #endif
279  foreach(QByteArray cookieStr, headers.values("cookie"))
280  {
281  QList<QByteArray> list=HttpCookie::splitCSV(cookieStr);
282  foreach(QByteArray part, list)
283  {
284  #ifdef SUPERVERBOSE
285  qDebug("HttpRequest::extractCookies: found cookie %s",part.data());
286  #endif // Split the part into name and value
287  QByteArray name;
288  QByteArray value;
289  int posi=part.indexOf('=');
290  if (posi)
291  {
292  name=part.left(posi).trimmed();
293  value=part.mid(posi+1).trimmed();
294  }
295  else
296  {
297  name=part.trimmed();
298  value="";
299  }
300  cookies.insert(name,value);
301  }
302  }
303  headers.remove("cookie");
304 }
305 
306 void HttpRequest::readFromSocket(QTcpSocket* socket)
307 {
308  Q_ASSERT(status!=complete);
309  if (status==waitForRequest)
310  {
311  readRequest(socket);
312  }
313  else if (status==waitForHeader)
314  {
315  readHeader(socket);
316  }
317  else if (status==waitForBody)
318  {
319  readBody(socket);
320  }
321  if ((boundary.isEmpty() && currentSize>maxSize) || (!boundary.isEmpty() && currentSize>maxMultiPartSize))
322  {
323  qWarning("HttpRequest::readFromSocket: received too many bytes");
324  status=abort;
325  }
326  if (status==complete)
327  {
328  // Extract and decode request parameters from url and body
330  // Extract cookies from headers
331  extractCookies();
332  }
333 }
334 
335 
337 {
338  return status;
339 }
340 
341 
342 QByteArray HttpRequest::getMethod() const
343 {
344  return method;
345 }
346 
347 
348 QByteArray HttpRequest::getPath() const
349 {
350  return urlDecode(path);
351 }
352 
353 
354 const QByteArray& HttpRequest::getRawPath() const
355 {
356  return path;
357 }
358 
359 
360 QByteArray HttpRequest::getVersion() const
361 {
362  return version;
363 }
364 
365 
366 QByteArray HttpRequest::getHeader(const QByteArray& name) const
367 {
368  return headers.value(name.toLower());
369 }
370 
371 QList<QByteArray> HttpRequest::getHeaders(const QByteArray& name) const
372 {
373  return headers.values(name.toLower());
374 }
375 
376 QMultiMap<QByteArray,QByteArray> HttpRequest::getHeaderMap() const
377 {
378  return headers;
379 }
380 
381 QByteArray HttpRequest::getParameter(const QByteArray& name) const
382 {
383  return parameters.value(name);
384 }
385 
386 QList<QByteArray> HttpRequest::getParameters(const QByteArray& name) const
387 {
388  return parameters.values(name);
389 }
390 
391 QMultiMap<QByteArray,QByteArray> HttpRequest::getParameterMap() const
392 {
393  return parameters;
394 }
395 
396 QByteArray HttpRequest::getBody() const
397 {
398  return bodyData;
399 }
400 
401 QByteArray HttpRequest::urlDecode(const QByteArray source)
402 {
403  QByteArray buffer(source);
404  buffer.replace('+',' ');
405  int percentChar=buffer.indexOf('%');
406  while (percentChar>=0)
407  {
408  bool ok;
409  char byte=buffer.mid(percentChar+1,2).toInt(&ok,16);
410  if (ok)
411  {
412  buffer.replace(percentChar,3,(char*)&byte,1);
413  }
414  percentChar=buffer.indexOf('%',percentChar+1);
415  }
416  return buffer;
417 }
418 
419 
421 {
422  qDebug("HttpRequest::parseMultiPartFile: parsing multipart temp file");
423  tempFile->seek(0);
424  bool finished=false;
425  while (!tempFile->atEnd() && !finished && !tempFile->error())
426  {
427  #ifdef SUPERVERBOSE
428  qDebug("HttpRequest::parseMultiPartFile: reading multpart headers");
429  #endif
430  QByteArray fieldName;
431  QByteArray fileName;
432  while (!tempFile->atEnd() && !finished && !tempFile->error())
433  {
434  QByteArray line=tempFile->readLine(65536).trimmed();
435  if (line.startsWith("Content-Disposition:"))
436  {
437  if (line.contains("form-data"))
438  {
439  int start=line.indexOf(" name=\"");
440  int end=line.indexOf("\"",start+7);
441  if (start>=0 && end>=start)
442  {
443  fieldName=line.mid(start+7,end-start-7);
444  }
445  start=line.indexOf(" filename=\"");
446  end=line.indexOf("\"",start+11);
447  if (start>=0 && end>=start)
448  {
449  fileName=line.mid(start+11,end-start-11);
450  }
451  #ifdef SUPERVERBOSE
452  qDebug("HttpRequest::parseMultiPartFile: multipart field=%s, filename=%s",fieldName.data(),fileName.data());
453  #endif
454  }
455  else
456  {
457  qDebug("HttpRequest::parseMultiPartFile: ignoring unsupported content part %s",line.data());
458  }
459  }
460  else if (line.isEmpty())
461  {
462  break;
463  }
464  }
465 
466  #ifdef SUPERVERBOSE
467  qDebug("HttpRequest::parseMultiPartFile: reading multpart data");
468  #endif
469  QTemporaryFile* uploadedFile=0;
470  QByteArray fieldValue;
471  while (!tempFile->atEnd() && !finished && !tempFile->error())
472  {
473  QByteArray line=tempFile->readLine(65536);
474  if (line.startsWith("--"+boundary))
475  {
476  // Boundary found. Until now we have collected 2 bytes too much,
477  // so remove them from the last result
478  if (fileName.isEmpty() && !fieldName.isEmpty())
479  {
480  // last field was a form field
481  fieldValue.remove(fieldValue.size()-2,2);
482  parameters.insert(fieldName,fieldValue);
483  qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fieldValue.data());
484  }
485  else if (!fileName.isEmpty() && !fieldName.isEmpty())
486  {
487  // last field was a file
488  #ifdef SUPERVERBOSE
489  qDebug("HttpRequest::parseMultiPartFile: finishing writing to uploaded file");
490  #endif
491  parameters.insert(fieldName,fileName);
492  if (uploadedFile)
493  {
494  uploadedFile->resize(uploadedFile->size()-2);
495  uploadedFile->flush();
496  uploadedFile->seek(0);
497  qDebug("HttpRequest::parseMultiPartFile: set parameter %s=%s",fieldName.data(),fileName.data());
498  uploadedFiles.insert(fieldName,uploadedFile);
499  qDebug("HttpRequest::parseMultiPartFile: uploaded file size is %i",(int) uploadedFile->size());
500  }
501  }
502  if (line.contains(boundary+"--"))
503  {
504  finished=true;
505  }
506  break;
507  }
508  else
509  {
510  if (fileName.isEmpty() && !fieldName.isEmpty())
511  {
512  // this is a form field.
513  currentSize+=line.size();
514  fieldValue.append(line);
515  }
516  else if (!fileName.isEmpty() && !fieldName.isEmpty())
517  {
518  // this is a file
519  if (!uploadedFile)
520  {
521  uploadedFile=new QTemporaryFile();
522  uploadedFile->open();
523  }
524  uploadedFile->write(line);
525  if (uploadedFile->error())
526  {
527  qCritical("HttpRequest::parseMultiPartFile: error writing temp file, %s",qPrintable(uploadedFile->errorString()));
528  }
529  }
530  }
531  }
532  }
533  if (tempFile->error())
534  {
535  qCritical("HttpRequest::parseMultiPartFile: cannot read temp file, %s",qPrintable(tempFile->errorString()));
536  }
537  #ifdef SUPERVERBOSE
538  qDebug("HttpRequest::parseMultiPartFile: finished parsing multipart temp file");
539  #endif
540 }
541 
543 {
544  foreach(QByteArray key, uploadedFiles.keys())
545  {
546  QTemporaryFile* file=uploadedFiles.value(key);
547  if (file->isOpen())
548  {
549  file->close();
550  }
551  delete file;
552  }
553  if (tempFile != NULL)
554  {
555  if (tempFile->isOpen())
556  {
557  tempFile->close();
558  }
559  delete tempFile;
560  }
561 }
562 
563 QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) const
564 {
565  return uploadedFiles.value(fieldName);
566 }
567 
568 QByteArray HttpRequest::getCookie(const QByteArray& name) const
569 {
570  return cookies.value(name);
571 }
572 
574 QMap<QByteArray,QByteArray>& HttpRequest::getCookieMap()
575 {
576  return cookies;
577 }
578 
584 QHostAddress HttpRequest::getPeerAddress() const
585 {
586  return peerAddress;
587 }
588 
QByteArray getBody() const
QTemporaryFile * tempFile
Definition: httprequest.h:221
RequestStatus getStatus() const
QMultiMap< QByteArray, QByteArray > parameters
Definition: httprequest.h:173
QMultiMap< QByteArray, QByteArray > getHeaderMap() const
HttpRequest(QSettings *settings)
Definition: httprequest.cpp:15
void readHeader(QTcpSocket *socket)
Definition: httprequest.cpp:75
QByteArray getParameter(const QByteArray &name) const
void readBody(QTcpSocket *socket)
QByteArray getPath() const
QList< QByteArray > getParameters(const QByteArray &name) const
QMap< QByteArray, QByteArray > & getCookieMap()
QByteArray getVersion() const
RequestStatus status
Definition: httprequest.h:197
static QByteArray urlDecode(const QByteArray source)
QByteArray getHeader(const QByteArray &name) const
QMultiMap< QByteArray, QByteArray > headers
Definition: httprequest.h:170
void readFromSocket(QTcpSocket *socket)
static QList< QByteArray > splitCSV(const QByteArray source)
Definition: httpcookie.cpp:219
QByteArray getCookie(const QByteArray &name) const
QHostAddress getPeerAddress() const
QTemporaryFile * getUploadedFile(const QByteArray fieldName) const
const QByteArray & getRawPath() const
QHostAddress peerAddress
Definition: httprequest.h:200
QList< QByteArray > getHeaders(const QByteArray &name) const
QMap< QByteArray, QByteArray > cookies
Definition: httprequest.h:179
QMap< QByteArray, QTemporaryFile * > uploadedFiles
Definition: httprequest.h:176
void readRequest(QTcpSocket *socket)
Definition: httprequest.cpp:40
QByteArray currentHeader
Definition: httprequest.h:215
QMultiMap< QByteArray, QByteArray > getParameterMap() const
QByteArray getMethod() const