PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/mordor/http/connection.cpp

http://github.com/mozy/mordor
C++ | 130 lines | 100 code | 7 blank | 23 comment | 61 complexity | 12fd33455ac7256c839602958f0651af MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "connection.h"
  3. #include "chunked.h"
  4. #include "mordor/streams/buffered.h"
  5. #include "mordor/streams/gzip.h"
  6. #include "mordor/streams/limited.h"
  7. #include "mordor/streams/notify.h"
  8. #include "mordor/streams/singleplex.h"
  9. #include "mordor/streams/zlib.h"
  10. namespace Mordor {
  11. namespace HTTP {
  12. Connection::Connection(Stream::ptr stream)
  13. : m_stream(stream)
  14. {
  15. MORDOR_ASSERT(stream);
  16. MORDOR_ASSERT(stream->supportsRead());
  17. MORDOR_ASSERT(stream->supportsWrite());
  18. if (!stream->supportsUnread() || !stream->supportsFind()) {
  19. BufferedStream *buffered = new BufferedStream(stream);
  20. buffered->allowPartialReads(true);
  21. m_stream.reset(buffered);
  22. }
  23. }
  24. bool
  25. Connection::hasMessageBody(const GeneralHeaders &general,
  26. const EntityHeaders &entity, const std::string &method, Status status,
  27. bool includeEmpty)
  28. {
  29. // Connect escapes HTTP
  30. if (method == CONNECT && (status == OK || status == INVALID))
  31. return false;
  32. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8
  33. // A TRACE request MUST NOT include an entity.
  34. if (status == INVALID && method == TRACE)
  35. return false;
  36. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
  37. // the server MUST NOT return a message-body in the response
  38. if (status != INVALID && method == HEAD)
  39. return false;
  40. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.1
  41. // This class of status code indicates a provisional response, consisting only of the Status-Line and optional headers
  42. if (((int)status >= 100 && status <= 199) ||
  43. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5
  44. // The 204 response MUST NOT include a message-body
  45. (int)status == 204 ||
  46. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
  47. // The 304 response MUST NOT contain a message-body
  48. (int)status == 304)
  49. return false;
  50. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
  51. // 2.
  52. for (ParameterizedList::const_iterator it(general.transferEncoding.begin());
  53. it != general.transferEncoding.end();
  54. ++it) {
  55. if (stricmp(it->value.c_str(), "identity") != 0)
  56. return true;
  57. }
  58. // 3.
  59. if (entity.contentLength == 0)
  60. return includeEmpty;
  61. if (entity.contentLength != ~0ull)
  62. return true;
  63. // 4.
  64. if (entity.contentType.type == "multipart")
  65. return true;
  66. // 5. By the server closing the connection.
  67. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
  68. // (by default, requests don't have a message body, because you can't
  69. // tell where it would end, without precluding the possibility of a
  70. // response)
  71. return status != INVALID;
  72. }
  73. Stream::ptr
  74. Connection::getStream(const GeneralHeaders &general,
  75. const EntityHeaders &entity, const std::string &method, Status status,
  76. boost::function<void()> notifyOnEof,
  77. boost::function<void()> notifyOnException, bool forRead)
  78. {
  79. MORDOR_ASSERT(hasMessageBody(general, entity, method, status));
  80. Stream::ptr stream;
  81. if (forRead) {
  82. stream.reset(new SingleplexStream(m_stream, SingleplexStream::READ, false));
  83. } else {
  84. stream.reset(new SingleplexStream(m_stream, SingleplexStream::WRITE, false));
  85. }
  86. Stream::ptr baseStream(stream);
  87. for (ParameterizedList::const_reverse_iterator it(general.transferEncoding.rbegin());
  88. it != general.transferEncoding.rend();
  89. ++it) {
  90. if (stricmp(it->value.c_str(), "chunked") == 0) {
  91. stream.reset(new ChunkedStream(stream));
  92. } else if (stricmp(it->value.c_str(), "deflate") == 0) {
  93. stream.reset(new ZlibStream(stream));
  94. } else if (stricmp(it->value.c_str(), "gzip") == 0 ||
  95. stricmp(it->value.c_str(), "x-gzip") == 0) {
  96. stream.reset(new GzipStream(stream));
  97. } else if (stricmp(it->value.c_str(), "compress") == 0 ||
  98. stricmp(it->value.c_str(), "x-compress") == 0) {
  99. MORDOR_ASSERT(false);
  100. } else if (stricmp(it->value.c_str(), "identity") == 0) {
  101. MORDOR_ASSERT(false);
  102. } else {
  103. MORDOR_ASSERT(false);
  104. }
  105. }
  106. if (stream != baseStream) {
  107. } else if (entity.contentLength != ~0ull) {
  108. LimitedStream::ptr limited(new LimitedStream(stream, entity.contentLength));
  109. limited->strict(true);
  110. stream = limited;
  111. } else if (entity.contentType.type == "multipart") {
  112. // Getting stream to pass to multipart; self-delimiting
  113. } else {
  114. // Delimited by closing the connection
  115. }
  116. NotifyStream::ptr notify(new NotifyStream(stream));
  117. stream = notify;
  118. notify->notifyOnClose(notifyOnEof);
  119. notify->notifyOnEof = notifyOnEof;
  120. notify->notifyOnException = notifyOnException;
  121. return stream;
  122. }
  123. }}