OPTION(rgw_objexp_chunk_size, OPT_U32, 100) // maximum number of entries in a single operation when processing objexp data
OPTION(rgw_enable_static_website, OPT_BOOL, false) // enable static website feature
+OPTION(rgw_log_http_headers, OPT_STR, "" ) // list of HTTP headers to log when seen, ignores case (e.g., http_x_forwarded_for
OPTION(rgw_num_async_rados_threads, OPT_INT, 32) // num of threads to use for async rados operations
OPTION(rgw_md_notify_interval_msec, OPT_INT, 200) // metadata changes notification interval to followers
return r;
}
+static inline std::vector<std::string> get_str_vec(const std::string& str)
+{
+ std::vector<std::string> str_vec;
+ const char *delims = ";,= \t";
+ get_str_vec(str, delims, str_vec);
+ return std::move(str_vec);
+}
+
#endif
<< e.what() << dendl;
}
if (should_log) {
- rgw_log_op(store, s, (op ? op->name() : "unknown"), olog);
+ rgw_log_op(store, nullptr /* !rest */, s,
+ (op ? op->name() : "unknown"), olog);
}
int http_ret = s->err.http_ret;
#include <string>
#include <map>
+#include "include/assert.h"
+
#define dout_subsys ceph_subsys_rgw
void RGWEnv::init(CephContext *cct)
#include "rgw_acl.h"
#include "rgw_rados.h"
#include "rgw_client_io.h"
+#include "rgw_rest.h"
#define dout_subsys ceph_subsys_rgw
formatter->dump_int("total_time", total_time);
formatter->dump_string("user_agent", entry.user_agent);
formatter->dump_string("referrer", entry.referrer);
+ if (entry.x_headers.size() > 0) {
+ formatter->open_array_section("http_x_headers");
+ for (const auto& iter: entry.x_headers) {
+ formatter->open_object_section(iter.first.c_str());
+ formatter->dump_string(iter.first.c_str(), iter.second);
+ formatter->close_section();
+ }
+ formatter->close_section();
+ }
formatter->close_section();
}
append_output(bl);
}
-int rgw_log_op(RGWRados *store, struct req_state *s, const string& op_name, OpsLogSocket *olog)
+int rgw_log_op(RGWRados *store, RGWREST* const rest, struct req_state *s,
+ const string& op_name, OpsLogSocket *olog)
{
struct rgw_log_entry entry;
string bucket_id;
set_param_str(s, "REQUEST_URI", entry.uri);
set_param_str(s, "REQUEST_METHOD", entry.op);
+ /* custom header logging */
+ if (rest) {
+ if (rest->log_x_headers()) {
+ for (const auto& iter : s->info.env->get_map()) {
+ if (rest->log_x_header(iter.first)) {
+ entry.x_headers.insert(
+ rgw_log_entry::headers_map::value_type(iter.first, iter.second));
+ }
+ }
+ }
+ }
+
entry.user = s->user->user_id.to_str();
if (s->object_acl)
entry.object_owner = s->object_acl->get_owner().get_id();
#ifndef CEPH_RGW_LOG_H
#define CEPH_RGW_LOG_H
+#include <boost/container/flat_set.hpp>
#include "rgw_common.h"
#include "include/utime.h"
#include "common/Formatter.h"
class RGWRados;
struct rgw_log_entry {
+
+ using headers_map = std::map<std::string, std::string>;
+
rgw_user object_owner;
rgw_user bucket_owner;
string bucket;
string user_agent;
string referrer;
string bucket_id;
+ headers_map x_headers;
void encode(bufferlist &bl) const {
- ENCODE_START(8, 5, bl);
+ ENCODE_START(9, 5, bl);
::encode(object_owner.id, bl);
::encode(bucket_owner.id, bl);
::encode(bucket, bl);
::encode(obj, bl);
::encode(object_owner, bl);
::encode(bucket_owner, bl);
+ ::encode(x_headers, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator &p) {
::decode(object_owner, p);
::decode(bucket_owner, p);
}
+ if (struct_v >= 9) {
+ ::decode(x_headers, p);
+ }
DECODE_FINISH(p);
}
void dump(Formatter *f) const;
void log(struct rgw_log_entry& entry);
};
-int rgw_log_op(RGWRados *store, struct req_state *s, const string& op_name, OpsLogSocket *olog);
+class RGWREST;
+
+int rgw_log_op(RGWRados *store, RGWREST* const rest, struct req_state *s,
+ const string& op_name, OpsLogSocket *olog);
void rgw_log_usage_init(CephContext *cct, RGWRados *store);
void rgw_log_usage_finalize();
-void rgw_format_ops_log_entry(struct rgw_log_entry& entry, Formatter *formatter);
+void rgw_format_ops_log_entry(struct rgw_log_entry& entry,
+ Formatter *formatter);
-#endif
+#endif /* CEPH_RGW_LOG_H */
rest.register_resource(g_conf->rgw_admin_entry, admin_resource);
}
+ /* Header custom behavior */
+ rest.register_x_headers(g_conf->rgw_log_http_headers);
+
OpsLogSocket *olog = NULL;
if (!g_conf->rgw_ops_log_socket_path.empty()) {
#include "rgw_cr_rados.h"
#include "rgw_boost_asio_yield.h"
+#include "include/assert.h"
+
#define dout_subsys ceph_subsys_rgw
void LogStatusDump::dump(Formatter *f) const {
#include "rgw_period_history.h"
#include "rgw_rados.h"
+#include "include/assert.h"
+
#define dout_subsys ceph_subsys_rgw
#undef dout_prefix
abort_early(s, NULL, -ERR_METHOD_NOT_ALLOWED, handler);
goto done;
}
+
req->op = op;
dout(10) << "op=" << typeid(*op).name() << dendl;
}
if (should_log) {
- rgw_log_op(store, s, (op ? op->name() : "unknown"), olog);
+ rgw_log_op(store, rest, s, (op ? op->name() : "unknown"), olog);
}
int http_ret = s->err.http_ret;
#include <errno.h>
#include <limits.h>
+#include <boost/algorithm/string.hpp>
#include "common/Formatter.h"
#include "common/HTMLFormatter.h"
#include "common/utf8.h"
return this;
}
+void RGWREST::register_x_headers(const string& s_headers)
+{
+ std::vector<std::string> hdrs = get_str_vec(s_headers);
+ for (auto& hdr : hdrs) {
+ boost::algorithm::to_upper(hdr); // XXX
+ (void) x_headers.insert(hdr);
+ }
+}
+
RGWRESTMgr::~RGWRESTMgr()
{
map<string, RGWRESTMgr *>::iterator iter;
#define TIME_BUF_SIZE 128
#include <boost/utility/string_ref.hpp>
-
+#include <boost/container/flat_set.hpp>
+#include "common/sstring.hh"
#include "common/ceph_json.h"
#include "include/assert.h" /* needed because of common/ceph_json.h */
#include "rgw_op.h"
class RGWHandler_REST : public RGWHandler {
protected:
+
virtual bool is_obj_update_op() { return false; }
virtual RGWOp *op_get() { return NULL; }
virtual RGWOp *op_put() { return NULL; }
class RGWRESTMgr {
bool should_log;
+
protected:
std::map<std::string, RGWRESTMgr*> resource_mgrs;
std::multimap<size_t, std::string> resources_by_size;
class RGWRestfulIO;
class RGWREST {
+ using x_header = basic_sstring<char, uint16_t, 32>;
+ boost::container::flat_set<x_header> x_headers;
RGWRESTMgr mgr;
static int preprocess(struct req_state *s, rgw::io::BasicClient* rio);
RGWLibIO *io, RGWRESTMgr **pmgr,
int *init_error);
#endif
+
void put_handler(RGWHandler_REST *handler) {
mgr.put_handler(handler);
}
mgr.register_resource(resource, m);
}
+
void register_default_mgr(RGWRESTMgr *m) {
mgr.register_default_mgr(m);
}
+
+ void register_x_headers(const std::string& headers);
+
+ bool log_x_headers(void) {
+ return (x_headers.size() > 0);
+ }
+
+ bool log_x_header(const std::string& header) {
+ return (x_headers.find(header) != x_headers.end());
+ }
};
static constexpr int64_t NO_CONTENT_LENGTH = -1;