PageRenderTime 46ms CodeModel.GetById 22ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 1ms

/mordor/http/chunked.cpp

http://github.com/mozy/mordor
C++ | 97 lines | 85 code | 11 blank | 1 comment | 22 complexity | 2e4c54f8caa90c0453b36b2f1e50bff5 MD5 | raw file
 1// Copyright (c) 2009 - Mozy, Inc.
 2
 3#include "chunked.h"
 4
 5#include <sstream>
 6#include <stdexcept>
 7
 8#include "mordor/assert.h"
 9#include "mordor/streams/buffer.h"
10#include "mordor/version.h"
11
12namespace Mordor {
13namespace HTTP {
14
15static Logger::ptr g_log = Log::lookup("mordor:http:chunked");
16
17InvalidChunkException::InvalidChunkException(const std::string &line,
18                                           Type type)
19: m_line(line),
20  m_type(type)
21{}
22
23ChunkedStream::ChunkedStream(Stream::ptr parent, bool own)
24: MutatingFilterStream(parent, own),
25  m_nextChunk(~0)
26{
27    if (parent->supportsRead())
28        MORDOR_ASSERT(parent->supportsFind());
29}
30
31void
32ChunkedStream::close(Stream::CloseType type)
33{
34    if (supportsWrite() && (type & WRITE)) {
35        MORDOR_LOG_VERBOSE(g_log) << this << " writing EOF";
36        parent()->write("0\r\n", 3);
37    }
38    if (ownsParent())
39        parent()->close(type);
40}
41
42size_t
43ChunkedStream::read(Buffer &b, size_t len)
44{
45    if (m_nextChunk == ~0ull - 1) {
46        std::string chunk = parent()->getDelimited();
47        MORDOR_ASSERT(!chunk.empty());
48        chunk.resize(chunk.size() - 1);
49        if (!chunk.empty() && chunk[chunk.size() - 1] == '\r')
50            chunk.resize(chunk.size() - 1);
51        MORDOR_LOG_TRACE(g_log) << this << " read CRLF '" << chunk << "'";
52        if (!chunk.empty())
53            MORDOR_THROW_EXCEPTION(InvalidChunkException(chunk, InvalidChunkException::FOOTER));
54        m_nextChunk = ~0;
55    }
56    if (m_nextChunk == ~0ull) {
57        std::string chunk = parent()->getDelimited();
58        MORDOR_ASSERT(!chunk.empty());
59        chunk.resize(chunk.size() - 1);
60        if (!chunk.empty() && chunk[chunk.size() - 1] == '\r')
61            chunk.resize(chunk.size() - 1);
62        MORDOR_LOG_DEBUG(g_log) << this << " read chunk header '" << chunk
63            << "'";
64        char *end;
65        m_nextChunk = strtoull(chunk.c_str(), &end, 16);
66        if (end == chunk.c_str())
67            MORDOR_THROW_EXCEPTION(InvalidChunkException(chunk, InvalidChunkException::HEADER));
68    }
69    if (m_nextChunk == 0)
70        return 0;
71    size_t toRead = (size_t)std::min<unsigned long long>(len, m_nextChunk);
72    size_t result = parent()->read(b, toRead);
73    m_nextChunk -= result;
74    if (m_nextChunk == 0)
75        m_nextChunk = ~0ull - 1;
76    return result;
77}
78
79size_t
80ChunkedStream::write(const Buffer &b, size_t len)
81{
82    std::ostringstream os;
83    os << std::hex << len << "\r\n";
84    std::string str = os.str();
85    MORDOR_LOG_DEBUG(g_log) << this << " writing chunk header " << str;
86    parent()->write(str.c_str(), str.size());
87    Buffer copy;
88    copy.copyIn(b, len);
89    while (copy.readAvailable()) {
90        size_t result = parent()->write(copy, copy.readAvailable());
91        copy.consume(result);
92    }
93    parent()->write("\r\n", 2);
94    return len;
95}
96
97}}