File indexing completed on 2025-01-26 04:24:52

0001 /*
0002  * Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
0003  * 
0004  * Permission is hereby granted, free of charge, to any person obtaining a copy
0005  * of this software and associated documentation files (the "Software"), to
0006  * deal in the Software without restriction, including without limitation the
0007  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
0008  * sell copies of the Software, and to permit persons to whom the Software is
0009  * furnished to do so, subject to the following conditions:
0010  * 
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  * 
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0019  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
0020  * IN THE SOFTWARE. 
0021  */
0022 
0023 #include "qhttpresponse.h"
0024 
0025 #include <QDateTime>
0026 
0027 #include "qhttpserver.h"
0028 #include "qhttpconnection.h"
0029 
0030 QHttpResponse::QHttpResponse(QHttpConnection *connection)
0031     // TODO: parent child relation
0032     : QObject(0)
0033     , m_connection(connection)
0034     , m_headerWritten(false)
0035     , m_sentConnectionHeader(false)
0036     , m_sentContentLengthHeader(false)
0037     , m_sentTransferEncodingHeader(false)
0038     , m_sentDate(false)
0039     , m_keepAlive(true)
0040     , m_last(false)
0041     , m_useChunkedEncoding(false)
0042     , m_finished(false)
0043 {
0044 }
0045 
0046 QHttpResponse::~QHttpResponse()
0047 {
0048 }
0049 
0050 void QHttpResponse::setHeader(const QString &field, const QString &value)
0051 {
0052     if(m_finished) {
0053       return;
0054     }
0055 
0056     m_headers[field] = value;
0057 }
0058 
0059 void QHttpResponse::writeHeader(const char *field, const QString &value)
0060 {
0061     if(m_finished) {
0062       return;
0063     }
0064 
0065     m_connection->write(field);
0066     m_connection->write(": ");
0067     m_connection->write(value.toUtf8());
0068     m_connection->write("\r\n");
0069 }
0070 
0071 void QHttpResponse::writeHeaders()
0072 {
0073     if(m_finished) {
0074       return;
0075     }
0076 
0077     foreach(QString name, m_headers.keys())
0078     {
0079         QString value = m_headers[name];
0080         if( name.compare("connection", Qt::CaseInsensitive) == 0 ) 
0081         {
0082             m_sentConnectionHeader = true;
0083             if( value == "close" )
0084                 m_last = true;
0085             else
0086                 m_keepAlive = true;
0087         }
0088         else if( name.compare("transfer-encoding", Qt::CaseInsensitive) == 0 )
0089         {
0090             m_sentTransferEncodingHeader = true;
0091             if( value == "chunked" )
0092                 m_useChunkedEncoding = true;
0093         }
0094         else if( name.compare("content-length", Qt::CaseInsensitive) == 0 )
0095         {
0096             m_sentContentLengthHeader = true;
0097         }
0098         else if( name.compare("date", Qt::CaseInsensitive) == 0 )
0099         {
0100             m_sentDate = true;
0101         }
0102         //TODO: Expect case
0103 
0104         writeHeader(name.toLatin1(), value.toLatin1());
0105     }
0106 
0107     if( !m_sentConnectionHeader )
0108     {
0109         if( m_keepAlive &&
0110                 ( m_sentContentLengthHeader || m_useChunkedEncoding ) )
0111         {
0112             writeHeader("Connection", "keep-alive");
0113         }
0114         else
0115         {
0116             m_last = true;
0117             writeHeader("Connection", "close");
0118         }
0119     }
0120 
0121     if( !m_sentContentLengthHeader && !m_sentTransferEncodingHeader )
0122     {
0123         if( m_useChunkedEncoding )
0124             writeHeader("Transfer-Encoding", "chunked");
0125         else
0126             m_last = true;
0127     }
0128 
0129     if( !m_sentDate )
0130     {
0131         writeHeader("Date", QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss G'M'T"));
0132     }
0133 }
0134 
0135 void QHttpResponse::writeHead(int status)
0136 {
0137     if(m_finished) {
0138       return;
0139     }
0140 
0141     if( m_headerWritten ) return;
0142 
0143     m_connection->write(QString("HTTP/1.1 %1 %2\r\n").arg(status).arg(STATUS_CODES[status]).toLatin1());
0144     
0145     writeHeaders();
0146 
0147     m_connection->write("\r\n");
0148     m_headerWritten = true;
0149 }
0150 
0151 void QHttpResponse::write(const QByteArray &data)
0152 {
0153     if(m_finished) {
0154       return;
0155     }
0156 
0157     if( !m_headerWritten )
0158     {
0159         qDebug() << "You MUST call writeHead() before writing body data";
0160         return;
0161     }
0162 
0163     m_connection->write(data);
0164 }
0165 
0166 void QHttpResponse::write(const QString &data)
0167 {
0168     if(m_finished) {
0169       return;
0170     }
0171 
0172     m_connection->write(data.toUtf8());
0173 }
0174 
0175 void QHttpResponse::write_b64(const QByteArray &data)
0176 {
0177     this->write(QByteArray::fromBase64(data));
0178 }
0179 
0180 void QHttpResponse::end(const QString &data)
0181 {
0182     if(m_finished) {
0183       return;
0184     }
0185 
0186     write(data);
0187 
0188     m_finished = true;
0189 
0190     Q_EMIT done();
0191     deleteLater();
0192     // TODO: end connection and delete ourselves
0193 }
0194 
0195 void QHttpResponse::connectionClosed()
0196 {
0197   m_finished = true;
0198   deleteLater();
0199 }