return true;
}
+void RGWFormatter::write_data(const char *fmt, ...)
+{
+#define LARGE_ENOUGH_LEN 128
+ int n, size = LARGE_ENOUGH_LEN;
+ char s[size];
+ char *p, *np;
+ bool p_on_stack;
+ va_list ap;
+ int pos;
+
+ p = s;
+ p_on_stack = true;
+
+ while (1) {
+ va_start(ap, fmt);
+ n = vsnprintf(p, size, fmt, ap);
+ va_end(ap);
+
+ if (n > -1 && n < size)
+ goto done;
+ /* Else try again with more space. */
+ if (n > -1) /* glibc 2.1 */
+ size = n+1; /* precisely what is needed */
+ else /* glibc 2.0 */
+ size *= 2; /* twice the old size */
+ if (p_on_stack)
+ np = (char *)malloc(size);
+ else
+ np = (char *)realloc(p, size);
+ if (!np)
+ goto done_free;
+ p = np;
+ p_on_stack = false;
+ }
+done:
+#define LARGE_ENOUGH_BUF 4096
+ if (!buf) {
+ max_len = max(LARGE_ENOUGH_BUF, size);
+ buf = (char *)malloc(max_len);
+ }
+ if (len + size > max_len) {
+ max_len = len + size + LARGE_ENOUGH_BUF;
+ buf = (char *)realloc(buf, max_len);
+ }
+ if (!buf) {
+ RGW_LOG(0) << "RGWFormatter::write_data: failed allocating " << max_len << " bytes" << std::endl;
+ goto done_free;
+ }
+ pos = len;
+ if (len)
+ pos--; // squash null termination
+ strcpy(buf + pos, p);
+ len = pos + strlen(p) + 1;
+ RGW_LOG(0) << "RGWFormatter::write_data: len= " << len << " bytes" << std::endl;
+done_free:
+ if (!p_on_stack)
+ free(p);
+}
+
+void RGWFormatter::flush()
+{
+ if (!buf)
+ return;
+
+ RGW_LOG(0) << "flush(): buf='" << buf << "' strlen(buf)=" << strlen(buf) << std::endl;
+ FCGX_PutStr(buf, len - 1, s->fcgx->out);
+ free(buf);
+ buf = NULL;
+ len = 0;
+ max_len = 0;
+}
class RGWFormatter {
protected:
struct req_state *s;
+ char *buf;
+ int len;
+ int max_len;
virtual void formatter_init() = 0;
public:
- RGWFormatter() {}
+ RGWFormatter() : buf(NULL), len(0), max_len(0) {}
virtual ~RGWFormatter() {}
void init(struct req_state *_s) {
s = _s;
+ if (buf)
+ free(buf);
+ buf = NULL;
+ len = 0;
+ max_len = 0;
formatter_init();
}
-
+ void write_data(const char *fmt, ...);
+ virtual void flush();
+ virtual int get_len() { return (len ? len - 1 : 0); } // don't include null termination in length
virtual void open_array_section(const char *name) = 0;
virtual void open_obj_section(const char *name) = 0;
virtual void close_section(const char *name) = 0;
va_end(ap);
if (n >= LARGE_SIZE)
return;
- CGI_PRINTF(s, "(%d %d) %s\n", (int)entry.is_array, entry.size, buf);
+ write_data("%s\n", buf);
}
void RGWFormatter_Plain::dump_value_str(const char *name, const char *fmt, ...)
va_end(ap);
if (n >= LARGE_SIZE)
return;
- CGI_PRINTF(s, "%s\n", buf);
+ write_data("%s\n", buf);
}
/* XML */
void RGWFormatter_XML::open_section(const char *name)
{
- CGI_PRINTF(s, "<%s>", name);
+ write_data("<%s>", name);
++indent;
}
void RGWFormatter_XML::close_section(const char *name)
{
--indent;
- CGI_PRINTF(s, "</%s>", name);
+ write_data("</%s>", name);
}
void RGWFormatter_XML::dump_value_int(const char *name, const char *fmt, ...)
va_end(ap);
if (n >= LARGE_SIZE)
return;
- CGI_PRINTF(s, "<%s>%s</%s>", name, buf, name);
+ write_data("<%s>%s</%s>", name, buf, name);
}
void RGWFormatter_XML::dump_value_str(const char *name, const char *fmt, ...)
va_end(ap);
if (n >= LARGE_SIZE)
return;
- CGI_PRINTF(s, "<%s>%s</%s>", name, buf, name);
+ write_data("<%s>%s</%s>", name, buf, name);
}
/* JSON */
{
if (stack.size()) {
struct json_stack_entry& entry = stack.back();
- CGI_PRINTF(s, "%s\n", (entry.size ? "," : ""));
+ write_data("%s\n", (entry.size ? "," : ""));
entry.size++;
}
- CGI_PRINTF(s, "%c", (is_array ? '[' : '{'));
+ write_data("%c", (is_array ? '[' : '{'));
struct json_stack_entry new_entry;
new_entry.is_array = is_array;
{
struct json_stack_entry& entry = stack.back();
- CGI_PRINTF(s, "%c", (entry.is_array ? ']' : '}'));
+ write_data("%c", (entry.is_array ? ']' : '}'));
stack.pop_back();
}
va_end(ap);
if (n >= LARGE_SIZE)
return;
- CGI_PRINTF(s, "%s\"%s\":%s", (entry.size ? ", " : ""), name, buf);
+ write_data("%s\"%s\":%s", (entry.size ? ", " : ""), name, buf);
entry.size++;
}
va_end(ap);
if (n >= LARGE_SIZE)
return;
- CGI_PRINTF(s, "%s\"%s\":\"%s\"", (entry.size ? ", " : ""), name, buf);
+ write_data("%s\"%s\":\"%s\"", (entry.size ? ", " : ""), name, buf);
entry.size++;
}
static void dump_entry(struct req_state *s, const char *val)
{
- CGI_PRINTF(s, "<?%s?>", val);
+ s->formatter->write_data("<?%s?>", val);
}
{
dump_errno(s, err);
end_header(s);
+ s->formatter->flush();
}
void dump_continue(struct req_state *s)
void RGWListBuckets_REST_OS::send_response()
{
dump_errno(s, ret);
- end_header(s);
+
dump_start(s);
+ if (ret < 0) {
+ end_header(s);
+ return;
+ }
+
s->formatter->open_array_section("account");
// dump_owner(s, s->user.user_id, s->user.display_name);
s->formatter->close_section("container");
}
s->formatter->close_section("account");
+
+ dump_content_length(s, s->formatter->get_len());
+ end_header(s);
+ s->formatter->flush();
}
void RGWListBucket_REST_OS::send_response()
{
dump_errno(s, (ret < 0 ? ret : 0));
- end_header(s);
dump_start(s);
- if (ret < 0)
+ if (ret < 0) {
+ end_header(s);
return;
+ }
vector<RGWObjEnt>::iterator iter = objs.begin();
}
#endif
s->formatter->close_section("container");
+
+ end_header(s);
+ s->formatter->flush();
}
static void dump_container_metadata(struct req_state *s, RGWBucketEnt& bucket)
end_header(s);
dump_start(s);
+ s->formatter->flush();
}
void RGWCreateBucket_REST_OS::send_response()
{
dump_errno(s, ret);
end_header(s);
+ s->formatter->flush();
}
void RGWDeleteBucket_REST_OS::send_response()
dump_errno(s, r);
end_header(s);
+ s->formatter->flush();
}
void RGWPutObj_REST_OS::send_response()
dump_etag(s, etag.c_str());
dump_errno(s, ret, &err);
end_header(s);
+ s->formatter->flush();
}
void RGWDeleteObj_REST_OS::send_response()
dump_errno(s, r);
end_header(s);
+ s->formatter->flush();
}
int RGWGetObj_REST_OS::send_response(void *handle)
if (get_data && !orig_ret) {
FCGX_PutStr(data, len, s->fcgx->out);
}
+ s->formatter->flush();
return 0;
}
if (!content_type)
content_type = "binary/octet-stream";
end_header(s, content_type);
-
+ s->formatter->flush();
+
sent_header = true;
send_data:
void RGWListBuckets_REST_S3::send_response()
{
dump_errno(s, ret);
- end_header(s, "application/xml");
dump_start(s);
list_all_buckets_start(s);
}
s->formatter->close_section("Buckets");
list_all_buckets_end(s);
+ dump_content_length(s, s->formatter->get_len());
+ end_header(s, "application/xml");
+ s->formatter->flush();
}
void RGWListBucket_REST_S3::send_response()
}
}
s->formatter->close_section("ListBucketResult");
+ s->formatter->flush();
}
void RGWCreateBucket_REST_S3::send_response()
{
dump_errno(s, ret);
end_header(s);
+ s->formatter->flush();
}
void RGWDeleteBucket_REST_S3::send_response()
dump_errno(s, r);
end_header(s);
+ s->formatter->flush();
}
void RGWPutObj_REST_S3::send_response()
dump_etag(s, etag.c_str());
dump_errno(s, ret, &err);
end_header(s);
+ s->formatter->flush();
}
void RGWDeleteObj_REST_S3::send_response()
dump_errno(s, r);
end_header(s);
+ s->formatter->flush();
}
void RGWCopyObj_REST_S3::send_response()
}
s->formatter->close_section("CopyObjectResult");
}
+ s->formatter->flush();
}
void RGWGetACLs_REST_S3::send_response()
dump_errno(s, ret);
end_header(s, "application/xml");
dump_start(s);
+ s->formatter->flush();
FCGX_PutStr(acls.c_str(), acls.size(), s->fcgx->out);
}
dump_errno(s, ret);
end_header(s, "application/xml");
dump_start(s);
+ s->formatter->flush();
}
RGWOp *RGWHandler_REST_S3::get_retrieve_obj_op(struct req_state *s, bool get_data)