Browse Source

Add proxy_base_prefix option to help run in a subdir behind a proxy

I have a setup where Radicale is running as the normal process, and
nginx is acting as proxy for Radicale for requests under /radicale/.
This means that all requests to nginx that go to /radicale/ are sent to
a radicale server that expects requests coming at /.

Obviously, all the href need to have the /radicale/ prefix to have
things work. So a new option proxy_base_prefix is added to allow having
such a setup.

All href that are sent in the replies will have the proxy_base_prefix
prepended.
Vincent Untz 13 years ago
parent
commit
74590cfd0c
3 changed files with 31 additions and 13 deletions
  1. 3 0
      config
  2. 2 1
      radicale/config.py
  3. 26 12
      radicale/xmlutils.py

+ 3 - 0
config

@@ -29,6 +29,9 @@ key = /etc/apache2/ssl/server.key
 dns_lookup = True
 # base URL if / is not the CalDAV root. If set, must start with /
 base_prefix =
+# base URL if Radicale is running with / as CalDAV root, but in a subdir behind a proxy (like nginx). If set, must start with /
+proxy_base_prefix =
+
 
 [encoding]
 # Encoding for responding requests

+ 2 - 1
radicale/config.py

@@ -46,7 +46,8 @@ INITIAL_CONFIG = {
         "certificate": "/etc/apache2/ssl/server.crt",
         "key": "/etc/apache2/ssl/server.key",
         "dns_lookup": "True",
-        "base_prefix": ""},
+        "base_prefix": "",
+        "proxy_base_prefix": ""},
     "encoding": {
         "request": "utf-8",
         "stock": "utf-8"},

+ 26 - 12
radicale/xmlutils.py

@@ -119,6 +119,11 @@ def _response(code):
     return "HTTP/1.1 %i %s" % (code, client.responses[code])
 
 
+def _href_with_proxy_base_prefix(href):
+    href = "%s%s" % (config.get("server", "proxy_base_prefix"), href)
+    return href.replace("//", "/")
+
+
 def name_from_path(path, collection):
     """Return Radicale item name from ``path``."""
     collection_parts = collection.path.strip("/").split("/")
@@ -174,7 +179,7 @@ def delete(path, collection):
     multistatus.append(response)
 
     href = ET.Element(_tag("D", "href"))
-    href.text = path
+    href.text = _href_with_proxy_base_prefix(path)
     response.append(href)
 
     status = ET.Element(_tag("D", "status"))
@@ -230,7 +235,7 @@ def _propfind_response(path, item, props, user):
         uri = "%s%s" % (config.get("server", "base_prefix"), item.url)
     else:
         uri = "%s/%s" % (path, item.name)
-    href.text = uri.replace("//", "/")
+    href.text = _href_with_proxy_base_prefix(uri)
     response.append(href)
 
     propstat404 = ET.Element(_tag("D", "propstat"))
@@ -250,14 +255,14 @@ def _propfind_response(path, item, props, user):
             element.text = item.etag
         elif tag == _tag("D", "principal-URL"):
             tag = ET.Element(_tag("D", "href"))
-            tag.text = path
+            tag.text = _href_with_proxy_base_prefix(path)
             element.append(tag)
         elif tag in (_tag("D", "principal-collection-set"),
                      _tag("C", "calendar-user-address-set"),
                      _tag("CR", "addressbook-home-set"),
                      _tag("C", "calendar-home-set")):
             tag = ET.Element(_tag("D", "href"))
-            tag.text = path
+            tag.text = _href_with_proxy_base_prefix(path)
             element.append(tag)
         elif tag == _tag("C", "supported-calendar-component-set"):
             # This is not a Todo
@@ -269,7 +274,8 @@ def _propfind_response(path, item, props, user):
             # pylint: enable=W0511
         elif tag == _tag("D", "current-user-principal") and user:
             tag = ET.Element(_tag("D", "href"))
-            tag.text = "%s/%s/" % (config.get("server", "base_prefix"), user)
+            prefixed_path = "%s/%s/" % (config.get("server", "base_prefix"), user)
+            tag.text = _href_with_proxy_base_prefix(prefixed_path)
             element.append(tag)
         elif tag == _tag("D", "current-user-privilege-set"):
             privilege = ET.Element(_tag("D", "privilege"))
@@ -393,7 +399,7 @@ def proppatch(path, xml_request, collection):
     multistatus.append(response)
 
     href = ET.Element(_tag("D", "href"))
-    href.text = path
+    href.text = _href_with_proxy_base_prefix(path)
     response.append(href)
 
     with collection.props as collection_props:
@@ -436,13 +442,23 @@ def report(path, xml_request, collection):
     prop_element = root.find(_tag("D", "prop"))
     props = [prop.tag for prop in prop_element]
 
+    proxy_prefix = config.get("server", "proxy_base_prefix")
+    base_prefix = config.get("server", "base_prefix")
+
     if collection:
         if root.tag in (_tag("C", "calendar-multiget"),
                         _tag("CR", "addressbook-multiget")):
             # Read rfc4791-7.9 for info
-            hreferences = set(
-                href_element.text for href_element
-                in root.findall(_tag("D", "href")))
+            hreferences = set()
+            for href_element in root.findall(_tag("D", "href")):
+                # skip elements that don't have the correct base prefixes
+                if not href_element.text.startswith(proxy_prefix):
+                    continue
+                unprefixed = href_element.text[len(proxy_prefix):]
+                if not unprefixed.startswith(base_prefix):
+                    continue
+                # we keep the base prefix here, to be aligned with other paths
+                hreferences.add(unprefixed)
         else:
             hreferences = (path,)
         # TODO: handle other filters
@@ -463,8 +479,6 @@ def report(path, xml_request, collection):
     collection_headers = collection.headers
     collection_timezones = collection.timezones
 
-    base_prefix = config.get("server", "base_prefix")
-
     for hreference in hreferences:
         unprefixed_hreference = hreference[len(base_prefix):]
         # Check if the reference is an item or a collection
@@ -486,7 +500,7 @@ def report(path, xml_request, collection):
             multistatus.append(response)
 
             href = ET.Element(_tag("D", "href"))
-            href.text = "%s/%s" % (path.rstrip("/"), item.name)
+            href.text = _href_with_proxy_base_prefix("%s/%s" % (path.rstrip("/"), item.name))
             response.append(href)
 
             propstat = ET.Element(_tag("D", "propstat"))