]>
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>
7 #include "rgw_asio_client.h"
9 #define dout_context g_ceph_context
10 #define dout_subsys ceph_subsys_rgw
12 using namespace rgw::asio
;
14 ClientIO::ClientIO(tcp::socket
& socket
,
16 beast::flat_buffer
& buffer
)
17 : socket(socket
), parser(parser
), buffer(buffer
), txbuf(*this)
21 ClientIO::~ClientIO() = default;
23 void ClientIO::init_env(CephContext
*cct
)
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();
34 if (field
== beast::http::field::content_length
) {
35 env
.set("CONTENT_LENGTH", value
.to_string());
38 if (field
== beast::http::field::content_type
) {
39 env
.set("CONTENT_TYPE", value
.to_string());
43 static const boost::string_ref HTTP_
{"HTTP_"};
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
) {
51 *dest
= std::toupper(*src
);
56 env
.set(buf
, value
.to_string());
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
));
63 env
.set("REQUEST_METHOD", request
.method_string().to_string());
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
);
73 env
.set("REQUEST_URI", url
.to_string());
74 env
.set("SCRIPT_URI", url
.to_string()); /* FIXME */
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
84 size_t ClientIO::write_data(const char* buf
, size_t len
)
86 boost::system::error_code ec
;
87 auto bytes
= boost::asio::write(socket
, boost::asio::buffer(buf
, len
), ec
);
89 derr
<< "write_data failed: " << ec
.message() << dendl
;
90 throw rgw::io::Exception(ec
.value(), std::system_category());
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. */
98 size_t ClientIO::read_data(char* buf
, size_t max
)
100 auto& message
= parser
.get();
101 auto& body_remaining
= message
.body();
102 body_remaining
.data
= buf
;
103 body_remaining
.size
= max
;
105 dout(30) << this << " read_data for " << max
<< " with "
106 << buffer
.size() << " bytes buffered" << dendl
;
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
) {
116 derr
<< "failed to read body: " << ec
.message() << dendl
;
117 throw rgw::io::Exception(ec
.value(), std::system_category());
120 return max
- body_remaining
.size
;
123 size_t ClientIO::complete_request()
128 void ClientIO::flush()
133 size_t ClientIO::send_status(int status
, const char* status_name
)
135 static constexpr size_t STATUS_BUF_SIZE
= 128;
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
);
141 return txbuf
.sputn(statusbuf
, statuslen
);
144 size_t ClientIO::send_100_continue()
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);
153 static constexpr size_t TIME_BUF_SIZE
= 128;
154 static size_t dump_date_header(char (×tr
)[TIME_BUF_SIZE
])
156 const time_t gtime
= time(nullptr);
158 struct tm
const * const tmp
= gmtime_r(>ime
, &result
);
159 if (tmp
== nullptr) {
162 return strftime(timestr
, sizeof(timestr
),
163 "Date: %a, %d %b %Y %H:%M:%S %Z\r\n", tmp
);
166 size_t ClientIO::complete_header()
170 char timestr
[TIME_BUF_SIZE
];
171 if (dump_date_header(timestr
)) {
172 sent
+= txbuf
.sputn(timestr
, strlen(timestr
));
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);
179 constexpr char CONN_KEEP_CLOSE
[] = "Connection: close\r\n";
180 sent
+= txbuf
.sputn(CONN_KEEP_CLOSE
, sizeof(CONN_KEEP_CLOSE
) - 1);
183 constexpr char HEADER_END
[] = "\r\n";
184 sent
+= txbuf
.sputn(HEADER_END
, sizeof(HEADER_END
) - 1);
190 size_t ClientIO::send_header(const boost::string_ref
& name
,
191 const boost::string_ref
& value
)
193 static constexpr char HEADER_SEP
[] = ": ";
194 static constexpr char HEADER_END
[] = "\r\n";
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);
206 size_t ClientIO::send_content_length(uint64_t len
)
208 static constexpr size_t CONLEN_BUF_SIZE
= 128;
210 char sizebuf
[CONLEN_BUF_SIZE
];
211 const auto sizelen
= snprintf(sizebuf
, sizeof(sizebuf
),
212 "Content-Length: %" PRIu64
"\r\n", len
);
214 return txbuf
.sputn(sizebuf
, sizelen
);