httprequest.cpp 17 KB

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