// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include #include #include #include "civetweb/civetweb.h" #include "rgw_civetweb.h" #define dout_subsys ceph_subsys_rgw size_t RGWCivetWeb::write_data(const char *buf, const size_t len) { auto to_sent = len; while (to_sent) { const int ret = mg_write(conn, buf, len); if (ret < 0 || ! ret) { /* According to the documentation of mg_write() it always returns -1 on * error. The details aren't available, so we will just throw EIO. Same * goes to 0 that is associated with writing to a closed connection. */ throw rgw::io::Exception(EIO, std::system_category()); } else { to_sent -= static_cast(ret); } } return len; } RGWCivetWeb::RGWCivetWeb(mg_connection* const conn) : conn(conn), explicit_keepalive(false), explicit_conn_close(false), txbuf(*this) { sockaddr *lsa = mg_get_local_addr(conn); switch(lsa->sa_family) { case AF_INET: port = ntohs(((struct sockaddr_in*)lsa)->sin_port); break; case AF_INET6: port = ntohs(((struct sockaddr_in6*)lsa)->sin6_port); break; default: port = -1; } } size_t RGWCivetWeb::read_data(char *buf, size_t len) { const int ret = mg_read(conn, buf, len); if (ret < 0) { throw rgw::io::Exception(EIO, std::system_category()); } return ret; } void RGWCivetWeb::flush() { txbuf.pubsync(); } size_t RGWCivetWeb::complete_request() { return 0; } void RGWCivetWeb::init_env(CephContext *cct) { env.init(cct); const struct mg_request_info* info = mg_get_request_info(conn); if (! info) { return; } for (int i = 0; i < info->num_headers; i++) { const struct mg_request_info::mg_header* header = &info->http_headers[i]; const boost::string_ref name(header->name); const auto& value = header->value; if (boost::algorithm::iequals(name, "content-length")) { env.set("CONTENT_LENGTH", value); continue; } if (boost::algorithm::iequals(name, "content-type")) { env.set("CONTENT_TYPE", value); continue; } if (boost::algorithm::iequals(name, "connection")) { explicit_keepalive = boost::algorithm::iequals(value, "keep-alive"); explicit_conn_close = boost::algorithm::iequals(value, "close"); } static const boost::string_ref HTTP_{"HTTP_"}; char buf[name.size() + HTTP_.size() + 1]; auto dest = std::copy(std::begin(HTTP_), std::end(HTTP_), buf); for (auto src = name.begin(); src != name.end(); ++src, ++dest) { if (*src == '-') { *dest = '_'; } else { *dest = std::toupper(*src); } } *dest = '\0'; env.set(buf, value); } env.set("REQUEST_METHOD", info->request_method); env.set("REQUEST_URI", info->request_uri); // get the full uri, we anyway handle abs uris later env.set("SCRIPT_URI", info->uri); /* FIXME */ if (info->query_string) { env.set("QUERY_STRING", info->query_string); } if (info->remote_user) { env.set("REMOTE_USER", info->remote_user); } if (port <= 0) lderr(cct) << "init_env: bug: invalid port number" << dendl; char port_buf[16]; snprintf(port_buf, sizeof(port_buf), "%d", port); env.set("SERVER_PORT", port_buf); if (info->is_ssl) { env.set("SERVER_PORT_SECURE", port_buf); } } size_t RGWCivetWeb::send_status(int status, const char *status_name) { mg_set_http_status(conn, status); static constexpr size_t STATUS_BUF_SIZE = 128; char statusbuf[STATUS_BUF_SIZE]; const auto statuslen = snprintf(statusbuf, sizeof(statusbuf), "HTTP/1.1 %d %s\r\n", status, status_name); return txbuf.sputn(statusbuf, statuslen); } size_t RGWCivetWeb::send_100_continue() { const char HTTTP_100_CONTINUE[] = "HTTP/1.1 100 CONTINUE\r\n\r\n"; const size_t sent = txbuf.sputn(HTTTP_100_CONTINUE, sizeof(HTTTP_100_CONTINUE) - 1); flush(); return sent; } size_t RGWCivetWeb::send_header(const boost::string_ref& name, const boost::string_ref& value) { static constexpr char HEADER_SEP[] = ": "; static constexpr char HEADER_END[] = "\r\n"; size_t sent = 0; sent += txbuf.sputn(name.data(), name.length()); sent += txbuf.sputn(HEADER_SEP, sizeof(HEADER_SEP) - 1); sent += txbuf.sputn(value.data(), value.length()); sent += txbuf.sputn(HEADER_END, sizeof(HEADER_END) - 1); return sent; } size_t RGWCivetWeb::dump_date_header() { char timestr[TIME_BUF_SIZE]; const time_t gtime = time(nullptr); struct tm result; struct tm const* const tmp = gmtime_r(>ime, &result); if (nullptr == tmp) { return 0; } if (! strftime(timestr, sizeof(timestr), "Date: %a, %d %b %Y %H:%M:%S %Z\r\n", tmp)) { return 0; } return txbuf.sputn(timestr, strlen(timestr)); } size_t RGWCivetWeb::complete_header() { size_t sent = dump_date_header(); if (explicit_keepalive) { constexpr char CONN_KEEP_ALIVE[] = "Connection: Keep-Alive\r\n"; sent += txbuf.sputn(CONN_KEEP_ALIVE, sizeof(CONN_KEEP_ALIVE) - 1); } else if (explicit_conn_close) { constexpr char CONN_KEEP_CLOSE[] = "Connection: close\r\n"; sent += txbuf.sputn(CONN_KEEP_CLOSE, sizeof(CONN_KEEP_CLOSE) - 1); } static constexpr char HEADER_END[] = "\r\n"; sent += txbuf.sputn(HEADER_END, sizeof(HEADER_END) - 1); flush(); return sent; } size_t RGWCivetWeb::send_content_length(uint64_t len) { static constexpr size_t CONLEN_BUF_SIZE = 128; char sizebuf[CONLEN_BUF_SIZE]; const auto sizelen = snprintf(sizebuf, sizeof(sizebuf), "Content-Length: %" PRIu64 "\r\n", len); return txbuf.sputn(sizebuf, sizelen); }