httpresponse.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /**
  2. @file
  3. @author Stefan Frings
  4. */
  5. #include "httpresponse.h"
  6. using namespace stefanfrings;
  7. HttpResponse::HttpResponse(QTcpSocket *socket)
  8. {
  9. this->socket=socket;
  10. statusCode=200;
  11. statusText="OK";
  12. sentHeaders=false;
  13. sentLastPart=false;
  14. chunkedMode=false;
  15. }
  16. void HttpResponse::setHeader(QByteArray name, QByteArray value)
  17. {
  18. Q_ASSERT(sentHeaders==false);
  19. headers.insert(name,value);
  20. }
  21. void HttpResponse::setHeader(QByteArray name, int value)
  22. {
  23. Q_ASSERT(sentHeaders==false);
  24. headers.insert(name,QByteArray::number(value));
  25. }
  26. QMap<QByteArray,QByteArray>& HttpResponse::getHeaders()
  27. {
  28. return headers;
  29. }
  30. void HttpResponse::setStatus(int statusCode, QByteArray description)
  31. {
  32. this->statusCode=statusCode;
  33. statusText=description;
  34. }
  35. int HttpResponse::getStatusCode() const
  36. {
  37. return this->statusCode;
  38. }
  39. void HttpResponse::writeHeaders()
  40. {
  41. Q_ASSERT(sentHeaders==false);
  42. QByteArray buffer;
  43. buffer.append("HTTP/1.1 ");
  44. buffer.append(QByteArray::number(statusCode));
  45. buffer.append(' ');
  46. buffer.append(statusText);
  47. buffer.append("\r\n");
  48. foreach(QByteArray name, headers.keys())
  49. {
  50. buffer.append(name);
  51. buffer.append(": ");
  52. buffer.append(headers.value(name));
  53. buffer.append("\r\n");
  54. }
  55. foreach(HttpCookie cookie,cookies.values())
  56. {
  57. buffer.append("Set-Cookie: ");
  58. buffer.append(cookie.toByteArray());
  59. buffer.append("\r\n");
  60. }
  61. buffer.append("\r\n");
  62. writeToSocket(buffer);
  63. socket->flush();
  64. sentHeaders=true;
  65. }
  66. bool HttpResponse::writeToSocket(QByteArray data)
  67. {
  68. int remaining=data.size();
  69. char* ptr=data.data();
  70. while (socket->isOpen() && remaining>0)
  71. {
  72. // If the output buffer has become large, then wait until it has been sent.
  73. if (socket->bytesToWrite()>16384)
  74. {
  75. socket->waitForBytesWritten(-1);
  76. }
  77. qint64 written=socket->write(ptr,remaining);
  78. if (written==-1)
  79. {
  80. return false;
  81. }
  82. ptr+=written;
  83. remaining-=written;
  84. }
  85. return true;
  86. }
  87. void HttpResponse::write(QByteArray data, bool lastPart)
  88. {
  89. Q_ASSERT(sentLastPart==false);
  90. // Send HTTP headers, if not already done (that happens only on the first call to write())
  91. if (sentHeaders==false)
  92. {
  93. // If the whole response is generated with a single call to write(), then we know the total
  94. // size of the response and therefore can set the Content-Length header automatically.
  95. if (lastPart)
  96. {
  97. // Automatically set the Content-Length header
  98. headers.insert("Content-Length",QByteArray::number(data.size()));
  99. }
  100. // else if we will not close the connection at the end and there is no Content-Length header,
  101. // then we must use the chunked mode.
  102. else
  103. {
  104. QByteArray connectionValue=headers.value("Connection",headers.value("connection"));
  105. bool connectionClose=QString::compare(connectionValue,"close",Qt::CaseInsensitive)==0;
  106. if (!connectionClose && !headers.contains("Content-Length"))
  107. {
  108. headers.insert("Transfer-Encoding","chunked");
  109. chunkedMode=true;
  110. }
  111. }
  112. writeHeaders();
  113. }
  114. // Send data
  115. if (data.size()>0)
  116. {
  117. if (chunkedMode)
  118. {
  119. if (data.size()>0)
  120. {
  121. QByteArray size=QByteArray::number(data.size(),16);
  122. writeToSocket(size);
  123. writeToSocket("\r\n");
  124. writeToSocket(data);
  125. writeToSocket("\r\n");
  126. }
  127. }
  128. else
  129. {
  130. writeToSocket(data);
  131. }
  132. }
  133. // Only for the last chunk, send the terminating marker and flush the buffer.
  134. if (lastPart)
  135. {
  136. if (chunkedMode)
  137. {
  138. writeToSocket("0\r\n\r\n");
  139. }
  140. socket->flush();
  141. sentLastPart=true;
  142. }
  143. }
  144. bool HttpResponse::hasSentLastPart() const
  145. {
  146. return sentLastPart;
  147. }
  148. void HttpResponse::setCookie(const HttpCookie& cookie)
  149. {
  150. Q_ASSERT(sentHeaders==false);
  151. if (!cookie.getName().isEmpty())
  152. {
  153. cookies.insert(cookie.getName(),cookie);
  154. }
  155. }
  156. QMap<QByteArray,HttpCookie>& HttpResponse::getCookies()
  157. {
  158. return cookies;
  159. }
  160. void HttpResponse::redirect(const QByteArray& url)
  161. {
  162. setStatus(303,"See Other");
  163. setHeader("Location",url);
  164. write("Redirect",true);
  165. }
  166. void HttpResponse::flush()
  167. {
  168. socket->flush();
  169. }
  170. bool HttpResponse::isConnected() const
  171. {
  172. return socket->isOpen();
  173. }