From 7bc6e887d8146a85f8709decaa1ee49d28a2bc97 Mon Sep 17 00:00:00 2001
From: Wido den Hollander <wido@widodh.nl>
Date: Fri, 23 Jul 2010 15:39:49 +0200
Subject: [PATCH] We shoud always return the Accept-Ranges header, with a GET or HEAD request, even when the client is not requesting for partial content.

This is to let the client know we accept requests for partial content.

On HEAD requests we should also return the Content-Length header.

And we should also return the Last-Modified header with GET and HEAD requests, this way browsers and proxy's can cache content.
---
 src/rgw/rgw_access.h |    1 +
 src/rgw/rgw_fs.cc    |    4 +-
 src/rgw/rgw_fs.h     |    1 +
 src/rgw/rgw_op.cc    |    2 +-
 src/rgw/rgw_op.h     |    1 +
 src/rgw/rgw_rados.cc |    5 +-
 src/rgw/rgw_rados.h  |    1 +
 src/rgw/rgw_rest.cc  |   28 +++-
 src/rgw/rgw_rest.h   |    1 +
 src/rgw/rgw_user.cc  |    5 +-
 10 files changed, 37 insertions(+), 346 deletions(-)

diff --git a/src/rgw/rgw_access.h b/src/rgw/rgw_access.h
index ad8a895..01a15f1 100644
--- a/src/rgw/rgw_access.h
+++ b/src/rgw/rgw_access.h
@@ -122,6 +122,7 @@ public:
             map<string, bufferlist> *attrs,
             const time_t *mod_ptr,
             const time_t *unmod_ptr,
+            time_t *lastmod,
             const char *if_match,
             const char *if_nomatch,
             size_t *total_size,
diff --git a/src/rgw/rgw_fs.cc b/src/rgw/rgw_fs.cc
index 044e761..1125b49 100644
--- a/src/rgw/rgw_fs.cc
+++ b/src/rgw/rgw_fs.cc
@@ -274,9 +274,10 @@ int RGWFS::copy_obj(std::string& id, std::string& dest_bucket, std::string& dest
   void *handle;
   off_t ofs = 0, end = -1;
   size_t total_len;
+  time_t lastmod;
 
   map<string, bufferlist> attrset;
-  ret = prepare_get_obj(src_bucket, src_obj, 0, &end, &attrset, mod_ptr, unmod_ptr,
+  ret = prepare_get_obj(src_bucket, src_obj, 0, &end, &attrset, mod_ptr, unmod_ptr, &lastmod,
                         if_match, if_nomatch, &total_len, &handle, err);
   if (ret < 0)
     return ret;
@@ -421,6 +422,7 @@ int RGWFS::prepare_get_obj(std::string& bucket, std::string& obj,
             map<string, bufferlist> *attrs,
             const time_t *mod_ptr,
             const time_t *unmod_ptr,
+            time_t *lastmod,
             const char *if_match,
             const char *if_nomatch,
             size_t *total_size,
diff --git a/src/rgw/rgw_fs.h b/src/rgw/rgw_fs.h
index d35ab13..5010bb2 100644
--- a/src/rgw/rgw_fs.h
+++ b/src/rgw/rgw_fs.h
@@ -45,6 +45,7 @@ public:
 	    map<std::string, bufferlist> *attrs,
             const time_t *mod_ptr,
             const time_t *unmod_ptr,
+            time_t *lastmod,
             const char *if_match,
             const char *if_nomatch,
             size_t *size,
diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc
index 54a2c94..c8e4fbe 100644
--- a/src/rgw/rgw_op.cc
+++ b/src/rgw/rgw_op.cc
@@ -142,7 +142,7 @@ void RGWGetObj::execute()
   init_common();
 
   ret = rgwstore->prepare_get_obj(s->bucket_str, s->object_str, ofs, &end, &attrs, mod_ptr,
-                                  unmod_ptr, if_match, if_nomatch, &total_len, &handle, &err);
+                                  unmod_ptr, &lastmod, if_match, if_nomatch, &total_len, &handle, &err);
   if (ret < 0)
     goto done;
 
diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h
index 054d6ea..2b74569 100644
--- a/src/rgw/rgw_op.h
+++ b/src/rgw/rgw_op.h
@@ -54,6 +54,7 @@ protected:
   size_t total_len;
   off_t end;
   time_t mod_time;
+  time_t lastmod;
   time_t unmod_time;
   time_t *mod_ptr;
   time_t *unmod_ptr;
diff --git a/src/rgw/rgw_rados.cc b/src/rgw/rgw_rados.cc
index 257e311..cda70ab 100644
--- a/src/rgw/rgw_rados.cc
+++ b/src/rgw/rgw_rados.cc
@@ -344,6 +344,7 @@ int RGWRados::copy_obj(std::string& id, std::string& dest_bucket, std::string& d
   char *data;
   off_t ofs = 0, end = -1;
   size_t total_len;
+  time_t lastmod;
   map<string, bufferlist>::iterator iter;
 
   cerr << "copy " << src_bucket << ":" << src_obj << " => " << dest_bucket << ":" << dest_obj << std::endl;
@@ -352,7 +353,7 @@ int RGWRados::copy_obj(std::string& id, std::string& dest_bucket, std::string& d
 
   map<string, bufferlist> attrset;
   ret = prepare_get_obj(src_bucket, src_obj, ofs, &end, &attrset,
-                mod_ptr, unmod_ptr, if_match, if_nomatch, &total_len, &handle, err);
+                mod_ptr, unmod_ptr, &lastmod, if_match, if_nomatch, &total_len, &handle, err);
 
   if (ret < 0)
     return ret;
@@ -511,6 +512,7 @@ int RGWRados::prepare_get_obj(std::string& bucket, std::string& oid,
             map<string, bufferlist> *attrs,
             const time_t *mod_ptr,
             const time_t *unmod_ptr,
+            time_t *lastmod,
             const char *if_match,
             const char *if_nomatch,
             size_t *total_size,
@@ -595,6 +597,7 @@ int RGWRados::prepare_get_obj(std::string& bucket, std::string& oid,
     *end = size - 1;
 
   *total_size = (ofs <= *end ? *end + 1 - ofs : 0);
+  *lastmod = mtime;
 
   return 0;
 
diff --git a/src/rgw/rgw_rados.h b/src/rgw/rgw_rados.h
index f8bd34b..22d331c 100644
--- a/src/rgw/rgw_rados.h
+++ b/src/rgw/rgw_rados.h
@@ -73,6 +73,7 @@ public:
             map<string, bufferlist> *attrs,
             const time_t *mod_ptr,
             const time_t *unmod_ptr,
+            time_t *lastmod,
             const char *if_match,
             const char *if_nomatch,
             size_t *total_size,
diff --git a/src/rgw/rgw_rest.cc b/src/rgw/rgw_rest.cc
index 78e782a..8bf59f7 100644
--- a/src/rgw/rgw_rest.cc
+++ b/src/rgw/rgw_rest.cc
@@ -85,6 +85,7 @@ void close_section(struct req_state *s, const char *name)
 static void dump_content_length(struct req_state *s, int len)
 {
   CGI_PRINTF(s->fcgx->out, "Content-Length: %d\n", len);
+  CGI_PRINTF(s->fcgx->out, "Accept-Ranges: %s\n", "bytes");
 }
 
 static void dump_etag(struct req_state *s, const char *etag)
@@ -92,6 +93,19 @@ static void dump_etag(struct req_state *s, const char *etag)
   CGI_PRINTF(s->fcgx->out,"ETag: \"%s\"\n", etag);
 }
 
+static void dump_last_modified(struct req_state *s, time_t t) {
+
+  char timestr[TIME_BUF_SIZE];
+  struct tm *tmp = localtime(&t);
+  if (tmp == NULL)
+    return;
+
+  if (strftime(timestr, sizeof(timestr), "%a, %d %b %Y %H:%M:%S %Z", tmp) == 0)
+    return;
+
+  CGI_PRINTF(s->fcgx->out, "Last-Modified: %s\n", timestr);
+}
+
 void dump_value(struct req_state *s, const char *name, const char *fmt, ...)
 {
 #define LARGE_SIZE 8192
@@ -116,7 +130,6 @@ static void dump_entry(struct req_state *s, const char *val)
 
 void dump_time(struct req_state *s, const char *name, time_t *t)
 {
-#define TIME_BUF_SIZE 128
   char buf[TIME_BUF_SIZE];
   struct tm *tmp = localtime(t);
   if (tmp == NULL)
@@ -187,7 +200,6 @@ void abort_early(struct req_state *s, int err)
 
 void dump_range(struct req_state *s, off_t ofs, off_t end)
 {
-    CGI_PRINTF(s->fcgx->out,"Accept-Ranges: bytes\n", "");
     CGI_PRINTF(s->fcgx->out,"Content-Range: bytes %d-%d/%d\n", (int)ofs, (int)end, (int)end + 1);
 }
 
@@ -209,11 +221,12 @@ int RGWGetObj_REST::send_response(void *handle)
   if (sent_header)
     goto send_data;
 
-  if (get_data && !ret) {
-    if (range_str)
-      dump_range(s, ofs, end);
-    dump_content_length(s, total_len);
-  }
+  if (range_str)
+    dump_range(s, ofs, end);
+
+  dump_content_length(s, total_len);
+  dump_last_modified(s, lastmod);
+
   if (!ret) {
     map<string, bufferlist>::iterator iter = attrs.find(RGW_ATTR_ETAG);
     if (iter != attrs.end()) {
@@ -223,6 +236,7 @@ int RGWGetObj_REST::send_response(void *handle)
         dump_etag(s, etag);
       }
     }
+
     for (iter = attrs.begin(); iter != attrs.end(); ++iter) {
        const char *name = iter->first.c_str();
        if (strncmp(name, RGW_ATTR_META_PREFIX, sizeof(RGW_ATTR_META_PREFIX)-1) == 0) {
diff --git a/src/rgw/rgw_rest.h b/src/rgw/rgw_rest.h
index dda33d4..65495d3 100644
--- a/src/rgw/rgw_rest.h
+++ b/src/rgw/rgw_rest.h
@@ -1,5 +1,6 @@
 #ifndef CEPH_RGW_REST_H
 #define CEPH_RGW_REST_H
+#define TIME_BUF_SIZE 128
 
 #include "rgw_op.h"
 
diff --git a/src/rgw/rgw_user.cc b/src/rgw/rgw_user.cc
index 2a1c440..9749ebd 100644
--- a/src/rgw/rgw_user.cc
+++ b/src/rgw/rgw_user.cc
@@ -28,9 +28,10 @@ int rgw_get_user_info(string user_id, RGWUserInfo& info)
   void *handle = NULL;
   off_t ofs = 0, end = -1;
   size_t total_len;
+  time_t lastmod;
   bufferlist::iterator iter;
 
-  ret = rgwstore->prepare_get_obj(ui_bucket, user_id, ofs, &end, NULL, NULL, NULL, NULL, NULL, &total_len, &handle, &err);
+  ret = rgwstore->prepare_get_obj(ui_bucket, user_id, ofs, &end, NULL, NULL, NULL, &lastmod, NULL, NULL, &total_len, &handle, &err);
   if (ret < 0)
     return ret;
   do {
@@ -121,7 +122,7 @@ int rgw_get_uid_by_email(string& email, string& user_id)
   size_t total_len;
 
   ret = rgwstore->prepare_get_obj(ui_email_bucket, email, ofs, &end, NULL, NULL,
-                                  NULL, NULL, NULL, &total_len, &handle, &err);
+                                  NULL, NULL, NULL, NULL, &total_len, &handle, &err);
   if (ret < 0)
     return ret;
   do {
-- 
1.7.0.4

