]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_asio_client.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / rgw / rgw_asio_client.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <boost/algorithm/string/predicate.hpp>
5 #include <boost/asio/write.hpp>
6 #include <beast/http/read.hpp>
7
8 #include "rgw_asio_client.h"
9
10 #define dout_context g_ceph_context
11 #define dout_subsys ceph_subsys_rgw
12
13 #undef dout_prefix
14 #define dout_prefix (*_dout << "asio: ")
15
16 using namespace rgw::asio;
17
18 ClientIO::ClientIO(tcp::socket& socket,
19 parser_type& parser,
20 beast::flat_streambuf& buffer)
21 : socket(socket), parser(parser), buffer(buffer), txbuf(*this)
22 {
23 }
24
25 ClientIO::~ClientIO() = default;
26
27 void ClientIO::init_env(CephContext *cct)
28 {
29 env.init(cct);
30
31 const auto& request = parser.get();
32 const auto& headers = request.fields;
33 for (auto header = headers.begin(); header != headers.end(); ++header) {
34 const auto& name = header->name();
35 const auto& value = header->value();
36
37 if (boost::algorithm::iequals(name, "content-length")) {
38 env.set("CONTENT_LENGTH", value);
39 continue;
40 }
41 if (boost::algorithm::iequals(name, "content-type")) {
42 env.set("CONTENT_TYPE", value);
43 continue;
44 }
45 if (boost::algorithm::iequals(name, "connection")) {
46 conn_keepalive = boost::algorithm::iequals(value, "keep-alive");
47 conn_close = boost::algorithm::iequals(value, "close");
48 }
49
50 static const boost::string_ref HTTP_{"HTTP_"};
51
52 char buf[name.size() + HTTP_.size() + 1];
53 auto dest = std::copy(std::begin(HTTP_), std::end(HTTP_), buf);
54 for (auto src = name.begin(); src != name.end(); ++src, ++dest) {
55 if (*src == '-') {
56 *dest = '_';
57 } else {
58 *dest = std::toupper(*src);
59 }
60 }
61 *dest = '\0';
62
63 env.set(buf, value);
64 }
65
66 env.set("REQUEST_METHOD", request.method);
67
68 // split uri from query
69 auto url = boost::string_ref{request.url};
70 auto pos = url.find('?');
71 auto query = url.substr(pos + 1);
72 url = url.substr(0, pos);
73
74 env.set("REQUEST_URI", url);
75 env.set("QUERY_STRING", query);
76 env.set("SCRIPT_URI", url); /* FIXME */
77
78 char port_buf[16];
79 snprintf(port_buf, sizeof(port_buf), "%d", socket.local_endpoint().port());
80 env.set("SERVER_PORT", port_buf);
81 // TODO: set SERVER_PORT_SECURE if using ssl
82 // TODO: set REMOTE_USER if authenticated
83 }
84
85 size_t ClientIO::write_data(const char* buf, size_t len)
86 {
87 boost::system::error_code ec;
88 auto bytes = boost::asio::write(socket, boost::asio::buffer(buf, len), ec);
89 if (ec) {
90 derr << "write_data failed: " << ec.message() << dendl;
91 throw rgw::io::Exception(ec.value(), std::system_category());
92 }
93 /* According to the documentation of boost::asio::write if there is
94 * no error (signalised by ec), then bytes == len. We don't need to
95 * take care of partial writes in such situation. */
96 return bytes;
97 }
98
99 size_t ClientIO::read_data(char* buf, size_t max)
100 {
101 auto& message = parser.get();
102 auto& body_remaining = message.body;
103 body_remaining = boost::asio::mutable_buffer{buf, max};
104
105 boost::system::error_code ec;
106
107 dout(30) << this << " read_data for " << max << " with "
108 << buffer.size() << " bytes buffered" << dendl;
109
110 while (boost::asio::buffer_size(body_remaining) && !parser.is_complete()) {
111 auto bytes = beast::http::read_some(socket, buffer, parser, ec);
112 buffer.consume(bytes);
113 if (ec == boost::asio::error::connection_reset ||
114 ec == boost::asio::error::eof ||
115 ec == beast::http::error::partial_message) {
116 break;
117 }
118 if (ec) {
119 derr << "failed to read body: " << ec.message() << dendl;
120 throw rgw::io::Exception(ec.value(), std::system_category());
121 }
122 }
123 return max - boost::asio::buffer_size(body_remaining);
124 }
125
126 size_t ClientIO::complete_request()
127 {
128 return 0;
129 }
130
131 void ClientIO::flush()
132 {
133 txbuf.pubsync();
134 }
135
136 size_t ClientIO::send_status(int status, const char* status_name)
137 {
138 static constexpr size_t STATUS_BUF_SIZE = 128;
139
140 char statusbuf[STATUS_BUF_SIZE];
141 const auto statuslen = snprintf(statusbuf, sizeof(statusbuf),
142 "HTTP/1.1 %d %s\r\n", status, status_name);
143
144 return txbuf.sputn(statusbuf, statuslen);
145 }
146
147 size_t ClientIO::send_100_continue()
148 {
149 const char HTTTP_100_CONTINUE[] = "HTTP/1.1 100 CONTINUE\r\n\r\n";
150 const size_t sent = txbuf.sputn(HTTTP_100_CONTINUE,
151 sizeof(HTTTP_100_CONTINUE) - 1);
152 flush();
153 return sent;
154 }
155
156 static constexpr size_t TIME_BUF_SIZE = 128;
157 static size_t dump_date_header(char (&timestr)[TIME_BUF_SIZE])
158 {
159 const time_t gtime = time(nullptr);
160 struct tm result;
161 struct tm const * const tmp = gmtime_r(&gtime, &result);
162 if (tmp == nullptr) {
163 return 0;
164 }
165 return strftime(timestr, sizeof(timestr),
166 "Date: %a, %d %b %Y %H:%M:%S %Z\r\n", tmp);
167 }
168
169 size_t ClientIO::complete_header()
170 {
171 size_t sent = 0;
172
173 char timestr[TIME_BUF_SIZE];
174 if (dump_date_header(timestr)) {
175 sent += txbuf.sputn(timestr, strlen(timestr));
176 }
177
178 if (conn_keepalive) {
179 constexpr char CONN_KEEP_ALIVE[] = "Connection: Keep-Alive\r\n";
180 sent += txbuf.sputn(CONN_KEEP_ALIVE, sizeof(CONN_KEEP_ALIVE) - 1);
181 } else if (conn_close) {
182 constexpr char CONN_KEEP_CLOSE[] = "Connection: close\r\n";
183 sent += txbuf.sputn(CONN_KEEP_CLOSE, sizeof(CONN_KEEP_CLOSE) - 1);
184 }
185
186 constexpr char HEADER_END[] = "\r\n";
187 sent += txbuf.sputn(HEADER_END, sizeof(HEADER_END) - 1);
188
189 flush();
190 return sent;
191 }
192
193 size_t ClientIO::send_header(const boost::string_ref& name,
194 const boost::string_ref& value)
195 {
196 static constexpr char HEADER_SEP[] = ": ";
197 static constexpr char HEADER_END[] = "\r\n";
198
199 size_t sent = 0;
200
201 sent += txbuf.sputn(name.data(), name.length());
202 sent += txbuf.sputn(HEADER_SEP, sizeof(HEADER_SEP) - 1);
203 sent += txbuf.sputn(value.data(), value.length());
204 sent += txbuf.sputn(HEADER_END, sizeof(HEADER_END) - 1);
205
206 return sent;
207 }
208
209 size_t ClientIO::send_content_length(uint64_t len)
210 {
211 static constexpr size_t CONLEN_BUF_SIZE = 128;
212
213 char sizebuf[CONLEN_BUF_SIZE];
214 const auto sizelen = snprintf(sizebuf, sizeof(sizebuf),
215 "Content-Length: %" PRIu64 "\r\n", len);
216
217 return txbuf.sputn(sizebuf, sizelen);
218 }