mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-25 12:12:02 -05:00
201 lines
4.4 KiB
C++
201 lines
4.4 KiB
C++
|
/**
|
||
|
@file
|
||
|
@author Stefan Frings
|
||
|
*/
|
||
|
|
||
|
#include "httpresponse.h"
|
||
|
|
||
|
using namespace stefanfrings;
|
||
|
|
||
|
HttpResponse::HttpResponse(QTcpSocket* socket)
|
||
|
{
|
||
|
this->socket=socket;
|
||
|
statusCode=200;
|
||
|
statusText="OK";
|
||
|
sentHeaders=false;
|
||
|
sentLastPart=false;
|
||
|
chunkedMode=false;
|
||
|
}
|
||
|
|
||
|
void HttpResponse::setHeader(QByteArray name, QByteArray value)
|
||
|
{
|
||
|
Q_ASSERT(sentHeaders==false);
|
||
|
headers.insert(name,value);
|
||
|
}
|
||
|
|
||
|
void HttpResponse::setHeader(QByteArray name, int value)
|
||
|
{
|
||
|
Q_ASSERT(sentHeaders==false);
|
||
|
headers.insert(name,QByteArray::number(value));
|
||
|
}
|
||
|
|
||
|
QMap<QByteArray,QByteArray>& HttpResponse::getHeaders()
|
||
|
{
|
||
|
return headers;
|
||
|
}
|
||
|
|
||
|
void HttpResponse::setStatus(int statusCode, QByteArray description)
|
||
|
{
|
||
|
this->statusCode=statusCode;
|
||
|
statusText=description;
|
||
|
}
|
||
|
|
||
|
int HttpResponse::getStatusCode() const
|
||
|
{
|
||
|
return this->statusCode;
|
||
|
}
|
||
|
|
||
|
void HttpResponse::writeHeaders()
|
||
|
{
|
||
|
Q_ASSERT(sentHeaders==false);
|
||
|
QByteArray buffer;
|
||
|
buffer.append("HTTP/1.1 ");
|
||
|
buffer.append(QByteArray::number(statusCode));
|
||
|
buffer.append(' ');
|
||
|
buffer.append(statusText);
|
||
|
buffer.append("\r\n");
|
||
|
foreach(QByteArray name, headers.keys())
|
||
|
{
|
||
|
buffer.append(name);
|
||
|
buffer.append(": ");
|
||
|
buffer.append(headers.value(name));
|
||
|
buffer.append("\r\n");
|
||
|
}
|
||
|
foreach(HttpCookie cookie,cookies.values())
|
||
|
{
|
||
|
buffer.append("Set-Cookie: ");
|
||
|
buffer.append(cookie.toByteArray());
|
||
|
buffer.append("\r\n");
|
||
|
}
|
||
|
buffer.append("\r\n");
|
||
|
writeToSocket(buffer);
|
||
|
sentHeaders=true;
|
||
|
}
|
||
|
|
||
|
bool HttpResponse::writeToSocket(QByteArray data)
|
||
|
{
|
||
|
int remaining=data.size();
|
||
|
char* ptr=data.data();
|
||
|
while (socket->isOpen() && remaining>0)
|
||
|
{
|
||
|
// If the output buffer has become large, then wait until it has been sent.
|
||
|
if (socket->bytesToWrite()>16384)
|
||
|
{
|
||
|
socket->waitForBytesWritten(-1);
|
||
|
}
|
||
|
|
||
|
int written=socket->write(ptr,remaining);
|
||
|
if (written==-1)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
ptr+=written;
|
||
|
remaining-=written;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void HttpResponse::write(QByteArray data, bool lastPart)
|
||
|
{
|
||
|
Q_ASSERT(sentLastPart==false);
|
||
|
|
||
|
// Send HTTP headers, if not already done (that happens only on the first call to write())
|
||
|
if (sentHeaders==false)
|
||
|
{
|
||
|
// If the whole response is generated with a single call to write(), then we know the total
|
||
|
// size of the response and therefore can set the Content-Length header automatically.
|
||
|
if (lastPart)
|
||
|
{
|
||
|
// Automatically set the Content-Length header
|
||
|
headers.insert("Content-Length",QByteArray::number(data.size()));
|
||
|
}
|
||
|
|
||
|
// else if we will not close the connection at the end, them we must use the chunked mode.
|
||
|
else
|
||
|
{
|
||
|
QByteArray connectionValue=headers.value("Connection",headers.value("connection"));
|
||
|
bool connectionClose=QString::compare(connectionValue,"close",Qt::CaseInsensitive)==0;
|
||
|
if (!connectionClose)
|
||
|
{
|
||
|
headers.insert("Transfer-Encoding","chunked");
|
||
|
chunkedMode=true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
writeHeaders();
|
||
|
}
|
||
|
|
||
|
// Send data
|
||
|
if (data.size()>0)
|
||
|
{
|
||
|
if (chunkedMode)
|
||
|
{
|
||
|
if (data.size()>0)
|
||
|
{
|
||
|
QByteArray size=QByteArray::number(data.size(),16);
|
||
|
writeToSocket(size);
|
||
|
writeToSocket("\r\n");
|
||
|
writeToSocket(data);
|
||
|
writeToSocket("\r\n");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
writeToSocket(data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Only for the last chunk, send the terminating marker and flush the buffer.
|
||
|
if (lastPart)
|
||
|
{
|
||
|
if (chunkedMode)
|
||
|
{
|
||
|
writeToSocket("0\r\n\r\n");
|
||
|
}
|
||
|
socket->flush();
|
||
|
sentLastPart=true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool HttpResponse::hasSentLastPart() const
|
||
|
{
|
||
|
return sentLastPart;
|
||
|
}
|
||
|
|
||
|
|
||
|
void HttpResponse::setCookie(const HttpCookie& cookie)
|
||
|
{
|
||
|
Q_ASSERT(sentHeaders==false);
|
||
|
if (!cookie.getName().isEmpty())
|
||
|
{
|
||
|
cookies.insert(cookie.getName(),cookie);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
QMap<QByteArray,HttpCookie>& HttpResponse::getCookies()
|
||
|
{
|
||
|
return cookies;
|
||
|
}
|
||
|
|
||
|
|
||
|
void HttpResponse::redirect(const QByteArray& url)
|
||
|
{
|
||
|
setStatus(303,"See Other");
|
||
|
setHeader("Location",url);
|
||
|
write("Redirect",true);
|
||
|
}
|
||
|
|
||
|
|
||
|
void HttpResponse::flush()
|
||
|
{
|
||
|
socket->flush();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool HttpResponse::isConnected() const
|
||
|
{
|
||
|
return socket->isOpen();
|
||
|
}
|