| A Streamlined HTTP Protocol for Subversion |
| |
| GOAL |
| ==== |
| |
| Write a new HTTP protocol for svn -- one which has no pretense of |
| adhering to some pre-existing standard, and which is designed for |
| speed and comprehensibility. This ain't your Daddy's WebDAV. |
| |
| |
| PURPOSE / HISTORY |
| ================= |
| |
| Subversion standardized on Apache and the WebDAV/DeltaV protocol as a |
| back in the earliest days of development, based on some very strong |
| value propositions: |
| |
| A. Able to go through corporate firewalls |
| B. Zillions of authn/authz options via Apache |
| C. Standardized encryption (SSL) |
| D. Excellent logging |
| E. Built-in repository browsing |
| F. Caching within intermediate proxies |
| G. Interoperability with other WebDAV clients |
| |
| Unfortunately, DeltaV is an insanely complex and inefficient protocol, |
| and doesn't fit Subversion's model well at all. The result is that |
| Subversion speaks a "limited portion" of DeltaV, and pays a huge |
| performance price for this complexity. |
| |
| |
| REQUIREMENTS |
| ============ |
| |
| Write a new HTTP protocol for svn ("HTTP v2"). Map RA requests |
| directly to HTTP requests. Subversion over HTTP should: |
| |
| * be much faster (eliminate extra turnarounds) |
| * be almost as easy to extend as Subversionserve |
| * be comprehensible to devs and users without knowledge of DeltaV concepts |
| * be designed for optimum cacheability by web proxies |
| * make use of pipelined and parallel requests when possible |
| |
| |
| OUR PLANS, IN A NUTSHELL |
| ======================== |
| |
| * Phase 1: Remove all DeltaV mechanics & formalities |
| |
| - Get rid of all the PROPFIND 'discovery' turnarounds. |
| - Stop doing CHECKOUT requests before each PUT |
| - Publish a public URI syntax for browsing historical objects |
| |
| * Phase 2: Speed up commits |
| |
| - Make PUT requests pipelined, the way ra_svn does. |
| |
| * Phase 3: (maybe) get rid of XML in request/response bodies |
| |
| - If there's a worthwhile speed gain, use serialized Thrift objects. |
| |
| |
| PHASE 1 IN DETAIL |
| ================= |
| |
| * Deprecated DeltaV resources and resource types: |
| |
| In compliance with the DeltaV spec, Subversion clients prior using |
| that standard protocol have to "discover" and manipulate the |
| following DeltaV objects: |
| |
| - Version Controlled Resource (VCC): !svn/vcc |
| - Baseline resource: !svn/bln |
| - Working baseline resource: !svn/wbl |
| - Baseline collection resource: !svn/bc/REV/ |
| - Activity collection: !svn/act/ACTIVITY-UUID/ |
| - Versioned resource: !svn/ver/REV/path |
| - Working resource: !svn/wrk/ACTIVITY-UUID/path |
| |
| All of these objects will be deprecated and no longer used. |
| mod_dav_svn will still support older clients, of course, but new |
| clients will be able to automatically construct all of the URIs |
| they need. |
| |
| * New resources and resource types: |
| |
| The following are some new resource and resource type concepts |
| we're introducing in HTTP protocol v2: |
| |
| - me resource (!svn/me) |
| |
| Represents the "repository itself". This is the URI that |
| custom REPORTS are sent against. (This eliminates our need for |
| the VCC resource.) |
| |
| - revision resource (!svn/rev/REV) |
| |
| Represents a Subversion revision at the metadata level, and |
| maps conceptually to a "revision" in the FS layer. Standard |
| PROPFIND and PROPATCH requests can be used against a revision |
| resource, with the understanding that the name/value pairs |
| being accessed are unversioned revision props, rather than file |
| or directory props. (This eliminates our need for baseline or |
| working baseline resources.) |
| |
| - revision root resource (!svn/rvr/REV/[PATH]) |
| |
| Represents the directory tree snapshot associated with a |
| Subversion revision, and maps conceptually to a revision-type |
| svn_fs_root_t/path pair in the FS layer. GET, PROPFIND, and |
| certain REPORT requests can be issued against these resources. |
| |
| - transaction resource (!svn/txn/TXN-NAME) |
| |
| Represents a Subversion commit transaction, and maps |
| conceptually to an svn_fs_txn_t in the FS layer. PROPFIND and |
| PROPATCH requests can be used against a transaction resource, |
| with the understanding that the name/value pairs being accessed |
| are unversioned transaction props, rather than file or directory |
| props. |
| |
| - transaction root resource (!svn/txr/TXN-NAME/[PATH]) |
| |
| Represents the directory tree snapshot associated with a |
| Subversion commit transaction, and maps conceptually to a |
| transaction-type svn_fs_root_t/path pair in the FS layer. |
| Various read- and write-type requests can be issued against |
| these resources (MKCOL, PUT, PROPFIND, PROPPATCH, GET, etc.). |
| |
| - alternate transaction resource (!svn/vtxn/VTXN-NAME) |
| - alternate transaction root resource (!svn/vtxr/VTXN-NAME/[PATH]) |
| |
| Alternative names for the transaction based on a virtual, or |
| visible, name supplied by the client when the transaction |
| was created. The client supplied name is optional, if not |
| supplied these resource names are not valid. |
| |
| * Opening an RA session: |
| |
| ra_serf will send an OPTIONS request when creating a new |
| ra_session. mod_dav_svn will send back what it already sends now, |
| but will also return new information as custom headers in the |
| OPTIONS response: |
| |
| SVN-Youngest-Rev: REV |
| SVN-Me-Resource: /REPOS-ROOT/!svn/me |
| |
| Additionally, this response will contain some new URL stub values: |
| |
| SVN-Rev-Stub: /REPOS-ROOT/!svn/rev |
| SVN-Rev-Root-Stub: /REPOS-ROOT/!svn/rvr |
| SVN-Txn-Stub: /REPOS-ROOT/!svn/txn |
| SVN-Txn-Root-Stub: /REPOS-ROOT/!svn/txr |
| SVN-VTxn-Stub: /REPOS-ROOT/!svn/vtxn |
| SVN-VTxn-Root-Stub: /REPOS-ROOT/!svn/vtxr |
| |
| The presence of these new stubs (which can be appended to by the |
| client to create full-fledged resource URLs) tells ra_serf that |
| this is a new server, and that the new streamlined HTTP protocol |
| can be used. ra_serf then caches them in the ra_session object. |
| If these new OPTIONS responses are not returned, ra_serf falls back |
| to 'classic' DeltaV protocol. |
| |
| NOTE: Recall that the !svn, while shown in this document as its |
| default value, can be changed via server configuration. Clients |
| MUST NOT assume that the "special URI" is "!svn". We're just |
| trying to keep this document as readable as possible. |
| |
| * Changes to Read Requests |
| |
| Most of the read requests performed by ra_serf fall into one of a |
| few categories, issuing GETs, PROPFINDs, or REPORTs against public |
| URIs, baseline collection URIs, or the default VCC (with some |
| exceptions). Now, these will all be changed to use the new |
| resources, all of which have URLs that can be constructed from the |
| information returned by the new OPTIONS response information. |
| |
| The implementations of the higher level Subversion update, switch, |
| status -u, and diff operations in a given RA module share almost |
| all their code. They really are just that similar to one another. |
| Like the changes to the other read requests, this family of |
| requests will now operate against our new resources. But |
| additionally, we'll be able to stop using the "wcprops" abstraction |
| layer, which is today used to cache version resource URLs in |
| Subversion's working copy layer (since the client can construct |
| URLs as easily as it can fetch them from a cache). |
| |
| * Simple Write Requests |
| |
| The 'lock' and 'unlock' operations won't change, because they |
| already operate on public HEAD URIs today. But revprop changes |
| will now happen as PROPPATCH's against a resource URI (which can be |
| constructed by the client). |
| |
| * Commits |
| |
| Commits will change significantly. The current methodology looks like: |
| |
| OPTIONS to start ra_session |
| PROPFINDs to discover various opaque URIs |
| MKACTIVITY to create a transaction |
| try: |
| for each changed object: |
| CHECKOUT object to get working resource |
| PUT/PROPPATCH/DELETE/COPY working resource |
| MKCOL to create new directories |
| MERGE to commit the transaction |
| finally: |
| DELETE the activity |
| |
| The new sequence is simpler, and looks like: |
| |
| OPTIONS to start ra_session |
| POST against "me resource", to create a transaction |
| try: |
| for each changed object: |
| PUT/PROPPATCH/DELETE/COPY/MKCOL against transaction resources |
| MERGE to commit the transaction |
| except: |
| DELETE the transaction resource |
| |
| Specific new changes: |
| |
| - The activity-UUID-to-Subversion-txn-name abstraction is gone. |
| We now expose the Subversion txn names explicitly through the |
| protocol. |
| |
| - The new POST request replaces the MKACTIVITY request. |
| |
| - no more need to "discover" the activity URI; !svn/act/ is gone. |
| |
| - client no longer needs to create an activity UUID itself. |
| |
| - instead, POST returns the name of the transaction it created, |
| as TXN-NAME, which can then be appended to the transaction |
| stub and transaction root stub as necessary. |
| |
| - if the client does choose to supply a UUID with the POST |
| request then the POST returns that UUID as VTXN-NAME, instead of |
| returning TXN-NAME, and the client then uses that with the |
| alternate transaction stub and transaction root stub in subsequent |
| requests. |
| |
| - Once the commit transaction is created, the client is free to |
| send write requests against transaction resources it constructs |
| itself. This eliminates the CHECKOUT requests, and also |
| removes our need to use versioned resources (!svn/ver) or |
| working resources (!svn/wrk). |
| |
| - When modifying transaction resources, clients should send |
| 'X-SVN-Version-Name:' headers (whose value carries the base |
| revision) to facilitate server-side out-of-dateness checks. |
| |
| |
| STATUS |
| ====== |
| |
| * Teach mod_dav_svn to answer the OPTIONS request with these |
| additional pieces of information: |
| |
| repository root URI [DONE] |
| repository UUID [DONE] |
| youngest revision [DONE] |
| me resource URI [DONE] |
| revision stub [DONE] |
| revision root stub [DONE] |
| transaction stub [DONE] |
| transaction root stub [DONE] |
| |
| * Teach mod_dav_svn to recognize and correctly interpret URLs which |
| make use of the new URI stubs: |
| |
| me resource URI -> !svn/me [DONE] |
| revision stub -> !svn/rev [STARTED] |
| revision root stub -> !svn/rvr [DONE] |
| transaction stub -> !svn/txn [STARTED] |
| transaction root stub -> !svn/txr [STARTED] |
| |
| * Teach mod_dav_svn to handle POST against the "me resource", |
| returning a transaction URI stub and transaction prop URI stub for |
| further use in the commit. [DONE] |
| |
| * Teach mod_dav_svn to notice and use X-SVN-Version-Name headers in |
| write requests aimed at transaction root resources for |
| out-of-dateness checks. This maps conceptually to the |
| 'base_revision' svn_delta_editor_t concept. (Note that some write |
| requests -- such as MKCOL, COPY and MOVE -- map to editor |
| functionality that doesn't carry a base_revision concept.) |
| |
| PROPPATCH [DONE] |
| DELETE [DONE] |
| PUT [DONE] |
| |
| * Teach mod_dav_svn to handle HTTPv2 requests in its mirroring |
| logic. [STARTED (by Dave Brown)] |
| |
| * Teach ra_serf operations to not do the multi-PROPFIND dance any |
| more, but to fetch the information they seek from mod_dav_svn using |
| the new stub URIs: |
| |
| get-file -> GET (against pegrev URI) [DONE] |
| get-dir -> PROPFIND (against pegrev URI) [DONE] |
| rev-prop -> PROPFIND (against revision URI) [DONE] |
| rev-proplist -> PROPFIND (against revision URI) [DONE] |
| check-path -> PROPFIND (against pegrev URI) [DONE] |
| stat -> PROPFIND (against pegrev URI) [DONE] |
| get-lock -> PROPFIND (against public HEAD URI) [DONE] |
| |
| * Teach ra_serf REPORT-type requests to use the URI stubs where |
| applicable, too: |
| |
| log -> REPORT (against pegrev URI) [DONE] |
| get-dated-rev -> REPORT (against "me resource") [DONE] |
| get-deleted-rev -> REPORT (against pegrev URI) [DONE] |
| get-locations -> REPORT (against pegrev URI) [DONE] |
| get-location-segments -> REPORT (against pegrev URI) [DONE] |
| get-file-revs -> REPORT (against pegrev URI) [DONE] |
| get-locks -> REPORT (against public HEAD URI) [DONE] |
| get-mergeinfo -> REPORT (against pegrev URI) [DONE] |
| replay -> REPORT (against "me resource") [DONE] |
| replay-range -> REPORTs (against "me resource") [DONE] |
| |
| * Teach ra_serf simple write requests to use new URI stubs: |
| |
| change-rev-prop -> PROPPATCH (against revision URI) [DONE] |
| lock -> LOCK (against public HEAD URI) [DONE] |
| unlock -> UNLOCK (against public HEAD URI) [DONE] |
| |
| * Teach ra_serf to do update-style REPORTs a little differently: |
| |
| - REPORT against the new "me resource" instead of VCC URI [DONE] |
| - use new URIs to avoid unnecessary PROPFIND discovery [DONE] |
| - eliminate now-unnecessary wcprops cache [DEFERRED] |
| |
| * Rework ra_serf commit editor implementation to use new direct |
| methods as described in the design doc. |
| |
| - use POST to 'me' resource to get txn name [DONE] |
| - set revprops using PROPPATCH on the txn resource [DONE] |
| - abort edit with DELETE against the txn resource [DONE] |
| - send write requests against txn and txn root URLs [DONE] |
| - send X-SVN-Version-Name for out-of-dateness checks [DONE] |
| - enable pipelined PUTs [OUT-OF-SCOPE] |
| |
| * Optional: Do some of this stuff for ra_neon, too: |
| |
| - get and cache UUID and repos_root from OPTIONS [DONE] |
| - get me resource, and pegrev stub from OPTIONS [DONE] |
| - use me resource instead of the VCC [STARTED] |
| - use pegrev stubs instead of get_baseline_info() walks [STARTED] |
| - use rev stubs for revprop stuff [DONE] |
| - use POST to 'me' resource to get txn name [DONE] |
| - set revprops using PROPPATCH on the txn resource [DONE] |
| - abort edit with DELETE against the txn resource [DONE] |
| - send write requests against txn and txn root URLs [DONE] |
| - send X-SVN-Version-Name for out-of-dateness checks [DONE] |
| - enable pipelined PUTs [OUT-OF-SCOPE] |