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