]>
git.proxmox.com Git - ceph.git/blob - 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
4 #include <boost/algorithm/string/predicate.hpp>
5 #include <boost/asio/write.hpp>
6 #include <beast/http/read.hpp>
8 #include "rgw_asio_client.h"
10 #define dout_context g_ceph_context
11 #define dout_subsys ceph_subsys_rgw
14 #define dout_prefix (*_dout << "asio: ")
16 using namespace rgw::asio
;
18 ClientIO::ClientIO(tcp::socket
& socket
,
20 beast::flat_streambuf
& buffer
)
21 : socket(socket
), parser(parser
), buffer(buffer
), txbuf(*this)
25 ClientIO::~ClientIO() = default;
27 void ClientIO::init_env(CephContext
*cct
)
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();
37 if (boost::algorithm::iequals(name
, "content-length")) {
38 env
.set("CONTENT_LENGTH", value
);
41 if (boost::algorithm::iequals(name
, "content-type")) {
42 env
.set("CONTENT_TYPE", value
);
45 if (boost::algorithm::iequals(name
, "connection")) {
46 conn_keepalive
= boost::algorithm::iequals(value
, "keep-alive");
47 conn_close
= boost::algorithm::iequals(value
, "close");
50 static const boost::string_ref HTTP_
{"HTTP_"};
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
) {
58 *dest
= std::toupper(*src
);
66 env
.set("REQUEST_METHOD", request
.method
);
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
);
74 env
.set("REQUEST_URI", url
);
75 env
.set("QUERY_STRING", query
);
76 env
.set("SCRIPT_URI", url
); /* FIXME */
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
85 size_t ClientIO::write_data(const char* buf
, size_t len
)
87 boost::system::error_code ec
;
88 auto bytes
= boost::asio::write(socket
, boost::asio::buffer(buf
, len
), ec
);
90 derr
<< "write_data failed: " << ec
.message() << dendl
;
91 throw rgw::io::Exception(ec
.value(), std::system_category());
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. */
99 size_t ClientIO::read_data(char* buf
, size_t max
)
101 auto& message
= parser
.get();
102 auto& body_remaining
= message
.body
;
103 body_remaining
= boost::asio::mutable_buffer
{buf
, max
};
105 boost::system::error_code ec
;
107 dout(30) << this << " read_data for " << max
<< " with "
108 << buffer
.size() << " bytes buffered" << dendl
;
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
) {
119 derr
<< "failed to read body: " << ec
.message() << dendl
;
120 throw rgw::io::Exception(ec
.value(), std::system_category());
123 return max
- boost::asio::buffer_size(body_remaining
);
126 size_t ClientIO::complete_request()
131 void ClientIO::flush()
136 size_t ClientIO::send_status(int status
, const char* status_name
)
138 static constexpr size_t STATUS_BUF_SIZE
= 128;
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
);
144 return txbuf
.sputn(statusbuf
, statuslen
);
147 size_t ClientIO::send_100_continue()
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);
156 static constexpr size_t TIME_BUF_SIZE
= 128;
157 static size_t dump_date_header(char (×tr
)[TIME_BUF_SIZE
])
159 const time_t gtime
= time(nullptr);
161 struct tm
const * const tmp
= gmtime_r(>ime
, &result
);
162 if (tmp
== nullptr) {
165 return strftime(timestr
, sizeof(timestr
),
166 "Date: %a, %d %b %Y %H:%M:%S %Z\r\n", tmp
);
169 size_t ClientIO::complete_header()
173 char timestr
[TIME_BUF_SIZE
];
174 if (dump_date_header(timestr
)) {
175 sent
+= txbuf
.sputn(timestr
, strlen(timestr
));
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);
186 constexpr char HEADER_END
[] = "\r\n";
187 sent
+= txbuf
.sputn(HEADER_END
, sizeof(HEADER_END
) - 1);
193 size_t ClientIO::send_header(const boost::string_ref
& name
,
194 const boost::string_ref
& value
)
196 static constexpr char HEADER_SEP
[] = ": ";
197 static constexpr char HEADER_END
[] = "\r\n";
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);
209 size_t ClientIO::send_content_length(uint64_t len
)
211 static constexpr size_t CONLEN_BUF_SIZE
= 128;
213 char sizebuf
[CONLEN_BUF_SIZE
];
214 const auto sizelen
= snprintf(sizebuf
, sizeof(sizebuf
),
215 "Content-Length: %" PRIu64
"\r\n", len
);
217 return txbuf
.sputn(sizebuf
, sizelen
);