summaryrefslogtreecommitdiff
path: root/http/http_file_body.hpp
blob: eaafd5dbeb24b39aa42e51ed57bb916449f5275a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#pragma once

#include "utility.hpp"

#include <boost/beast/core/file_posix.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/system/error_code.hpp>

namespace bmcweb
{
struct FileBody
{
    class writer;
    class value_type;

    static std::uint64_t size(const value_type& body);
};

enum class EncodingType
{
    Raw,
    Base64,
};

class FileBody::value_type
{
    boost::beast::file_posix fileHandle;

    std::uint64_t fileSize = 0;

  public:
    EncodingType encodingType = EncodingType::Raw;

    ~value_type() = default;
    value_type() = default;
    explicit value_type(EncodingType enc) : encodingType(enc) {}
    value_type(value_type&& other) = default;
    value_type& operator=(value_type&& other) = default;
    value_type(const value_type& other) = delete;
    value_type& operator=(const value_type& other) = delete;

    boost::beast::file_posix& file()
    {
        return fileHandle;
    }

    std::uint64_t size() const
    {
        return fileSize;
    }

    void open(const char* path, boost::beast::file_mode mode,
              boost::system::error_code& ec)
    {
        fileHandle.open(path, mode, ec);
        fileSize = fileHandle.size(ec);
    }

    void setFd(int fd, boost::system::error_code& ec)
    {
        fileHandle.native_handle(fd);
        fileSize = fileHandle.size(ec);
    }
};

inline std::uint64_t FileBody::size(const value_type& body)
{
    return body.size();
}

class FileBody::writer
{
  public:
    using const_buffers_type = boost::asio::const_buffer;

  private:
    std::string buf;
    crow::utility::Base64Encoder encoder;

    value_type& body;
    std::uint64_t remain;
    constexpr static size_t readBufSize = 4096;
    std::array<char, readBufSize> fileReadBuf{};

  public:
    template <bool IsRequest, class Fields>
    writer(boost::beast::http::header<IsRequest, Fields>& /*header*/,
           value_type& bodyIn) :
        body(bodyIn),
        remain(body.size())
    {}

    static void init(boost::beast::error_code& ec)
    {
        ec = {};
    }

    boost::optional<std::pair<const_buffers_type, bool>>
        get(boost::beast::error_code& ec)
    {
        size_t toRead = fileReadBuf.size();
        if (remain < toRead)
        {
            toRead = static_cast<size_t>(remain);
        }
        size_t read = body.file().read(fileReadBuf.data(), toRead, ec);
        if (read != toRead || ec)
        {
            return boost::none;
        }
        remain -= read;

        std::string_view chunkView(fileReadBuf.data(), read);

        std::pair<const_buffers_type, bool> ret;
        ret.second = remain > 0;
        if (body.encodingType == EncodingType::Base64)
        {
            buf.clear();
            buf.reserve(
                crow::utility::Base64Encoder::encodedSize(chunkView.size()));
            encoder.encode(chunkView, buf);
            if (!ret.second)
            {
                encoder.finalize(buf);
            }
            ret.first = const_buffers_type(buf.data(), buf.size());
        }
        else
        {
            ret.first = const_buffers_type(chunkView.data(), chunkView.size());
        }
        return ret;
    }
};
} // namespace bmcweb