You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2010/08/31 18:47:23 UTC

svn commit: r991239 - in /subversion/trunk: ./ subversion/include/ subversion/libsvn_ra_serf/ subversion/mod_dav_svn/ subversion/mod_dav_svn/posts/

Author: cmpilato
Date: Tue Aug 31 16:47:22 2010
New Revision: 991239

URL: http://svn.apache.org/viewvc?rev=991239&view=rev
Log:
Add code to parse skel-type request bodies in mod_dav_svn (honoring
the LimitRequestBody Apache configuration directive), and start using
it to create commit transactions.

NOTE: Clients using ra_serf to perform commits against older 1.7-to-be
      HTTP servers will fail.  I'd prefer to close the gap of "any old
      POST request we can't figure out should create a commit
      transaction" now before we release 1.7.  ra_neon doesn't have
      this problem because it never used the new POST approach.

* subversion/include/svn_dav.h
  (SVN_SKEL_MIME_TYPE): New.

* subversion/mod_dav_svn/dav_svn.h
  (dav_svn__parse_request_skel, dav_svn__post_create_txn): New prototypes.
  (dav_svn__posts_list): New static list.

* subversion/mod_dav_svn/util.c
  (request_body_to_string, dav_svn__parse_request_skel): New functions.

* subversion/mod_dav_svn/posts/,
* subversion/mod_dav_svn/posts/create_txn.c
  New.

* subversion/mod_dav_svn/repos.c
  (handle_post_request): New function.
  (dav_svn__method_post): Tweak this thing to expect skel-type POST
    requests, which are handed off to a dispatcher function.

* subversion/libsvn_ra_serf/commit.c
  (create_txn_post_body): New body delegate function.
  (open_root): Register create_txn_post_body() as a body delegate when
    issuing the POST request to create a new transaction.

* build.conf
  (mod_dav_svn): Add posts/*.c to the sources list.

Added:
    subversion/trunk/subversion/mod_dav_svn/posts/   (with props)
    subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c   (with props)
Modified:
    subversion/trunk/build.conf
    subversion/trunk/subversion/include/svn_dav.h
    subversion/trunk/subversion/libsvn_ra_serf/commit.c
    subversion/trunk/subversion/mod_dav_svn/dav_svn.h
    subversion/trunk/subversion/mod_dav_svn/repos.c
    subversion/trunk/subversion/mod_dav_svn/util.c

Modified: subversion/trunk/build.conf
URL: http://svn.apache.org/viewvc/subversion/trunk/build.conf?rev=991239&r1=991238&r2=991239&view=diff
==============================================================================
--- subversion/trunk/build.conf (original)
+++ subversion/trunk/build.conf Tue Aug 31 16:47:22 2010
@@ -345,7 +345,7 @@ msvc-export = svn_wc.h private\svn_wc_pr
 description = Subversion plug-in for the Apache DAV module
 type = apache-mod
 path = subversion/mod_dav_svn
-sources = *.c reports/*.c
+sources = *.c reports/*.c posts/*.c
 libs = libsvn_repos libsvn_fs libsvn_delta libsvn_subr
 nonlibs = apr aprutil
 install = apache-mod

Modified: subversion/trunk/subversion/include/svn_dav.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_dav.h?rev=991239&r1=991238&r2=991239&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_dav.h (original)
+++ subversion/trunk/subversion/include/svn_dav.h Tue Aug 31 16:47:22 2010
@@ -43,6 +43,12 @@ extern "C" {
  */
 #define SVN_SVNDIFF_MIME_TYPE "application/vnd.svn-svndiff"
 
+/** This is the MIME type that Subversion users for its "skel" format.
+ *
+ * This is an application type, for the "svn" vendor. The specific subtype
+ * is "skel".
+ */
+#define SVN_SKEL_MIME_TYPE "application/vnd.svn-skel"
 
 /** This header is *TEMPORARILY* used to transmit the delta base to the
  * server. It contains a version resource URL for what is on the client.

Modified: subversion/trunk/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/commit.c?rev=991239&r1=991238&r2=991239&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/commit.c Tue Aug 31 16:47:22 2010
@@ -1061,6 +1061,18 @@ svndiff_stream_write(void *file_baton,
 
 /* POST against 'me' resource handlers. */
 
+/* Implements svn_ra_serf__request_body_delegate_t */
+static svn_error_t *
+create_txn_post_body(serf_bucket_t **body_bkt,
+                     void *baton,
+                     serf_bucket_alloc_t *alloc,
+                     apr_pool_t *pool)
+{
+  *body_bkt = SERF_BUCKET_SIMPLE_STRING("( create-txn )", alloc);
+  return SVN_NO_ERROR;
+}
+
+
 /* Handler baton for POST request. */
 typedef struct
 {
@@ -1140,6 +1152,9 @@ open_root(void *edit_baton,
       /* Create our activity URL now on the server. */
       handler = apr_pcalloc(ctx->pool, sizeof(*handler));
       handler->method = "POST";
+      handler->body_type = SVN_SKEL_MIME_TYPE;
+      handler->body_delegate = create_txn_post_body;
+      handler->body_delegate_baton = NULL;
       handler->path = ctx->session->me_resource;
       handler->conn = ctx->session->conns[0];
       handler->session = ctx->session;

Modified: subversion/trunk/subversion/mod_dav_svn/dav_svn.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/dav_svn.h?rev=991239&r1=991238&r2=991239&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/dav_svn.h (original)
+++ subversion/trunk/subversion/mod_dav_svn/dav_svn.h Tue Aug 31 16:47:22 2010
@@ -37,6 +37,7 @@
 #include "svn_path.h"
 #include "svn_xml.h"
 #include "private/svn_dav_protocol.h"
+#include "private/svn_skel.h"
 #include "mod_authz_svn.h"
 
 #ifdef __cplusplus
@@ -419,7 +420,7 @@ dav_svn__store_activity(const dav_svn_re
                         const char *txn_name);
 
 
-/* HTTP protocol v2:  client does POST against 'me' resource. */
+/* POST request handler.  (Used by HTTP protocol v2 clients only.)  */
 int dav_svn__method_post(request_rec *r);
 
 
@@ -640,6 +641,23 @@ dav_svn__get_deleted_rev_report(const da
                                 const apr_xml_doc *doc,
                                 ap_filter_t *output);
 
+
+/*** posts/ ***/
+
+/* The list of Subversion's custom POSTs. */
+/* ### should move these report names to a public header to share with
+   ### the client (and third parties). */
+static const char * dav_svn__posts_list[] = {
+  "create-txn",
+  NULL
+};
+
+/* The various POST handlers, defined in posts/, and used by repos.c.  */
+dav_error *
+dav_svn__post_create_txn(const dav_resource *resource,
+                         svn_skel_t *request_skel,
+                         ap_filter_t *output);
+
 /*** authz.c ***/
 
 /* A baton needed by dav_svn__authz_read_func(). */
@@ -890,6 +908,13 @@ dav_svn__final_flush_or_error(request_re
  */
 int dav_svn__error_response_tag(request_rec *r, dav_error *err);
 
+
+/* Set *SKEL to a parsed skel read from the body of request R, and
+ * allocated in POOL.
+ */
+int dav_svn__parse_request_skel(svn_skel_t **skel, request_rec *r,
+                                apr_pool_t *pool);
+
 /*** mirror.c ***/
 
 /* Perform the fixup hook for the R request.  */

Propchange: subversion/trunk/subversion/mod_dav_svn/posts/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Aug 31 16:47:22 2010
@@ -0,0 +1 @@
+.libs

Added: subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c?rev=991239&view=auto
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c (added)
+++ subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c Tue Aug 31 16:47:22 2010
@@ -0,0 +1,54 @@
+/*
+ * create_txn.c: mod_dav_svn POST handler for creating a commit transaction
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <httpd.h>
+#include <mod_dav.h>
+
+#include "svn_dav.h"
+#include "../dav_svn.h"
+
+/* Respond to a "create-txn" POST request.
+ *
+ * Syntax:  ( create-txn )
+ */
+dav_error *
+dav_svn__post_create_txn(const dav_resource *resource,
+                         svn_skel_t *request_skel,
+                         ap_filter_t *output)
+{
+  const char *txn_name;
+  dav_error *derr;
+  request_rec *r = resource->info->r;
+
+  /* Create a Subversion repository transaction based on HEAD. */
+  if ((derr = dav_svn__create_txn(resource->info->repos, &txn_name,
+                                  resource->pool)))
+    return derr;
+
+  /* Build a "201 Created" response with header that tells the
+     client our new transaction's name. */
+  apr_table_set(r->headers_out, SVN_DAV_TXN_NAME_HEADER, txn_name);
+  r->status = HTTP_CREATED;
+
+  return NULL;
+}

Propchange: subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/trunk/subversion/mod_dav_svn/repos.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/repos.c?rev=991239&r1=991238&r2=991239&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/repos.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/repos.c Tue Aug 31 16:47:22 2010
@@ -4263,58 +4263,73 @@ dav_svn__create_version_resource(dav_res
 }
 
 
-/* POST handler for HTTP protocol v2.
- 
-   Currently we allow POSTs only against the "me resource", which may
-   in the future act as a dispatcher of sorts for handling potentially
-   many different kinds of operations as specified by the body of the
-   POST request itself.
-
-   ### TODO: Define what the format of those POST bodies might be.  If
-   ### XML, we have access to Apache's streamy XML parsing code, but
-   ### ... it's XML.  Meh.  If skels, we get skels!  But we need to
-   ### write our own streamy skel parsing routine around a brigade
-   ### read loop.  Ewww...
-   ###
-   ### Today we only support transaction creation requests, but we
-   ### could conceivable support the likes of a multi-path lock
-   ### and/or unlock request, or some other thing for which stock
-   ### WebDAV doesn't work or doesn't work well enough.
-   ###
-   ### Fortunately, today we don't use the POST body at all, and we'll
-   ### be able to get away with not defining the body format in the
-   ### future thanks to the following:
-
-   As a special consideration, an empty POST body is interpreted as a
-   simple request to create a new commit transaction based on the HEAD
-   revision.  The new transaction name will be returned via a custom
-   response header SVN_DAV_TXN_NAME_HEADER.
-*/
+
+static dav_error *
+handle_post_request(request_rec *r,
+                    dav_resource *resource,
+                    ap_filter_t *output)
+{
+  svn_skel_t *request_skel;
+  int status;
+  apr_pool_t *pool = resource->pool;
+
+  /* Make sure our skel-based request parses okay, has an initial atom
+     that identifies what kind of action is expected, and that that
+     action is something we understand.  */
+  status = dav_svn__parse_request_skel(&request_skel, r, pool);
+
+  if (status != OK)
+    return dav_new_error(pool, status, 0,
+                         "Error parsing skel POST request body.");
+
+  if (svn_skel__list_length(request_skel) < 1)
+    return dav_new_error(pool, HTTP_BAD_REQUEST, 0,
+                         "Unable to identify skel POST request flavor.");
+
+  if (svn_skel__matches_atom(request_skel->children, "create-txn"))
+    {
+      return dav_svn__post_create_txn(resource, request_skel, output);
+    }
+  else
+    {
+      return dav_new_error(pool, HTTP_BAD_REQUEST, 0,
+                           "Unsupported skel POST request flavor.");
+    }
+  /* NOTREACHED */
+}
+
 int dav_svn__method_post(request_rec *r)
 {
   dav_resource *resource;
   dav_error *derr;
-  const char *txn_name;
+  const char *content_type;
 
+  /* We only allow POSTs against the "me resource" right now. */
   derr = get_resource(r, dav_svn__get_root_dir(r),
                       "ignored", 0, &resource);
   if (derr != NULL)
     return derr->status;
-
   if (resource->info->restype != DAV_SVN_RESTYPE_ME)
     return HTTP_BAD_REQUEST;
 
-  /* Create a Subversion repository transaction based on HEAD. */
-  derr = dav_svn__create_txn(resource->info->repos, &txn_name, resource->pool);
+  /* Pass skel-type POST request handling off to a dispatcher; any
+     other type of request is considered bogus. */
+  content_type = apr_table_get(r->headers_in, "content-type");
+  if (content_type && (strcmp(content_type, SVN_SKEL_MIME_TYPE) == 0))
+    {
+      derr = handle_post_request(r, resource, r->output_filters);
+    }
+  else
+    {
+      derr = dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                           "Unsupported POST request type.");
+    }
+
+  /* If something went wrong above, we'll generate a response back to
+     the client with (hopefully) some helpful information. */
   if (derr)
     return dav_svn__error_response_tag(r, derr);
-
-  /* Build a "201 Created" response with header that tells the client
-     our new transaction's name. */
-  apr_table_set(resource->info->r->headers_out, SVN_DAV_TXN_NAME_HEADER,
-                txn_name);
-  r->status = HTTP_CREATED;
-
+    
   return OK;
 }
 

Modified: subversion/trunk/subversion/mod_dav_svn/util.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/util.c?rev=991239&r1=991238&r2=991239&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/util.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/util.c Tue Aug 31 16:47:22 2010
@@ -28,6 +28,7 @@
 
 #include <mod_dav.h>
 #include <http_protocol.h>
+#include <http_core.h>
 
 #include "svn_error.h"
 #include "svn_fs.h"
@@ -611,3 +612,137 @@ dav_svn__error_response_tag(request_rec 
    */
   return DONE;
 }
+
+
+/* Set *REQUEST_STR to a string containing the contents of the body of
+   request R, allocated from POOL.
+
+   NOTE: This was shamelessly stolen and modified from Apache's
+   ap_xml_parse_input().  */
+static int
+request_body_to_string(svn_string_t **request_str,
+                       request_rec *r,
+                       apr_pool_t *pool)
+{
+  apr_bucket_brigade *brigade;
+  int seen_eos;
+  apr_status_t status;
+  apr_off_t total_read = 0;
+  apr_off_t limit_req_body = ap_get_limit_req_body(r);
+  int result = HTTP_BAD_REQUEST;
+  const char *content_length_str;
+  char *endp;
+  apr_off_t content_length;
+  svn_stringbuf_t *buf;
+
+  *request_str = NULL;
+
+  content_length_str = apr_table_get(r->headers_in, "Content-Length");
+  if (content_length_str)
+    {
+      if (apr_strtoff(&content_length, content_length_str, &endp, 10)
+          || endp == content_length_str || *endp || content_length < 0)
+        {
+          ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid Content-Length");
+          return HTTP_REQUEST_ENTITY_TOO_LARGE;
+        }
+    }
+  if (limit_req_body && (limit_req_body < content_length))
+    {
+      ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                    "Requested content-length of %" APR_OFF_T_FMT " is larger "
+                    "than the configured limit of %" APR_OFF_T_FMT,
+                    content_length, limit_req_body);
+      return HTTP_REQUEST_ENTITY_TOO_LARGE;
+    }
+
+  if (content_length)
+    {
+      buf = svn_stringbuf_create_ensure(content_length, pool);
+    }
+  else
+    {
+      buf = svn_stringbuf_create("", pool);
+    }
+
+  brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+  seen_eos = 0;
+  total_read = 0;
+
+  do
+    {
+      apr_bucket *bucket;
+
+      status = ap_get_brigade(r->input_filters, brigade, AP_MODE_READBYTES,
+                              APR_BLOCK_READ, 2048);
+      if (status != APR_SUCCESS)
+        goto cleanup;
+
+      for (bucket = APR_BRIGADE_FIRST(brigade);
+           bucket != APR_BRIGADE_SENTINEL(brigade);
+           bucket = APR_BUCKET_NEXT(bucket))
+        {
+          const char *data;
+          apr_size_t len;
+          
+          if (APR_BUCKET_IS_EOS(bucket))
+            {
+              seen_eos = 1;
+              break;
+            }
+          
+          if (APR_BUCKET_IS_METADATA(bucket))
+            continue;
+          
+          status = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+          if (status != APR_SUCCESS)
+            goto cleanup;
+          
+          total_read += len;
+          if (limit_req_body && total_read > limit_req_body)
+            {
+              ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                            "Request body is larger than the configured "
+                            "limit of %lu", (unsigned long)limit_req_body);
+              result = HTTP_REQUEST_ENTITY_TOO_LARGE;
+              goto cleanup;
+            }
+          
+          svn_stringbuf_appendbytes(buf, data, len);
+        }
+      
+      apr_brigade_cleanup(brigade);
+    }
+  while (!seen_eos);
+
+  apr_brigade_destroy(brigade);
+
+  /* Make an svn_string_t from our svn_stringbuf_t. */
+  *request_str = svn_string_create("", pool);
+  (*request_str)->data = buf->data;
+  (*request_str)->len = buf->len;
+  return OK;
+
+ cleanup:
+  apr_brigade_destroy(brigade);
+  
+  /* Apache will supply a default error, plus the error log above. */
+  return result;
+}
+
+int
+dav_svn__parse_request_skel(svn_skel_t **skel,
+                            request_rec *r,
+                            apr_pool_t *pool)
+{
+  svn_string_t *skel_str;
+  int status;
+
+  *skel = NULL;
+  status = request_body_to_string(&skel_str, r, pool);
+  if (status != OK)
+    return OK;
+
+  *skel = svn_skel__parse(skel_str->data, skel_str->len, pool);
+  return OK;
+}



Re: svn commit: r991239 - in /subversion/trunk: ./ subversion/include/ subversion/libsvn_ra_serf/ subversion/mod_dav_svn/ subversion/mod_dav_svn/posts/

Posted by "C. Michael Pilato" <cm...@collab.net>.
On 09/14/2010 10:07 AM, Philip Martin wrote:
> What is the purpose of dav_svn__posts_list?  It doesn't seem to be
> used and it leads to compiler warnings.

I intend to broadcast the list of support POSTs in the OPTIONs request (like
we do REPORT types), but haven't gotten around to it yet.

-- 
C. Michael Pilato <cm...@collab.net>
CollabNet   <>   www.collab.net   <>   Distributed Development On Demand


Re: svn commit: r991239 - in /subversion/trunk: ./ subversion/include/ subversion/libsvn_ra_serf/ subversion/mod_dav_svn/ subversion/mod_dav_svn/posts/

Posted by Philip Martin <ph...@wandisco.com>.
cmpilato@apache.org writes:

> Author: cmpilato
> Date: Tue Aug 31 16:47:22 2010
> New Revision: 991239
>
> URL: http://svn.apache.org/viewvc?rev=991239&view=rev
> Log:
> Add code to parse skel-type request bodies in mod_dav_svn (honoring
> the LimitRequestBody Apache configuration directive), and start using
> it to create commit transactions.
>
> NOTE: Clients using ra_serf to perform commits against older 1.7-to-be
>       HTTP servers will fail.  I'd prefer to close the gap of "any old
>       POST request we can't figure out should create a commit
>       transaction" now before we release 1.7.  ra_neon doesn't have
>       this problem because it never used the new POST approach.
>
> * subversion/include/svn_dav.h
>   (SVN_SKEL_MIME_TYPE): New.
>
> * subversion/mod_dav_svn/dav_svn.h
>   (dav_svn__parse_request_skel, dav_svn__post_create_txn): New prototypes.
>   (dav_svn__posts_list): New static list.

What is the purpose of dav_svn__posts_list?  It doesn't seem to be
used and it leads to compiler warnings.

-- 
Philip