Skip to content

Commit

Permalink
Merge pull request #9844 from rzarzynski/wip-rgw-swift-website
Browse files Browse the repository at this point in the history
rgw: add support for Static Website of Swift API

Reviewed-by: Casey Bodley <cbodley@redhat.com>
  • Loading branch information
cbodley committed Sep 20, 2016
2 parents 7ba8d71 + 80ecae2 commit 34b74e1
Show file tree
Hide file tree
Showing 14 changed files with 847 additions and 120 deletions.
8 changes: 8 additions & 0 deletions src/rgw/rgw_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,14 @@ void url_encode(const string& src, string& dst)
}
}

std::string url_encode(const std::string& src)
{
std::string dst;
url_encode(src, dst);

return dst;
}

string rgw_trim_whitespace(const string& src)
{
if (src.empty()) {
Expand Down
15 changes: 13 additions & 2 deletions src/rgw/rgw_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ using ceph::crypto::MD5;
#define RGW_ATTR_QUOTA_NOBJS RGW_ATTR_META_PREFIX "quota-count"
#define RGW_ATTR_QUOTA_MSIZE RGW_ATTR_META_PREFIX "quota-bytes"

/* Static Web Site of Swift API. */
#define RGW_ATTR_WEB_INDEX RGW_ATTR_META_PREFIX "web-index"
#define RGW_ATTR_WEB_ERROR RGW_ATTR_META_PREFIX "web-error"
#define RGW_ATTR_WEB_LISTINGS RGW_ATTR_META_PREFIX "web-listings"
#define RGW_ATTR_WEB_LIST_CSS RGW_ATTR_META_PREFIX "web-listings-css"
#define RGW_ATTR_SUBDIR_MARKER RGW_ATTR_META_PREFIX "web-directory-type"

#define RGW_ATTR_OLH_PREFIX RGW_ATTR_PREFIX "olh."

#define RGW_ATTR_OLH_INFO RGW_ATTR_OLH_PREFIX "info"
Expand Down Expand Up @@ -1929,8 +1936,12 @@ extern bool verify_object_permission(struct req_state *s, int perm);
/** Convert an input URL into a sane object name
* by converting %-escaped strings into characters, etc*/
extern void rgw_uri_escape_char(char c, string& dst);
extern bool url_decode(const string& src_str, string& dest_str, bool in_query = false);
extern void url_encode(const string& src, string& dst);
extern bool url_decode(const std::string& src_str,
std::string& dest_str,
bool in_query = false);
extern void url_encode(const std::string& src,
string& dst);
extern std::string url_encode(const std::string& src);

/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
extern void calc_hmac_sha1(const char *key, int key_len,
Expand Down
91 changes: 91 additions & 0 deletions src/rgw/rgw_formats.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
*
*/

#include <boost/format.hpp>

#include "common/escape.h"
#include "common/Formatter.h"
#include "rgw/rgw_common.h"
#include "rgw/rgw_formats.h"
#include "rgw/rgw_rest.h"

#define LARGE_SIZE 8192

Expand Down Expand Up @@ -279,3 +282,91 @@ void RGWFormatter_Plain::dump_value_int(const char *name, const char *fmt, ...)
write_data("%s%s", eol, buf);

}


/* An utility class that serves as a mean to access the protected static
* methods of XMLFormatter. */
class HTMLHelper : public XMLFormatter {
public:
static std::string escape(const std::string& unescaped_str) {
return escape_xml_str(unescaped_str.c_str());
}
};

void RGWSwiftWebsiteListingFormatter::generate_header(
const std::string& dir_path,
const std::string& css_path)
{
ss << R"(<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 )"
<< R"(Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">)";

ss << "<html><head><title>Listing of " << HTMLHelper::escape(dir_path)
<< "</title>";

if (! css_path.empty()) {
ss << boost::format(R"(<link rel="stylesheet" type="text/css" href="%s" />)")
% url_encode(css_path);
} else {
ss << R"(<style type="text/css">)"
<< R"(h1 {font-size: 1em; font-weight: bold;})"
<< R"(th {text-align: left; padding: 0px 1em 0px 1em;})"
<< R"(td {padding: 0px 1em 0px 1em;})"
<< R"(a {text-decoration: none;})"
<< R"(</style>)";
}

ss << "</head><body>";

ss << R"(<h1 id="title">Listing of )" << HTMLHelper::escape(dir_path) << "</h1>"
<< R"(<table id="listing">)"
<< R"(<tr id="heading">)"
<< R"(<th class="colname">Name</th>)"
<< R"(<th class="colsize">Size</th>)"
<< R"(<th class="coldate">Date</th>)"
<< R"(</tr>)";

if (! prefix.empty()) {
ss << R"(<tr id="parent" class="item">)"
<< R"(<td class="colname"><a href="../">../</a></td>)"
<< R"(<td class="colsize">&nbsp;</td>)"
<< R"(<td class="coldate">&nbsp;</td>)"
<< R"(</tr>)";
}
}

void RGWSwiftWebsiteListingFormatter::generate_footer()
{
ss << R"(</table></body></html>)";
}

std::string RGWSwiftWebsiteListingFormatter::format_name(
const std::string& item_name) const
{
return item_name.substr(prefix.length());
}

void RGWSwiftWebsiteListingFormatter::dump_object(const RGWObjEnt& objent)
{
const auto name = format_name(objent.key.name);
ss << boost::format(R"(<tr class="item %s">)")
% "default"
<< boost::format(R"(<td class="colname"><a href="%s">%s</a></td>)")
% url_encode(name)
% HTMLHelper::escape(name)
<< boost::format(R"(<td class="colsize">%lld</td>)") % objent.size
<< boost::format(R"(<td class="coldate">%s</td>)")
% dump_time_to_str(objent.mtime)
<< R"(</tr>)";
}

void RGWSwiftWebsiteListingFormatter::dump_subdir(const std::string& name)
{
const auto fname = format_name(name);
ss << R"(<tr class="item subdir">)"
<< boost::format(R"(<td class="colname"><a href="%s">%s</a></td>)")
% url_encode(fname)
% HTMLHelper::escape(fname)
<< R"(<td class="colsize">&nbsp;</td>)"
<< R"(<td class="coldate">&nbsp;</td>)"
<< R"(</tr>)";
}
25 changes: 25 additions & 0 deletions src/rgw/rgw_formats.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <list>
#include <stdint.h>
#include <string>
#include <ostream>

struct plain_stack_entry {
int size;
Expand Down Expand Up @@ -58,6 +59,30 @@ class RGWFormatter_Plain : public Formatter {
bool use_kv;
};


/* This is a presentation layer. No logic inside, please. */
class RGWSwiftWebsiteListingFormatter {
std::ostream& ss;
const std::string prefix;
protected:
std::string format_name(const std::string& item_name) const;
public:
RGWSwiftWebsiteListingFormatter(std::ostream& ss,
std::string prefix)
: ss(ss),
prefix(std::move(prefix)) {
}

/* The supplied css_path can be empty. In such situation a default,
* embedded style sheet will be generated. */
void generate_header(const std::string& dir_path,
const std::string& css_path);
void generate_footer();
void dump_object(const RGWObjEnt& objent);
void dump_subdir(const std::string& name);
};


class RGWFormatterFlusher {
protected:
Formatter *formatter;
Expand Down
65 changes: 61 additions & 4 deletions src/rgw/rgw_op.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include <sstream>

#include <boost/algorithm/string/predicate.hpp>

#include "common/Clock.h"
#include "common/armor.h"
#include "common/mime.h"
Expand Down Expand Up @@ -1406,11 +1408,14 @@ void RGWGetObj::execute()
start = ofs;

/* STAT ops don't need data, and do no i/o */
if (get_type() == RGW_OP_STAT_OBJ)
if (get_type() == RGW_OP_STAT_OBJ) {
return;
}

if (!get_data || ofs > end)
goto done_err;
if (!get_data || ofs > end) {
send_response_data(bl, 0, 0);
return;
}

perfcounter->inc(l_rgw_get_b, end - ofs);

Expand Down Expand Up @@ -2122,6 +2127,46 @@ static int filter_out_quota_info(std::map<std::string, bufferlist>& add_attrs,
}


static void filter_out_website(std::map<std::string, ceph::bufferlist>& add_attrs,
const std::set<std::string>& rmattr_names,
RGWBucketWebsiteConf& ws_conf)
{
std::string lstval;

/* Let's define a mapping between each custom attribute and the memory where
* attribute's value should be stored. The memory location is expressed by
* a non-const reference. */
const auto mapping = {
std::make_pair(RGW_ATTR_WEB_INDEX, std::ref(ws_conf.index_doc_suffix)),
std::make_pair(RGW_ATTR_WEB_ERROR, std::ref(ws_conf.error_doc)),
std::make_pair(RGW_ATTR_WEB_LISTINGS, std::ref(lstval)),
std::make_pair(RGW_ATTR_WEB_LIST_CSS, std::ref(ws_conf.listing_css_doc)),
std::make_pair(RGW_ATTR_SUBDIR_MARKER, std::ref(ws_conf.subdir_marker))
};

for (const auto& kv : mapping) {
const char * const key = kv.first;
auto& target = kv.second;

auto iter = add_attrs.find(key);

if (std::end(add_attrs) != iter) {
/* The "target" is a reference to ws_conf. */
target = iter->second.c_str();
add_attrs.erase(iter);
}

if (rmattr_names.count(key)) {
target = std::string();
}
}

if (! lstval.empty()) {
ws_conf.listing_enabled = boost::algorithm::iequals(lstval, "true");
}
}


void RGWCreateBucket::execute()
{
RGWAccessControlPolicy old_policy(s->cct);
Expand Down Expand Up @@ -2236,9 +2281,13 @@ void RGWCreateBucket::execute()
op_ret = filter_out_quota_info(attrs, rmattr_names, quota_info);
if (op_ret < 0) {
return;
} else {
pquota_info = &quota_info;
}

pquota_info = &quota_info;
/* Web site of Swift API. */
filter_out_website(attrs, rmattr_names, s->bucket_info.website_conf);
s->bucket_info.has_website = !s->bucket_info.website_conf.is_empty();
}

s->bucket.tenant = s->bucket_tenant; /* ignored if bucket exists */
Expand Down Expand Up @@ -2332,6 +2381,10 @@ void RGWCreateBucket::execute()
s->bucket_info.swift_versioning = (! swift_ver_location->empty());
}

/* Web site of Swift API. */
filter_out_website(attrs, rmattr_names, s->bucket_info.website_conf);
s->bucket_info.has_website = !s->bucket_info.website_conf.is_empty();

/* This will also set the quota on the bucket. */
op_ret = rgw_bucket_set_attrs(store, s->bucket_info, attrs,
&s->bucket_info.objv_tracker);
Expand Down Expand Up @@ -3199,6 +3252,10 @@ void RGWPutMetadataBucket::execute()
s->bucket_info.swift_versioning = (! swift_ver_location->empty());
}

/* Web site of Swift API. */
filter_out_website(attrs, rmattr_names, s->bucket_info.website_conf);
s->bucket_info.has_website = !s->bucket_info.website_conf.is_empty();

/* Setting attributes also stores the provided bucket info. Due to this
* fact, the new quota settings can be serialized with the same call. */
op_ret = rgw_bucket_set_attrs(store, s->bucket_info, attrs,
Expand Down

0 comments on commit 34b74e1

Please sign in to comment.