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