sdrangel/httpserver/httpresponse.cpp

201 lines
4.4 KiB
C++

/**
@file
@author Stefan Frings
*/
#include "httpresponse.h"
using namespace qtwebapp;
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();
}