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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab ft=cpp
/*
* Ceph - scalable distributed file system
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*
*/
#include "rgw_rest_zero.h"
#include <mutex>
#include <vector>
#include "common/strtol.h"
namespace rgw {
// all paths refer to a single resource that contains only a size
class ZeroResource {
public:
std::mutex mutex;
std::size_t size = 0;
};
// base op
class ZeroOp : public RGWOp {
protected:
ZeroResource* const resource;
const char* response_content_type = nullptr;
int64_t response_content_length = NO_CONTENT_LENGTH;
public:
explicit ZeroOp(ZeroResource* resource) : resource(resource) {}
int verify_permission(optional_yield y) override { return 0; }
void send_response() override;
};
void ZeroOp::send_response()
{
if (op_ret) {
set_req_state_err(s, op_ret);
}
dump_errno(s);
end_header(s, this, response_content_type, response_content_length);
}
// DELETE op resets resource size to 0
class ZeroDeleteOp : public ZeroOp {
public:
explicit ZeroDeleteOp(ZeroResource* resource) : ZeroOp(resource) {}
const char* name() const override { return "zero_delete"; }
void execute(optional_yield y) override;
};
void ZeroDeleteOp::execute(optional_yield y)
{
auto lock = std::scoped_lock(resource->mutex);
resource->size = 0;
}
// GET op returns a request body of all zeroes
class ZeroGetOp : public ZeroOp {
public:
explicit ZeroGetOp(ZeroResource* resource) : ZeroOp(resource) {}
const char* name() const override { return "zero_get"; }
void execute(optional_yield y) override;
void send_response() override;
};
void ZeroGetOp::execute(optional_yield y)
{
response_content_type = "application/octet-stream";
auto lock = std::scoped_lock(resource->mutex);
response_content_length = resource->size;
}
void ZeroGetOp::send_response()
{
// send the response header
ZeroOp::send_response();
// write zeroes for the entire response body
size_t remaining = static_cast<size_t>(response_content_length);
const size_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
std::vector<char> zeroes;
zeroes.resize(std::min(remaining, chunk_size), '\0');
try {
while (remaining) {
const size_t count = std::min(zeroes.size(), remaining);
const int bytes = dump_body(s, zeroes.data(), count);
remaining -= bytes;
}
} catch (const std::exception& e) {
ldpp_dout(this, 0) << "recv_body failed with " << e.what() << dendl;
op_ret = -EIO;
return;
}
}
// HEAD op returns the current content length
class ZeroHeadOp : public ZeroOp {
public:
explicit ZeroHeadOp(ZeroResource* resource) : ZeroOp(resource) {}
const char* name() const override { return "zero_head"; }
void execute(optional_yield y) override;
};
void ZeroHeadOp::execute(optional_yield y)
{
response_content_type = "application/octet-stream";
auto lock = std::scoped_lock(resource->mutex);
response_content_length = resource->size;
}
// PUT op discards the entire request body then updates the content length
class ZeroPutOp : public ZeroOp {
public:
explicit ZeroPutOp(ZeroResource* resource) : ZeroOp(resource) {}
const char* name() const override { return "zero_put"; }
void execute(optional_yield y) override;
};
void ZeroPutOp::execute(optional_yield y)
{
if (!s->length) {
ldpp_dout(this, 0) << "missing content length" << dendl;
op_ret = -ERR_LENGTH_REQUIRED;
return;
}
const auto content_length = ceph::parse<size_t>(s->length);
if (!content_length) {
ldpp_dout(this, 0) << "failed to parse content length \""
<< s->length << '"' << dendl;
op_ret = -EINVAL;
return;
}
// read and discard the entire request body
size_t remaining = *content_length;
const size_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
std::vector<char> buffer;
buffer.resize(std::min(remaining, chunk_size));
try {
while (remaining) {
const size_t count = std::min(buffer.size(), remaining);
const int bytes = recv_body(s, buffer.data(), count);
remaining -= bytes;
}
} catch (const std::exception& e) {
ldpp_dout(this, 0) << "recv_body failed with " << e.what() << dendl;
op_ret = -EIO;
return;
}
// on success, update the resource size
auto lock = std::scoped_lock(resource->mutex);
resource->size = *content_length;
}
class ZeroHandler : public RGWHandler_REST {
ZeroResource* const resource;
public:
explicit ZeroHandler(ZeroResource* resource) : resource(resource) {}
int init_permissions(RGWOp*, optional_yield) override { return 0; }
int read_permissions(RGWOp*, optional_yield) override { return 0; }
int authorize(const DoutPrefixProvider* dpp, optional_yield y) override { return 0; }
int postauth_init(optional_yield y) override { return 0; }
// op factory functions
RGWOp* op_delete() override { return new ZeroDeleteOp(resource); }
RGWOp* op_get() override { return new ZeroGetOp(resource); }
RGWOp* op_head() override { return new ZeroHeadOp(resource); }
RGWOp* op_put() override { return new ZeroPutOp(resource); }
};
RESTMgr_Zero::RESTMgr_Zero() : resource(std::make_unique<ZeroResource>()) {}
RGWHandler_REST* RESTMgr_Zero::get_handler(sal::Driver* driver, req_state* s,
const auth::StrategyRegistry& auth,
const std::string& prefix)
{
return new ZeroHandler(resource.get());
}
} // namespace rgw
|