Hobrasoft httpd server
Embedded HTTP server for Qt and C++
httpresponse.cpp
Go to the documentation of this file.
1 
8 #include "httpresponse.h"
9 #include "httprequest.h"
10 #include "httpconnection.h"
11 #include "httpsettings.h"
12 #include "httpgzipcompression.h"
13 #include <QStringList>
14 
15 #include <QDebug>
16 
17 using namespace HobrasoftHttpd;
18 
19 
20 HttpResponse::~HttpResponse() {
21 }
22 
23 
24 HttpResponse::HttpResponse(HttpConnection *connection) : QObject(connection) {
25  m_connection = connection;
26  m_socket = m_connection->socket();
27  m_flushed = false;
28  m_statusCode = 200;
29  m_statusText = "OK";
30  m_sentHeaders = false;
31  m_chunked = false;
32  m_dataBodyPointer = 0;
33  m_dataHeadersPointer = 0;
34  m_canWriteToSocket = false;
35  m_closeAfterFlush = false;
36  m_deleteAfterFlush = false;
37  m_writerTimer = new QTimer(this);
38  m_writerTimer->setInterval(1000);
39  m_writerTimer->setSingleShot(true);
40  connect (m_writerTimer, SIGNAL(timeout()),
41  this, SLOT(slotWrite()));
42  connect (m_socket, SIGNAL(bytesWritten(qint64)),
43  this, SLOT(slotWrite()));
44  connect (m_socket, SIGNAL(disconnected()),
45  this, SLOT(socketDisconnected()));
46 
47  connect (m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
48  this, SLOT(socketError(QAbstractSocket::SocketError)));
49 }
50 
51 
52 void HttpResponse::socketError(QAbstractSocket::SocketError error) {
53  qDebug() << "socketError" << error;
54 }
55 
56 
58  return m_connection->isConnected();
59 }
60 
61 
62 void HttpResponse::socketDisconnected() {
63 }
64 
65 
66 void HttpResponse::setHeader(const QString& name, const QString& value) {
67  if (m_sentHeaders) { return; }
68  m_headers[name] = value;
69 }
70 
71 
72 void HttpResponse::setHeader(const QString& name, int value) {
73  if (m_sentHeaders) { return; }
74  m_headers[name] = QString("%1").arg(value);
75 }
76 
77 
78 QMap<QString, QString>& HttpResponse::headers() {
79  return m_headers;
80 }
81 
82 
84  m_headers.clear();
85 }
86 
87 
88 void HttpResponse::setStatus(int statusCode, const QString& statusText) {
89  Q_ASSERT(m_sentHeaders == false);
90  m_statusCode = statusCode;
91  m_statusText = statusText;
92 }
93 
94 
96  if (m_chunked) { return true; }
97  if (m_headers.value("Transfer-Encoding").toLower() == "chunked") {
98  m_chunked = true;
99  return true;
100  }
101  return false;
102 }
103 
104 
105 void HttpResponse::writeHeaders() {
106  Q_ASSERT(m_sentHeaders == false);
107  if (m_sentHeaders) { return; }
108 
109  QString contentType = m_headers.value("Content-Type").toLower();
110  bool cancompress = (m_connection->settings()->gzip() && !chunked() && (
111  contentType.startsWith("text/") ||
112  contentType.startsWith("application/json") ||
113  contentType.startsWith("application/javascript"))
114  );
115 
116  bool requestGzip = (
117  m_connection->request() != NULL &&
118  m_connection->request()->header("Accept-Encoding").contains("gzip")
119  );
120 
121  bool c200 = (m_statusCode == 200);
122 
123  if (cancompress && requestGzip && !chunked() && c200) {
124  setHeader("Content-Encoding", "gzip");
125  }
126 
127  if (!cancompress) {
128  m_headers.remove("Content-Length");
129  }
130 
131  // int dbs = m_dataBody.size();
132  if (m_headers.value("Content-Encoding").toLower() == "gzip" ) {
133  m_dataBody = HttpGZipCompression::compressData(m_dataBody);
134  }
135 
136  if (!chunked()) {
137  m_headers["Content-Length"] = QString("%1").arg(m_dataBody.size());
138  }
139  /*
140  qDebug() << "gzip" << cancompress << requestGzip << !chunked << m_headers.value("Content-Type") << dbs << m_dataBody.size()
141  << ( (m_connection->request() != NULL) ? m_connection->request()->path() : "")
142  << ( (m_connection->request() != NULL) ? m_connection->request()->header("Accept-Encoding") : "")
143  << contentType
144  ;
145  */
146 
147  m_dataHeaders += "HTTP/1.1 ";
148  m_dataHeaders += QByteArray::number(m_statusCode);
149  m_dataHeaders += " ";
150  m_dataHeaders += m_statusText;
151  m_dataHeaders += "\r\n";
152 
153  QStringList keys = m_headers.keys();
154  for (int i=0; i<keys.size(); i++) {
155  m_dataHeaders += keys[i].toUtf8();
156  m_dataHeaders += ": ";
157  m_dataHeaders += m_headers[keys[i]].toUtf8();
158  m_dataHeaders += "\r\n";
159  }
160 
161  keys = m_cookies.keys();
162  for (int i=0; i<keys.size(); i++) {
163  m_dataHeaders += "Set-Cookie: ";
164  m_dataHeaders += m_cookies[keys[i]].toByteArray();
165  m_dataHeaders += "\r\n";
166  }
167 
168  m_dataHeaders += "\r\n";
169  m_sentHeaders = true;
170 
171 }
172 
173 
174 void HttpResponse::setCookie(const HttpCookie& cookie) {
175  if (cookie.name().isEmpty()) return;
176  m_cookies[cookie.name()] = cookie;
177 }
178 
179 
181  if (!isConnected()) { return; }
182  slotWrite();
183  m_socket->flush();
184  return;
185 }
186 
187 
189  if (!isConnected()) { return; }
190  if (chunked()) {
191  m_socket->write("0\r\n\r\n");
192  }
193  m_socket->flush();
194  if (!isConnected()) { return; }
195  m_socket->waitForBytesWritten(10000);
196  m_socket->disconnectFromHost();
197 }
198 
199 
200 void HttpResponse::write(const QByteArray& data) {
201  if (!isConnected()) { return; }
202  if (chunked() && data.size() > 0) {
203  if (!m_sentHeaders) {
204  writeHeaders();
205  }
206  m_canWriteToSocket = true;
207  m_closeAfterFlush = false;
208  m_dataBody.clear();
209  m_dataBodyPointer = 0;
210  m_dataBody += QByteArray::number(data.size(),16) ;
211  m_dataBody += "\r\n";
212  m_dataBody += data;
213  m_dataBody += "\r\n";
214  while (isConnected() && m_dataBody.size() > m_dataBodyPointer) {
215  slotWrite(false);
216  m_socket->flush();
217  }
218  // m_writerTimer->setInterval(0);
219  // m_writerTimer->start();
220  return;
221  }
222 
223  if (!chunked()) {
224  if (m_flushed) {
225  qDebug() << "You could not write to HttpRespose when the response is flushed. Data written are ignored.";
226  return;
227  }
228  m_closeAfterFlush = true;
229  m_dataBody += data;
230  }
231 
232 }
233 
234 
236  m_flushed = true;
237  if (!isConnected()) { return; }
238  m_canWriteToSocket = true;
239  m_writerTimer->setInterval(0);
240  m_writerTimer->start();
241 }
242 
243 
244 void HttpResponse::flushAndClose() {
245  m_closeAfterFlush = true;
246  flush();
247 }
248 
249 
250 void HttpResponse::flushAndDelete() {
251  m_deleteAfterFlush = true;
252  flush();
253 }
254 
255 
256 void HttpResponse::slotWrite(bool startTimer) {
257  if (!m_sentHeaders) {
258  m_canWriteToSocket = true;
259  writeHeaders();
260  }
261  if (!m_canWriteToSocket) { goto konec; }
262  if (m_socket->bytesToWrite() > 0) { goto konec; }
263  if (!isConnected()) { goto konec; }
264  if (m_socket->isOpen() != true) { goto konec; }
265  if (m_socket->isWritable() != true) { goto konec; }
266  if (m_dataHeaders.size() > m_dataHeadersPointer) {
267  m_dataHeadersPointer += m_socket->write(m_dataHeaders.mid(m_dataHeadersPointer));
268  if (m_dataHeaders.size() > m_dataHeadersPointer) { goto konec; }
269  }
270 
271  if (m_dataBody.size() > m_dataBodyPointer) {
272  m_dataBodyPointer += m_socket->write(m_dataBody.mid(m_dataBodyPointer));
273  if (m_dataBody.size() > m_dataBodyPointer) { goto konec; }
274  }
275 
276  if (m_dataHeaders.size() <= m_dataHeadersPointer &&
277  m_dataBody.size() <= m_dataBodyPointer &&
278  m_closeAfterFlush) {
279  m_socket->flush();
280  m_writerTimer->stop();
281  close();
282  return;
283  }
284 
285  if (m_dataHeaders.size() <= m_dataHeadersPointer &&
286  m_dataBody.size() <= m_dataBodyPointer &&
287  m_deleteAfterFlush) {
288  m_socket->flush();
289  m_writerTimer->stop();
290  deleteLater();
291  return;
292  }
293 
294  konec:
295  if (startTimer) {
296  m_writerTimer->setInterval(100);
297  m_writerTimer->start();
298  }
299 }
300 
301 
QTcpSocket * socket() const
Returns the socket.
HttpResponse(HttpConnection *)
Constructor sets default values for headers (status 200, OK)
HttpCookie cookie(const QString &name)
Returns cookie.
Definition: httpresponse.h:79
QString name() const
Returns the name of cookie.
Definition: httpcookie.h:54
One cookie of HTTP protocol.
Definition: httpcookie.h:20
void flush()
Writes last part of the response and closes the socket when possible.
void flushSocket()
Flushed sockets data to network.
void write(const QByteArray &data)
Writes data to response body.
void setCookie(const HttpCookie &cookie)
Sets a cookie.
bool isConnected() const
Returns true if the http connection is in connected state.
void setStatus(int code, const QString &description=QString())
Set the status code and the description of the response.
void close()
Closes socket and destroys connection. Should by called only when "chunked" transport is choosen...
bool chunked()
Returns true if the trasport mode is chunked.
void setHeader(const QString &name, const QString &value)
Sets or rewrite one header.
void clearHeaders()
Clears all headers set.
Namespace of HTTP server.
QMap< QString, QString > & headers()
Returns headers of the response in QMap.
One single connection to http server.