mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 04:50:29 -04:00 
			
		
		
		
	
		
			
	
	
		
			570 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			570 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | /**
 | ||
|  |   @file | ||
|  |   @author Stefan Frings | ||
|  | */ | ||
|  | 
 | ||
|  | #include "httprequest.h"
 | ||
|  | #include <QList>
 | ||
|  | #include <QDir>
 | ||
|  | #include "httpcookie.h"
 | ||
|  | 
 | ||
|  | using namespace stefanfrings; | ||
|  | 
 | ||
|  | HttpRequest::HttpRequest(QSettings* settings) | ||
|  | { | ||
|  |     status=waitForRequest; | ||
|  |     currentSize=0; | ||
|  |     expectedBodySize=0; | ||
|  |     maxSize=settings->value("maxRequestSize","16000").toInt(); | ||
|  |     maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt(); | ||
|  |     tempFile=NULL; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void HttpRequest::readRequest(QTcpSocket* socket) | ||
|  | { | ||
|  |     #ifdef SUPERVERBOSE
 | ||
|  |         qDebug("HttpRequest: read request"); | ||
|  |     #endif
 | ||
|  |     int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow
 | ||
|  |     lineBuffer.append(socket->readLine(toRead)); | ||
|  |     currentSize+=lineBuffer.size(); | ||
|  |     if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n')) | ||
|  |     { | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: collecting more parts until line break"); | ||
|  |         #endif
 | ||
|  |         return; | ||
|  |     } | ||
|  |     QByteArray newData=lineBuffer.trimmed(); | ||
|  |     lineBuffer.clear(); | ||
|  |     if (!newData.isEmpty()) | ||
|  |     { | ||
|  |         QList<QByteArray> list=newData.split(' '); | ||
|  |         if (list.count()!=3 || !list.at(2).contains("HTTP")) | ||
|  |         { | ||
|  |             qWarning("HttpRequest: received broken HTTP request, invalid first line"); | ||
|  |             status=abort; | ||
|  |         } | ||
|  |         else { | ||
|  |             method=list.at(0).trimmed(); | ||
|  |             path=list.at(1); | ||
|  |             version=list.at(2); | ||
|  |             peerAddress = socket->peerAddress(); | ||
|  |             status=waitForHeader; | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void HttpRequest::readHeader(QTcpSocket* socket) | ||
|  | { | ||
|  |     #ifdef SUPERVERBOSE
 | ||
|  |         qDebug("HttpRequest: read header"); | ||
|  |     #endif
 | ||
|  |     int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow
 | ||
|  |     lineBuffer.append(socket->readLine(toRead)); | ||
|  |     currentSize+=lineBuffer.size(); | ||
|  |     if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n')) | ||
|  |     { | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: collecting more parts until line break"); | ||
|  |         #endif
 | ||
|  |         return; | ||
|  |     } | ||
|  |     QByteArray newData=lineBuffer.trimmed(); | ||
|  |     lineBuffer.clear(); | ||
|  |     int colon=newData.indexOf(':'); | ||
|  |     if (colon>0) | ||
|  |     { | ||
|  |         // Received a line with a colon - a header
 | ||
|  |         currentHeader=newData.left(colon).toLower(); | ||
|  |         QByteArray value=newData.mid(colon+1).trimmed(); | ||
|  |         headers.insert(currentHeader,value); | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: received header %s: %s",currentHeader.data(),value.data()); | ||
|  |         #endif
 | ||
|  |     } | ||
|  |     else if (!newData.isEmpty()) | ||
|  |     { | ||
|  |         // received another line - belongs to the previous header
 | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: read additional line of header"); | ||
|  |         #endif
 | ||
|  |         // Received additional line of previous header
 | ||
|  |         if (headers.contains(currentHeader)) { | ||
|  |             headers.insert(currentHeader,headers.value(currentHeader)+" "+newData); | ||
|  |         } | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         // received an empty line - end of headers reached
 | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: headers completed"); | ||
|  |         #endif
 | ||
|  |         // Empty line received, that means all headers have been received
 | ||
|  |         // Check for multipart/form-data
 | ||
|  |         QByteArray contentType=headers.value("content-type"); | ||
|  |         if (contentType.startsWith("multipart/form-data")) | ||
|  |         { | ||
|  |             int posi=contentType.indexOf("boundary="); | ||
|  |             if (posi>=0) { | ||
|  |                 boundary=contentType.mid(posi+9); | ||
|  |                 if  (boundary.startsWith('"') && boundary.endsWith('"')) | ||
|  |                 { | ||
|  |                    boundary = boundary.mid(1,boundary.length()-2); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |         QByteArray contentLength=headers.value("content-length"); | ||
|  |         if (!contentLength.isEmpty()) | ||
|  |         { | ||
|  |             expectedBodySize=contentLength.toInt(); | ||
|  |         } | ||
|  |         if (expectedBodySize==0) | ||
|  |         { | ||
|  |             #ifdef SUPERVERBOSE
 | ||
|  |                 qDebug("HttpRequest: expect no body"); | ||
|  |             #endif
 | ||
|  |             status=complete; | ||
|  |         } | ||
|  |         else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize) | ||
|  |         { | ||
|  |             qWarning("HttpRequest: expected body is too large"); | ||
|  |             status=abort; | ||
|  |         } | ||
|  |         else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize) | ||
|  |         { | ||
|  |             qWarning("HttpRequest: expected multipart body is too large"); | ||
|  |             status=abort; | ||
|  |         } | ||
|  |         else { | ||
|  |             #ifdef SUPERVERBOSE
 | ||
|  |                 qDebug("HttpRequest: expect %i bytes body",expectedBodySize); | ||
|  |             #endif
 | ||
|  |             status=waitForBody; | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void HttpRequest::readBody(QTcpSocket* socket) | ||
|  | { | ||
|  |     Q_ASSERT(expectedBodySize!=0); | ||
|  |     if (boundary.isEmpty()) | ||
|  |     { | ||
|  |         // normal body, no multipart
 | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: receive body"); | ||
|  |         #endif
 | ||
|  |         int toRead=expectedBodySize-bodyData.size(); | ||
|  |         QByteArray newData=socket->read(toRead); | ||
|  |         currentSize+=newData.size(); | ||
|  |         bodyData.append(newData); | ||
|  |         if (bodyData.size()>=expectedBodySize) | ||
|  |         { | ||
|  |             status=complete; | ||
|  |         } | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         // multipart body, store into temp file
 | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: receiving multipart body"); | ||
|  |         #endif
 | ||
|  |         // Create an object for the temporary file, if not already present
 | ||
|  |         if (tempFile == NULL) | ||
|  |         { | ||
|  |             tempFile = new QTemporaryFile; | ||
|  |         } | ||
|  |         if (!tempFile->isOpen()) | ||
|  |         { | ||
|  |             tempFile->open(); | ||
|  |         } | ||
|  |         // Transfer data in 64kb blocks
 | ||
|  |         int fileSize=tempFile->size(); | ||
|  |         int toRead=expectedBodySize-fileSize; | ||
|  |         if (toRead>65536) | ||
|  |         { | ||
|  |             toRead=65536; | ||
|  |         } | ||
|  |         fileSize+=tempFile->write(socket->read(toRead)); | ||
|  |         if (fileSize>=maxMultiPartSize) | ||
|  |         { | ||
|  |             qWarning("HttpRequest: received too many multipart bytes"); | ||
|  |             status=abort; | ||
|  |         } | ||
|  |         else if (fileSize>=expectedBodySize) | ||
|  |         { | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: received whole multipart body"); | ||
|  |         #endif
 | ||
|  |             tempFile->flush(); | ||
|  |             if (tempFile->error()) | ||
|  |             { | ||
|  |                 qCritical("HttpRequest: Error writing temp file for multipart body"); | ||
|  |             } | ||
|  |             parseMultiPartFile(); | ||
|  |             tempFile->close(); | ||
|  |             status=complete; | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void HttpRequest::decodeRequestParams() | ||
|  | { | ||
|  |     #ifdef SUPERVERBOSE
 | ||
|  |         qDebug("HttpRequest: extract and decode request parameters"); | ||
|  |     #endif
 | ||
|  |     // Get URL parameters
 | ||
|  |     QByteArray rawParameters; | ||
|  |     int questionMark=path.indexOf('?'); | ||
|  |     if (questionMark>=0) | ||
|  |     { | ||
|  |         rawParameters=path.mid(questionMark+1); | ||
|  |         path=path.left(questionMark); | ||
|  |     } | ||
|  |     // Get request body parameters
 | ||
|  |     QByteArray contentType=headers.value("content-type"); | ||
|  |     if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith("application/x-www-form-urlencoded"))) | ||
|  |     { | ||
|  |         if (!rawParameters.isEmpty()) | ||
|  |         { | ||
|  |             rawParameters.append('&'); | ||
|  |             rawParameters.append(bodyData); | ||
|  |         } | ||
|  |         else | ||
|  |         { | ||
|  |             rawParameters=bodyData; | ||
|  |         } | ||
|  |     } | ||
|  |     // Split the parameters into pairs of value and name
 | ||
|  |     QList<QByteArray> list=rawParameters.split('&'); | ||
|  |     foreach (QByteArray part, list) | ||
|  |     { | ||
|  |         int equalsChar=part.indexOf('='); | ||
|  |         if (equalsChar>=0) | ||
|  |         { | ||
|  |             QByteArray name=part.left(equalsChar).trimmed(); | ||
|  |             QByteArray value=part.mid(equalsChar+1).trimmed(); | ||
|  |             parameters.insert(urlDecode(name),urlDecode(value)); | ||
|  |         } | ||
|  |         else if (!part.isEmpty()) | ||
|  |         { | ||
|  |             // Name without value
 | ||
|  |             parameters.insert(urlDecode(part),""); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void HttpRequest::extractCookies() | ||
|  | { | ||
|  |     #ifdef SUPERVERBOSE
 | ||
|  |         qDebug("HttpRequest: extract cookies"); | ||
|  |     #endif
 | ||
|  |     foreach(QByteArray cookieStr, headers.values("cookie")) | ||
|  |     { | ||
|  |         QList<QByteArray> list=HttpCookie::splitCSV(cookieStr); | ||
|  |         foreach(QByteArray part, list) | ||
|  |         { | ||
|  |             #ifdef SUPERVERBOSE
 | ||
|  |                 qDebug("HttpRequest: found cookie %s",part.data()); | ||
|  |             #endif                // Split the part into name and value
 | ||
|  |             QByteArray name; | ||
|  |             QByteArray value; | ||
|  |             int posi=part.indexOf('='); | ||
|  |             if (posi) | ||
|  |             { | ||
|  |                 name=part.left(posi).trimmed(); | ||
|  |                 value=part.mid(posi+1).trimmed(); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 name=part.trimmed(); | ||
|  |                 value=""; | ||
|  |             } | ||
|  |             cookies.insert(name,value); | ||
|  |         } | ||
|  |     } | ||
|  |     headers.remove("cookie"); | ||
|  | } | ||
|  | 
 | ||
|  | void HttpRequest::readFromSocket(QTcpSocket* socket) | ||
|  | { | ||
|  |     Q_ASSERT(status!=complete); | ||
|  |     if (status==waitForRequest) | ||
|  |     { | ||
|  |         readRequest(socket); | ||
|  |     } | ||
|  |     else if (status==waitForHeader) | ||
|  |     { | ||
|  |         readHeader(socket); | ||
|  |     } | ||
|  |     else if (status==waitForBody) | ||
|  |     { | ||
|  |         readBody(socket); | ||
|  |     } | ||
|  |     if ((boundary.isEmpty() && currentSize>maxSize) || (!boundary.isEmpty() && currentSize>maxMultiPartSize)) | ||
|  |     { | ||
|  |         qWarning("HttpRequest: received too many bytes"); | ||
|  |         status=abort; | ||
|  |     } | ||
|  |     if (status==complete) | ||
|  |     { | ||
|  |         // Extract and decode request parameters from url and body
 | ||
|  |         decodeRequestParams(); | ||
|  |         // Extract cookies from headers
 | ||
|  |         extractCookies(); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | HttpRequest::RequestStatus HttpRequest::getStatus() const | ||
|  | { | ||
|  |     return status; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | QByteArray HttpRequest::getMethod() const | ||
|  | { | ||
|  |     return method; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | QByteArray HttpRequest::getPath() const | ||
|  | { | ||
|  |     return urlDecode(path); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | const QByteArray& HttpRequest::getRawPath() const | ||
|  | { | ||
|  |     return path; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | QByteArray HttpRequest::getVersion() const | ||
|  | { | ||
|  |     return version; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | QByteArray HttpRequest::getHeader(const QByteArray& name) const | ||
|  | { | ||
|  |     return headers.value(name.toLower()); | ||
|  | } | ||
|  | 
 | ||
|  | QList<QByteArray> HttpRequest::getHeaders(const QByteArray& name) const | ||
|  | { | ||
|  |     return headers.values(name.toLower()); | ||
|  | } | ||
|  | 
 | ||
|  | QMultiMap<QByteArray,QByteArray> HttpRequest::getHeaderMap() const | ||
|  | { | ||
|  |     return headers; | ||
|  | } | ||
|  | 
 | ||
|  | QByteArray HttpRequest::getParameter(const QByteArray& name) const | ||
|  | { | ||
|  |     return parameters.value(name); | ||
|  | } | ||
|  | 
 | ||
|  | QList<QByteArray> HttpRequest::getParameters(const QByteArray& name) const | ||
|  | { | ||
|  |     return parameters.values(name); | ||
|  | } | ||
|  | 
 | ||
|  | QMultiMap<QByteArray,QByteArray> HttpRequest::getParameterMap() const | ||
|  | { | ||
|  |     return parameters; | ||
|  | } | ||
|  | 
 | ||
|  | QByteArray HttpRequest::getBody() const | ||
|  | { | ||
|  |     return bodyData; | ||
|  | } | ||
|  | 
 | ||
|  | QByteArray HttpRequest::urlDecode(const QByteArray source) | ||
|  | { | ||
|  |     QByteArray buffer(source); | ||
|  |     buffer.replace('+',' '); | ||
|  |     int percentChar=buffer.indexOf('%'); | ||
|  |     while (percentChar>=0) | ||
|  |     { | ||
|  |         bool ok; | ||
|  |         char byte=buffer.mid(percentChar+1,2).toInt(&ok,16); | ||
|  |         if (ok) | ||
|  |         { | ||
|  |             buffer.replace(percentChar,3,(char*)&byte,1); | ||
|  |         } | ||
|  |         percentChar=buffer.indexOf('%',percentChar+1); | ||
|  |     } | ||
|  |     return buffer; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void HttpRequest::parseMultiPartFile() | ||
|  | { | ||
|  |     qDebug("HttpRequest: parsing multipart temp file"); | ||
|  |     tempFile->seek(0); | ||
|  |     bool finished=false; | ||
|  |     while (!tempFile->atEnd() && !finished && !tempFile->error()) | ||
|  |     { | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: reading multpart headers"); | ||
|  |         #endif
 | ||
|  |         QByteArray fieldName; | ||
|  |         QByteArray fileName; | ||
|  |         while (!tempFile->atEnd() && !finished && !tempFile->error()) | ||
|  |         { | ||
|  |             QByteArray line=tempFile->readLine(65536).trimmed(); | ||
|  |             if (line.startsWith("Content-Disposition:")) | ||
|  |             { | ||
|  |                 if (line.contains("form-data")) | ||
|  |                 { | ||
|  |                     int start=line.indexOf(" name=\""); | ||
|  |                     int end=line.indexOf("\"",start+7); | ||
|  |                     if (start>=0 && end>=start) | ||
|  |                     { | ||
|  |                         fieldName=line.mid(start+7,end-start-7); | ||
|  |                     } | ||
|  |                     start=line.indexOf(" filename=\""); | ||
|  |                     end=line.indexOf("\"",start+11); | ||
|  |                     if (start>=0 && end>=start) | ||
|  |                     { | ||
|  |                         fileName=line.mid(start+11,end-start-11); | ||
|  |                     } | ||
|  |                     #ifdef SUPERVERBOSE
 | ||
|  |                         qDebug("HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data()); | ||
|  |                     #endif
 | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     qDebug("HttpRequest: ignoring unsupported content part %s",line.data()); | ||
|  |                 } | ||
|  |             } | ||
|  |             else if (line.isEmpty()) | ||
|  |             { | ||
|  |                 break; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         #ifdef SUPERVERBOSE
 | ||
|  |             qDebug("HttpRequest: reading multpart data"); | ||
|  |         #endif
 | ||
|  |         QTemporaryFile* uploadedFile=0; | ||
|  |         QByteArray fieldValue; | ||
|  |         while (!tempFile->atEnd() && !finished && !tempFile->error()) | ||
|  |         { | ||
|  |             QByteArray line=tempFile->readLine(65536); | ||
|  |             if (line.startsWith("--"+boundary)) | ||
|  |             { | ||
|  |                 // Boundary found. Until now we have collected 2 bytes too much,
 | ||
|  |                 // so remove them from the last result
 | ||
|  |                 if (fileName.isEmpty() && !fieldName.isEmpty()) | ||
|  |                 { | ||
|  |                     // last field was a form field
 | ||
|  |                     fieldValue.remove(fieldValue.size()-2,2); | ||
|  |                     parameters.insert(fieldName,fieldValue); | ||
|  |                     qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fieldValue.data()); | ||
|  |                 } | ||
|  |                 else if (!fileName.isEmpty() && !fieldName.isEmpty()) | ||
|  |                 { | ||
|  |                     // last field was a file
 | ||
|  |                     #ifdef SUPERVERBOSE
 | ||
|  |                         qDebug("HttpRequest: finishing writing to uploaded file"); | ||
|  |                     #endif
 | ||
|  |                     uploadedFile->resize(uploadedFile->size()-2); | ||
|  |                     uploadedFile->flush(); | ||
|  |                     uploadedFile->seek(0); | ||
|  |                     parameters.insert(fieldName,fileName); | ||
|  |                     qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fileName.data()); | ||
|  |                     uploadedFiles.insert(fieldName,uploadedFile); | ||
|  |                     qDebug("HttpRequest: uploaded file size is %i",(int) uploadedFile->size()); | ||
|  |                 } | ||
|  |                 if (line.contains(boundary+"--")) | ||
|  |                 { | ||
|  |                     finished=true; | ||
|  |                 } | ||
|  |                 break; | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 if (fileName.isEmpty() && !fieldName.isEmpty()) | ||
|  |                 { | ||
|  |                     // this is a form field.
 | ||
|  |                     currentSize+=line.size(); | ||
|  |                     fieldValue.append(line); | ||
|  |                 } | ||
|  |                 else if (!fileName.isEmpty() && !fieldName.isEmpty()) | ||
|  |                 { | ||
|  |                     // this is a file
 | ||
|  |                     if (!uploadedFile) | ||
|  |                     { | ||
|  |                         uploadedFile=new QTemporaryFile(); | ||
|  |                         uploadedFile->open(); | ||
|  |                     } | ||
|  |                     uploadedFile->write(line); | ||
|  |                     if (uploadedFile->error()) | ||
|  |                     { | ||
|  |                         qCritical("HttpRequest: error writing temp file, %s",qPrintable(uploadedFile->errorString())); | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |     if (tempFile->error()) | ||
|  |     { | ||
|  |         qCritical("HttpRequest: cannot read temp file, %s",qPrintable(tempFile->errorString())); | ||
|  |     } | ||
|  |     #ifdef SUPERVERBOSE
 | ||
|  |         qDebug("HttpRequest: finished parsing multipart temp file"); | ||
|  |     #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | HttpRequest::~HttpRequest() | ||
|  | { | ||
|  |     foreach(QByteArray key, uploadedFiles.keys()) | ||
|  |     { | ||
|  |         QTemporaryFile* file=uploadedFiles.value(key); | ||
|  |         if (file->isOpen()) | ||
|  |         { | ||
|  |             file->close(); | ||
|  |         } | ||
|  |         delete file; | ||
|  |     } | ||
|  |     if (tempFile != NULL) | ||
|  |     { | ||
|  |         if (tempFile->isOpen()) | ||
|  |         { | ||
|  |             tempFile->close(); | ||
|  |         } | ||
|  |         delete tempFile; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) const | ||
|  | { | ||
|  |     return uploadedFiles.value(fieldName); | ||
|  | } | ||
|  | 
 | ||
|  | QByteArray HttpRequest::getCookie(const QByteArray& name) const | ||
|  | { | ||
|  |     return cookies.value(name); | ||
|  | } | ||
|  | 
 | ||
|  | /** Get the map of cookies */ | ||
|  | QMap<QByteArray,QByteArray>& HttpRequest::getCookieMap() | ||
|  | { | ||
|  |     return cookies; | ||
|  | } | ||
|  | 
 | ||
|  | /**
 | ||
|  |   Get the address of the connected client. | ||
|  |   Note that multiple clients may have the same IP address, if they | ||
|  |   share an internet connection (which is very common). | ||
|  |  */ | ||
|  | QHostAddress HttpRequest::getPeerAddress() const | ||
|  | { | ||
|  |     return peerAddress; | ||
|  | } | ||
|  | 
 |