11 using namespace stefanfrings;
15 status=waitForRequest;
18 maxSize=settings->value(
"maxRequestSize",
"16000").toInt();
19 maxMultiPartSize=settings->value(
"maxMultiPartSize",
"1000000").toInt();
24 void HttpRequest::readRequest(QTcpSocket* socket)
27 qDebug(
"HttpRequest: read request");
29 int toRead=maxSize-currentSize+1;
30 QByteArray dataRead = socket->readLine(toRead);
31 currentSize += dataRead.size();
32 lineBuffer.append(dataRead);
33 if (!lineBuffer.contains(
"\r\n"))
36 qDebug(
"HttpRequest: collecting more parts until line break");
40 QByteArray newData=lineBuffer.trimmed();
42 if (!newData.isEmpty())
44 qDebug(
"HttpRequest: from %s: %s",qPrintable(socket->peerAddress().toString()),newData.data());
45 QList<QByteArray> list=newData.split(
' ');
46 if (list.count()!=3 || !list.at(2).contains(
"HTTP"))
48 qWarning(
"HttpRequest: received broken HTTP request, invalid first line");
53 method=list.at(0).trimmed();
56 peerAddress = socket->peerAddress();
62 void HttpRequest::readHeader(QTcpSocket* socket)
64 int toRead=maxSize-currentSize+1;
65 QByteArray dataRead = socket->readLine(toRead);
66 currentSize += dataRead.size();
67 lineBuffer.append(dataRead);
68 if (!lineBuffer.contains(
"\r\n"))
71 qDebug(
"HttpRequest: collecting more parts until line break");
75 QByteArray newData=lineBuffer.trimmed();
77 int colon=newData.indexOf(
':');
81 currentHeader=newData.left(colon).toLower();
82 QByteArray value=newData.mid(colon+1).trimmed();
83 headers.insert(currentHeader,value);
85 qDebug(
"HttpRequest: received header %s: %s",currentHeader.data(),value.data());
88 else if (!newData.isEmpty())
92 qDebug(
"HttpRequest: read additional line of header");
95 if (headers.contains(currentHeader)) {
96 headers.insert(currentHeader,headers.value(currentHeader)+
" "+newData);
103 qDebug(
"HttpRequest: headers completed");
107 QByteArray contentType=headers.value(
"content-type");
108 if (contentType.startsWith(
"multipart/form-data"))
110 int posi=contentType.indexOf(
"boundary=");
112 boundary=contentType.mid(posi+9);
113 if (boundary.startsWith(
'"') && boundary.endsWith(
'"'))
115 boundary = boundary.mid(1,boundary.length()-2);
119 QByteArray contentLength=headers.value(
"content-length");
120 if (!contentLength.isEmpty())
122 expectedBodySize=contentLength.toInt();
124 if (expectedBodySize==0)
127 qDebug(
"HttpRequest: expect no body");
131 else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize)
133 qWarning(
"HttpRequest: expected body is too large");
136 else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize)
138 qWarning(
"HttpRequest: expected multipart body is too large");
143 qDebug(
"HttpRequest: expect %i bytes body",expectedBodySize);
150 void HttpRequest::readBody(QTcpSocket* socket)
152 Q_ASSERT(expectedBodySize!=0);
153 if (boundary.isEmpty())
157 qDebug(
"HttpRequest: receive body");
159 int toRead=expectedBodySize-bodyData.size();
160 QByteArray newData=socket->read(toRead);
161 currentSize+=newData.size();
162 bodyData.append(newData);
163 if (bodyData.size()>=expectedBodySize)
172 qDebug(
"HttpRequest: receiving multipart body");
175 if (tempFile ==
nullptr)
177 tempFile =
new QTemporaryFile;
179 if (!tempFile->isOpen())
184 qint64 fileSize=tempFile->size();
185 qint64 toRead=expectedBodySize-fileSize;
190 fileSize+=tempFile->write(socket->read(toRead));
191 if (fileSize>=maxMultiPartSize)
193 qWarning(
"HttpRequest: received too many multipart bytes");
196 else if (fileSize>=expectedBodySize)
199 qDebug(
"HttpRequest: received whole multipart body");
202 if (tempFile->error())
204 qCritical(
"HttpRequest: Error writing temp file for multipart body");
206 parseMultiPartFile();
213 void HttpRequest::decodeRequestParams()
216 qDebug(
"HttpRequest: extract and decode request parameters");
219 QByteArray rawParameters;
220 int questionMark=path.indexOf(
'?');
223 rawParameters=path.mid(questionMark+1);
224 path=path.left(questionMark);
227 QByteArray contentType=headers.value(
"content-type");
228 if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith(
"application/x-www-form-urlencoded")))
230 if (!rawParameters.isEmpty())
232 rawParameters.append(
'&');
233 rawParameters.append(bodyData);
237 rawParameters=bodyData;
241 QList<QByteArray> list=rawParameters.split(
'&');
242 foreach (QByteArray part, list)
244 int equalsChar=part.indexOf(
'=');
247 QByteArray name=part.left(equalsChar).trimmed();
248 QByteArray value=part.mid(equalsChar+1).trimmed();
251 else if (!part.isEmpty())
259 void HttpRequest::extractCookies()
262 qDebug(
"HttpRequest: extract cookies");
264 foreach(QByteArray cookieStr, headers.values(
"cookie"))
267 foreach(QByteArray part, list)
270 qDebug(
"HttpRequest: found cookie %s",part.data());
274 int posi=part.indexOf(
'=');
277 name=part.left(posi).trimmed();
278 value=part.mid(posi+1).trimmed();
285 cookies.insert(name,value);
288 headers.remove(
"cookie");
293 Q_ASSERT(status!=complete);
294 if (status==waitForRequest)
298 else if (status==waitForHeader)
302 else if (status==waitForBody)
306 if ((boundary.isEmpty() && currentSize>maxSize) || (!boundary.isEmpty() && currentSize>maxMultiPartSize))
308 qWarning(
"HttpRequest: received too many bytes");
311 if (status==complete)
314 decodeRequestParams();
353 return headers.value(name.toLower());
358 return headers.values(name.toLower());
368 return parameters.value(name);
373 return parameters.values(name);
388 QByteArray buffer(source);
389 buffer.replace(
'+',
' ');
390 int percentChar=buffer.indexOf(
'%');
391 while (percentChar>=0)
394 int hexCode=buffer.mid(percentChar+1,2).toInt(&ok,16);
397 char c=char(hexCode);
398 buffer.replace(percentChar,3,&c,1);
400 percentChar=buffer.indexOf(
'%',percentChar+1);
406 void HttpRequest::parseMultiPartFile()
408 qDebug(
"HttpRequest: parsing multipart temp file");
411 while (!tempFile->atEnd() && !finished && !tempFile->error())
414 qDebug(
"HttpRequest: reading multpart headers");
416 QByteArray fieldName;
418 while (!tempFile->atEnd() && !finished && !tempFile->error())
420 QByteArray line=tempFile->readLine(65536).trimmed();
421 if (line.startsWith(
"Content-Disposition:"))
423 if (line.contains(
"form-data"))
425 int start=line.indexOf(
" name=\"");
426 int end=line.indexOf(
"\"",start+7);
427 if (start>=0 && end>=start)
429 fieldName=line.mid(start+7,end-start-7);
431 start=line.indexOf(
" filename=\"");
432 end=line.indexOf(
"\"",start+11);
433 if (start>=0 && end>=start)
435 fileName=line.mid(start+11,end-start-11);
438 qDebug(
"HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data());
443 qDebug(
"HttpRequest: ignoring unsupported content part %s",line.data());
446 else if (line.isEmpty())
453 qDebug(
"HttpRequest: reading multpart data");
455 QTemporaryFile* uploadedFile=
nullptr;
456 QByteArray fieldValue;
457 while (!tempFile->atEnd() && !finished && !tempFile->error())
459 QByteArray line=tempFile->readLine(65536);
460 if (line.startsWith(
"--"+boundary))
464 if (fileName.isEmpty() && !fieldName.isEmpty())
467 fieldValue.remove(fieldValue.size()-2,2);
468 parameters.insert(fieldName,fieldValue);
469 qDebug(
"HttpRequest: set parameter %s=%s",fieldName.data(),fieldValue.data());
471 else if (!fileName.isEmpty() && !fieldName.isEmpty())
477 qDebug(
"HttpRequest: finishing writing to uploaded file");
479 uploadedFile->resize(uploadedFile->size()-2);
480 uploadedFile->flush();
481 uploadedFile->seek(0);
482 parameters.insert(fieldName,fileName);
483 qDebug(
"HttpRequest: set parameter %s=%s",fieldName.data(),fileName.data());
484 uploadedFiles.insert(fieldName,uploadedFile);
485 long int fileSize=(
long int) uploadedFile->size();
486 qDebug(
"HttpRequest: uploaded file size is %li",fileSize);
490 qWarning(
"HttpRequest: format error, unexpected end of file data");
493 if (line.contains(boundary+
"--"))
501 if (fileName.isEmpty() && !fieldName.isEmpty())
504 currentSize+=line.size();
505 fieldValue.append(line);
507 else if (!fileName.isEmpty() && !fieldName.isEmpty())
512 uploadedFile=
new QTemporaryFile();
513 uploadedFile->open();
515 uploadedFile->write(line);
516 if (uploadedFile->error())
518 qCritical(
"HttpRequest: error writing temp file, %s",qPrintable(uploadedFile->errorString()));
524 if (tempFile->error())
526 qCritical(
"HttpRequest: cannot read temp file, %s",qPrintable(tempFile->errorString()));
529 qDebug(
"HttpRequest: finished parsing multipart temp file");
535 foreach(QByteArray key, uploadedFiles.keys())
537 QTemporaryFile* file=uploadedFiles.value(key);
544 if (tempFile !=
nullptr)
546 if (tempFile->isOpen())
556 return uploadedFiles.value(fieldName);
561 return cookies.value(name);
static QList< QByteArray > splitCSV(const QByteArray source)
Split a string list into parts, where each part is delimited by semicolon.
QList< QByteArray > getParameters(const QByteArray &name) const
Get the values of a HTTP request parameter.
QMultiMap< QByteArray, QByteArray > getHeaderMap() const
Get all HTTP request headers.
RequestStatus getStatus() const
Get the status of this reqeust.
QMultiMap< QByteArray, QByteArray > getParameterMap() const
Get all HTTP request parameters.
HttpRequest(const QSettings *settings)
Constructor.
QMap< QByteArray, QByteArray > & getCookieMap()
Get all cookies.
RequestStatus
Values for getStatus()
QByteArray getHeader(const QByteArray &name) const
Get the value of a HTTP request header.
static QByteArray urlDecode(const QByteArray source)
Decode an URL parameter.
void readFromSocket(QTcpSocket *socket)
Read the HTTP request from a socket.
QByteArray getCookie(const QByteArray &name) const
Get the value of a cookie.
QByteArray getPath() const
Get the decoded path of the HTPP request (e.g.
QByteArray getBody() const
Get the HTTP request body.
QList< QByteArray > getHeaders(const QByteArray &name) const
Get the values of a HTTP request header.
QTemporaryFile * getUploadedFile(const QByteArray fieldName) const
Get an uploaded file.
QByteArray getParameter(const QByteArray &name) const
Get the value of a HTTP request parameter.
const QByteArray & getRawPath() const
Get the raw path of the HTTP request (e.g.
virtual ~HttpRequest()
Destructor.
QByteArray getVersion() const
Get the version of the HTPP request (e.g.
QByteArray getMethod() const
Get the method of the HTTP request (e.g.
QHostAddress getPeerAddress() const
Get the address of the connected client.