• Main Page
  • Classes
  • Files
  • File List
  • File Members

lib/bfHttpServer/src/httprequest.cpp

Go to the documentation of this file.
00001 
00006 #include "httprequest.h"
00007 #include <QList>
00008 #include <QDir>
00009 #include "httpcookie.h"
00010 
00011 HttpRequest::HttpRequest(QSettings* settings) {
00012     status=waitForRequest;
00013     currentSize=0;
00014     expectedBodySize=0;
00015     maxSize=settings->value("maxRequestSize","16000").toInt();
00016     maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt();
00017 
00018     // Convert relative path to absolute, based on the directory of the config file.
00019 #ifdef Q_OS_WIN32
00020     if (QDir::isRelativePath(tempDir) && settings->format()!=QSettings::NativeFormat)
00021 #else
00022         if (QDir::isRelativePath(tempDir))
00023 #endif
00024         {
00025         QFileInfo configFile(settings->fileName());
00026         tempDir=QFileInfo(configFile.absolutePath(),tempDir).absoluteFilePath();
00027     }
00028 }
00029 
00030 void HttpRequest::readRequest(QTcpSocket& socket) {
00031 #ifdef SUPERVERBOSE
00032     qDebug("HttpRequest: read request");
00033 #endif
00034     int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow
00035     QByteArray newData=socket.readLine(toRead).trimmed();
00036     currentSize+=newData.size();
00037     if (!newData.isEmpty()) {
00038         QList<QByteArray> list=newData.split(' ');
00039         if (list.count()!=3 || !list.at(2).contains("HTTP")) {
00040             qWarning("HttpRequest: received broken HTTP request, invalid first line");
00041             status=abort;
00042         }
00043         else {
00044             method=list.at(0);
00045             path=list.at(1);
00046             version=list.at(2);
00047             status=waitForHeader;
00048         }
00049     }
00050 }
00051 
00052 void HttpRequest::readHeader(QTcpSocket& socket) {
00053 #ifdef SUPERVERBOSE
00054     qDebug("HttpRequest: read header");
00055 #endif
00056     int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow
00057     QByteArray newData=socket.readLine(toRead).trimmed();
00058     currentSize+=newData.size();
00059     int colon=newData.indexOf(':');
00060     if (colon>0)  {
00061         // Received a line with a colon - a header
00062         currentHeader=newData.left(colon);
00063         QByteArray value=newData.mid(colon+1).trimmed();
00064         headers.insert(currentHeader,value);
00065 #ifdef SUPERVERBOSE
00066         qDebug("HttpRequest: received header %s: %s",currentHeader.data(),value.data());
00067 #endif
00068     }
00069     else if (!newData.isEmpty()) {
00070         // received another line - belongs to the previous header
00071 #ifdef SUPERVERBOSE
00072         qDebug("HttpRequest: read additional line of header");
00073 #endif
00074         // Received additional line of previous header
00075         if (headers.contains(currentHeader)) {
00076             headers.insert(currentHeader,headers.value(currentHeader)+" "+newData);
00077         }
00078     }
00079     else {
00080         // received an empty line - end of headers reached
00081 #ifdef SUPERVERBOSE
00082         qDebug("HttpRequest: headers completed");
00083 #endif
00084         // Empty line received, that means all headers have been received
00085         // Check for multipart/form-data
00086         QByteArray contentType=headers.value("Content-Type");
00087         if (contentType.startsWith("multipart/form-data")) {
00088             int posi=contentType.indexOf("boundary=");
00089             if (posi>=0) {
00090                 boundary=contentType.mid(posi+9);
00091             }
00092         }
00093         QByteArray contentLength=getHeader("Content-Length");
00094         if (!contentLength.isEmpty()) {
00095             expectedBodySize=contentLength.toInt();
00096         }
00097         if (expectedBodySize==0) {
00098 #ifdef SUPERVERBOSE
00099             qDebug("HttpRequest: expect no body");
00100 #endif
00101             status=complete;
00102         }
00103         else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize) {
00104             qWarning("HttpRequest: expected body is too large");
00105             status=abort;
00106         }
00107         else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize) {
00108             qWarning("HttpRequest: expected multipart body is too large");
00109             status=abort;
00110         }
00111         else {
00112 #ifdef SUPERVERBOSE
00113             qDebug("HttpRequest: expect %i bytes body",expectedBodySize);
00114 #endif
00115             status=waitForBody;
00116         }
00117     }
00118 }
00119 
00120 void HttpRequest::readBody(QTcpSocket& socket) {
00121     Q_ASSERT(expectedBodySize!=0);
00122     if (boundary.isEmpty()) {
00123         // normal body, no multipart
00124 #ifdef SUPERVERBOSE
00125         qDebug("HttpRequest: receive body");
00126 #endif
00127         int toRead=expectedBodySize-bodyData.size();
00128         QByteArray newData=socket.read(toRead);
00129         currentSize+=newData.size();
00130         bodyData.append(newData);
00131         if (bodyData.size()>=expectedBodySize) {
00132             status=complete;
00133         }
00134     }
00135     else {
00136         // multipart body, store into temp file
00137 #ifdef SUPERVERBOSE
00138         qDebug("HttpRequest: receiving multipart body");
00139 #endif
00140         if (!tempFile.isOpen()) {
00141             tempFile.open();
00142         }
00143         // Transfer data in 64kb blocks
00144         int fileSize=tempFile.size();
00145         int toRead=expectedBodySize-fileSize;
00146         if (toRead>65536) {
00147             toRead=65536;
00148         }
00149         fileSize+=tempFile.write(socket.read(toRead));
00150         if (fileSize>=maxMultiPartSize) {
00151             qWarning("HttpRequest: received too many multipart bytes");
00152             status=abort;
00153         }
00154         else if (fileSize>=expectedBodySize) {
00155 #ifdef SUPERVERBOSE
00156             qDebug("HttpRequest: received whole multipart body");
00157 #endif
00158             tempFile.flush();
00159             if (tempFile.error()) {
00160                 qCritical("HttpRequest: Error writing temp file for multipart body");
00161             }
00162             parseMultiPartFile();
00163             tempFile.close();
00164             status=complete;
00165         }
00166     }
00167 }
00168 
00169 void HttpRequest::decodeRequestParams() {
00170 #ifdef SUPERVERBOSE
00171     qDebug("HttpRequest: extract and decode request parameters");
00172 #endif
00173     QByteArray rawParameters;
00174     if (headers.value("Content-Type")=="application/x-www-form-urlencoded") {
00175         rawParameters=bodyData;
00176     }
00177     else {
00178         int questionMark=path.indexOf('?');
00179         if (questionMark>=0) {
00180             rawParameters=path.mid(questionMark+1);
00181             path=path.left(questionMark);
00182         }
00183     }
00184     // Split the parameters into pairs of value and name
00185     QList<QByteArray> list=rawParameters.split('&');
00186     foreach (QByteArray part, list) {
00187         int equalsChar=part.indexOf('=');
00188         if (equalsChar>=0) {
00189             QByteArray name=part.left(equalsChar).trimmed();
00190             QByteArray value=part.mid(equalsChar+1).trimmed();
00191             parameters.insert(urlDecode(name),urlDecode(value));
00192         }
00193         else if (!part.isEmpty()){
00194             // Name without value
00195             parameters.insert(urlDecode(part),"");
00196         }
00197     }
00198 }
00199 
00200 void HttpRequest::extractCookies() {
00201 #ifdef SUPERVERBOSE
00202     qDebug("HttpRequest: extract cookies");
00203 #endif
00204     foreach(QByteArray cookieStr, headers.values("Cookie")) {
00205         QList<QByteArray> list=HttpCookie::splitCSV(cookieStr);
00206         foreach(QByteArray part, list) {
00207 #ifdef SUPERVERBOSE
00208             qDebug("HttpRequest: found cookie %s",part.data());
00209 #endif                // Split the part into name and value
00210             QByteArray name;
00211             QByteArray value;
00212             int posi=part.indexOf('=');
00213             if (posi) {
00214                 name=part.left(posi).trimmed();
00215                 value=part.mid(posi+1).trimmed();
00216             }
00217             else {
00218                 name=part.trimmed();
00219                 value="";
00220             }
00221             cookies.insert(name,value);
00222         }
00223     }
00224     headers.remove("Cookie");
00225 }
00226 
00227 void HttpRequest::readFromSocket(QTcpSocket& socket) {
00228     Q_ASSERT(status!=complete);
00229     if (status==waitForRequest) {
00230         readRequest(socket);
00231     }
00232     else if (status==waitForHeader) {
00233         readHeader(socket);
00234     }
00235     else if (status==waitForBody) {
00236         readBody(socket);
00237     }
00238     if (currentSize>maxSize) {
00239         qWarning("HttpRequest: received too many bytes");
00240         status=abort;
00241     }
00242     if (status==complete) {
00243         // Extract and decode request parameters from url and body
00244         decodeRequestParams();
00245         // Extract cookies from headers
00246         extractCookies();
00247     }
00248 }
00249 
00250 
00251 HttpRequest::RequestStatus HttpRequest::getStatus() const {
00252     return status;
00253 }
00254 
00255 
00256 QByteArray HttpRequest::getMethod() const {
00257     return method;
00258 }
00259 
00260 
00261 QByteArray HttpRequest::getPath() const {
00262     return urlDecode(path);
00263 }
00264 
00265 
00266 QByteArray HttpRequest::getVersion() const {
00267     return version;
00268 }
00269 
00270 
00271 QByteArray HttpRequest::getHeader(const QByteArray& name) const {
00272     return headers.value(name);
00273 }
00274 
00275 QList<QByteArray> HttpRequest::getHeaders(const QByteArray& name) const {
00276     return headers.values(name);
00277 }
00278 
00279 QMultiMap<QByteArray,QByteArray> HttpRequest::getHeaderMap() const {
00280     return headers;
00281 }
00282 
00283 QByteArray HttpRequest::getParameter(const QByteArray& name) const {
00284     return parameters.value(name);
00285 }
00286 
00287 QList<QByteArray> HttpRequest::getParameters(const QByteArray& name) const {
00288     return parameters.values(name);
00289 }
00290 
00291 QMultiMap<QByteArray,QByteArray> HttpRequest::getParameterMap() const {
00292     return parameters;
00293 }
00294 
00295 QByteArray HttpRequest::getBody() const {
00296     return bodyData;
00297 }
00298 
00299 QByteArray HttpRequest::urlDecode(const QByteArray source) {
00300     QByteArray buffer(source);
00301     buffer.replace('+',' ');
00302     int percentChar=buffer.indexOf('%');
00303     while (percentChar>=0) {
00304         bool ok;
00305         char byte=buffer.mid(percentChar+1,2).toInt(&ok,16);
00306         if (ok) {
00307             buffer.replace(percentChar,3,(char*)&byte,1);
00308         }
00309         percentChar=buffer.indexOf('%',percentChar+1);
00310     }
00311     return buffer;
00312 }
00313 
00314 
00315 void HttpRequest::parseMultiPartFile() {
00316     qDebug("HttpRequest: parsing multipart temp file");
00317     tempFile.seek(0);
00318     bool finished=false;
00319     while (!tempFile.atEnd() && !finished && !tempFile.error()) {
00320 
00321 #ifdef SUPERVERBOSE
00322         qDebug("HttpRequest: reading multpart headers");
00323 #endif
00324         QByteArray fieldName;
00325         QByteArray fileName;
00326         while (!tempFile.atEnd() && !finished && !tempFile.error()) {
00327             QByteArray line=tempFile.readLine(65536).trimmed();
00328             if (line.startsWith("Content-Disposition:")) {
00329                 if (line.contains("form-data")) {
00330                     int start=line.indexOf(" name=\"");
00331                     int end=line.indexOf("\"",start+7);
00332                     if (start>=0 && end>=start) {
00333                         fieldName=line.mid(start+7,end-start-7);
00334                     }
00335                     start=line.indexOf(" filename=\"");
00336                     end=line.indexOf("\"",start+11);
00337                     if (start>=0 && end>=start) {
00338                         fileName=line.mid(start+11,end-start-11);
00339                     }
00340 #ifdef SUPERVERBOSE
00341                     qDebug("HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data());
00342 #endif
00343                 }
00344                 else {
00345                     qDebug("HttpRequest: ignoring unsupported content part %s",line.data());
00346                 }
00347             }
00348             else if (line.isEmpty()) {
00349                 break;
00350             }
00351         }
00352 
00353 #ifdef SUPERVERBOSE
00354         qDebug("HttpRequest: reading multpart data");
00355 #endif
00356         QTemporaryFile* uploadedFile=0;
00357         QByteArray fieldValue;
00358         while (!tempFile.atEnd() && !finished && !tempFile.error()) {
00359             QByteArray line=tempFile.readLine(65536);
00360             if (line.startsWith("--"+boundary)) {
00361                 // Boundary found. Until now we have collected 2 bytes too much,
00362                 // so remove them from the last result
00363                 if (fileName.isEmpty() && !fieldName.isEmpty()) {
00364                     // last field was a form field
00365                     fieldValue.remove(fieldValue.size()-2,2);
00366                     parameters.insert(fieldName,fieldValue);
00367                     qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fieldValue.data());
00368                 }
00369                 else if (!fileName.isEmpty() && !fieldName.isEmpty()) {
00370                     // last field was a file
00371 #ifdef SUPERVERBOSE
00372                     qDebug("HttpRequest: finishing writing to uploaded file");
00373 #endif
00374                     uploadedFile->resize(uploadedFile->size()-2);
00375                     uploadedFile->flush();
00376                     uploadedFile->seek(0);
00377                     parameters.insert(fieldName,fileName);
00378                     qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fileName.data());
00379                     uploadedFiles.insert(fieldName,uploadedFile);
00380                     qDebug("HttpRequest: uploaded file size is %i",(int) uploadedFile->size());
00381                 }
00382                 if (line.contains(boundary+"--")) {
00383                     finished=true;
00384                 }
00385                 break;
00386             }
00387             else {
00388                 if (fileName.isEmpty() && !fieldName.isEmpty()) {
00389                     // this is a form field.
00390                     currentSize+=line.size();
00391                     fieldValue.append(line);
00392                 }
00393                 else if (!fileName.isEmpty() && !fieldName.isEmpty()) {
00394                     // this is a file
00395                     if (!uploadedFile) {
00396                         uploadedFile=new QTemporaryFile();
00397                         uploadedFile->open();
00398                     }
00399                     uploadedFile->write(line);
00400                     if (uploadedFile->error()) {
00401                         qCritical("HttpRequest: error writing temp file, %s",qPrintable(uploadedFile->errorString()));
00402                     }
00403                 }
00404             }
00405         }
00406     }
00407     if (tempFile.error()) {
00408         qCritical("HttpRequest: cannot read temp file, %s",qPrintable(tempFile.errorString()));
00409     }
00410 #ifdef SUPERVERBOSE
00411     qDebug("HttpRequest: finished parsing multipart temp file");
00412 #endif
00413 }
00414 
00415 HttpRequest::~HttpRequest() {
00416     foreach(QByteArray key, uploadedFiles.keys()) {
00417         QTemporaryFile* file=uploadedFiles.value(key);
00418         file->close();
00419         delete file;
00420     }
00421 }
00422 
00423 QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) {
00424     return uploadedFiles.value(fieldName);
00425 }
00426 
00427 QByteArray HttpRequest::getCookie(const QByteArray& name) const {
00428     return cookies.value(name);
00429 }
00430 
00432 QMap<QByteArray,QByteArray>& HttpRequest::getCookieMap() {
00433     return cookies;
00434 }
00435 

Generated on Mon Dec 26 2011 12:09:22 for QtWebApp by  doxygen 1.7.1