PageRenderTime 509ms CodeModel.GetById 148ms app.highlight 154ms RepoModel.GetById 98ms 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
  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}}