Index: subversion/mod_dav_svn/dav_svn.h
===================================================================
--- subversion/mod_dav_svn/dav_svn.h	(revision 1441791)
+++ subversion/mod_dav_svn/dav_svn.h	(working copy)
@@ -322,6 +322,9 @@ svn_boolean_t dav_svn__get_fulltext_cache_flag(req
 /* for the repository referred to by this request, is revprop caching active? */
 svn_boolean_t dav_svn__get_revprop_cache_flag(request_rec *r);
 
+/* for the repository referred to by this request, is keywords substitution active? */
+svn_boolean_t dav_svn__get_keyword_subst_flag(request_rec *r);
+
 /* for the repository referred to by this request, are subrequests bypassed?
  * A function pointer if yes, NULL if not.
  */
Index: subversion/mod_dav_svn/mod_dav_svn.c
===================================================================
--- subversion/mod_dav_svn/mod_dav_svn.c	(revision 1441791)
+++ subversion/mod_dav_svn/mod_dav_svn.c	(working copy)
@@ -104,6 +104,7 @@ typedef struct dir_conf_t {
   enum conf_flag txdelta_cache;      /* whether to enable txdelta caching */
   enum conf_flag fulltext_cache;     /* whether to enable fulltext caching */
   enum conf_flag revprop_cache;      /* whether to enable revprop caching */
+  enum conf_flag keyword_subst;      /* whether to substitute svn:keywords */
   const char *hooks_env;             /* path to hook script env config file */
 } dir_conf_t;
 
@@ -246,6 +247,7 @@ merge_dir_config(apr_pool_t *p, void *base, void *
   newconf->txdelta_cache = INHERIT_VALUE(parent, child, txdelta_cache);
   newconf->fulltext_cache = INHERIT_VALUE(parent, child, fulltext_cache);
   newconf->revprop_cache = INHERIT_VALUE(parent, child, revprop_cache);
+  newconf->keyword_subst = INHERIT_VALUE(parent, child, keyword_subst);
   newconf->root_dir = INHERIT_VALUE(parent, child, root_dir);
   newconf->hooks_env = INHERIT_VALUE(parent, child, hooks_env);
 
@@ -542,6 +544,19 @@ SVNCacheRevProps_cmd(cmd_parms *cmd, void *config,
 }
 
 static const char *
+SVNKeywordSubst_cmd(cmd_parms *cmd, void *config, int arg)
+{
+  dir_conf_t *conf = config;
+
+  if (arg)
+    conf->keyword_subst = CONF_FLAG_ON;
+  else
+    conf->keyword_subst = CONF_FLAG_OFF;
+
+  return NULL;
+}
+
+static const char *
 SVNInMemoryCacheSize_cmd(cmd_parms *cmd, void *config, const char *arg1)
 {
   svn_cache_config_t settings = *svn_cache_config_get();
@@ -929,6 +944,16 @@ dav_svn__get_revprop_cache_flag(request_rec *r)
 }
 
 
+svn_boolean_t
+dav_svn__get_keyword_subst_flag(request_rec *r)
+{
+  dir_conf_t *conf;
+
+  conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
+  return conf->keyword_subst == CONF_FLAG_ON;
+}
+
+
 int
 dav_svn__get_compression_level(request_rec *r)
 {
@@ -1191,6 +1216,13 @@ static const command_rec cmds[] =
                "in the documentation"
                "(default is Off)."),
 
+  /* per directory/location */
+  AP_INIT_FLAG("SVNKeywordSubstitution", SVNKeywordSubst_cmd, NULL,
+               ACCESS_CONF|RSRC_CONF,
+               "enables substitution of SVN keywords in file content "
+               "using svn:keywords property, the way SVN clients do it "
+               "(default is Off)."),
+
   /* per server */
   AP_INIT_TAKE1("SVNInMemoryCacheSize", SVNInMemoryCacheSize_cmd, NULL,
                 RSRC_CONF,
Index: subversion/mod_dav_svn/repos.c
===================================================================
--- subversion/mod_dav_svn/repos.c	(revision 1441791)
+++ subversion/mod_dav_svn/repos.c	(working copy)
@@ -39,6 +39,7 @@
 
 #include "svn_types.h"
 #include "svn_pools.h"
+#include "svn_subst.h"
 #include "svn_error.h"
 #include "svn_time.h"
 #include "svn_fs.h"
@@ -3031,18 +3032,22 @@ set_headers(request_rec *r, const dav_resource *re
 
 
       /* if we aren't sending a diff, then we know the length of the file,
-         so set up the Content-Length header */
-      serr = svn_fs_file_length(&length,
-                                resource->info->root.root,
-                                resource->info->repos_path,
-                                resource->pool);
-      if (serr != NULL)
+         so set up the Content-Length header
+         If keyword substitution is active, length may increase during deliver! */
+      if (! dav_svn__get_keyword_subst_flag(resource->info->r))
         {
-          return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
-                                      "could not fetch the resource length",
-                                      resource->pool);
+          serr = svn_fs_file_length(&length,
+                                    resource->info->root.root,
+                                    resource->info->repos_path,
+                                    resource->pool);
+          if (serr != NULL)
+            {
+              return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                          "could not fetch the resource length",
+                                          resource->pool);
+            }
+          ap_set_content_length(r, (apr_off_t) length);
         }
-      ap_set_content_length(r, (apr_off_t) length);
     }
 
   /* set the discovered MIME type */
@@ -3568,6 +3573,64 @@ deliver(const dav_resource *resource, ap_filter_t
                                       resource->pool);
         }
 
+      if (dav_svn__get_keyword_subst_flag(resource->info->r))
+        {
+          svn_string_t *keywords;
+          serr = svn_fs_node_prop(&keywords,
+                                  resource->info->root.root,
+                                  resource->info->repos_path,
+                                  SVN_PROP_KEYWORDS,
+                                  resource->pool);
+          if (serr != NULL)
+            {
+              return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                          "Could not get 'keywords' property",
+                                          resource->pool);
+            }
+          if (keywords)
+            {
+              apr_hash_t *kw;
+              svn_revnum_t cmt_revnum;
+              char *cmt_rev, *uri;
+              const char *cmt_date, *cmt_author;
+              apr_time_t when = 0;
+
+              serr = svn_repos_get_committed_info(&cmt_revnum, &cmt_date, &cmt_author,
+                                                  resource->info->root.root,
+                                                  resource->info->repos_path,
+                                                  resource->pool);
+              if (serr != NULL)
+                {
+                  return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                              "Could not get properties for keyword substitution",
+                                              resource->pool);
+                }
+              cmt_rev = apr_psprintf(resource->pool,
+                                     "%ld", cmt_revnum);
+              uri = apr_psprintf(resource->pool,
+                                 "%s%s", resource->info->repos->base_url, resource->info->r->uri);
+              serr = svn_time_from_cstring(&when, cmt_date, resource->pool);
+              if (serr != NULL)
+                {
+                  return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                              "Could not convert time from revision property",
+                                              resource->pool);
+                }
+
+              serr = svn_subst_build_keywords2(&kw, keywords->data,
+                                               cmt_rev, uri, when, cmt_author,
+                                               resource->pool);
+              if (serr != NULL)
+              {
+                return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                            "could not substitute keywords in file contents",
+                                            resource->pool);
+              }
+              stream = svn_subst_stream_translated(svn_stream_disown(stream, resource->pool),
+                                                    NULL, FALSE, kw, TRUE, resource->pool);
+          }
+        }
+
       /* ### one day in the future, we can create a custom bucket type
          ### which will read from the FS stream on demand */
 
