[mapcache] 02/12: Imported Upstream version 1.4.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Tue Jul 28 20:08:34 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository mapcache.

commit 37181a90a74252f612c93d76c0f014296db026bf
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Tue Jul 28 16:51:34 2015 +0200

    Imported Upstream version 1.4.0
---
 .gitignore                        |    2 +
 CMakeLists.txt                    |   19 +-
 Vagrantfile                       |   27 +
 apache/mod_mapcache.c             |  120 +++-
 cgi/mapcache.c                    |    2 +-
 cmake/FindRIAK.cmake              |   14 +
 include/mapcache-config.h.in      |    1 +
 include/mapcache.h                |  404 +++++++++++--
 lib/buffer.c                      |    1 +
 lib/cache_bdb.c                   |  213 +++----
 lib/cache_composite.c             |  205 +++++++
 lib/cache_couchbase.c             |  462 +++++++++++++++
 lib/cache_disk.c                  |   89 +--
 lib/cache_fallback.c              |  202 +++++++
 lib/cache_memcache.c              |  244 +++++---
 lib/cache_multitier.c             |  165 ++++++
 lib/cache_rest.c                  | 1165 +++++++++++++++++++++++++++++++++++++
 lib/cache_riak.c                  |  452 ++++++++++++++
 lib/cache_sqlite.c                |  551 ++++++++++--------
 lib/cache_tiff.c                  |  163 +++---
 lib/configuration.c               |   37 --
 lib/configuration_xml.c           |  115 +++-
 lib/connection_pool.c             |  190 ++++++
 lib/core.c                        |   38 +-
 lib/dimension.c                   |  376 +++++++-----
 lib/grid.c                        |   19 +-
 lib/hmac-sha.c                    |  413 +++++++++++++
 lib/http.c                        |  114 ++--
 lib/image.c                       |   19 +
 lib/imageio_png.c                 |  164 +++++-
 lib/lock.c                        |  364 +++++++++++-
 lib/service_mapguide.c            |    2 +-
 lib/service_tms.c                 |    2 +-
 lib/service_ve.c                  |    2 +-
 lib/service_wms.c                 |  117 +++-
 lib/service_wmts.c                |   38 +-
 lib/source_mapserver.c            |    1 +
 lib/source_wms.c                  |   15 +-
 lib/tileset.c                     |  138 +++--
 lib/util.c                        |  102 +++-
 mapcache.xml                      |   13 +-
 mapcache.xml.sample               |  107 +++-
 nginx/ngx_http_mapcache_module.c  |    4 +-
 scripts/vagrant/mapcache.sh       |   13 +
 scripts/vagrant/packages.sh       |   17 +
 scripts/vagrant/virtualbox-fix.sh |    5 +
 util/mapcache_seed.c              |  278 +++++----
 47 files changed, 6116 insertions(+), 1088 deletions(-)

diff --git a/.gitignore b/.gitignore
index f594a23..b1d61cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
 .*.swp
 nbproject/
 /build/
+/build_vagrant/
+/.vagrant/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b5026e6..218f87a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,8 +13,8 @@ endif ()
 
 
 set (MAPCACHE_VERSION_MAJOR 1)
-set (MAPCACHE_VERSION_MINOR 2)
-set (MAPCACHE_VERSION_REVISION 1)
+set (MAPCACHE_VERSION_MINOR 4)
+set (MAPCACHE_VERSION_REVISION 0)
 
 
 if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
@@ -61,6 +61,9 @@ check_function_exists("symlink"  HAVE_SYMLINK)
 
 
 set(CMAKE_SKIP_BUILD_RPATH FALSE)
+if(APPLE)
+  set(CMAKE_MACOSX_RPATH ON)
+endif()
 set(CMAKE_LINK_INTERFACE_LIBRARY "")
 
 file(GLOB mapcache_SOURCES lib/*.c )
@@ -81,6 +84,7 @@ option(WITH_TIFF_WRITE_SUPPORT "Enable (experimental) support for writable TIFF
 option(WITH_GEOTIFF "Allow GeoTIFF metadata creation for TIFF cache backends" OFF)
 option(WITH_PCRE "Use PCRE for regex tests" OFF)
 option(WITH_MAPSERVER "Enable (experimental) support for the mapserver library" OFF)
+option(WITH_RIAK "Use Riak as a cache backend" OFF)
 
 find_package(PNG)
 if(PNG_FOUND)
@@ -228,6 +232,16 @@ if(WITH_MAPSERVER)
   endif(MAPSERVER_FOUND)
 endif (WITH_MAPSERVER)
 
+if(WITH_RIAK)
+  find_package(RIAK)
+  if(RIAK_FOUND)
+    include_directories(${RIAK_INCLUDE_DIR})
+    target_link_libraries(mapcache ${RIAK_LIBRARY})
+    set (USE_RIAK 1)
+  else(RIAK_FOUND)
+    report_optional_not_found(RIAK)
+  endif(RIAK_FOUND)
+endif (WITH_RIAK)
 
 if(UNIX)
 target_link_libraries(mapcache ${CMAKE_DL_LIBS} m )
@@ -276,6 +290,7 @@ status_optional_component("GeoTIFF" "${USE_GEOTIFF}" "${GEOTIFF_LIBRARY}")
 status_optional_component("Experimental TIFF write support" "${USE_TIFF_WRITE}" "${TIFF_LIBRARY}")
 status_optional_component("PCRE" "${USE_PCRE}" "${PCRE_LIBRARY}")
 status_optional_component("Experimental mapserver support" "${USE_MAPSERVER}" "${MAPSERVER_LIBRARY}")
+status_optional_component("RIAK" "${USE_RIAK}" "${RIAK_LIBRARY}")
 
 INSTALL(TARGETS mapcache DESTINATION ${CMAKE_INSTALL_LIBDIR})
 
diff --git a/Vagrantfile b/Vagrantfile
new file mode 100644
index 0000000..8435db5
--- /dev/null
+++ b/Vagrantfile
@@ -0,0 +1,27 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+require 'socket'
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+  config.vm.box = "precise64"
+  config.vm.box_url = "http://files.vagrantup.com/precise64.box"
+
+  config.vm.hostname = "mapcache-vagrant"
+
+  config.vm.network :forwarded_port, guest: 80, host: 8080
+
+  config.vm.provider "virtualbox" do |v|
+     v.customize ["modifyvm", :id, "--memory", 1024, "--cpus", 2]
+     v.customize ["modifyvm", :id, "--ioapic", "on", "--largepages", "off", "--vtxvpid", "off"]
+     v.name = "mapcache-vagrant"
+   end
+
+  config.vm.provision "shell", path: "scripts/vagrant/virtualbox-fix.sh"
+  config.vm.provision "shell", path: "scripts/vagrant/packages.sh"
+  config.vm.provision "shell", path: "scripts/vagrant/mapcache.sh"
+
+end
diff --git a/apache/mod_mapcache.c b/apache/mod_mapcache.c
index 2546348..61dc994 100644
--- a/apache/mod_mapcache.c
+++ b/apache/mod_mapcache.c
@@ -59,8 +59,6 @@ typedef struct mapcache_context_apache mapcache_context_apache;
 typedef struct mapcache_context_apache_request mapcache_context_apache_request;
 typedef struct mapcache_context_apache_server mapcache_context_apache_server;
 
-apr_pool_t *pchild = NULL;
-
 struct mapcache_context_apache {
   mapcache_context ctx;
 };
@@ -188,7 +186,6 @@ static mapcache_context_apache_request* apache_request_context_create(request_re
   mapcache_cfg *config = NULL;
 
   ctx->ctx.ctx.pool = r->pool;
-  ctx->ctx.ctx.process_pool = pchild;
 #ifdef APR_HAS_THREADS
   ctx->ctx.ctx.threadlock = thread_mutex;
 #endif
@@ -198,6 +195,7 @@ static mapcache_context_apache_request* apache_request_context_create(request_re
   config = apr_hash_get(cfg->aliases,(void*)r->filename,APR_HASH_KEY_STRING);
   ctx->ctx.ctx.config = config;
   ctx->request = r;
+  ctx->ctx.ctx.connection_pool = cfg->cp;
   init_apache_request_context(ctx);
   return ctx;
 }
@@ -212,6 +210,69 @@ static mapcache_context_apache_server* apache_server_context_create(server_rec *
   return ctx;
 }
 
+/* read post body. code taken from "The apache modules book, Nick Kew" */
+static void read_post_body(mapcache_context_apache_request *ctx, mapcache_request_proxy *p) {
+  request_rec *r = ctx->request;
+  mapcache_context *mctx = (mapcache_context*)ctx;
+  int bytes,eos;
+  apr_bucket_brigade *bb, *bbin;
+  apr_bucket *b;
+  apr_status_t rv;
+  const char *clen = apr_table_get(r->headers_in, "Content-Length");
+  if(clen) {
+    bytes = strtol(clen, NULL, 0);
+    if(bytes >= p->rule->max_post_len) {
+      mctx->set_error(mctx, HTTP_REQUEST_ENTITY_TOO_LARGE, "post request too big");
+      return;
+    }
+  } else {
+    bytes = p->rule->max_post_len;
+  } 
+
+  bb = apr_brigade_create(mctx->pool, r->connection->bucket_alloc);
+  bbin = apr_brigade_create(mctx->pool, r->connection->bucket_alloc);
+  p->post_len = 0;
+
+  do {
+    rv = ap_get_brigade(r->input_filters, bbin, AP_MODE_READBYTES, APR_BLOCK_READ, bytes);
+    if(rv != APR_SUCCESS) {
+      mctx->set_error(mctx, 500, "failed to read form input");
+      return;
+    }
+    for(b = APR_BRIGADE_FIRST(bbin); b != APR_BRIGADE_SENTINEL(bbin); b = APR_BUCKET_NEXT(b)) {
+      if(APR_BUCKET_IS_EOS(b)) {
+        eos = 1;
+      }
+    }
+    if(!APR_BUCKET_IS_METADATA(b)) {
+      if(b->length != (apr_size_t)(-1)) {
+        p->post_len += b->length;
+        if(p->post_len > p->rule->max_post_len) {
+          apr_bucket_delete(b);
+        }
+      }
+    }
+    if(p->post_len <= p->rule->max_post_len) {
+      APR_BUCKET_REMOVE(b);
+      APR_BRIGADE_INSERT_TAIL(bb, b);
+    }
+  } while (!eos);
+
+  if(p->post_len > p->rule->max_post_len) {
+    mctx->set_error(mctx, HTTP_REQUEST_ENTITY_TOO_LARGE, "request too big");
+    return;
+  }
+
+  p->post_buf = apr_palloc(mctx->pool, p->post_len+1);
+
+  rv = apr_brigade_flatten(bb, p->post_buf, &(p->post_len));
+  if(rv != APR_SUCCESS) {
+    mctx->set_error(mctx, 500, "error (flatten) reading form data");
+    return;
+  }
+  p->post_buf[p->post_len] = 0;
+}
+
 static int write_http_response(mapcache_context_apache_request *ctx, mapcache_http_response *response)
 {
   request_rec *r = ctx->request;
@@ -239,7 +300,7 @@ static int write_http_response(mapcache_context_apache_request *ctx, mapcache_ht
       }
     }
   }
-  if(response->data) {
+  if(response->data && response->data->size) {
     ap_set_content_length(r,response->data->size);
     ap_rwrite((void*)response->data->buf, response->data->size, r);
   }
@@ -251,10 +312,16 @@ static int write_http_response(mapcache_context_apache_request *ctx, mapcache_ht
 
 static void mod_mapcache_child_init(apr_pool_t *pool, server_rec *s)
 {
-  pchild = pool;
+  int rv;
+  mapcache_server_cfg* cfg = ap_get_module_config(s->module_config, &mapcache_module);
 #ifdef APR_HAS_THREADS
   apr_thread_mutex_create(&thread_mutex,APR_THREAD_MUTEX_DEFAULT,pool);
 #endif
+  rv = mapcache_connection_pool_create(&(cfg->cp),pool);
+  ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "creating a mapcache connection pool for server");
+  if(rv!=APR_SUCCESS) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "failed to create mapcache connection pool");
+  }
 }
 
 static int mod_mapcache_request_handler(request_rec *r)
@@ -268,13 +335,15 @@ static int mod_mapcache_request_handler(request_rec *r)
   if (!r->handler || strcmp(r->handler, "mapcache")) {
     return DECLINED;
   }
-  if (r->method_number != M_GET) {
+  if (r->method_number != M_GET && r->method_number != M_POST) {
     return HTTP_METHOD_NOT_ALLOWED;
   }
 
 
   apache_ctx = apache_request_context_create(r);
   global_ctx = (mapcache_context*)apache_ctx;
+  global_ctx->supports_redirects = 1;
+  global_ctx->headers_in = r->headers_in;
 
   params = mapcache_http_parse_param_string(global_ctx, r->args);
 
@@ -316,7 +385,45 @@ static int mod_mapcache_request_handler(request_rec *r)
     mapcache_request_get_tile *req_tile = (mapcache_request_get_tile*)request;
     http_response = mapcache_core_get_tile(global_ctx,req_tile);
   } else if( request->type == MAPCACHE_REQUEST_PROXY ) {
+    const char *buf;
     mapcache_request_proxy *req_proxy = (mapcache_request_proxy*)request;
+    if(r->method_number == M_POST) {
+      read_post_body(apache_ctx, req_proxy);
+      if(GC_HAS_ERROR(global_ctx)) {
+        return write_http_response(apache_ctx, mapcache_core_respond_to_error(global_ctx));
+      }
+      if(!req_proxy->headers) {
+        req_proxy->headers = apr_table_make(global_ctx->pool, 2);
+      }
+      apr_table_set(req_proxy->headers, "Content-Type", r->content_type);
+      if((buf = apr_table_get(r->headers_in,"X-Forwarded-For"))) {
+#if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4)
+        apr_table_set(req_proxy->headers, "X-Forwarded-For", apr_psprintf(global_ctx->pool,"%s, %s", buf, r->connection->remote_ip));
+#else
+        apr_table_set(req_proxy->headers, "X-Forwarded-For", apr_psprintf(global_ctx->pool,"%s, %s", buf, r->connection->client_ip));
+#endif
+      } else {
+#if (AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER < 4)
+        apr_table_set(req_proxy->headers, "X-Forwarded-For", r->connection->remote_ip);
+#else
+        apr_table_set(req_proxy->headers, "X-Forwarded-For", r->connection->client_ip);
+#endif
+      }
+      if ((buf = apr_table_get(r->headers_in, "Host"))) {
+        const char *buf2;
+        if((buf2 = apr_table_get(r->headers_in,"X-Forwarded-Host"))) {
+          apr_table_set(req_proxy->headers, "X-Forwarded-Host", apr_psprintf(global_ctx->pool,"%s, %s",buf2,buf));
+        } else {
+          apr_table_set(req_proxy->headers, "X-Forwarded-Host", buf);
+        }
+      }
+      
+      if ((buf = apr_table_get(r->headers_in, "X-Forwarded-Server"))) {
+        apr_table_set(req_proxy->headers, "X-Forwarded-Server", apr_psprintf(global_ctx->pool, "%s, %s", buf, r->server->server_hostname));
+      } else {
+        apr_table_set(req_proxy->headers, "X-Forwarded-Server", r->server->server_hostname);
+      }
+    }
     http_response = mapcache_core_proxy_request(global_ctx, req_proxy);
   } else if( request->type == MAPCACHE_REQUEST_GET_MAP) {
     mapcache_request_get_map *req_map = (mapcache_request_get_map*)request;
@@ -342,6 +449,7 @@ static int mod_mapcache_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t
     ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "configuration not found in server context");
     return 1;
   }
+  
 
 #ifdef USE_VERSION_STRING
   ap_add_version_component(p, MAPCACHE_USERAGENT);
diff --git a/cgi/mapcache.c b/cgi/mapcache.c
index 9f2caca..e7b9c20 100644
--- a/cgi/mapcache.c
+++ b/cgi/mapcache.c
@@ -198,6 +198,7 @@ static void load_config(mapcache_context *ctx, char *filename)
     apr_pool_destroy(config_pool);
   }
   config_pool = tmp_config_pool;
+  mapcache_connection_pool_create(&ctx->connection_pool, config_pool);
 
   return;
 
@@ -273,7 +274,6 @@ int main(int argc, const char **argv)
       }
     }
     apr_pool_create(&(ctx->pool),config_pool);
-    ctx->process_pool = config_pool;
     ctx->threadlock = NULL;
     request = NULL;
     pathInfo = getenv("PATH_INFO");
diff --git a/cmake/FindRIAK.cmake b/cmake/FindRIAK.cmake
new file mode 100644
index 0000000..b0b6735
--- /dev/null
+++ b/cmake/FindRIAK.cmake
@@ -0,0 +1,14 @@
+
+FIND_PATH(RIAK_INCLUDE_DIR
+    NAMES riack.h
+)
+
+FIND_LIBRARY(RIAK_LIBRARY
+    NAMES riack
+)
+
+set(RIAK_INCLUDE_DIRS ${RIAK_INCLUDE_DIR})
+set(RIAK_LIBRARIES ${RIAK_LIBRARY})
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(RIAK DEFAULT_MSG RIAK_LIBRARY RIAK_INCLUDE_DIR)
+mark_as_advanced(RIAK_LIBRARY RIAK_INCLUDE_DIR)
diff --git a/include/mapcache-config.h.in b/include/mapcache-config.h.in
index 537afca..805a394 100644
--- a/include/mapcache-config.h.in
+++ b/include/mapcache-config.h.in
@@ -11,6 +11,7 @@
 #cmakedefine USE_GEOTIFF 1
 #cmakedefine USE_PCRE 1
 #cmakedefine USE_MAPSERVER 1
+#cmakedefine USE_RIAK 1
 
 #cmakedefine HAVE_STRNCASECMP 1
 #cmakedefine HAVE_SYMLINK 1
diff --git a/include/mapcache.h b/include/mapcache.h
index 5d3bded..c4ed758 100644
--- a/include/mapcache.h
+++ b/include/mapcache.h
@@ -39,6 +39,7 @@
 
 #include <apr_tables.h>
 #include <apr_hash.h>
+#include <apr_reslist.h>
 
 #include "util.h"
 #include "ezxml.h"
@@ -66,6 +67,14 @@
 #include <apr_memcache.h>
 #endif
 
+#ifdef USE_COUCHBASE
+#include <libcouchbase/couchbase.h>
+#endif
+
+#ifdef USE_RIAK
+#include <riack.h>
+#endif
+
 #define MAPCACHE_SUCCESS 0
 #define MAPCACHE_FAILURE 1
 #define MAPCACHE_TRUE 1
@@ -75,10 +84,12 @@
 #define MAPCACHE_TILESET_WRONG_EXTENT 4
 #define MAPCACHE_CACHE_MISS 5
 #define MAPCACHE_FILE_LOCKED 6
+#define MAPCACHE_CACHE_RELOAD 7
+
+#define MAPCACHE_MAX_NUM_TILES 1000
 
 #define MAPCACHE_USERAGENT "mod-mapcache/"MAPCACHE_VERSION
 
-#define MAPCACHE_LOCKFILE_PREFIX "_gc_lock"
 
 
 
@@ -103,11 +114,19 @@ typedef struct mapcache_source_wms mapcache_source_wms;
 typedef struct mapcache_source_gdal mapcache_source_gdal;
 #endif
 typedef struct mapcache_cache_disk mapcache_cache_disk;
+typedef struct mapcache_cache_composite mapcache_cache_composite;
+typedef struct mapcache_cache_fallback mapcache_cache_fallback;
+typedef struct mapcache_cache_multitier mapcache_cache_multitier;
+typedef struct mapcache_cache_rest mapcache_cache_rest;
+typedef struct mapcache_cache_s3 mapcache_cache_s3;
+typedef struct mapcache_cache_azure mapcache_cache_azure;
+typedef struct mapcache_cache_google mapcache_cache_google;
 #ifdef USE_TIFF
 typedef struct mapcache_cache_tiff mapcache_cache_tiff;
 #endif
 typedef struct mapcache_http mapcache_http;
 typedef struct mapcache_request mapcache_request;
+typedef struct mapcache_request_image mapcache_request_image;
 typedef struct mapcache_request_proxy mapcache_request_proxy;
 typedef struct mapcache_request_get_capabilities mapcache_request_get_capabilities;
 typedef struct mapcache_request_get_capabilities_demo mapcache_request_get_capabilities_demo;
@@ -139,9 +158,12 @@ typedef struct mapcache_dimension_time mapcache_dimension_time;
 typedef struct mapcache_timedimension mapcache_timedimension;
 typedef struct mapcache_dimension_intervals mapcache_dimension_intervals;
 typedef struct mapcache_dimension_values mapcache_dimension_values;
+typedef struct mapcache_dimension_sqlite mapcache_dimension_sqlite;
 typedef struct mapcache_dimension_regex mapcache_dimension_regex;
 typedef struct mapcache_extent mapcache_extent;
 typedef struct mapcache_extent_i mapcache_extent_i;
+typedef struct mapcache_connection_pool mapcache_connection_pool;
+typedef struct mapcache_locker mapcache_locker;
 
 /** \defgroup utility Utility */
 /** @{ */
@@ -197,6 +219,18 @@ struct mapcache_context {
    * \memberof mapcache_context
    */
   void (*clear_errors)(mapcache_context * ctx);
+  
+  /**
+   * \brief clear current error and store it in mapcache_error
+   * \memberof mapcache_context
+   */
+  void (*pop_errors)(mapcache_context * ctx, void **error);
+  
+  /**
+   * \brief restore error status from mapcache_error
+   * \memberof mapcache_context
+   */
+  void (*push_errors)(mapcache_context * ctx, void *error);
 
 
   /**
@@ -208,7 +242,7 @@ struct mapcache_context {
   const char* (*get_instance_id)(mapcache_context * ctx);
   mapcache_context* (*clone)(mapcache_context *ctx);
   apr_pool_t *pool;
-  apr_pool_t *process_pool;
+  mapcache_connection_pool *connection_pool;
   void *threadlock;
   char *_contenttype;
   char *_errmsg;
@@ -216,12 +250,14 @@ struct mapcache_context {
   mapcache_cfg *config;
   mapcache_service *service;
   apr_table_t *exceptions;
+  int supports_redirects;
+  apr_table_t *headers_in;
 };
 
 void mapcache_context_init(mapcache_context *ctx);
 void mapcache_context_copy(mapcache_context *src, mapcache_context *dst);
 
-#define GC_CHECK_ERROR_RETURN(ctx) (if(((mapcache_context*)ctx)->_errcode) return MAPCACHE_FAILURE;)
+#define GC_CHECK_ERROR_RETURN(ctx) if(((mapcache_context*)ctx)->_errcode) return MAPCACHE_FAILURE;
 #define GC_CHECK_ERROR(ctx) if(((mapcache_context*)ctx)->_errcode) return;
 #define GC_HAS_ERROR(ctx) (((mapcache_context*)ctx)->_errcode > 0)
 
@@ -299,6 +335,8 @@ mapcache_http* mapcache_http_clone(mapcache_context *ctx, mapcache_http *orig);
 struct mapcache_http {
   char *url; /**< the base url to request */
   apr_table_t *headers; /**< additional headers to add to the http request, eg, Referer */
+  char *post_body;
+  size_t post_len;
   int connection_timeout;
   int timeout;
   /* TODO: authentication */
@@ -355,7 +393,8 @@ struct mapcache_source_gdal {
 
 /** @{ */
 typedef enum {
-  MAPCACHE_CACHE_DISK
+  MAPCACHE_CACHE_DISK,
+  MAPCACHE_CACHE_REST
 #ifdef USE_MEMCACHE
   ,MAPCACHE_CACHE_MEMCACHE
 #endif
@@ -371,6 +410,13 @@ typedef enum {
 #ifdef USE_TIFF
   ,MAPCACHE_CACHE_TIFF
 #endif
+  ,MAPCACHE_CACHE_COMPOSITE
+#ifdef USE_COUCHBASE
+  ,MAPCACHE_CACHE_COUCHBASE
+#endif
+#ifdef USE_RIAK
+  ,MAPCACHE_CACHE_RIAK
+#endif
 } mapcache_cache_type;
 
 /** \interface mapcache_cache
@@ -388,23 +434,23 @@ struct mapcache_cache {
    * \returns MAPCACHE_CACHE_MISS if the file does not exist on the disk
    * \memberof mapcache_cache
    */
-  int (*tile_get)(mapcache_context *ctx, mapcache_tile * tile);
+  int (*tile_get)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
 
   /**
    * delete tile from cache
    *
    * \memberof mapcache_cache
    */
-  void (*tile_delete)(mapcache_context *ctx, mapcache_tile * tile);
+  void (*tile_delete)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
 
-  int (*tile_exists)(mapcache_context *ctx, mapcache_tile * tile);
+  int (*tile_exists)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
 
   /**
    * set tile content to cache
    * \memberof mapcache_cache
    */
-  void (*tile_set)(mapcache_context *ctx, mapcache_tile * tile);
-  void (*tile_multi_set)(mapcache_context *ctx, mapcache_tile *tiles, int ntiles);
+  void (*tile_set)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile * tile);
+  void (*tile_multi_set)(mapcache_context *ctx, mapcache_cache *cache, mapcache_tile *tiles, int ntiles);
 
   void (*configuration_parse_xml)(mapcache_context *ctx, ezxml_t xml, mapcache_cache * cache, mapcache_cfg *config);
   void (*configuration_post_config)(mapcache_context *ctx, mapcache_cache * cache, mapcache_cfg *config);
@@ -425,7 +471,109 @@ struct mapcache_cache_disk {
    * Set filename for a given tile
    * \memberof mapcache_cache_disk
    */
-  void (*tile_key)(mapcache_context *ctx, mapcache_tile *tile, char **path);
+  void (*tile_key)(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path);
+};
+
+typedef struct mapcache_cache_composite_cache_link mapcache_cache_composite_cache_link;
+struct mapcache_cache_composite_cache_link {
+  mapcache_cache *cache;
+  int minzoom;
+  int maxzoom;
+  apr_array_header_t *grids;
+  apr_array_header_t *dimensions; //TODO
+};
+
+struct mapcache_cache_composite {
+  mapcache_cache cache;
+  apr_array_header_t *cache_links;
+};
+
+struct mapcache_cache_fallback {
+  mapcache_cache cache;
+  apr_array_header_t *caches;
+};
+
+struct mapcache_cache_multitier {
+  mapcache_cache cache;
+  apr_array_header_t *caches;
+};
+
+
+typedef enum {
+  MAPCACHE_REST_METHOD_GET,
+  MAPCACHE_REST_METHOD_HEAD,
+  MAPCACHE_REST_METHOD_PUT,
+  MAPCACHE_REST_METHOD_POST,
+  MAPCACHE_REST_METHOD_DELETE
+} mapcache_rest_method;
+
+typedef enum {
+  MAPCACHE_REST_PROVIDER_NONE,
+  MAPCACHE_REST_PROVIDER_S3,
+  MAPCACHE_REST_PROVIDER_AZURE,
+  MAPCACHE_REST_PROVIDER_GOOGLE,
+} mapcache_rest_provider;
+
+void sha256(const unsigned char *message, unsigned int len, unsigned char *digest);
+void hmac_sha256(const unsigned char *message, unsigned int message_len,
+          const unsigned char *key, unsigned int key_size,
+          unsigned char *mac, unsigned mac_size);
+void hmac_sha1(const char *message, unsigned int message_len,
+          const unsigned char *key, unsigned int key_size,
+          void *mac);
+void sha_hex_encode(unsigned char *sha, unsigned int sha_size);
+char *base64_encode(apr_pool_t *pool, const unsigned char *data, size_t input_length);
+
+typedef struct mapcache_rest_operation mapcache_rest_operation;
+struct mapcache_rest_operation {
+  apr_table_t *headers;
+  mapcache_rest_method method;
+  char *tile_url;
+  void (*add_headers)(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers);
+};
+
+typedef struct mapcache_rest_configuration mapcache_rest_configuration;
+struct mapcache_rest_configuration {
+  apr_table_t *common_headers;
+  char *tile_url;
+  mapcache_rest_operation has_tile;
+  mapcache_rest_operation get_tile;
+  mapcache_rest_operation set_tile;
+  mapcache_rest_operation multi_set_tile;
+  mapcache_rest_operation delete_tile;
+  void (*add_headers)(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers);
+};
+
+/**\class mapcache_cache_rest
+ * \brief a mapcache_cache on a 3rd party HTTP Rest API
+ * \implements mapcache_cache
+ */
+struct mapcache_cache_rest {
+  mapcache_cache cache;
+  mapcache_rest_configuration rest;
+  int use_redirects;
+  int retry_count;
+  mapcache_rest_provider provider;
+};
+
+struct mapcache_cache_s3 {
+  mapcache_cache_rest cache;
+  char *id;
+  char *secret;
+  char *region;
+};
+
+struct mapcache_cache_azure {
+  mapcache_cache_rest cache;
+  char *id;
+  char *secret;
+  char *container;
+};
+
+struct mapcache_cache_google {
+  mapcache_cache_rest cache;
+  char *access;
+  char *secret;
 };
 
 #ifdef USE_TIFF
@@ -436,6 +584,7 @@ struct mapcache_cache_tiff {
   int count_x;
   int count_y;
   mapcache_image_format_jpeg *format;
+  mapcache_locker *locker;
 };
 #endif
 
@@ -451,6 +600,8 @@ struct mapcache_cache_sqlite_stmt {
   char *sql;
 };
 
+struct sqlite_conn;
+
 struct mapcache_cache_sqlite {
   mapcache_cache cache;
   char *dbfile;
@@ -460,9 +611,11 @@ struct mapcache_cache_sqlite {
   mapcache_cache_sqlite_stmt set_stmt;
   mapcache_cache_sqlite_stmt delete_stmt;
   apr_table_t *pragmas;
-  void (*bind_stmt)(mapcache_context*ctx, void *stmt, mapcache_tile *tile);
+  void (*bind_stmt)(mapcache_context *ctx, void *stmt, mapcache_cache_sqlite *cache, mapcache_tile *tile);
   int n_prepared_statements;
   int detect_blank;
+  char *x_fmt,*y_fmt,*z_fmt,*inv_x_fmt,*inv_y_fmt,*div_x_fmt,*div_y_fmt,*inv_div_x_fmt,*inv_div_y_fmt;
+  int count_x, count_y;
 };
 
 /**
@@ -499,9 +652,17 @@ typedef struct mapcache_cache_memcache mapcache_cache_memcache;
  * \brief a mapcache_cache on memcached servers
  * \implements mapcache_cache
  */
+
+struct mapcache_cache_memcache_server {
+    char* host;
+    int port;
+};
+
 struct mapcache_cache_memcache {
   mapcache_cache cache;
-  apr_memcache_t *memcache;
+  int nservers;
+  struct mapcache_cache_memcache_server *servers;
+  int detect_blank;
 };
 
 /**
@@ -510,6 +671,49 @@ struct mapcache_cache_memcache {
 mapcache_cache* mapcache_cache_memcache_create(mapcache_context *ctx);
 #endif
 
+#ifdef USE_COUCHBASE
+typedef struct mapcache_cache_couchbase mapcache_cache_couchbase;
+
+/**\class mapcache_cache_couchbase
+ * \brief a mapcache_cache on couchbase servers
+ * \implements mapcache_cache
+ */
+struct mapcache_cache_couchbase {
+   mapcache_cache cache;
+//   apr_reslist_t *connection_pool;
+   char *host;
+   char *username;
+   char *password;
+   char *bucket;
+   mapcache_context *ctx;
+};
+
+/**
+ * \memberof mapcache_cache_couchbase
+ */
+mapcache_cache* mapcache_cache_couchbase_create(mapcache_context *ctx);
+#endif
+
+#ifdef USE_RIAK
+typedef struct mapcache_cache_riak mapcache_cache_riak;
+
+/**\class mapcache_cache_riak
+ * \brief a mapcache_cache for riak servers
+ * \implements mapcache_cache
+ */
+struct mapcache_cache_riak {
+   mapcache_cache cache;
+   char *host;
+   int port;
+   RIACK_STRING bucket;
+};
+
+/**
+ * \memberof mapcache_cache_riak
+ */
+mapcache_cache* mapcache_cache_riak_create(mapcache_context *ctx);
+#endif
+
 /** @} */
 
 
@@ -542,8 +746,13 @@ struct mapcache_request {
   mapcache_service *service;
 };
 
+struct mapcache_request_image {
+    mapcache_request request;
+    mapcache_image_format *format;
+};
+
 struct mapcache_request_get_tile {
-  mapcache_request request;
+  mapcache_request_image image_request;
 
   /**
    * a list of tiles requested by the client
@@ -557,8 +766,7 @@ struct mapcache_request_get_tile {
    * before being returned to the client
    */
   int ntiles;
-  mapcache_image_format *format;
-
+  int allow_redirect;
 };
 
 struct mapcache_http_response {
@@ -594,12 +802,11 @@ struct mapcache_request_get_feature_info {
 };
 
 struct mapcache_request_get_map {
-  mapcache_request request;
+  mapcache_request_image image_request;
   mapcache_map **maps;
   int nmaps;
   mapcache_getmap_strategy getmap_strategy;
   mapcache_resample_mode resample_mode;
-  mapcache_image_format *getmap_format;
 };
 
 struct mapcache_request_get_capabilities {
@@ -647,18 +854,22 @@ struct mapcache_request_get_capabilities_demo {
   mapcache_service *service;
 };
 
-struct mapcache_request_proxy {
-  mapcache_request request;
-  mapcache_http *http;
-  apr_table_t *params;
-  const char *pathinfo;
-};
-
 struct mapcache_forwarding_rule {
   char *name;
   mapcache_http *http;
   apr_array_header_t *match_params;  /* actually those are mapcache_dimensions */
   int append_pathinfo;
+  size_t max_post_len;
+};
+
+struct mapcache_request_proxy {
+  mapcache_request request;
+  mapcache_forwarding_rule *rule;
+  apr_table_t *params;
+  apr_table_t *headers;
+  const char *pathinfo;
+  char *post_buf;
+  size_t post_len;
 };
 
 
@@ -728,6 +939,7 @@ struct mapcache_service_wms {
   mapcache_getmap_strategy getmap_strategy;
   mapcache_resample_mode resample_mode;
   mapcache_image_format *getmap_format;
+  int allow_format_override; /* can the client specify which image format should be returned */
 };
 
 /**\class mapcache_service_kml
@@ -926,14 +1138,14 @@ int mapcache_image_blank_color(mapcache_image* image);
  */
 int mapcache_image_has_alpha(mapcache_image *img);
 
+void mapcache_image_fill(mapcache_context *ctx, mapcache_image *image, const unsigned char *fill_color);
+
 /** @} */
 
 
 /** \defgroup http HTTP Request handling*/
 /** @{ */
 void mapcache_http_do_request(mapcache_context *ctx, mapcache_http *req, mapcache_buffer *data, apr_table_t *headers, long *http_code);
-void mapcache_http_do_request_with_params(mapcache_context *ctx, mapcache_http *req, apr_table_t *params,
-    mapcache_buffer *data, apr_table_t *headers, long *http_code);
 char* mapcache_http_build_url(mapcache_context *ctx, char *base, apr_table_t *params);
 apr_table_t *mapcache_http_parse_param_string(mapcache_context *ctx, char *args);
 /** @} */
@@ -944,6 +1156,7 @@ apr_table_t *mapcache_http_parse_param_string(mapcache_context *ctx, char *args)
 
 struct mapcache_server_cfg {
   apr_hash_t *aliases; /**< list of mapcache configurations aliased to a server uri */
+  mapcache_connection_pool *cp;
 };
 
 
@@ -954,6 +1167,68 @@ typedef enum {
   MAPCACHE_MODE_MIRROR_SPLIT
 } mapcache_mode;
 
+typedef enum {
+  MAPCACHE_LOCKER_DISK,
+  MAPCACHE_LOCKER_MEMCACHE,
+  MAPCACHE_LOCKER_FALLBACK
+} mapcache_lock_mode;
+
+typedef enum {
+    MAPCACHE_LOCK_AQUIRED,
+    MAPCACHE_LOCK_LOCKED,
+    MAPCACHE_LOCK_NOENT
+} mapcache_lock_result;
+
+
+struct mapcache_locker{
+  mapcache_lock_result (*aquire_lock)(mapcache_context *ctx, mapcache_locker *self, char *resource, void **lock);
+  mapcache_lock_result (*ping_lock)(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock);
+  void (*release_lock)(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock);
+  
+  void (*parse_xml)(mapcache_context *ctx, mapcache_locker *self, ezxml_t node);
+  mapcache_lock_mode type;
+  double timeout;
+  double retry_interval; /* time to wait before checking again on a lock, in seconds */
+};
+
+typedef struct {
+  mapcache_locker locker;
+
+  /**
+   * directory where lock files will be placed.
+   * Must be readable and writable by the apache user.
+   * Must be placed on a network mounted shared directory if multiple mapcache instances
+   * need to be synchronized
+   */
+  const char *dir;
+} mapcache_locker_disk;
+
+mapcache_locker* mapcache_locker_disk_create(mapcache_context *ctx);
+
+#ifdef USE_MEMCACHE
+typedef struct {
+  char *host;
+  int port;
+} mapcache_locker_memcache_server;
+
+typedef struct {
+  mapcache_locker locker;
+  int nservers;
+  mapcache_locker_memcache_server *servers;
+} mapcache_locker_memcache;
+
+mapcache_locker* mapcache_locker_memcache_create(mapcache_context *ctx);
+#endif
+
+typedef struct {
+    mapcache_locker locker;
+    apr_array_header_t *lockers;
+} mapcache_locker_fallback;
+
+mapcache_locker* mapcache_locker_fallback_create(mapcache_context *ctx);
+
+void mapcache_config_parse_locker(mapcache_context *ctx, ezxml_t node, mapcache_locker **locker);
+
 /**
  * a configuration that will be served
  */
@@ -1011,18 +1286,7 @@ struct mapcache_cfg {
 
   apr_table_t *metadata;
 
-  /**
-   * directory where lock files will be placed.
-   * Must be readable and writable by the apache user.
-   * Must be placed on a network mounted shared directory if multiple mapcache instances
-   * need to be synchronized
-   */
-  const char *lockdir;
-
-  /**
-   * time in nanoseconds to wait before rechecking for lockfile presence
-   */
-  apr_interval_time_t lock_retry_interval; /* time in nanoseconds to wait before rechecking for lockfile presence */
+  mapcache_locker *locker;
 
   int threaded_fetching;
 
@@ -1096,6 +1360,14 @@ mapcache_source* mapcache_source_dummy_create(mapcache_context *ctx);
  */
 mapcache_cache* mapcache_cache_disk_create(mapcache_context *ctx);
 
+/**
+ * \memberof mapcache_cache_rest
+ */
+mapcache_cache* mapcache_cache_rest_create(mapcache_context *ctx);
+mapcache_cache* mapcache_cache_s3_create(mapcache_context *ctx);
+mapcache_cache* mapcache_cache_azure_create(mapcache_context *ctx);
+mapcache_cache* mapcache_cache_google_create(mapcache_context *ctx);
+
 #ifdef USE_TIFF
 /**
  * \memberof mapcache_cache_tiff
@@ -1103,6 +1375,10 @@ mapcache_cache* mapcache_cache_disk_create(mapcache_context *ctx);
 mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx);
 #endif
 
+mapcache_cache* mapcache_cache_composite_create(mapcache_context *ctx);
+mapcache_cache* mapcache_cache_fallback_create(mapcache_context *ctx);
+mapcache_cache* mapcache_cache_multitier_create(mapcache_context *ctx);
+
 
 /** \defgroup tileset Tilesets*/
 /** @{ */
@@ -1125,6 +1401,8 @@ struct mapcache_tile {
    * \sa mapcache_image_format
    */
   mapcache_buffer *encoded_data;
+  char *redirect;
+  int allow_redirect;
   mapcache_image *raw_image;
   apr_time_t mtime; /**< last modification time */
   int expires; /**< time in seconds after which the tile should be rechecked for validity */
@@ -1209,6 +1487,8 @@ struct mapcache_grid_link {
 
   int max_cached_zoom;
   mapcache_outofzoom_strategy outofzoom_strategy;
+
+  apr_array_header_t *intermediate_grids;
 };
 
 /**\class mapcache_tileset
@@ -1262,7 +1542,7 @@ struct mapcache_tileset {
   /**
    * the cache in which the tiles should be stored
    */
-  mapcache_cache *cache;
+  mapcache_cache *_cache;
 
   /**
    * the source from which tiles should be requested
@@ -1301,7 +1581,8 @@ void mapcache_tileset_get_map_tiles(mapcache_context *ctx, mapcache_tileset *til
                                     mapcache_grid_link *grid_link,
                                     mapcache_extent *bbox, int width, int height,
                                     int *ntiles,
-                                    mapcache_tile ***tiles);
+                                    mapcache_tile ***tiles,
+                                    mapcache_grid_link **effectively_used_grid_link);
 
 mapcache_image* mapcache_tileset_assemble_map_tiles(mapcache_context *ctx, mapcache_tileset *tileset,
     mapcache_grid_link *grid_link,
@@ -1338,7 +1619,7 @@ void mapcache_tileset_tile_validate(mapcache_context *ctx, mapcache_tile *tile);
  */
 void mapcache_tileset_get_level(mapcache_context *ctx, mapcache_tileset *tileset, double *resolution, int *level);
 
-void mapcache_grid_get_closest_level(mapcache_context *ctx, mapcache_grid_link *grid, double resolution, int *level);
+mapcache_grid_link* mapcache_grid_get_closest_wms_level(mapcache_context *ctx, mapcache_grid_link *grid, double resolution, int *level);
 void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile);
 
 /**
@@ -1389,8 +1670,8 @@ void mapcache_tileset_configuration_check(mapcache_context *ctx, mapcache_tilese
 void mapcache_tileset_add_watermark(mapcache_context *ctx, mapcache_tileset *tileset, const char *filename);
 
 
-int mapcache_lock_or_wait_for_resource(mapcache_context *ctx, char *resource);
-void mapcache_unlock_resource(mapcache_context *ctx, char *resource);
+int mapcache_lock_or_wait_for_resource(mapcache_context *ctx, mapcache_locker *locker, char *resource, void **lock);
+void mapcache_unlock_resource(mapcache_context *ctx, mapcache_locker *locker, char *resource, void *lock);
 
 mapcache_metatile* mapcache_tileset_metatile_get(mapcache_context *ctx, mapcache_tile *tile);
 void mapcache_tileset_render_metatile(mapcache_context *ctx, mapcache_metatile *mt);
@@ -1484,6 +1765,7 @@ char* mapcache_util_get_tile_key(mapcache_context *ctx, mapcache_tile *tile, cha
 typedef enum {
   MAPCACHE_COMPRESSION_BEST, /**< best but slowest compression*/
   MAPCACHE_COMPRESSION_FAST, /**< fast compression*/
+  MAPCACHE_COMPRESSION_DISABLE, /**< no compression*/
   MAPCACHE_COMPRESSION_DEFAULT /**< default compression*/
 } mapcache_compression_type;
 
@@ -1535,7 +1817,7 @@ struct mapcache_image_format_mixed {
   mapcache_image_format *opaque;
 };
 
-mapcache_buffer* mapcache_empty_png_decode(mapcache_context *ctx, const unsigned char *hex_color, int *is_empty);
+mapcache_buffer* mapcache_empty_png_decode(mapcache_context *ctx, int width, int height, const unsigned char *hex_color, int *is_empty);
 
 
 mapcache_image_format* mapcache_imageio_create_mixed_format(apr_pool_t *pool,
@@ -1658,7 +1940,8 @@ typedef enum {
   MAPCACHE_DIMENSION_VALUES,
   MAPCACHE_DIMENSION_REGEX,
   MAPCACHE_DIMENSION_INTERVALS,
-  MAPCACHE_DIMENSION_TIME
+  MAPCACHE_DIMENSION_TIME,
+  MAPCACHE_DIMENSION_SQLITE
 } mapcache_dimension_type;
 
 struct mapcache_dimension {
@@ -1667,6 +1950,7 @@ struct mapcache_dimension {
   char *unit;
   apr_table_t *metadata;
   char *default_value;
+  int skip_validation;
 
   /**
    * \brief validate the given value
@@ -1683,7 +1967,7 @@ struct mapcache_dimension {
    *
    * \returns a list of character strings that will be included in the capabilities <dimension> element
    */
-  const char** (*print_ogc_formatted_values)(mapcache_context *context, mapcache_dimension *dimension);
+  apr_array_header_t*  (*print_ogc_formatted_values)(mapcache_context *context, mapcache_dimension *dimension);
 
   /**
    * \brief parse the value given in the configuration
@@ -1698,6 +1982,13 @@ struct mapcache_dimension_values {
   int case_sensitive;
 };
 
+struct mapcache_dimension_sqlite {
+  mapcache_dimension dimension;
+  char *dbfile;
+  char *validate_query;
+  char *list_query;
+};
+
 struct mapcache_dimension_regex {
   mapcache_dimension dimension;
   char *regex_string;
@@ -1721,6 +2012,7 @@ struct mapcache_dimension_time {
 };
 
 mapcache_dimension* mapcache_dimension_values_create(apr_pool_t *pool);
+mapcache_dimension* mapcache_dimension_sqlite_create(apr_pool_t *pool);
 mapcache_dimension* mapcache_dimension_regex_create(apr_pool_t *pool);
 mapcache_dimension* mapcache_dimension_intervals_create(apr_pool_t *pool);
 mapcache_dimension* mapcache_dimension_time_create(apr_pool_t *pool);
@@ -1759,6 +2051,26 @@ mapcache_timedimension* mapcache_timedimension_sqlite_create(apr_pool_t *pool);
 
 int mapcache_is_axis_inverted(const char *srs);
 
+typedef struct mapcache_pooled_connection_container mapcache_pooled_connection_container;
+typedef struct mapcache_pooled_connection mapcache_pooled_connection;
+typedef struct mapcache_pooled_connection_private_data mapcache_pooled_connection_private_data;
+
+struct mapcache_pooled_connection {
+    mapcache_pooled_connection_private_data *private;
+    void *connection;
+};
+
+typedef void (*mapcache_connection_constructor)(mapcache_context *ctx, void **connection, void *params, apr_pool_t *process_pool);
+typedef void (*mapcache_connection_destructor)(void *connection, apr_pool_t *process_pool);
+
+apr_status_t mapcache_connection_pool_create(mapcache_connection_pool **cp, apr_pool_t *server_pool);
+mapcache_pooled_connection* mapcache_connection_pool_get_connection(mapcache_context *ctx, char *key,
+        mapcache_connection_constructor constructor,
+        mapcache_connection_destructor destructor,
+        void *params);
+void mapcache_connection_pool_invalidate_connection(mapcache_context *ctx, mapcache_pooled_connection *connection);
+void mapcache_connection_pool_release_connection(mapcache_context *ctx, mapcache_pooled_connection *connection);
+
 #endif /* MAPCACHE_H_ */
 /* vim: ts=2 sts=2 et sw=2
 */
diff --git a/lib/buffer.c b/lib/buffer.c
index 4282a57..4f0fd3b 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -57,6 +57,7 @@ mapcache_buffer *mapcache_buffer_create(size_t initialStorage, apr_pool_t* pool)
   mapcache_buffer *buffer = apr_pcalloc(pool, sizeof(mapcache_buffer));
   if(!buffer) return NULL;
   buffer->pool = pool;
+  if(initialStorage <=0) initialStorage = 1;
   buffer->avail = initialStorage;
   if(buffer->avail) {
     buffer->buf = malloc(buffer->avail);
diff --git a/lib/cache_bdb.c b/lib/cache_bdb.c
index bb08367..fd40d43 100644
--- a/lib/cache_bdb.c
+++ b/lib/cache_bdb.c
@@ -51,9 +51,6 @@
 #define PAGESIZE 64*1024
 #define CACHESIZE 1024*1024
 
-static apr_hash_t *ro_connection_pools = NULL;
-static apr_hash_t *rw_connection_pools = NULL;
-
 struct bdb_env {
   DB* db;
   DB_ENV *env;
@@ -61,7 +58,7 @@ struct bdb_env {
   char *errmsg;
 };
 
-static apr_status_t _bdb_reslist_get_connection(void **conn_, void *params, apr_pool_t *pool)
+void mapcache_bdb_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *pool)
 {
   int ret;
   int env_flags;
@@ -73,156 +70,84 @@ static apr_status_t _bdb_reslist_get_connection(void **conn_, void *params, apr_
 
   ret = db_env_create(&benv->env, 0);
   if(ret) {
-    benv->errmsg = apr_psprintf(pool,"bdb cache failure for db_env_create: %s", db_strerror(ret));
-    return APR_EGENERAL;
+    ctx->set_error(ctx, 500, "bdb cache failure for db_env_create: %s", db_strerror(ret));
+    free(benv);
+    return;
   }
   ret = benv->env->set_cachesize(benv->env,0,CACHESIZE,1); /* set a larger cache size than default */
   if(ret) {
-    benv->errmsg = apr_psprintf(pool, "bdb cache failure for db->set_cachesize: %s", db_strerror(ret));
-    return APR_EGENERAL;
+    ctx->set_error(ctx, 500, "bdb cache failure for db->set_cachesize: %s", db_strerror(ret));
+    free(benv);
+    return;
   }
   env_flags = DB_INIT_CDB|DB_INIT_MPOOL|DB_CREATE;
   ret = benv->env->open(benv->env,cache->basedir,env_flags,0);
   if(ret) {
-    benv->errmsg = apr_psprintf(pool,"bdb cache failure for env->open: %s", db_strerror(ret));
-    return APR_EGENERAL;
+    ctx->set_error(ctx,500,"bdb cache failure for env->open: %s", db_strerror(ret));
+    free(benv);
+    return;
   }
 
   if ((ret = db_create(&benv->db, benv->env, 0)) != 0) {
-    benv->errmsg = apr_psprintf(pool,"bdb cache failure for db_create: %s", db_strerror(ret));
-    return APR_EGENERAL;
+    ctx->set_error(ctx,500,"bdb cache failure for db_create: %s", db_strerror(ret));
+    free(benv);
   }
   mode = DB_BTREE;
   ret = benv->db->set_pagesize(benv->db,PAGESIZE); /* set pagesize to maximum allowed, as tile data is usually pretty large */
   if(ret) {
-    benv->errmsg = apr_psprintf(pool,"bdb cache failure for db->set_pagesize: %s", db_strerror(ret));
-    return APR_EGENERAL;
+    ctx->set_error(ctx,500,"bdb cache failure for db->set_pagesize: %s", db_strerror(ret));
+    free(benv);
+    return;
   }
 
   if ((ret = benv->db->open(benv->db, NULL, dbfile, NULL, mode, DB_CREATE, 0664)) != 0) {
-    benv->errmsg = apr_psprintf(pool,"bdb cache failure 1 for db->open: %s", db_strerror(ret));
-    return APR_EGENERAL;
+    ctx->set_error(ctx,500,"bdb cache failure 1 for db->open: %s", db_strerror(ret));
+    free(benv);
+    return;
   }
-  return APR_SUCCESS;
 }
 
-static apr_status_t _bdb_reslist_free_connection(void *conn_, void *params, apr_pool_t *pool)
+void mapcache_bdb_connection_destructor(void *conn_, apr_pool_t *pool)
 {
   struct bdb_env *benv = (struct bdb_env*)conn_;
   benv->db->close(benv->db,0);
   benv->env->close(benv->env,0);
   free(benv);
-
-  return APR_SUCCESS;
 }
 
 
 
-static struct bdb_env* _bdb_get_conn(mapcache_context *ctx, mapcache_tile* tile, int readonly) {
-  apr_status_t rv;
-  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache;
-
+static mapcache_pooled_connection* _bdb_get_conn(mapcache_context *ctx, mapcache_cache_bdb *cache, mapcache_tile* tile, int readonly) {
   struct bdb_env *benv;
-  apr_hash_t *pool_container;
-  apr_reslist_t *pool = NULL;
-  if(readonly) {
-    pool_container = ro_connection_pools;
-  } else {
-    pool_container = rw_connection_pools;
-  }
-  if(!pool_container || NULL == (pool = apr_hash_get(pool_container,cache->cache.name, APR_HASH_KEY_STRING)) ) {
-#ifdef APR_HAS_THREADS
-    if(ctx->threadlock)
-      apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-    if(!ro_connection_pools) {
-      ro_connection_pools = apr_hash_make(ctx->process_pool);
-      rw_connection_pools = apr_hash_make(ctx->process_pool);
-    }
-
-    /* probably doesn't exist, unless the previous mutex locked us, so we check */
-    pool = apr_hash_get(ro_connection_pools,cache->cache.name, APR_HASH_KEY_STRING);
-    if(!pool) {
-      /* there where no existing connection pools, create them*/
-      rv = apr_reslist_create(&pool,
-                              0 /* min */,
-                              10 /* soft max */,
-                              200 /* hard max */,
-                              60*1000000 /*60 seconds, ttl*/,
-                              _bdb_reslist_get_connection, /* resource constructor */
-                              _bdb_reslist_free_connection, /* resource destructor */
-                              cache, ctx->process_pool);
-      if(rv != APR_SUCCESS) {
-        ctx->set_error(ctx,500,"failed to create bdb ro connection pool");
-#ifdef APR_HAS_THREADS
-        if(ctx->threadlock)
-          apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-        return NULL;
-      }
-      apr_hash_set(ro_connection_pools,cache->cache.name,APR_HASH_KEY_STRING,pool);
-      rv = apr_reslist_create(&pool,
-                              0 /* min */,
-                              1 /* soft max */,
-                              1 /* hard max */,
-                              60*1000000 /*60 seconds, ttl*/,
-                              _bdb_reslist_get_connection, /* resource constructor */
-                              _bdb_reslist_free_connection, /* resource destructor */
-                              cache, ctx->process_pool);
-      if(rv != APR_SUCCESS) {
-        ctx->set_error(ctx,500,"failed to create bdb rw connection pool");
-#ifdef APR_HAS_THREADS
-        if(ctx->threadlock)
-          apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-        return NULL;
-      }
-      apr_hash_set(rw_connection_pools,cache->cache.name,APR_HASH_KEY_STRING,pool);
-    }
-#ifdef APR_HAS_THREADS
-    if(ctx->threadlock)
-      apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-    if(readonly)
-      pool = apr_hash_get(ro_connection_pools,cache->cache.name, APR_HASH_KEY_STRING);
-    else
-      pool = apr_hash_get(rw_connection_pools,cache->cache.name, APR_HASH_KEY_STRING);
-    assert(pool);
-  }
-  rv = apr_reslist_acquire(pool, (void **)&benv);
-  if(rv != APR_SUCCESS) {
-    ctx->set_error(ctx,500,"failed to aquire connection to bdb backend: %s", (benv&& benv->errmsg)?benv->errmsg:"unknown error");
-    return NULL;
-  }
+  mapcache_pooled_connection *pc;
+  char *conn_key = apr_pstrcat(ctx->pool,readonly?"ro_":"rw_",cache->cache.name,NULL);
+  pc = mapcache_connection_pool_get_connection(ctx,conn_key,mapcache_bdb_connection_constructor, mapcache_bdb_connection_destructor, cache);
+  if(GC_HAS_ERROR(ctx)) return NULL;
+  benv = pc->connection;
   benv->readonly = readonly;
-  return benv;
+  return pc;
 }
 
-static void _bdb_release_conn(mapcache_context *ctx, mapcache_tile *tile, struct bdb_env *benv)
+static void _bdb_release_conn(mapcache_context *ctx, mapcache_cache_bdb *cache, mapcache_tile *tile, mapcache_pooled_connection *pc)
 {
-  apr_reslist_t *pool;
-  apr_hash_t *pool_container;
-  if(benv->readonly) {
-    pool_container = ro_connection_pools;
-  } else {
-    pool_container = rw_connection_pools;
-  }
-  pool = apr_hash_get(pool_container,tile->tileset->cache->name, APR_HASH_KEY_STRING);
   if(GC_HAS_ERROR(ctx)) {
-    apr_reslist_invalidate(pool,(void*)benv);
+    mapcache_connection_pool_invalidate_connection(ctx, pc);
   } else {
-    apr_reslist_release(pool, (void*)benv);
+    mapcache_connection_pool_release_connection(ctx,pc);
   }
 }
 
-static int _mapcache_cache_bdb_has_tile(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_bdb_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   int ret;
   DBT key;
-  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache;
+  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache;
   char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL);
-  struct bdb_env *benv = _bdb_get_conn(ctx,tile,1);
+  mapcache_pooled_connection *pc;
+  struct bdb_env *benv; 
+  pc = _bdb_get_conn(ctx,cache,tile,1);
   if(GC_HAS_ERROR(ctx)) return MAPCACHE_FALSE;
+  benv = pc->connection;
   memset(&key, 0, sizeof(DBT));
   key.data = skey;
   key.size = strlen(skey)+1;
@@ -237,18 +162,21 @@ static int _mapcache_cache_bdb_has_tile(mapcache_context *ctx, mapcache_tile *ti
     ctx->set_error(ctx,500,"bdb backend failure on tile_exists: %s",db_strerror(ret));
     ret= MAPCACHE_FALSE;
   }
-  _bdb_release_conn(ctx,tile,benv);
+  _bdb_release_conn(ctx,cache,tile,pc);
   return ret;
 }
 
-static void _mapcache_cache_bdb_delete(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_bdb_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   DBT key;
   int ret;
-  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache;
+  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache;
   char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL);
-  struct bdb_env *benv = _bdb_get_conn(ctx,tile,0);
+  mapcache_pooled_connection *pc;
+  struct bdb_env *benv; 
+  pc = _bdb_get_conn(ctx,cache,tile,0);
   GC_CHECK_ERROR(ctx);
+  benv = pc->connection;
   memset(&key, 0, sizeof(DBT));
   key.data = skey;
   key.size = strlen(skey)+1;
@@ -260,17 +188,20 @@ static void _mapcache_cache_bdb_delete(mapcache_context *ctx, mapcache_tile *til
     if(ret)
       ctx->set_error(ctx,500,"bdb backend sync failure on tile_delete: %s",db_strerror(ret));
   }
-  _bdb_release_conn(ctx,tile,benv);
+  _bdb_release_conn(ctx,cache,tile,pc);
 }
 
-static int _mapcache_cache_bdb_get(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_bdb_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   DBT key,data;
   int ret;
-  struct bdb_env *benv = _bdb_get_conn(ctx,tile,1);
-  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache;
+  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache;
+  mapcache_pooled_connection *pc;
+  struct bdb_env *benv; 
+  pc = _bdb_get_conn(ctx,cache,tile,1);
+  if(GC_HAS_ERROR(ctx)) return MAPCACHE_FALSE;
+  benv = pc->connection;
   char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL);
-  if(GC_HAS_ERROR(ctx)) return MAPCACHE_FAILURE;
   memset(&key, 0, sizeof(DBT));
   memset(&data, 0, sizeof(DBT));
   data.flags = DB_DBT_MALLOC;
@@ -282,7 +213,7 @@ static int _mapcache_cache_bdb_get(mapcache_context *ctx, mapcache_tile *tile)
 
   if(ret == 0) {
     if(((char*)(data.data))[0] == '#') {
-      tile->encoded_data = mapcache_empty_png_decode(ctx,(unsigned char*)data.data,&tile->nodata);
+      tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy, (unsigned char*)data.data,&tile->nodata);
     } else {
       tile->encoded_data = mapcache_buffer_create(0,ctx->pool);
       tile->encoded_data->buf = data.data;
@@ -298,20 +229,20 @@ static int _mapcache_cache_bdb_get(mapcache_context *ctx, mapcache_tile *tile)
     ctx->set_error(ctx,500,"bdb backend failure on tile_get: %s",db_strerror(ret));
     ret = MAPCACHE_FAILURE;
   }
-  _bdb_release_conn(ctx,tile,benv);
+  _bdb_release_conn(ctx,cache,tile,pc);
   return ret;
 }
 
 
-static void _mapcache_cache_bdb_set(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_bdb_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   DBT key,data;
   int ret;
   apr_time_t now;
-  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tile->tileset->cache;
+  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache;
   char *skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL);
-  struct bdb_env *benv = _bdb_get_conn(ctx,tile,0);
-  GC_CHECK_ERROR(ctx);
+  mapcache_pooled_connection *pc;
+  struct bdb_env *benv; 
   now = apr_time_now();
   memset(&key, 0, sizeof(DBT));
   memset(&data, 0, sizeof(DBT));
@@ -323,6 +254,7 @@ static void _mapcache_cache_bdb_set(mapcache_context *ctx, mapcache_tile *tile)
     tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
     GC_CHECK_ERROR(ctx);
   }
+  
   if(tile->raw_image->h==256 && tile->raw_image->w==256 && mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
     data.size = 5+sizeof(apr_time_t);
     data.data = apr_palloc(ctx->pool,data.size);
@@ -339,6 +271,11 @@ static void _mapcache_cache_bdb_set(mapcache_context *ctx, mapcache_tile *tile)
     data.size = tile->encoded_data->size;
     tile->encoded_data->size -= sizeof(apr_time_t);
   }
+  
+  pc = _bdb_get_conn(ctx,cache,tile,0);
+  GC_CHECK_ERROR(ctx);
+  benv = pc->connection;
+
 
   ret = benv->db->put(benv->db,NULL,&key,&data,0);
   if(ret != 0) {
@@ -348,21 +285,25 @@ static void _mapcache_cache_bdb_set(mapcache_context *ctx, mapcache_tile *tile)
     if(ret)
       ctx->set_error(ctx,500,"bdb backend sync failure on tile_set: %s",db_strerror(ret));
   }
-  _bdb_release_conn(ctx,tile,benv);
+  _bdb_release_conn(ctx,cache,tile,pc);
 }
 
-static void _mapcache_cache_bdb_multiset(mapcache_context *ctx, mapcache_tile *tiles, int ntiles)
+static void _mapcache_cache_bdb_multiset(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
 {
   DBT key,data;
   int ret,i;
   apr_time_t now;
-  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)tiles[0].tileset->cache;
-  struct bdb_env *benv = _bdb_get_conn(ctx,&tiles[0],0);
-  GC_CHECK_ERROR(ctx);
+  mapcache_cache_bdb *cache = (mapcache_cache_bdb*)pcache;
+  mapcache_pooled_connection *pc;
+  struct bdb_env *benv; 
   now = apr_time_now();
   memset(&key, 0, sizeof(DBT));
   memset(&data, 0, sizeof(DBT));
 
+  pc = _bdb_get_conn(ctx,cache,&tiles[0],0);
+  GC_CHECK_ERROR(ctx);
+  benv = pc->connection;
+  
   for(i=0; i<ntiles; i++) {
     char *skey;
     mapcache_tile *tile;
@@ -372,7 +313,10 @@ static void _mapcache_cache_bdb_multiset(mapcache_context *ctx, mapcache_tile *t
     skey = mapcache_util_get_tile_key(ctx,tile,cache->key_template,NULL,NULL);
     if(!tile->raw_image) {
       tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
-      GC_CHECK_ERROR(ctx);
+      if(GC_HAS_ERROR(ctx)) {
+        _bdb_release_conn(ctx,cache,&tiles[0],pc);
+        return;
+      }
     }
     if(tile->raw_image->h==256 && tile->raw_image->w==256 && mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
       data.size = 5+sizeof(apr_time_t);
@@ -383,7 +327,10 @@ static void _mapcache_cache_bdb_multiset(mapcache_context *ctx, mapcache_tile *t
     } else {
       if(!tile->encoded_data) {
         tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
-        GC_CHECK_ERROR(ctx);
+        if(GC_HAS_ERROR(ctx)) {
+          _bdb_release_conn(ctx,cache,&tiles[0],pc);
+          return;
+        }
       }
       mapcache_buffer_append(tile->encoded_data,sizeof(apr_time_t),&now);
       data.data = tile->encoded_data->buf;
@@ -404,7 +351,7 @@ static void _mapcache_cache_bdb_multiset(mapcache_context *ctx, mapcache_tile *t
     if(ret)
       ctx->set_error(ctx,500,"bdb backend sync failure on sync in tile_multiset: %s",db_strerror(ret));
   }
-  _bdb_release_conn(ctx,&tiles[0],benv);
+  _bdb_release_conn(ctx,cache,&tiles[0],pc);
 }
 
 
diff --git a/lib/cache_composite.c b/lib/cache_composite.c
new file mode 100644
index 0000000..4b307ef
--- /dev/null
+++ b/lib/cache_composite.c
@@ -0,0 +1,205 @@
+/******************************************************************************
+ *
+ * Project:  MapServer
+ * Purpose:  MapCache tile caching: composite cache backend.
+ * Author:   Thomas Bonfort and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2011 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without compositeriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "mapcache.h"
+
+static mapcache_cache_composite_cache_link* _mapcache_cache_link_create(apr_pool_t *pool) {
+  mapcache_cache_composite_cache_link *cl = apr_pcalloc(pool, sizeof(mapcache_cache_composite_cache_link));
+  cl->cache=NULL;
+  cl->dimensions=NULL;
+  cl->grids=NULL;
+  cl->maxzoom=-1;
+  cl->minzoom=-1;
+  return cl;
+}
+/**
+ * returns the mapcache_cache to use for a given tile
+ * @param ctx
+ * @param tile
+ * @return 
+ */
+static mapcache_cache* _mapcache_composite_cache_get(mapcache_context *ctx, mapcache_cache_composite *cache, mapcache_tile *tile) {
+  int i;
+  for(i=0; i<cache->cache_links->nelts; i++) {
+    mapcache_cache_composite_cache_link *cache_link = APR_ARRAY_IDX(cache->cache_links,i,mapcache_cache_composite_cache_link*);
+    if(cache_link->minzoom != -1 && tile->z < cache_link->minzoom) continue;
+    if(cache_link->maxzoom != -1 && tile->z > cache_link->maxzoom) continue;
+    if(cache_link->grids) {
+      int j;
+      for(j=0;j<cache_link->grids->nelts;j++) {
+        char *grid_name = APR_ARRAY_IDX(cache_link->grids,j,char*);
+        if(!strcmp(tile->grid_link->grid->name,grid_name))
+          break;
+      }
+      /* not found */
+      if(j == cache_link->grids->nelts) continue;
+    }
+    return cache_link->cache;
+  }
+  ctx->set_error(ctx, 500, "no cache matches for given tile request");
+  return NULL;
+}
+static int _mapcache_cache_composite_tile_exists(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
+  mapcache_cache *subcache;
+  subcache = _mapcache_composite_cache_get(ctx, cache, tile);
+  if(GC_HAS_ERROR(ctx) || !subcache)
+    return MAPCACHE_FAILURE;
+  return subcache->tile_exists(ctx, subcache, tile);
+}
+
+static void _mapcache_cache_composite_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
+  mapcache_cache *subcache;
+  subcache = _mapcache_composite_cache_get(ctx, cache, tile);
+  GC_CHECK_ERROR(ctx);
+  /*delete the tile itself*/
+  subcache->tile_delete(ctx,subcache,tile);
+}
+
+/**
+ * \brief get content of given tile
+ *
+ * fills the mapcache_tile::data of the given tile with content stored on the composite server
+ * \private \memberof mapcache_cache_composite
+ * \sa mapcache_cache::tile_get()
+ */
+static int _mapcache_cache_composite_tile_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
+  mapcache_cache *subcache;
+  subcache = _mapcache_composite_cache_get(ctx, cache, tile);
+  GC_CHECK_ERROR_RETURN(ctx);
+  return subcache->tile_get(ctx,subcache,tile);
+}
+
+static void _mapcache_cache_composite_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
+  mapcache_cache *subcache;
+  subcache = _mapcache_composite_cache_get(ctx, cache, tile);
+  GC_CHECK_ERROR(ctx);
+  return subcache->tile_set(ctx,subcache,tile);
+}
+
+static void _mapcache_cache_composite_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
+{
+  mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
+  mapcache_cache *subcache;
+  subcache = _mapcache_composite_cache_get(ctx, cache, &tiles[0]);
+  GC_CHECK_ERROR(ctx);
+  if(subcache->tile_multi_set) {
+    return subcache->tile_multi_set(ctx,subcache,tiles,ntiles);
+  } else {
+    int i;
+    for(i=0; i<ntiles; i++) {
+      subcache->tile_set(ctx, subcache, &tiles[i]);
+    }
+  }
+}
+
+/**
+ * \private \memberof mapcache_cache_composite
+ */
+static void _mapcache_cache_composite_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
+{
+  ezxml_t cur_node;
+  mapcache_cache_composite *cache = (mapcache_cache_composite*)pcache;
+  cache->cache_links = apr_array_make(ctx->pool,3,sizeof(mapcache_cache_composite_cache_link*));
+  for(cur_node = ezxml_child(node,"cache"); cur_node; cur_node = cur_node->next) {
+    char *sZoom;
+    int zoom;
+    mapcache_cache *refcache = mapcache_configuration_get_cache(config, cur_node->txt);
+    mapcache_cache_composite_cache_link *cachelink;
+    if(!refcache) {
+      ctx->set_error(ctx, 400, "composite cache \"%s\" references cache \"%s\","
+                     " but it is not configured (hint:referenced caches must be declared before this composite cache in the xml file)", pcache->name, cur_node->txt);
+      return;
+    }
+    cachelink = _mapcache_cache_link_create(ctx->pool);
+    cachelink->cache = refcache;
+
+    sZoom = (char*)ezxml_attr(cur_node,"max-zoom");
+    if(sZoom) {
+      char *endptr;
+      zoom = (int)strtol(sZoom,&endptr,10);
+      if(*endptr != 0 || zoom < 0) {
+        ctx->set_error(ctx, 400, "failed to parse cache max-zoom %s (expecting a positive integer)",
+                       sZoom);
+        return;
+      }
+      cachelink->maxzoom = zoom;
+    }
+    sZoom = (char*)ezxml_attr(cur_node,"min-zoom");
+    if(sZoom) {
+      char *endptr;
+      zoom = (int)strtol(sZoom,&endptr,10);
+      if(*endptr != 0 || zoom < 0) {
+        ctx->set_error(ctx, 400, "failed to parse cache min-zoom %s (expecting a positive integer)",
+                       sZoom);
+        return;
+      }
+      cachelink->minzoom = zoom;
+    }
+    
+    APR_ARRAY_PUSH(cache->cache_links,mapcache_cache_composite_cache_link*) = cachelink;
+  }
+}
+
+/**
+ * \private \memberof mapcache_cache_composite
+ */
+static void _mapcache_cache_composite_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
+    mapcache_cfg *cfg)
+{
+}
+
+
+/**
+ * \brief creates and initializes a mapcache_cache_composite
+ */
+mapcache_cache* mapcache_cache_composite_create(mapcache_context *ctx)
+{
+  mapcache_cache_composite *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_composite));
+  if(!cache) {
+    ctx->set_error(ctx, 500, "failed to allocate composite cache");
+    return NULL;
+  }
+  cache->cache.metadata = apr_table_make(ctx->pool,3);
+  cache->cache.type = MAPCACHE_CACHE_COMPOSITE;
+  cache->cache.tile_delete = _mapcache_cache_composite_tile_delete;
+  cache->cache.tile_get = _mapcache_cache_composite_tile_get;
+  cache->cache.tile_exists = _mapcache_cache_composite_tile_exists;
+  cache->cache.tile_set = _mapcache_cache_composite_tile_set;
+  cache->cache.tile_multi_set = _mapcache_cache_composite_tile_multi_set;
+  cache->cache.configuration_post_config = _mapcache_cache_composite_configuration_post_config;
+  cache->cache.configuration_parse_xml = _mapcache_cache_composite_configuration_parse_xml;
+  return (mapcache_cache*)cache;
+}
diff --git a/lib/cache_couchbase.c b/lib/cache_couchbase.c
new file mode 100644
index 0000000..356402d
--- /dev/null
+++ b/lib/cache_couchbase.c
@@ -0,0 +1,462 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  MapServer
+ * Purpose:  MapCache tile caching support file: couchbase cache backend.
+ * Author:   Michael Downey and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2011 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "mapcache-config.h"
+#ifdef USE_COUCHBASE
+
+#include "mapcache.h"
+#include <apr_strings.h>
+#include <string.h>
+#include <errno.h>
+
+typedef struct getStruct
+{
+   mapcache_buffer *tileBuffer;
+   libcouchbase_error_t error;
+} getStruct_t;
+
+/* Not sure if we need this. */
+static void _couchbase_error_callback(libcouchbase_t instance,
+                                      libcouchbase_error_t error,
+                                      const char *errinfo)
+{
+   /* Ignore timeouts... */
+   if (error != LIBCOUCHBASE_ETIMEDOUT) {
+      fprintf(stderr, "\nFATAL ERROR: %s\n",
+              libcouchbase_strerror(instance, error));
+      if (errinfo && strlen(errinfo) != 0) {
+         fprintf(stderr, "\t\"%s\"\n", errinfo);
+      }
+   }
+}
+
+static void _couchbase_get_callback(libcouchbase_t instance,
+                                    const void *cookie,
+                                    libcouchbase_error_t error,
+                                    const void *key, libcouchbase_size_t nkey,
+                                    const void *bytes, libcouchbase_size_t nbytes,
+                                    libcouchbase_uint32_t flags, libcouchbase_cas_t cas)
+{
+   (void)instance;
+   (void)key;
+   (void)nkey;
+   (void)flags;
+   (void)cas;
+
+   if (cookie)
+   {
+       getStruct_t *request = (getStruct_t*)cookie;
+
+       request->error = error;
+
+       if (error == LIBCOUCHBASE_SUCCESS && request->tileBuffer)
+       {
+           mapcache_buffer_append(request->tileBuffer, nbytes, (void*)bytes);
+       }
+    }
+}
+
+static void _couchbase_store_callback(libcouchbase_t instance,
+                                      const void* cookie,
+                                      libcouchbase_storage_t unknown,
+                                      libcouchbase_error_t error,
+                                      const void* unknown2,
+                                      libcouchbase_size_t unknown3,
+                                      libcouchbase_cas_t cas)
+{
+   (void)instance;
+   (void)unknown;
+   (void)unknown2;
+   (void)unknown3;
+   (void)cas;
+
+   libcouchbase_error_t* userError = (libcouchbase_error_t*)cookie;
+
+   *userError = error;
+}
+
+static apr_status_t _couchbase_reslist_get_connection(void **conn_, void *params, apr_pool_t *pool) {
+   mapcache_cache_couchbase *cache = (mapcache_cache_couchbase*)params;
+
+   libcouchbase_t *instance = apr_pcalloc(pool,sizeof(libcouchbase_t));
+   const char *host = cache->host;
+   const char *username = cache->username;
+   const char *passwd = cache->password;
+   const char *bucket = "default";
+
+  *instance = libcouchbase_create(host, username, passwd, bucket, NULL);
+   if (*instance == NULL) {
+      return APR_EGENERAL;
+   } 
+
+   libcouchbase_set_error_callback(*instance, _couchbase_error_callback);
+   libcouchbase_set_get_callback(*instance, _couchbase_get_callback);
+   libcouchbase_set_storage_callback(*instance, _couchbase_store_callback);
+
+   if (libcouchbase_connect(*instance) != LIBCOUCHBASE_SUCCESS) {
+       return APR_EGENERAL;
+   }
+   
+   /* Wait for the connect to compelete */
+   libcouchbase_wait(*instance);
+
+   *conn_ = instance;
+   return APR_SUCCESS;
+}
+
+static apr_status_t _couchbase_reslist_free_connection(void *conn_, void *params, apr_pool_t *pool) {
+   libcouchbase_t *instance = (libcouchbase_t*)conn_;
+   libcouchbase_destroy(*instance);
+   return APR_SUCCESS; 
+}
+
+static libcouchbase_t* _couchbase_get_connection(mapcache_context *ctx, mapcache_tile *tile)
+{
+   apr_status_t rv;
+   libcouchbase_t *instance;
+   mapcache_cache_couchbase *cache = (mapcache_cache_couchbase*)tile->tileset->cache;
+   
+   rv = apr_reslist_acquire(cache->connection_pool, (void **)&instance);
+   if(rv != APR_SUCCESS) {
+      ctx->set_error(ctx, 500, "failed to aquire connection to couchbase backend: %s", ctx->get_error_message(ctx));
+      return NULL;
+   }
+
+   return instance;
+}
+
+static void _couchbase_release_connection(mapcache_tile *tile, libcouchbase_t* instance)
+{
+   mapcache_cache_couchbase* cache = (mapcache_cache_couchbase*)tile->tileset->cache;
+   apr_reslist_release(cache->connection_pool, (void*)instance);
+}
+
+static void _couchbase_invalidate_connection(mapcache_tile *tile, libcouchbase_t* instance)
+{
+   mapcache_cache_couchbase* cache = (mapcache_cache_couchbase*)tile->tileset->cache;
+   apr_reslist_invalidate(cache->connection_pool, (void*)instance);
+}
+
+static int _mapcache_cache_couchbase_has_tile(mapcache_context *ctx, mapcache_tile *tile) {
+   char *key[1];
+   libcouchbase_t *instance;
+   libcouchbase_error_t error;
+   size_t keySize[1];
+   getStruct_t request;
+
+   key[0] = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+   if(GC_HAS_ERROR(ctx)) {
+      return MAPCACHE_FALSE;
+   }
+
+   keySize[0] = strlen(key[0]);
+
+   instance = _couchbase_get_connection(ctx, tile);
+   request.tileBuffer = 0;
+   request.error = LIBCOUCHBASE_KEY_ENOENT;
+   error = libcouchbase_mget(*instance, &request, 1, (const void * const*)key, keySize, 0);
+   if (error != LIBCOUCHBASE_SUCCESS) {
+      ctx->set_error(ctx, 500, "couchbase: failed to get key %s: %s", key, libcouchbase_strerror(*instance, error));
+      _couchbase_invalidate_connection(tile, instance);
+      return MAPCACHE_FALSE;
+   }
+   
+   libcouchbase_wait(*instance);
+
+   error = request.error;
+   if (error != LIBCOUCHBASE_SUCCESS) {
+      ctx->set_error(ctx, 500, "couchbase: failed to get key %s: %s", key, libcouchbase_strerror(*instance, error));
+      _couchbase_invalidate_connection(tile, instance);
+      return MAPCACHE_FALSE;
+   }
+
+   _couchbase_release_connection(tile, instance);
+   return MAPCACHE_TRUE;
+}
+
+static void _mapcache_cache_couchbase_delete(mapcache_context *ctx, mapcache_tile *tile) {
+   char *key;
+   libcouchbase_t *instance;
+   libcouchbase_error_t error;
+
+   key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
+   GC_CHECK_ERROR(ctx);
+
+   instance = _couchbase_get_connection(ctx, tile);
+
+   error = libcouchbase_remove(*instance, 0, key, strlen(key), 0);
+   if (error != LIBCOUCHBASE_SUCCESS) {
+      ctx->set_error(ctx, 500, "couchbase: failed to delete key %s: %s", key, libcouchbase_strerror(*instance, error));
+   }
+   
+   libcouchbase_wait(*instance);
+
+   error = libcouchbase_get_last_error(*instance);
+   if (error != LIBCOUCHBASE_SUCCESS) {
+      ctx->set_error(ctx, 500, "couchbase: failed to delete key %s: %s", key, libcouchbase_strerror(*instance, error));
+   }
+
+   _couchbase_release_connection(tile, instance);
+}
+
+/**
+ * \brief get content of given tile
+ * 
+ * fills the mapcache_tile::data of the given tile with content stored on the couchbase server
+ * \private \memberof mapcache_cache_couchbase
+ * \sa mapcache_cache::tile_get()
+ */
+static int _mapcache_cache_couchbase_get(mapcache_context *ctx, mapcache_tile *tile) {
+   char *key[1];
+   size_t keySize[1];
+   libcouchbase_t *instance;
+   libcouchbase_error_t error;
+   getStruct_t request;
+
+   key[0] = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+   if(GC_HAS_ERROR(ctx)) {
+      return MAPCACHE_FAILURE;
+   }
+   
+   keySize[0] = strlen(key[0]);
+
+   tile->encoded_data = mapcache_buffer_create(0, ctx->pool);
+
+   libcouchbase_time_t expires = 86400;
+   if(tile->tileset->auto_expire)
+      expires = tile->tileset->auto_expire;
+
+   instance = _couchbase_get_connection(ctx, tile);
+   if (GC_HAS_ERROR(ctx)) {
+      return MAPCACHE_FAILURE;
+   }
+
+   request.tileBuffer = tile->encoded_data;
+   error = libcouchbase_mget(*instance, &request, 1, (const void * const*)key, keySize, &expires);
+   if (error != LIBCOUCHBASE_SUCCESS) {
+      ctx->set_error(ctx, 500, "couchbase cache returned error on mget %s", libcouchbase_strerror(*instance, error));
+      _couchbase_invalidate_connection(tile, instance);
+      return MAPCACHE_FAILURE;
+   }
+   
+   libcouchbase_wait(*instance);
+
+   if(request.error != LIBCOUCHBASE_SUCCESS) {
+       _couchbase_release_connection(tile, instance);
+       return MAPCACHE_CACHE_MISS;
+   }
+
+   if (tile->encoded_data->size == 0) {
+      _couchbase_release_connection(tile, instance);
+      ctx->set_error(ctx, 500, "couchbase cache returned 0-length data for tile %d %d %d", tile->x, tile->y, tile->z);
+      return MAPCACHE_FAILURE;
+   }
+   
+   apr_time_t now = apr_time_now();
+   tile->mtime = now;
+
+   _couchbase_release_connection(tile, instance);
+   return MAPCACHE_SUCCESS;
+}
+
+/**
+ * \brief push tile data to couchbase
+ * 
+ * writes the content of mapcache_tile::data to the configured couchbased instance(s)
+ * \private \memberof mapcache_cache_couchbase
+ * \sa mapcache_cache::tile_set()
+ */
+static void _mapcache_cache_couchbase_set(mapcache_context *ctx, mapcache_tile *tile) {
+   char *key;
+   libcouchbase_t *instance;
+   libcouchbase_error_t error;
+   const int max_retries = 3;
+   int retries = max_retries;
+   apr_interval_time_t delay;
+
+   /* set expiration to one day if not configured */
+   libcouchbase_time_t expires = 86400;
+   if(tile->tileset->auto_expire)
+      expires = tile->tileset->auto_expire;
+
+   mapcache_cache_couchbase *cache = (mapcache_cache_couchbase*)tile->tileset->cache;
+   key = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+   GC_CHECK_ERROR(ctx);
+   
+   if(!tile->encoded_data) {
+      tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
+      GC_CHECK_ERROR(ctx);
+   }
+   
+   instance = _couchbase_get_connection(ctx, tile);
+   GC_CHECK_ERROR(ctx);
+
+   do
+   {
+      error = libcouchbase_store(*instance, &error, LIBCOUCHBASE_SET, key, strlen(key), tile->encoded_data->buf, tile->encoded_data->size, 0, expires, 0);
+      if (error != LIBCOUCHBASE_SUCCESS) {
+          _couchbase_release_connection(tile, instance);
+          ctx->set_error(ctx, 500, "failed to store tile %d %d %d to couchbase cache %s due to eror %s.",
+                         tile->x, tile->y, tile->z, cache->cache.name, libcouchbase_strerror(*instance, error));
+          return;
+      }
+   
+      libcouchbase_wait(*instance);
+
+      if (error == LIBCOUCHBASE_ETMPFAIL) {
+          if (retries > 0) {
+              delay = 100000 * (1 << (max_retries - retries));	// Do an exponential back off of starting at 100 milliseconds
+              apr_sleep(delay);
+          }
+          else {
+              _couchbase_release_connection(tile, instance);
+              ctx->set_error(ctx, 500, "failed to store tile %d %d %d to couchbase cache %s due to %s. Maximum number of retries used.",
+                             tile->x, tile->y, tile->z, cache->cache.name, libcouchbase_strerror(*instance, error));
+              return;
+          }
+
+          --retries;
+      }
+
+      else if (error != LIBCOUCHBASE_SUCCESS) {
+          _couchbase_release_connection(tile, instance);
+          ctx->set_error(ctx, 500, "failed to store tile %d %d %d to couchbase cache %s due to error %s.",
+                         tile->x, tile->y, tile->z, cache->cache.name, libcouchbase_strerror(*instance, error));
+          return;
+      }
+   }
+   while (error == LIBCOUCHBASE_ETMPFAIL);
+
+   _couchbase_release_connection(tile, instance);
+}
+
+/**
+ * \private \memberof mapcache_cache_couchbase
+ */
+static void _mapcache_cache_couchbase_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) {
+   ezxml_t cur_node;
+   apr_status_t rv;
+   mapcache_cache_couchbase *dcache = (mapcache_cache_couchbase*)cache;
+   int servercount = 0;
+
+   for(cur_node = ezxml_child(node,"server"); cur_node; cur_node = cur_node->next) {
+      servercount++;
+   }
+
+   if(!servercount) {
+      ctx->set_error(ctx, 400, "couchbase cache %s has no <server>s configured", cache->name);
+      return;
+   }
+
+   if(servercount > 1) {
+      ctx->set_error(ctx, 400, "couchbase cache %s has more than 1 server configured", cache->name);
+      return;
+   }
+
+   cur_node = ezxml_child(node, "server");
+   ezxml_t xhost = ezxml_child(cur_node, "host");   /* Host should contain server:port */
+   ezxml_t xusername = ezxml_child(cur_node, "username");
+   ezxml_t xpasswd = ezxml_child(cur_node, "password");
+   ezxml_t xbucket = ezxml_child(cur_node, "bucket");
+
+   if(!xhost || !xhost->txt || ! *xhost->txt) {
+      ctx->set_error(ctx, 400, "cache %s: <server> with no <host>", cache->name);
+      return;
+   } else {
+      dcache->host = apr_pstrdup(ctx->pool, xhost->txt);
+      if (dcache->host == NULL) {
+          ctx->set_error(ctx, 400, "cache %s: failed to allocate host string!", cache->name);
+          return;
+      }
+   }
+
+   if(xusername && xusername->txt && *xusername->txt) {
+      dcache->username = apr_pstrdup(ctx->pool, xusername->txt);
+   }
+   
+   if(xpasswd && xpasswd->txt && *xpasswd->txt) {
+      dcache->password = apr_pstrdup(ctx->pool, xpasswd->txt);
+   }
+   
+   if(xbucket && xbucket->txt && *xbucket->txt) {
+      dcache->bucket = apr_pstrdup(ctx->pool, xbucket->txt);
+   }
+
+   dcache->ctx = ctx;
+
+   rv = apr_reslist_create(&(dcache->connection_pool),
+         0 /* min */,
+         10 /* soft max */,
+         200 /* hard max */,
+         60*1000000 /*60 seconds, ttl*/,
+         _couchbase_reslist_get_connection, /* resource constructor */
+         _couchbase_reslist_free_connection, /* resource destructor */
+         dcache, ctx->pool);
+   if(rv != APR_SUCCESS) {
+      ctx->set_error(ctx, 500, "failed to create couchbase connection pool");
+      return;
+   }
+}
+
+/**
+ * \private \memberof mapcache_cache_couchbase
+ */
+static void _mapcache_cache_couchbase_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) {
+}
+
+/**
+ * \brief creates and initializes a mapcache_couchbase_cache
+ */
+mapcache_cache* mapcache_cache_couchbase_create(mapcache_context *ctx) {
+   mapcache_cache_couchbase *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_couchbase));
+   if(!cache) {
+      ctx->set_error(ctx, 500, "failed to allocate couchbase cache");
+      return NULL;
+   }
+
+   cache->cache.metadata = apr_table_make(ctx->pool, 3);
+   cache->cache.type = MAPCACHE_CACHE_COUCHBASE;
+   cache->cache.tile_get = _mapcache_cache_couchbase_get;
+   cache->cache.tile_exists = _mapcache_cache_couchbase_has_tile;
+   cache->cache.tile_set = _mapcache_cache_couchbase_set;
+   cache->cache.tile_delete = _mapcache_cache_couchbase_delete;
+   cache->cache.configuration_parse_xml = _mapcache_cache_couchbase_configuration_parse_xml;
+   cache->cache.configuration_post_config = _mapcache_cache_couchbase_configuration_post_config;
+   cache->host = NULL;
+   cache->username = NULL;
+   cache->password = NULL;
+   cache->bucket = NULL;
+
+   return (mapcache_cache*)cache;
+}
+
+#endif
+
+/* vim: ai ts=3 sts=3 et sw=3
+*/
diff --git a/lib/cache_disk.c b/lib/cache_disk.c
index a20101c..304e21c 100644
--- a/lib/cache_disk.c
+++ b/lib/cache_disk.c
@@ -111,10 +111,10 @@ char* relative_path(mapcache_context *ctx, char* tilename, char* blankname)
  * \param path pointer to a char* that will contain the filename
  * \private \memberof mapcache_cache_disk
  */
-static void _mapcache_cache_disk_base_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
+static void _mapcache_cache_disk_base_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path)
 {
   *path = apr_pstrcat(ctx->pool,
-                      ((mapcache_cache_disk*)tile->tileset->cache)->base_directory,"/",
+                      cache->base_directory,"/",
                       tile->tileset->name,"/",
                       tile->grid_link->grid->name,
                       NULL);
@@ -129,11 +129,11 @@ static void _mapcache_cache_disk_base_tile_key(mapcache_context *ctx, mapcache_t
   }
 }
 
-static void _mapcache_cache_disk_blank_tile_key(mapcache_context *ctx, mapcache_tile *tile, unsigned char *color, char **path)
+static void _mapcache_cache_disk_blank_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, unsigned char *color, char **path)
 {
   /* not implemented for template caches, as symlink_blank will never be set */
   *path = apr_psprintf(ctx->pool,"%s/%s/%s/blanks/%02X%02X%02X%02X.%s",
-                       ((mapcache_cache_disk*)tile->tileset->cache)->base_directory,
+                       cache->base_directory,
                        tile->tileset->name,
                        tile->grid_link->grid->name,
                        color[0],
@@ -154,12 +154,11 @@ static void _mapcache_cache_disk_blank_tile_key(mapcache_context *ctx, mapcache_
  * \param r
  * \private \memberof mapcache_cache_disk
  */
-static void _mapcache_cache_disk_tilecache_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
+static void _mapcache_cache_disk_tilecache_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path)
 {
-  mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache;
-  if(dcache->base_directory) {
+  if(cache->base_directory) {
     char *start;
-    _mapcache_cache_disk_base_tile_key(ctx, tile, &start);
+    _mapcache_cache_disk_base_tile_key(ctx, cache, tile, &start);
     *path = apr_psprintf(ctx->pool,"%s/%02d/%03d/%03d/%03d/%03d/%03d/%03d.%s",
                          start,
                          tile->z,
@@ -171,7 +170,7 @@ static void _mapcache_cache_disk_tilecache_tile_key(mapcache_context *ctx, mapca
                          tile->y % 1000,
                          tile->tileset->format?tile->tileset->format->extension:"png");
   } else {
-    *path = dcache->filename_template;
+    *path = cache->filename_template;
     *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}", tile->tileset->name);
     *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}", tile->grid_link->grid->name);
     *path = mapcache_util_str_replace(ctx->pool,*path, "{ext}",
@@ -222,11 +221,10 @@ static void _mapcache_cache_disk_tilecache_tile_key(mapcache_context *ctx, mapca
   }
 }
 
-static void _mapcache_cache_disk_template_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
+static void _mapcache_cache_disk_template_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path)
 {
-  mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache;
 
-  *path = dcache->filename_template;
+  *path = cache->filename_template;
   *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}", tile->tileset->name);
   *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}", tile->grid_link->grid->name);
   *path = mapcache_util_str_replace(ctx->pool,*path, "{ext}",
@@ -278,12 +276,11 @@ static void _mapcache_cache_disk_template_tile_key(mapcache_context *ctx, mapcac
   }
 }
 
-static void _mapcache_cache_disk_arcgis_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
+static void _mapcache_cache_disk_arcgis_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path)
 {
-  mapcache_cache_disk *dcache = (mapcache_cache_disk*)tile->tileset->cache;
-  if(dcache->base_directory) {
+  if(cache->base_directory) {
     char *start;
-    _mapcache_cache_disk_base_tile_key(ctx, tile, &start);
+    _mapcache_cache_disk_base_tile_key(ctx, cache, tile, &start);
     *path = apr_psprintf(ctx->pool,"%s/L%02d/R%08x/C%08x.%s" ,
                          start,
                          tile->z,
@@ -297,13 +294,31 @@ static void _mapcache_cache_disk_arcgis_tile_key(mapcache_context *ctx, mapcache
   }
 }
 
+static void _mapcache_cache_disk_worldwind_tile_key(mapcache_context *ctx, mapcache_cache_disk *cache, mapcache_tile *tile, char **path)
+{
+  if(cache->base_directory) {
+    *path = apr_psprintf(ctx->pool,"%s/%d/%04d/%04d_%04d.%s" ,
+                         cache->base_directory,
+                         tile->z,
+                         tile->y,
+                         tile->y,
+                         tile->x,
+                         tile->tileset->format?tile->tileset->format->extension:"png");
+  }
+
+  if(!*path) {
+    ctx->set_error(ctx,500, "failed to allocate tile key");
+  }
+}
+
 
-static int _mapcache_cache_disk_has_tile(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_disk_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   char *filename;
   apr_finfo_t finfo;
   int rv;
-  ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, tile, &filename);
+  mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache;
+  cache->tile_key(ctx, cache, tile, &filename);
   if(GC_HAS_ERROR(ctx)) {
     return MAPCACHE_FALSE;
   }
@@ -315,12 +330,13 @@ static int _mapcache_cache_disk_has_tile(mapcache_context *ctx, mapcache_tile *t
   }
 }
 
-static void _mapcache_cache_disk_delete(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_disk_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   apr_status_t ret;
   char errmsg[120];
   char *filename;
-  ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, tile, &filename);
+  mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache;
+  cache->tile_key(ctx, cache, tile, &filename);
   GC_CHECK_ERROR(ctx);
 
   ret = apr_file_remove(filename,ctx->pool);
@@ -337,7 +353,7 @@ static void _mapcache_cache_disk_delete(mapcache_context *ctx, mapcache_tile *ti
  * \private \memberof mapcache_cache_disk
  * \sa mapcache_cache::tile_get()
  */
-static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   char *filename;
   apr_file_t *f;
@@ -345,11 +361,13 @@ static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_tile *tile)
   apr_status_t rv;
   apr_size_t size;
   apr_mmap_t *tilemmap;
+  mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache;
 
-  ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, tile, &filename);
+  cache->tile_key(ctx, cache, tile, &filename);
   if(GC_HAS_ERROR(ctx)) {
     return MAPCACHE_FAILURE;
   }
+  ctx->log(ctx,MAPCACHE_DEBUG,"checking for tile %s",filename);
   if((rv=apr_file_open(&f, filename,
 #ifndef NOMMAP
                        APR_FOPEN_READ, APR_UREAD | APR_GREAD,
@@ -418,14 +436,15 @@ static int _mapcache_cache_disk_get(mapcache_context *ctx, mapcache_tile *tile)
  * \private \memberof mapcache_cache_disk
  * \sa mapcache_cache::tile_set()
  */
-static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   apr_size_t bytes;
   apr_file_t *f;
   apr_status_t ret;
   char errmsg[120];
   char *filename, *hackptr1, *hackptr2=NULL;
-  const int creation_retry = ((mapcache_cache_disk*)tile->tileset->cache)->creation_retry;
+  mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache;
+  const int creation_retry = cache->creation_retry;
   int retry_count_create_file = 0;
 
 #ifdef DEBUG
@@ -440,7 +459,7 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_tile *tile)
   }
 #endif
 
-  ((mapcache_cache_disk*)tile->tileset->cache)->tile_key(ctx, tile, &filename);
+  cache->tile_key(ctx, cache, tile, &filename);
   GC_CHECK_ERROR(ctx);
 
   /* find the location of the last '/' in the string */
@@ -471,22 +490,24 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_tile *tile)
 
 
 #ifdef HAVE_SYMLINK
-  if(((mapcache_cache_disk*)tile->tileset->cache)->symlink_blank) {
+  if(cache->symlink_blank) {
     if(!tile->raw_image) {
       tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
       GC_CHECK_ERROR(ctx);
     }
     if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
       char *blankname;
-      _mapcache_cache_disk_blank_tile_key(ctx,tile,tile->raw_image->data,&blankname);
+      _mapcache_cache_disk_blank_tile_key(ctx,cache,tile,tile->raw_image->data,&blankname);
       if(apr_file_open(&f, blankname, APR_FOPEN_READ, APR_OS_DEFAULT, ctx->pool) != APR_SUCCESS) {
+        int isLocked;
+        void *lock;
         if(!tile->encoded_data) {
           tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
           GC_CHECK_ERROR(ctx);
         }
         /* create the blank file */
         char *blankdirname = apr_psprintf(ctx->pool, "%s/%s/%s/blanks",
-                                          ((mapcache_cache_disk*)tile->tileset->cache)->base_directory,
+                                          cache->base_directory,
                                           tile->tileset->name,
                                           tile->grid_link->grid->name);
         if(APR_SUCCESS != (ret = apr_dir_make_recursive(
@@ -498,7 +519,7 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_tile *tile)
         }
 
         /* aquire a lock on the blank file */
-        int isLocked = mapcache_lock_or_wait_for_resource(ctx,blankname);
+        isLocked = mapcache_lock_or_wait_for_resource(ctx,ctx->config->locker,blankname, &lock);
 
         if(isLocked == MAPCACHE_TRUE) {
 
@@ -506,7 +527,7 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_tile *tile)
                                   APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,
                                   APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) {
             ctx->set_error(ctx, 500,  "failed to create file %s: %s",blankname, apr_strerror(ret,errmsg,120));
-            mapcache_unlock_resource(ctx,blankname);
+            mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
             return; /* we could not create the file */
           }
 
@@ -514,17 +535,17 @@ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_tile *tile)
           ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes);
           if(ret != APR_SUCCESS) {
             ctx->set_error(ctx, 500,  "failed to write data to file %s (wrote %d of %d bytes): %s",blankname, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120));
-            mapcache_unlock_resource(ctx,blankname);
+            mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
             return; /* we could not create the file */
           }
 
           if(bytes != tile->encoded_data->size) {
             ctx->set_error(ctx, 500,  "failed to write image data to %s, wrote %d of %d bytes", blankname, (int)bytes, (int)tile->encoded_data->size);
-            mapcache_unlock_resource(ctx,blankname);
+            mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
             return;
           }
           apr_file_close(f);
-          mapcache_unlock_resource(ctx,blankname);
+          mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock);
 #ifdef DEBUG
           ctx->log(ctx,MAPCACHE_DEBUG,"created blank tile %s",blankname);
 #endif
@@ -643,6 +664,8 @@ static void _mapcache_cache_disk_configuration_parse_xml(mapcache_context *ctx,
     dcache->tile_key = _mapcache_cache_disk_tilecache_tile_key;
   } else if(!strcmp(layout,"arcgis")) {
     dcache->tile_key = _mapcache_cache_disk_arcgis_tile_key;
+  } else if(!strcmp(layout,"worldwind")) {
+    dcache->tile_key = _mapcache_cache_disk_worldwind_tile_key;
   } else if (!strcmp(layout,"template")) {
     dcache->tile_key = _mapcache_cache_disk_template_tile_key;
     template_layout = MAPCACHE_TRUE;
diff --git a/lib/cache_fallback.c b/lib/cache_fallback.c
new file mode 100644
index 0000000..885ee30
--- /dev/null
+++ b/lib/cache_fallback.c
@@ -0,0 +1,202 @@
+/******************************************************************************
+ *
+ * Project:  MapServer
+ * Purpose:  MapCache tile caching: fallback cache backend.
+ * Author:   Thomas Bonfort and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2011 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without fallbackriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "mapcache.h"
+
+static int _mapcache_cache_fallback_tile_exists(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
+  mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*);
+  return subcache->tile_exists(ctx, subcache, tile);
+}
+
+static void _mapcache_cache_fallback_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
+  int i;
+  for(i=0; i<cache->caches->nelts; i++) {
+    mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
+    subcache->tile_delete(ctx, subcache, tile);
+    ctx->clear_errors(ctx); /* ignore errors */
+  }
+}
+
+/**
+ * \brief get content of given tile
+ *
+ * fills the mapcache_tile::data of the given tile with content stored on the fallback server
+ * \private \memberof mapcache_cache_fallback
+ * \sa mapcache_cache::tile_get()
+ */
+static int _mapcache_cache_fallback_tile_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
+  mapcache_cache *subcache;
+  int i,ret;
+  subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*);
+  ret = subcache->tile_get(ctx, subcache, tile);
+  
+  if(ret == MAPCACHE_FAILURE) {
+    int first_error = ctx->get_error(ctx);
+    char *first_error_message = ctx->get_error_message(ctx);
+    ctx->log(ctx,MAPCACHE_WARN,"failed \"GET\" on primary cache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\". Falling back on secondary caches",
+            APR_ARRAY_IDX(cache->caches,0,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name);
+    ctx->clear_errors(ctx);
+    for(i=1; i<cache->caches->nelts; i++) {
+      subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
+      if((ret = subcache->tile_get(ctx, subcache, tile)) == MAPCACHE_FAILURE) {
+        ctx->log(ctx,MAPCACHE_WARN,"failed \"GET\" on fallback cache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\". Continuing with other fallback caches if available",
+                APR_ARRAY_IDX(cache->caches,0,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name);
+        ctx->clear_errors(ctx);
+        continue;
+      } else {
+        return ret;
+      }
+    }
+    /* all backends failed, return primary error message */
+    ctx->set_error(ctx,first_error,first_error_message);
+    return MAPCACHE_FAILURE;
+  } else {
+    /* success or notfound */
+    return ret;
+  }
+}
+
+static void _mapcache_cache_fallback_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
+  int i,oneok=0,first_error=0;
+  char *first_error_message;
+  for(i=0; i<cache->caches->nelts; i++) {
+    mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
+    subcache->tile_set(ctx, subcache, tile);
+    if(GC_HAS_ERROR(ctx)) {
+      if(!first_error) {
+        first_error = ctx->get_error(ctx);
+        first_error_message = ctx->get_error_message(ctx);
+      }
+      ctx->log(ctx,MAPCACHE_WARN,"failed \"SET\" on subcache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\"",
+              APR_ARRAY_IDX(cache->caches,i,mapcache_cache*)->name,tile->z,tile->x,tile->y,tile->tileset->name);
+      ctx->clear_errors(ctx);
+    } else {
+      oneok = 1;
+    }
+  }
+  if(!oneok) {
+    ctx->set_error(ctx,first_error,first_error_message);
+  }
+}
+
+static void _mapcache_cache_fallback_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
+{
+  mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
+  int i,oneok=0,first_error=0;
+  char *first_error_message;
+  for(i=0; i<cache->caches->nelts; i++) {
+    mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
+    if(subcache->tile_multi_set) {
+      subcache->tile_multi_set(ctx, subcache, tiles, ntiles);
+    }
+    else {
+      int j;
+      for(j=0;j<ntiles;j++) {
+        subcache->tile_set(ctx,subcache,&tiles[j]);
+        if(GC_HAS_ERROR(ctx))
+          break;
+      }
+    } 
+    if(GC_HAS_ERROR(ctx)) {
+      if(!first_error) {
+        first_error = ctx->get_error(ctx);
+        first_error_message = ctx->get_error_message(ctx);
+      }
+      ctx->log(ctx,MAPCACHE_WARN,"failed \"MULTISET\" on subcache \"%s\" for tile (z=%d,x=%d,y=%d) of tileset \"%s\"",
+              APR_ARRAY_IDX(cache->caches,i,mapcache_cache*)->name,tiles[0].z,tiles[0].x,tiles[0].y,tiles[0].tileset->name);
+      ctx->clear_errors(ctx);
+    } else {
+      oneok = 1;
+    }
+  }
+  if(!oneok) {
+    ctx->set_error(ctx,first_error,first_error_message);
+  }
+}
+
+/**
+ * \private \memberof mapcache_cache_fallback
+ */
+static void _mapcache_cache_fallback_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
+{
+  ezxml_t cur_node;
+  mapcache_cache_fallback *cache = (mapcache_cache_fallback*)pcache;
+  cache->caches = apr_array_make(ctx->pool,3,sizeof(mapcache_cache*));
+  for(cur_node = ezxml_child(node,"cache"); cur_node; cur_node = cur_node->next) {
+    mapcache_cache *refcache = mapcache_configuration_get_cache(config, cur_node->txt);
+    if(!refcache) {
+      ctx->set_error(ctx, 400, "fallback cache \"%s\" references cache \"%s\","
+                     " but it is not configured (hint:referenced caches must be declared before this fallback cache in the xml file)", pcache->name, cur_node->txt);
+      return;
+    }
+    APR_ARRAY_PUSH(cache->caches,mapcache_cache*) = refcache;
+  }
+  if(cache->caches->nelts == 0) {
+    ctx->set_error(ctx,400,"fallback cache \"%s\" does not reference any child caches", pcache->name);
+  }
+}
+
+/**
+ * \private \memberof mapcache_cache_fallback
+ */
+static void _mapcache_cache_fallback_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
+    mapcache_cfg *cfg)
+{
+}
+
+
+/**
+ * \brief creates and initializes a mapcache_cache_fallback
+ */
+mapcache_cache* mapcache_cache_fallback_create(mapcache_context *ctx)
+{
+  mapcache_cache_fallback *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_fallback));
+  if(!cache) {
+    ctx->set_error(ctx, 500, "failed to allocate fallback cache");
+    return NULL;
+  }
+  cache->cache.metadata = apr_table_make(ctx->pool,3);
+  cache->cache.type = MAPCACHE_CACHE_COMPOSITE;
+  cache->cache.tile_delete = _mapcache_cache_fallback_tile_delete;
+  cache->cache.tile_get = _mapcache_cache_fallback_tile_get;
+  cache->cache.tile_exists = _mapcache_cache_fallback_tile_exists;
+  cache->cache.tile_set = _mapcache_cache_fallback_tile_set;
+  cache->cache.tile_multi_set = _mapcache_cache_fallback_tile_multi_set;
+  cache->cache.configuration_post_config = _mapcache_cache_fallback_configuration_post_config;
+  cache->cache.configuration_parse_xml = _mapcache_cache_fallback_configuration_parse_xml;
+  return (mapcache_cache*)cache;
+}
+
diff --git a/lib/cache_memcache.c b/lib/cache_memcache.c
index ccf6b93..f6d3334 100644
--- a/lib/cache_memcache.c
+++ b/lib/cache_memcache.c
@@ -31,41 +31,117 @@
 #ifdef USE_MEMCACHE
 
 #include "mapcache.h"
+struct mapcache_memcache_conn_param {
+  mapcache_cache_memcache *cache;
+};
 
-static int _mapcache_cache_memcache_has_tile(mapcache_context *ctx, mapcache_tile *tile)
+struct mapcache_memcache_pooled_connection {
+  apr_memcache_t *memcache;
+  apr_pool_t *pool;
+};
+
+void mapcache_memcache_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *process_pool) {
+  struct mapcache_memcache_conn_param *param = params;
+  mapcache_cache_memcache *cache = param->cache;
+  struct mapcache_memcache_pooled_connection *pc;
+  int i;
+  pc = calloc(1,sizeof(struct mapcache_memcache_pooled_connection));
+  apr_pool_create(&pc->pool,process_pool);
+  if(APR_SUCCESS != apr_memcache_create(pc->pool, cache->nservers, 0, &(pc->memcache))) {
+    ctx->set_error(ctx,500,"cache %s: failed to create memcache backend", cache->cache.name);
+    return;
+  }
+  for(i=0; i<param->cache->nservers; i++) {
+    apr_memcache_server_t *server;
+    if(APR_SUCCESS != apr_memcache_server_create(pc->pool,cache->servers[i].host,cache->servers[i].port,4,5,50,10000,&server)) {
+      ctx->set_error(ctx,500,"cache %s: failed to create server %s:%d",cache->cache.name,cache->servers[i].host,cache->servers[i].port);
+      return;
+    }
+    if(APR_SUCCESS != apr_memcache_add_server(pc->memcache,server)) {
+      ctx->set_error(ctx,500,"cache %s: failed to add server %s:%d",cache->cache.name,cache->servers[i].host,cache->servers[i].port);
+      return;
+    }
+  }
+  *conn_ = pc;
+}
+
+void mapcache_memcache_connection_destructor(void *conn_, apr_pool_t *process_pool) {
+  struct mapcache_memcache_pooled_connection *pc = conn_;
+  apr_pool_destroy(pc->pool);
+  free(pc);
+}
+
+static mapcache_pooled_connection* _mapcache_memcache_get_conn(mapcache_context *ctx,
+        mapcache_cache_memcache *cache, mapcache_tile *tile) {
+  mapcache_pooled_connection *pc;
+  struct mapcache_memcache_conn_param param;
+
+  param.cache = cache;
+
+  pc = mapcache_connection_pool_get_connection(ctx,cache->cache.name, mapcache_memcache_connection_constructor, mapcache_memcache_connection_destructor, &param);
+  return pc;
+}
+
+static void _mapcache_memcache_release_conn(mapcache_context *ctx, mapcache_pooled_connection *con) {
+  mapcache_connection_pool_release_connection(ctx, con);
+}
+
+static int _mapcache_cache_memcache_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   char *key;
   char *tmpdata;
   int rv;
   size_t tmpdatasize;
-  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)tile->tileset->cache;
-  key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
-  if(GC_HAS_ERROR(ctx)) {
+  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache;
+  mapcache_pooled_connection *pc;
+  struct mapcache_memcache_pooled_connection *mpc;
+  pc = _mapcache_memcache_get_conn(ctx,cache,tile);
+  if(GC_HAS_ERROR(ctx))
     return MAPCACHE_FALSE;
+  mpc = pc->connection;
+  
+  key = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b","#");
+  if(GC_HAS_ERROR(ctx)) {
+    rv = MAPCACHE_FALSE;
+    goto cleanup;
   }
-  rv = apr_memcache_getp(cache->memcache,ctx->pool,key,&tmpdata,&tmpdatasize,NULL);
+  rv = apr_memcache_getp(mpc->memcache,ctx->pool,key,&tmpdata,&tmpdatasize,NULL);
   if(rv != APR_SUCCESS) {
-    return MAPCACHE_FALSE;
+    rv = MAPCACHE_FALSE;
+    goto cleanup;
   }
   if(tmpdatasize == 0) {
-    return MAPCACHE_FALSE;
+    rv = MAPCACHE_FALSE;
+    goto cleanup;
   }
-  return MAPCACHE_TRUE;
+  rv = MAPCACHE_TRUE;
+cleanup:
+  _mapcache_memcache_release_conn(ctx,pc);
+  return rv;
 }
 
-static void _mapcache_cache_memcache_delete(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_memcache_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   char *key;
   int rv;
   char errmsg[120];
-  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)tile->tileset->cache;
-  key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
+  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache;
+  mapcache_pooled_connection *pc;
+  struct mapcache_memcache_pooled_connection *mpc;
+  pc = _mapcache_memcache_get_conn(ctx,cache,tile);
   GC_CHECK_ERROR(ctx);
-  rv = apr_memcache_delete(cache->memcache,key,0);
+  mpc = pc->connection;
+  key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
+  if(GC_HAS_ERROR(ctx)) goto cleanup;
+  
+  rv = apr_memcache_delete(mpc->memcache,key,0);
   if(rv != APR_SUCCESS && rv!= APR_NOTFOUND) {
-    int code = 500;
-    ctx->set_error(ctx,code,"memcache: failed to delete key %s: %s", key, apr_strerror(rv,errmsg,120));
+    ctx->set_error(ctx,500,"memcache: failed to delete key %s: %s", key, apr_strerror(rv,errmsg,120));
+    goto cleanup;
   }
+
+cleanup:
+  _mapcache_memcache_release_conn(ctx,pc);
 }
 
 /**
@@ -75,33 +151,55 @@ static void _mapcache_cache_memcache_delete(mapcache_context *ctx, mapcache_tile
  * \private \memberof mapcache_cache_memcache
  * \sa mapcache_cache::tile_get()
  */
-static int _mapcache_cache_memcache_get(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_memcache_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   char *key;
   int rv;
-  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)tile->tileset->cache;
-  key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
+  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache;
+  mapcache_pooled_connection *pc;
+  mapcache_buffer *encoded_data;
+  struct mapcache_memcache_pooled_connection *mpc;
+  pc = _mapcache_memcache_get_conn(ctx,cache,tile);
   if(GC_HAS_ERROR(ctx)) {
     return MAPCACHE_FAILURE;
   }
-  tile->encoded_data = mapcache_buffer_create(0,ctx->pool);
-  rv = apr_memcache_getp(cache->memcache,ctx->pool,key,(char**)&tile->encoded_data->buf,&tile->encoded_data->size,NULL);
+  mpc = pc->connection;
+  key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
+  if(GC_HAS_ERROR(ctx)) {
+    rv = MAPCACHE_FAILURE;
+    goto cleanup;
+  }
+  encoded_data = mapcache_buffer_create(0,ctx->pool);
+  rv = apr_memcache_getp(mpc->memcache,ctx->pool,key,(char**)&encoded_data->buf,&encoded_data->size,NULL);
   if(rv != APR_SUCCESS) {
-    return MAPCACHE_CACHE_MISS;
+    rv = MAPCACHE_CACHE_MISS;
+    goto cleanup;
   }
-  if(tile->encoded_data->size == 0) {
+  if(encoded_data->size == 0) {
     ctx->set_error(ctx,500,"memcache cache returned 0-length data for tile %d %d %d\n",tile->x,tile->y,tile->z);
-    return MAPCACHE_FAILURE;
+    rv = MAPCACHE_FAILURE;
+    goto cleanup;
   }
   /* extract the tile modification time from the end of the data returned */
   memcpy(
     &tile->mtime,
-    &(((char*)tile->encoded_data->buf)[tile->encoded_data->size-sizeof(apr_time_t)]),
+    &(((char*)encoded_data->buf)[encoded_data->size-sizeof(apr_time_t)]),
     sizeof(apr_time_t));
-  ((char*)tile->encoded_data->buf)[tile->encoded_data->size+sizeof(apr_time_t)]='\0';
-  tile->encoded_data->avail = tile->encoded_data->size;
-  tile->encoded_data->size -= sizeof(apr_time_t);
-  return MAPCACHE_SUCCESS;
+  
+  ((char*)encoded_data->buf)[encoded_data->size+sizeof(apr_time_t)]='\0';
+  encoded_data->avail = encoded_data->size;
+  encoded_data->size -= sizeof(apr_time_t);
+  if(((char*)encoded_data->buf)[0] == '#' && encoded_data->size > 1) {
+    tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy ,encoded_data->buf,&tile->nodata);
+  } else {
+    tile->encoded_data = encoded_data;
+  }
+  rv = MAPCACHE_SUCCESS;
+  
+cleanup:
+  _mapcache_memcache_release_conn(ctx,pc);
+  
+  return rv;
 }
 
 /**
@@ -113,37 +211,62 @@ static int _mapcache_cache_memcache_get(mapcache_context *ctx, mapcache_tile *ti
  * \private \memberof mapcache_cache_memcache
  * \sa mapcache_cache::tile_set()
  */
-static void _mapcache_cache_memcache_set(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_memcache_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   char *key;
   int rv;
-  /* set expiration to one day if not configured */
-  int expires = 86400;
+  /* set no expiration if not configured */
+  int expires =0;
+  mapcache_buffer *encoded_data = NULL;
+  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache;
+  mapcache_pooled_connection *pc;
+  struct mapcache_memcache_pooled_connection *mpc;
+  pc = _mapcache_memcache_get_conn(ctx,cache,tile);
+  GC_CHECK_ERROR(ctx);
+  mpc = pc->connection;
+  key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
+  if(GC_HAS_ERROR(ctx)) goto cleanup;
+  
   if(tile->tileset->auto_expire)
     expires = tile->tileset->auto_expire;
-  mapcache_cache_memcache *cache = (mapcache_cache_memcache*)tile->tileset->cache;
-  key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#");
-  GC_CHECK_ERROR(ctx);
 
-  if(!tile->encoded_data) {
-    tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
-    GC_CHECK_ERROR(ctx);
+  if(cache->detect_blank) {
+    if(!tile->raw_image) {
+      tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
+      GC_CHECK_ERROR(ctx);
+    }
+    if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
+      encoded_data = mapcache_buffer_create(5,ctx->pool);
+      ((char*)encoded_data->buf)[0] = '#';
+      memcpy(((char*)encoded_data->buf)+1,tile->raw_image->data,4);
+      encoded_data->size = 5;
+    }
+  }
+  if(!encoded_data) {
+    if(!tile->encoded_data) {
+      tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
+      if(GC_HAS_ERROR(ctx)) goto cleanup;
+    }
+    encoded_data = tile->encoded_data;
   }
 
   /* concatenate the current time to the end of the memcache data so we can extract it out
    * when we re-get the tile */
-  char *data = calloc(1,tile->encoded_data->size+sizeof(apr_time_t));
+  char *data = calloc(1,encoded_data->size+sizeof(apr_time_t));
   apr_time_t now = apr_time_now();
   apr_pool_cleanup_register(ctx->pool, data, (void*)free, apr_pool_cleanup_null);
-  memcpy(data,tile->encoded_data->buf,tile->encoded_data->size);
-  memcpy(&(data[tile->encoded_data->size]),&now,sizeof(apr_time_t));
+  memcpy(data,encoded_data->buf,encoded_data->size);
+  memcpy(&(data[encoded_data->size]),&now,sizeof(apr_time_t));
 
-  rv = apr_memcache_set(cache->memcache,key,data,tile->encoded_data->size+sizeof(apr_time_t),expires,0);
+  rv = apr_memcache_set(mpc->memcache,key,data,encoded_data->size+sizeof(apr_time_t),expires,0);
   if(rv != APR_SUCCESS) {
     ctx->set_error(ctx,500,"failed to store tile %d %d %d to memcache cache %s",
                    tile->x,tile->y,tile->z,cache->cache.name);
-    return;
+    goto cleanup;
   }
+
+cleanup:
+  _mapcache_memcache_release_conn(ctx,pc);
 }
 
 /**
@@ -152,30 +275,25 @@ static void _mapcache_cache_memcache_set(mapcache_context *ctx, mapcache_tile *t
 static void _mapcache_cache_memcache_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
 {
   ezxml_t cur_node;
+  int i = 0;
   mapcache_cache_memcache *dcache = (mapcache_cache_memcache*)cache;
-  int servercount = 0;
   for(cur_node = ezxml_child(node,"server"); cur_node; cur_node = cur_node->next) {
-    servercount++;
+    dcache->nservers++;
   }
-  if(!servercount) {
+  if(!dcache->nservers) {
     ctx->set_error(ctx,400,"memcache cache %s has no <server>s configured",cache->name);
     return;
   }
-  if(APR_SUCCESS != apr_memcache_create(ctx->pool, servercount, 0, &dcache->memcache)) {
-    ctx->set_error(ctx,400,"cache %s: failed to create memcache backend", cache->name);
-    return;
-  }
+  dcache->servers = apr_pcalloc(ctx->pool, dcache->nservers * sizeof(struct mapcache_cache_memcache_server));
+
   for(cur_node = ezxml_child(node,"server"); cur_node; cur_node = cur_node->next) {
     ezxml_t xhost = ezxml_child(cur_node,"host");
     ezxml_t xport = ezxml_child(cur_node,"port");
-    const char *host;
-    apr_memcache_server_t *server;
-    apr_port_t port;
     if(!xhost || !xhost->txt || ! *xhost->txt) {
       ctx->set_error(ctx,400,"cache %s: <server> with no <host>",cache->name);
       return;
     } else {
-      host = apr_pstrdup(ctx->pool,xhost->txt);
+      dcache->servers[i].host = apr_pstrdup(ctx->pool,xhost->txt);
     }
 
     if(!xport || !xport->txt || ! *xport->txt) {
@@ -188,19 +306,15 @@ static void _mapcache_cache_memcache_configuration_parse_xml(mapcache_context *c
         ctx->set_error(ctx,400,"failed to parse value %s for memcache cache %s", xport->txt,cache->name);
         return;
       }
-      port = iport;
+      dcache->servers[i].port = iport;
     }
-    if(APR_SUCCESS != apr_memcache_server_create(ctx->pool,host,port,4,5,50,10000,&server)) {
-      ctx->set_error(ctx,400,"cache %s: failed to create server %s:%d",cache->name,host,port);
-      return;
-    }
-    if(APR_SUCCESS != apr_memcache_add_server(dcache->memcache,server)) {
-      ctx->set_error(ctx,400,"cache %s: failed to add server %s:%d",cache->name,host,port);
-      return;
-    }
-    if(APR_SUCCESS != apr_memcache_set(dcache->memcache,"mapcache_test_key","mapcache",8,0,0)) {
-      ctx->set_error(ctx,400,"cache %s: failed to add test key to server %s:%d",cache->name,host,port);
-      return;
+    i++;
+  }
+  
+  dcache->detect_blank = 0;
+  if ((cur_node = ezxml_child(node, "detect_blank")) != NULL) {
+    if(!strcasecmp(cur_node->txt,"true")) {
+      dcache->detect_blank = 1;
     }
   }
 }
@@ -212,7 +326,7 @@ static void _mapcache_cache_memcache_configuration_post_config(mapcache_context
     mapcache_cfg *cfg)
 {
   mapcache_cache_memcache *dcache = (mapcache_cache_memcache*)cache;
-  if(!dcache->memcache || dcache->memcache->ntotal==0) {
+  if(!dcache->nservers) {
     ctx->set_error(ctx,400,"cache %s has no servers configured",cache->name);
   }
 }
diff --git a/lib/cache_multitier.c b/lib/cache_multitier.c
new file mode 100644
index 0000000..9741191
--- /dev/null
+++ b/lib/cache_multitier.c
@@ -0,0 +1,165 @@
+/******************************************************************************
+ *
+ * Project:  MapServer
+ * Purpose:  MapCache tile caching: multitier cache backend.
+ * Author:   Thomas Bonfort and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2011 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without multitierriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "mapcache.h"
+
+static int _mapcache_cache_multitier_tile_exists(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
+  int i;
+  for(i=0; i<cache->caches->nelts; i++) {
+    mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
+    if(subcache->tile_exists(ctx, subcache, tile) == MAPCACHE_TRUE) {
+      return MAPCACHE_TRUE;
+    }
+  }
+  return MAPCACHE_FALSE;
+}
+
+static void _mapcache_cache_multitier_tile_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
+  int i;
+  for(i=0; i<cache->caches->nelts; i++) {
+    mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
+    subcache->tile_delete(ctx, subcache, tile);
+    ctx->clear_errors(ctx); /* ignore errors */
+  }
+}
+
+/**
+ * \brief get content of given tile
+ *
+ * fills the mapcache_tile::data of the given tile with content stored on the multitier server
+ * \private \memberof mapcache_cache_multitier
+ * \sa mapcache_cache::tile_get()
+ */
+static int _mapcache_cache_multitier_tile_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
+  mapcache_cache *subcache;
+  int i,ret;
+  subcache = APR_ARRAY_IDX(cache->caches,0,mapcache_cache*);
+  ret = subcache->tile_get(ctx, subcache, tile);
+  
+  if(ret == MAPCACHE_CACHE_MISS) {
+    for(i=1; i<cache->caches->nelts; i++) {
+      subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
+      if(subcache->tile_get(ctx, subcache, tile) == MAPCACHE_SUCCESS) {
+        ctx->log(ctx,MAPCACHE_DEBUG,"got tile (%s,z=%d,y=%d,x=%d) from secondary cache (%s)",tile->tileset->name, tile->z, tile->y, tile->x, subcache->name);
+        for(--i;i>=0;i--) {
+          subcache = APR_ARRAY_IDX(cache->caches,i,mapcache_cache*);
+          subcache->tile_set(ctx, subcache, tile);
+          ctx->clear_errors(ctx); /* silently ignore these errors */
+          ctx->log(ctx,MAPCACHE_DEBUG,"transferring tile (%s,z=%d,y=%d,x=%d) to cache (%s)",tile->tileset->name, tile->z, tile->y, tile->x, subcache->name);
+        }
+        return MAPCACHE_SUCCESS;
+      }
+    }
+    return MAPCACHE_CACHE_MISS;
+  } else {
+    ctx->log(ctx,MAPCACHE_DEBUG,"got tile (%s,z=%d,y=%d,x=%d) from primary cache (%s)",tile->tileset->name, tile->z, tile->y, tile->x, subcache->name);
+    return ret;
+  }
+}
+
+static void _mapcache_cache_multitier_tile_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
+  mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,cache->caches->nelts-1,mapcache_cache*);
+  return subcache->tile_set(ctx, subcache, tile);
+}
+
+static void _mapcache_cache_multitier_tile_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
+{
+  mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
+  mapcache_cache *subcache = APR_ARRAY_IDX(cache->caches,cache->caches->nelts-1,mapcache_cache*);
+  if(subcache->tile_multi_set) {
+    return subcache->tile_multi_set(ctx, subcache, tiles, ntiles);
+  } else {
+    int i;
+    for( i=0;i<ntiles;i++ ) {
+      subcache->tile_set(ctx, subcache, &tiles[i]);
+      GC_CHECK_ERROR(ctx);
+    }
+  }
+}
+
+/**
+ * \private \memberof mapcache_cache_multitier
+ */
+static void _mapcache_cache_multitier_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
+{
+  ezxml_t cur_node;
+  mapcache_cache_multitier *cache = (mapcache_cache_multitier*)pcache;
+  cache->caches = apr_array_make(ctx->pool,3,sizeof(mapcache_cache*));
+  for(cur_node = ezxml_child(node,"cache"); cur_node; cur_node = cur_node->next) {
+    mapcache_cache *refcache = mapcache_configuration_get_cache(config, cur_node->txt);
+    if(!refcache) {
+      ctx->set_error(ctx, 400, "multitier cache \"%s\" references cache \"%s\","
+                     " but it is not configured (hint:referenced caches must be declared before this multitier cache in the xml file)", pcache->name, cur_node->txt);
+      return;
+    }
+    APR_ARRAY_PUSH(cache->caches,mapcache_cache*) = refcache;
+  }
+  if(cache->caches->nelts == 0) {
+    ctx->set_error(ctx,400,"multitier cache \"%s\" does not reference any child caches", pcache->name);
+  }
+}
+
+/**
+ * \private \memberof mapcache_cache_multitier
+ */
+static void _mapcache_cache_multitier_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
+    mapcache_cfg *cfg)
+{
+}
+
+
+/**
+ * \brief creates and initializes a mapcache_cache_multitier
+ */
+mapcache_cache* mapcache_cache_multitier_create(mapcache_context *ctx)
+{
+  mapcache_cache_multitier *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_multitier));
+  if(!cache) {
+    ctx->set_error(ctx, 500, "failed to allocate multitier cache");
+    return NULL;
+  }
+  cache->cache.metadata = apr_table_make(ctx->pool,3);
+  cache->cache.type = MAPCACHE_CACHE_COMPOSITE;
+  cache->cache.tile_delete = _mapcache_cache_multitier_tile_delete;
+  cache->cache.tile_get = _mapcache_cache_multitier_tile_get;
+  cache->cache.tile_exists = _mapcache_cache_multitier_tile_exists;
+  cache->cache.tile_set = _mapcache_cache_multitier_tile_set;
+  cache->cache.tile_multi_set = _mapcache_cache_multitier_tile_multi_set;
+  cache->cache.configuration_post_config = _mapcache_cache_multitier_configuration_post_config;
+  cache->cache.configuration_parse_xml = _mapcache_cache_multitier_configuration_parse_xml;
+  return (mapcache_cache*)cache;
+}
+
diff --git a/lib/cache_rest.c b/lib/cache_rest.c
new file mode 100644
index 0000000..cb6adfb
--- /dev/null
+++ b/lib/cache_rest.c
@@ -0,0 +1,1165 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  MapServer
+ * Purpose:  MapCache tile caching: HTTP Rest cache backend.
+ * Author:   Thomas Bonfort and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 2014 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "mapcache.h"
+#include <apr_strings.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <curl/curl.h>
+#include <apr_base64.h>
+#include <apr_md5.h>
+
+typedef struct {
+  mapcache_buffer *buffer;
+  size_t offset;
+} buffer_struct;
+
+static size_t buffer_read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+  buffer_struct *buffer = (buffer_struct*)stream;
+  void *start = ((char*)(buffer->buffer->buf)) + buffer->offset;
+  size_t bytes = MAPCACHE_MIN((buffer->buffer->size-buffer->offset),(size * nmemb));
+  if(bytes) {
+    memcpy(ptr,start,bytes);
+    buffer->offset += bytes;
+  }
+  return bytes;
+}
+
+size_t buffer_write_callback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+  mapcache_buffer *buffer = (mapcache_buffer*)data;
+  size_t realsize = size * nmemb;
+  return mapcache_buffer_append(buffer, realsize, ptr);
+}
+
+static void _set_headers(mapcache_context *ctx, CURL *curl, apr_table_t *headers) {
+  if(!headers) {
+    return;
+  } else {
+    struct curl_slist *curl_headers=NULL;
+    const apr_array_header_t *array = apr_table_elts(headers);
+    apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
+    int i;
+    for (i = 0; i < array->nelts; i++) {
+      curl_headers = curl_slist_append(curl_headers, apr_pstrcat(ctx->pool,elts[i].key,": ",elts[i].val,NULL));
+    }
+    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers);
+  }
+}
+
+static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buffer, char *url, apr_table_t *headers) {
+  CURLcode res;
+  buffer_struct data;
+  mapcache_buffer *response;
+
+  data.buffer = buffer;
+  data.offset = 0;
+
+  response = mapcache_buffer_create(10,ctx->pool);
+
+#if LIBCURL_VERSION_NUM < 0x071700
+  /* 
+   * hack around a bug in curl <= 7.22 where the content-length is added
+   * a second time even if ti was present in the manually set headers
+   */
+  apr_table_unset(headers, "Content-Length");
+#endif
+
+  _set_headers(ctx, curl, headers);
+
+  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+
+  /* we want to use our own read function */ 
+  curl_easy_setopt(curl, CURLOPT_READFUNCTION, buffer_read_callback);
+
+  /* enable uploading */ 
+  curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
+  /* HTTP PUT please */ 
+  curl_easy_setopt(curl, CURLOPT_PUT, 1L);
+
+  /* specify target URL, and note that this URL should include a file
+   *        name, not only a directory */ 
+  curl_easy_setopt(curl, CURLOPT_URL, url);
+
+  /* now specify which file to upload */ 
+  curl_easy_setopt(curl, CURLOPT_READDATA, &data);
+
+  /* provide the size of the upload, we specicially typecast the value
+   *        to curl_off_t since we must be sure to use the correct data size */ 
+  curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer->size);
+  
+  /* send all data to this function  */
+  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, buffer_write_callback);
+
+  /* we pass our mapcache_buffer struct to the callback function */
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response);
+
+  /* Now run off and do what you've been told! */ 
+  res = curl_easy_perform(curl);
+  /* Check for errors */ 
+  if(res != CURLE_OK) {
+    ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest put: %s",curl_easy_strerror(res));
+  } else {
+    long http_code;
+    curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
+    if(http_code != 200 && http_code != 201 && http_code != 204) {
+      char *msg = response->buf;
+      msg[response->size]=0;
+      ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest put with code %ld: %s", http_code, msg);
+    }
+  }
+
+}
+
+static int _head_request(mapcache_context *ctx, char *url, apr_table_t *headers) {
+
+  CURL *curl;
+  CURLcode res;
+  long http_code;
+
+  curl = curl_easy_init();
+
+  if(!curl) {
+    ctx->set_error(ctx,500,"failed to create curl handle");
+    return -1;
+  }
+
+  _set_headers(ctx, curl, headers);
+  
+  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+
+  /* specify target URL, and note that this URL should include a file
+   *        name, not only a directory */ 
+  curl_easy_setopt(curl, CURLOPT_URL, url);
+  
+  curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
+
+  /* Now run off and do what you've been told! */ 
+  res = curl_easy_perform(curl);
+  /* Check for errors */ 
+  if(res != CURLE_OK) {
+    ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest head %s",curl_easy_strerror(res));
+    http_code = 500;
+  } else {
+    curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
+  }
+
+  /* always cleanup */ 
+  curl_easy_cleanup(curl);
+  
+  return (int)http_code;
+}
+
+static int _delete_request(mapcache_context *ctx, char *url, apr_table_t *headers) {
+
+  CURL *curl;
+  CURLcode res;
+  long http_code;
+
+  curl = curl_easy_init();
+  if(!curl) {
+    ctx->set_error(ctx,500,"failed to create curl handle");
+    return -1;
+  }
+
+  _set_headers(ctx, curl, headers);
+
+  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+
+  /* specify target URL, and note that this URL should include a file
+   *        name, not only a directory */ 
+  curl_easy_setopt(curl, CURLOPT_URL, url);
+  
+  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+
+  /* Now run off and do what you've been told! */ 
+  res = curl_easy_perform(curl);
+  /* Check for errors */ 
+  if(res != CURLE_OK) {
+    ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest head %s",curl_easy_strerror(res));
+    http_code = 500;
+  } else {
+    curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
+  }
+
+  /* always cleanup */ 
+  curl_easy_cleanup(curl);
+  
+  return (int)http_code;
+}
+
+static mapcache_buffer* _get_request(mapcache_context *ctx, char *url, apr_table_t *headers) {
+
+  CURL *curl;
+  CURLcode res;
+  mapcache_buffer *data = NULL;
+  long http_code;
+
+  curl = curl_easy_init();
+  if(!curl) {
+    ctx->set_error(ctx,500,"failed to create curl handle");
+    return NULL;
+  }
+
+  _set_headers(ctx, curl, headers);
+
+  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+
+  data = mapcache_buffer_create(4000, ctx->pool);
+
+  /* send all data to this function  */
+  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, buffer_write_callback);
+
+  /* we pass our mapcache_buffer struct to the callback function */
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)data);
+
+  /* specify target URL, and note that this URL should include a file
+   *        name, not only a directory */ 
+  curl_easy_setopt(curl, CURLOPT_URL, url);
+
+  /* Now run off and do what you've been told! */ 
+  res = curl_easy_perform(curl);
+  /* Check for errors */ 
+  if(res != CURLE_OK) {
+    ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest get: %s",curl_easy_strerror(res));
+    data = NULL;
+  } else {
+    curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
+    /* handle special behavior of s3 */ 
+    if(http_code == 403) {
+      char *msg = data->buf;
+      while(msg && *msg) {
+        if(!strncmp(msg,"NoSuchKey",strlen("NoSuchKey"))) {
+          ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest get with code %ld: %s", http_code, msg);
+          http_code = 404;
+          data = NULL;
+          break;
+        }
+        msg++;
+      }
+    }
+    if(http_code != 200 && http_code != 404) {
+      char *msg = data->buf;
+      msg[data->size]=0;
+      ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest get with code %ld: %s", http_code, msg);
+    }
+    if(http_code == 404) {
+      data = NULL; /* not an error */
+    }
+  }
+
+  /* always cleanup */ 
+  curl_easy_cleanup(curl);
+  
+  return data;
+}
+
+apr_table_t* _mapcache_cache_rest_headers(mapcache_context *ctx, mapcache_tile *tile, mapcache_rest_configuration *config,
+   mapcache_rest_operation *operation) {
+  apr_table_t *ret = apr_table_make(ctx->pool,3);
+  const apr_array_header_t *array;
+  
+  if(config->common_headers) {
+    array = apr_table_elts(config->common_headers);
+    apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
+    int i;
+    for (i = 0; i < array->nelts; i++) {
+      apr_table_set(ret, elts[i].key,elts[i].val);
+    }
+  }
+  if(operation->headers) {
+    array = apr_table_elts(operation->headers);
+    apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
+    int i;
+    for (i = 0; i < array->nelts; i++) {
+      apr_table_set(ret, elts[i].key,elts[i].val);
+    }
+  }
+  return ret;
+}
+
+/* Converts an integer value to its hex character*/
+static char to_hex(char code) {
+  static char hex[] = "0123456789ABCDEF";
+  return hex[code & 15];
+}
+
+/* Returns a url-encoded version of str */
+static char *url_encode(apr_pool_t *pool, char *str) {
+  char *pstr = str, *buf = apr_pcalloc(pool, strlen(str) * 3 + 1), *pbuf = buf;
+  while (*pstr) {
+    if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~' || *pstr=='/')
+      *pbuf++ = *pstr;
+    else if (*pstr == ' ')
+      *pbuf++ = '+';
+    else
+      *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
+    pstr++;
+  }
+  *pbuf = '\0';
+  return buf;
+}
+/**
+ * \brief return url for given tile given a template
+ *
+ * \param tile the tile to get the key from
+ * \param template the template to build the url from
+ * \param path pointer to a char* that will contain the url
+ * \param r
+ * \private \memberof mapcache_cache_rest
+ */
+static void _mapcache_cache_rest_tile_url(mapcache_context *ctx, mapcache_tile *tile, mapcache_rest_configuration *config,
+   mapcache_rest_operation *operation, char **url)
+{
+  char *slashptr,*path;
+  int cnt=0;
+  if(operation && operation->tile_url) {
+    *url = apr_pstrdup(ctx->pool, operation->tile_url);
+  } else {
+    *url = apr_pstrdup(ctx->pool, config->tile_url);
+  }
+
+  *url = mapcache_util_str_replace(ctx->pool, *url, "{tileset}", tile->tileset->name);
+  *url = mapcache_util_str_replace(ctx->pool, *url, "{grid}", tile->grid_link->grid->name);
+  *url = mapcache_util_str_replace(ctx->pool, *url, "{ext}",
+      tile->tileset->format?tile->tileset->format->extension:"png");
+  if(strstr(*url,"{x}"))
+    *url = mapcache_util_str_replace(ctx->pool,*url, "{x}",
+        apr_psprintf(ctx->pool,"%d",tile->x));
+  else
+    *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_x}",
+        apr_psprintf(ctx->pool,"%d",
+          tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1));
+  if(strstr(*url,"{y}"))
+    *url = mapcache_util_str_replace(ctx->pool,*url, "{y}",
+        apr_psprintf(ctx->pool,"%d",tile->y));
+  else
+    *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_y}",
+        apr_psprintf(ctx->pool,"%d",
+          tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1));
+  if(strstr(*url,"{z}"))
+    *url = mapcache_util_str_replace(ctx->pool,*url, "{z}",
+        apr_psprintf(ctx->pool,"%d",tile->z));
+  else
+    *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_z}",
+        apr_psprintf(ctx->pool,"%d",
+          tile->grid_link->grid->nlevels - tile->z - 1));
+  if(tile->dimensions) {
+    char *dimstring="";
+    const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
+    int i = elts->nelts;
+    while(i--) {
+      apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
+      dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->key,"#",entry->val,NULL);
+    }
+    *url = mapcache_util_str_replace(ctx->pool,*url, "{dim}", dimstring);
+  }
+  /* url-encode everything after the host name */
+
+  /* find occurence of third "/" in url */
+  slashptr = *url;
+  while(*slashptr) {
+    if(*slashptr == '/') cnt++;
+    if(cnt == 3) break;
+    slashptr++;
+  }
+  if(!*slashptr) {
+    ctx->set_error(ctx,500,"invalid rest url provided, expecting http(s)://server/path format");
+    return;
+  }
+  path=slashptr;
+  path = url_encode(ctx->pool,path);
+  *slashptr=0;
+
+
+  *url = apr_pstrcat(ctx->pool,*url,path,NULL);
+  /*ctx->log(ctx,MAPCACHE_WARN,"rest url: %s",*url);*/
+}
+
+
+// Simple comparison function for comparing two HTTP header names that are
+// embedded within an HTTP header line, returning true if header1 comes
+// before header2 alphabetically, false if not
+static int headerle(const char *header1, const char *header2)
+{
+    while (1) {
+        if (*header1 == ':') {
+            return (*header2 == ':');
+        }
+        else if (*header2 == ':') {
+            return 0;
+        }
+        else if (*header2 < *header1) {
+            return 0;
+        }
+        else if (*header2 > *header1) {
+            return 1;
+        }
+        header1++, header2++;
+    }
+}
+
+
+// Replace this with merge sort eventually, it's the best stable sort.  But
+// since typically the number of elements being sorted is small, it doesn't
+// matter that much which sort is used, and gnome sort is the world's simplest
+// stable sort.  Added a slight twist to the standard gnome_sort - don't go
+// forward +1, go forward to the last highest index considered.  This saves
+// all the string comparisons that would be done "going forward", and thus
+// only does the necessary string comparisons to move values back into their
+// sorted position.
+static void header_gnome_sort(char **headers, int size)
+{
+    int i = 0, last_highest = 0;
+
+    while (i < size) {
+        if ((i == 0) || headerle(headers[i - 1], headers[i])) {
+            i = ++last_highest;
+        }
+        else {
+            char *tmp = headers[i];
+            headers[i] = headers[i - 1];
+            headers[--i] = tmp;
+        }
+    }
+}
+
+static void _mapcache_cache_google_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
+{
+  char *stringToSign, **aheaders, *resource = url, x_amz_date[64];
+  const char *head;
+  const apr_array_header_t *ahead;
+  apr_table_entry_t *elts;
+  int i,nCanonicalHeaders=0,cnt=0;
+  assert(rcache->provider == MAPCACHE_REST_PROVIDER_GOOGLE);
+  mapcache_cache_google *google = (mapcache_cache_google*)rcache;
+  time_t now = time(NULL);
+  struct tm *tnow = gmtime(&now);
+  unsigned char sha[65];
+  char b64[150];
+  sha[64]=0;
+
+  strftime(x_amz_date, 64 , "%a, %d %b %Y %H:%M:%S GMT", tnow);
+  apr_table_set(headers,"x-amz-date",x_amz_date);
+
+  if(!strcmp(method,"PUT")) {
+    assert(tile->encoded_data);
+    apr_md5(sha,tile->encoded_data->buf,tile->encoded_data->size);
+    apr_base64_encode(b64, (char*)sha, 16);
+    apr_table_set(headers, "Content-MD5", b64);
+  }
+
+  head = apr_table_get(headers, "Content-MD5");
+  if(!head) head = "";
+  stringToSign=apr_pstrcat(ctx->pool, method, "\n", head, "\n", NULL);
+  head = apr_table_get(headers, "Content-Type");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  
+  /* Date: header, left empty as we are using x-amz-date */
+  stringToSign=apr_pstrcat(ctx->pool, stringToSign, "\n", NULL);
+
+  ahead = apr_table_elts(headers);
+  aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*));
+  elts = (apr_table_entry_t *) ahead->elts;
+
+  for (i = 0; i < ahead->nelts; i++) {
+    if(!strncmp(elts[i].key,"x-amz-",6)) {
+      char *k = aheaders[nCanonicalHeaders] = apr_pstrdup(ctx->pool, elts[i].key);
+      while(*k) {
+        *k = tolower(*k);
+        k++;
+      }
+      nCanonicalHeaders++;
+    }
+  }
+  header_gnome_sort(aheaders, nCanonicalHeaders);
+
+  for(i=0; i<nCanonicalHeaders; i++) {
+    stringToSign = apr_pstrcat(ctx->pool, stringToSign, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL);
+  }
+  
+  /* find occurence of third "/" in url */
+  while(*resource) {
+    if(*resource == '/') cnt++;
+    if(cnt == 3) break;
+    resource++;
+  }
+  if(!*resource) {
+    ctx->set_error(ctx,500,"invalid google url provided");
+    return;
+  }
+
+  stringToSign = apr_pstrcat(ctx->pool, stringToSign, resource, NULL);
+
+  hmac_sha1(stringToSign, strlen(stringToSign), (unsigned char*)google->secret, strlen(google->secret), sha);
+
+  apr_base64_encode(b64, (char*)sha, 20);
+
+
+  apr_table_set( headers, "Authorization", apr_pstrcat(ctx->pool,"AWS ", google->access, ":", b64, NULL));
+}
+static void _mapcache_cache_azure_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
+{
+  char *stringToSign, **aheaders, *canonical_headers="", *canonical_resource=NULL, *resource = url, x_ms_date[64];
+  const char *head;
+  const apr_array_header_t *ahead;
+  apr_table_entry_t *elts;
+  int i,nCanonicalHeaders=0,cnt=0;
+  assert(rcache->provider == MAPCACHE_REST_PROVIDER_AZURE);
+  mapcache_cache_azure *azure = (mapcache_cache_azure*)rcache;
+  time_t now = time(NULL);
+  struct tm *tnow = gmtime(&now);
+  unsigned char sha[65];
+  char *b64sign,*keyub64;
+  sha[64]=0;
+
+  strftime(x_ms_date, sizeof(x_ms_date), "%a, %d %b %Y %H:%M:%S GMT", tnow);
+  apr_table_set(headers,"x-ms-date",x_ms_date);
+  apr_table_set(headers,"x-ms-version","2009-09-19");
+  apr_table_set(headers,"x-ms-blob-type","BlockBlob");
+
+  stringToSign = apr_pstrcat(ctx->pool, method, "\n", NULL);
+  head = apr_table_get(headers, "Content-Encoding");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "Content-Language");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "Content-Length");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "Content-MD5");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "Content-Type");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "Date");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "If-Modified-Since");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "If-Match");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "If-None-Match");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "If-Unmodified-Since");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+  head = apr_table_get(headers, "Range");
+  if(!head) head = ""; stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
+
+  ahead = apr_table_elts(headers);
+  aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*));
+  elts = (apr_table_entry_t *) ahead->elts;
+
+  for (i = 0; i < ahead->nelts; i++) {
+    if(strncmp(elts[i].key,"x-ms-",5) || elts[i].key[5]==0) continue;
+    char *k = aheaders[nCanonicalHeaders] = apr_pstrdup(ctx->pool, elts[i].key);
+    while(*k) {
+      *k = tolower(*k);
+      k++;
+    }
+    nCanonicalHeaders++;
+  }
+  header_gnome_sort(aheaders, nCanonicalHeaders);
+
+  for(i=0; i<nCanonicalHeaders; i++) {
+    canonical_headers = apr_pstrcat(ctx->pool, canonical_headers, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL);
+  }
+  
+  /* find occurence of third "/" in url */
+  while(*resource) {
+    if(*resource == '/') cnt++;
+    if(cnt == 3) break;
+    resource++;
+  }
+  if(!*resource) {
+    ctx->set_error(ctx,500,"invalid azure url provided");
+    return;
+  }
+
+  canonical_resource = apr_pstrcat(ctx->pool, "/", azure->id, resource, NULL);
+
+  stringToSign = apr_pstrcat(ctx->pool, stringToSign, canonical_headers, canonical_resource, NULL);
+
+  keyub64 = (char*)apr_pcalloc(ctx->pool, apr_base64_decode_len(azure->secret));
+  apr_base64_decode(keyub64, azure->secret);
+
+  hmac_sha256((unsigned char*)stringToSign, strlen(stringToSign), (unsigned char*)keyub64, strlen(keyub64), sha, 32);
+
+  b64sign = (char*)apr_pcalloc(ctx->pool, apr_base64_encode_len(32));
+  apr_base64_encode(b64sign, (char*)sha, 32);
+
+
+  apr_table_set( headers, "Authorization", apr_pstrcat(ctx->pool,"SharedKey ", azure->id, ":", b64sign, NULL));
+
+  
+}
+static void _mapcache_cache_s3_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
+{
+  unsigned char sha1[65],sha2[65];
+  int cnt=0,i;
+  time_t now = time(NULL);
+  struct tm *tnow = gmtime(&now);
+  const apr_array_header_t *ahead;
+  char *tosign, *key, *canonical_request, x_amz_date[64], *resource = url, **aheaders, *auth;
+  apr_table_entry_t *elts;
+
+  sha1[64]=sha2[64]=0;
+  assert(rcache->provider == MAPCACHE_REST_PROVIDER_S3);
+  mapcache_cache_s3 *s3 = (mapcache_cache_s3*)rcache;
+
+  if(!strcmp(method,"PUT")) {
+    assert(tile->encoded_data);
+    sha256((unsigned char*)tile->encoded_data->buf, tile->encoded_data->size, sha1);
+    sha_hex_encode(sha1,32); 
+  } else {
+    /* sha256 hash of empty string */
+    memcpy(sha1,"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",64);
+  }
+  apr_table_set(headers,"x-amz-content-sha256", (char*)sha1);
+  /* sha1 contains the hash of the payload */
+
+  /* find occurence of third "/" in url */
+  while(*resource) {
+    if(*resource == '/') cnt++;
+    if(cnt == 3) break;
+    resource++;
+  }
+  if(!*resource) {
+    ctx->set_error(ctx,500,"invalid s3 url provided");
+    return;
+  }
+  
+  strftime(x_amz_date, sizeof(x_amz_date), "%Y%m%dT%H%M%SZ", tnow);
+  apr_table_set(headers, "x-amz-date", x_amz_date);
+
+  canonical_request = apr_pstrcat(ctx->pool, method, "\n" ,resource, "\n\n",NULL);
+
+  ahead = apr_table_elts(headers);
+  aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*));
+  elts = (apr_table_entry_t *) ahead->elts;
+
+  for (i = 0; i < ahead->nelts; i++) {
+    char *k = aheaders[i] = apr_pstrdup(ctx->pool, elts[i].key);
+    while(*k) {
+      *k = tolower(*k);
+      k++;
+    }
+  }
+  
+  header_gnome_sort(aheaders, ahead->nelts);
+  for(i=0; i<ahead->nelts; i++) {
+    canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL);
+  }
+  canonical_request = apr_pstrcat(ctx->pool, canonical_request, "\n", NULL);
+  for(i=0; i<ahead->nelts; i++) {
+    if(i==ahead->nelts-1) {
+      canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],NULL);
+    } else {
+      canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],";",NULL);
+    }
+  }
+  canonical_request = apr_pstrcat(ctx->pool, canonical_request, "\n", sha1, NULL);
+  //printf("canonical request: %s\n",canonical_request);
+
+  tosign = apr_pstrcat(ctx->pool, "AWS4-HMAC-SHA256\n",x_amz_date,"\n",NULL);
+  x_amz_date[8]=0;
+  sha256((unsigned char*)canonical_request, strlen(canonical_request), sha1);
+  sha_hex_encode(sha1,32);
+  tosign = apr_pstrcat(ctx->pool, tosign, x_amz_date, "/", s3->region, "/s3/aws4_request\n", sha1,NULL);
+  //printf("key to sign: %s\n",tosign);
+
+  key = apr_pstrcat(ctx->pool, "AWS4", s3->secret, NULL);
+  hmac_sha256((unsigned char*)x_amz_date, 8, (unsigned char*)key, strlen(key), sha1, 32);
+  hmac_sha256((unsigned char*)s3->region, strlen(s3->region), sha1, 32, sha2, 32);
+  hmac_sha256((unsigned char*)"s3", 2, sha2, 32, sha1, 32);
+  hmac_sha256((unsigned char*)"aws4_request", 12, sha1, 32, sha2, 32);
+  hmac_sha256((unsigned char*)tosign, strlen(tosign), sha2, 32, sha1, 32);
+  sha_hex_encode(sha1,32);
+
+
+  auth = apr_pstrcat(ctx->pool, "AWS4-HMAC-SHA256 Credential=",s3->id,"/",x_amz_date,"/",s3->region,"/s3/aws4_request,SignedHeaders=",NULL);
+
+  for(i=0; i<ahead->nelts; i++) {
+    if(i==ahead->nelts-1) {
+      auth = apr_pstrcat(ctx->pool, auth, aheaders[i],NULL);
+    } else {
+      auth = apr_pstrcat(ctx->pool, auth, aheaders[i],";",NULL);
+    }
+  }
+  auth = apr_pstrcat(ctx->pool, auth, ",Signature=", sha1, NULL);
+
+  apr_table_set(headers, "Authorization", auth);
+}
+
+static void _mapcache_cache_s3_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_s3_headers_add(ctx, "PUT", pcache, tile, url, headers);
+}
+static void _mapcache_cache_s3_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_s3_headers_add(ctx, "GET", pcache, tile, url, headers);
+}
+static void _mapcache_cache_s3_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_s3_headers_add(ctx, "HEAD", pcache, tile, url, headers);
+}
+static void _mapcache_cache_s3_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_s3_headers_add(ctx, "DELETE", pcache, tile, url, headers);
+}
+static void _mapcache_cache_azure_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_azure_headers_add(ctx, "PUT", pcache, tile, url, headers);
+}
+static void _mapcache_cache_azure_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_azure_headers_add(ctx, "GET", pcache, tile, url, headers);
+}
+static void _mapcache_cache_azure_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_azure_headers_add(ctx, "HEAD", pcache, tile, url, headers);
+}
+static void _mapcache_cache_azure_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_azure_headers_add(ctx, "DELETE", pcache, tile, url, headers);
+}
+static void _mapcache_cache_google_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_google_headers_add(ctx, "PUT", pcache, tile, url, headers);
+}
+static void _mapcache_cache_google_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_google_headers_add(ctx, "GET", pcache, tile, url, headers);
+}
+static void _mapcache_cache_google_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_google_headers_add(ctx, "HEAD", pcache, tile, url, headers);
+}
+static void _mapcache_cache_google_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
+  _mapcache_cache_google_headers_add(ctx, "DELETE", pcache, tile, url, headers);
+}
+
+
+static int _mapcache_cache_rest_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
+  char *url;
+  apr_table_t *headers;
+  int status;
+  _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.has_tile, &url);
+  headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.has_tile);
+  if(rcache->rest.add_headers) {
+    rcache->rest.add_headers(ctx,rcache,tile,url,headers);
+  }
+  if(rcache->rest.has_tile.add_headers) {
+    rcache->rest.has_tile.add_headers(ctx,rcache,tile,url,headers);
+  }
+  
+  status = _head_request(ctx, url, headers);
+
+  if(GC_HAS_ERROR(ctx))
+    return MAPCACHE_FAILURE;
+
+  if( status == 200)
+    return MAPCACHE_TRUE;
+  else
+    return MAPCACHE_FALSE;
+}
+
+static void _mapcache_cache_rest_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
+  char *url;
+  apr_table_t *headers;
+  int status;
+  _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.delete_tile, &url);
+  headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.delete_tile);
+  if(rcache->rest.add_headers) {
+    rcache->rest.add_headers(ctx,rcache,tile,url,headers);
+  }
+  if(rcache->rest.delete_tile.add_headers) {
+    rcache->rest.delete_tile.add_headers(ctx,rcache,tile,url,headers);
+  }
+  
+  status = _delete_request(ctx, url, headers);
+  GC_CHECK_ERROR(ctx);
+
+  if(status!=200 && status!=202 && status!=204) {
+    //ctx->set_error(ctx,500,"rest delete returned code %d", status);
+  }
+}
+
+
+/**
+ * \brief get file content of given tile
+ *
+ * fills the mapcache_tile::data of the given tile with content stored in the file
+ * \private \memberof mapcache_cache_rest
+ * \sa mapcache_cache::tile_get()
+ */
+static int _mapcache_cache_rest_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
+  char *url;
+  apr_table_t *headers;
+  _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.get_tile, &url);
+  if(tile->allow_redirect && rcache->use_redirects) {
+    tile->redirect = url;
+    return MAPCACHE_SUCCESS;
+  }
+  headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.get_tile);
+  if(rcache->rest.add_headers) {
+    rcache->rest.add_headers(ctx,rcache,tile,url,headers);
+  }
+  if(rcache->rest.get_tile.add_headers) {
+    rcache->rest.get_tile.add_headers(ctx,rcache,tile,url,headers);
+  }
+  tile->encoded_data = _get_request(ctx, url, headers);
+  if(GC_HAS_ERROR(ctx))
+    return MAPCACHE_FAILURE;
+
+  if(!tile->encoded_data)
+    return MAPCACHE_CACHE_MISS;
+
+  return MAPCACHE_SUCCESS;
+}
+
+static void _mapcache_cache_rest_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles) {
+  mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
+  char *url;
+  apr_table_t *headers;
+  CURL *curl = curl_easy_init();
+  int i;
+
+  if(!curl) {
+    ctx->set_error(ctx,500,"failed to create curl handle");
+    return;
+  }
+
+  for(i=0; i<ntiles; i++) {
+    if(i)
+      curl_easy_reset(curl);
+    mapcache_tile *tile = tiles + i;
+    _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.set_tile, &url);
+    headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.set_tile);
+
+    if(!tile->encoded_data) {
+      tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
+      if(GC_HAS_ERROR(ctx)) {
+        goto multi_put_cleanup;
+      }
+    }
+
+    apr_table_set(headers,"Content-Length",apr_psprintf(ctx->pool,"%lu",tile->encoded_data->size));
+    if(tile->tileset->format && tile->tileset->format->mime_type)
+      apr_table_set(headers, "Content-Type", tile->tileset->format->mime_type);
+    else {
+      mapcache_image_format_type imgfmt = mapcache_imageio_header_sniff(ctx,tile->encoded_data);
+      if(imgfmt == GC_JPEG) {
+        apr_table_set(headers, "Content-Type", "image/jpeg");
+      } else if (imgfmt == GC_PNG) {
+        apr_table_set(headers, "Content-Type", "image/png");
+      }
+    }
+
+    if(rcache->rest.add_headers) {
+      rcache->rest.add_headers(ctx,rcache,tile,url,headers);
+    }
+    if(rcache->rest.set_tile.add_headers) {
+      rcache->rest.set_tile.add_headers(ctx,rcache,tile,url,headers);
+    }
+    _put_request(ctx, curl, tile->encoded_data, url, headers);
+    if(GC_HAS_ERROR(ctx)) {
+      goto multi_put_cleanup;
+    }
+  }
+
+multi_put_cleanup:
+  /* always cleanup */ 
+  curl_easy_cleanup(curl);
+
+}
+
+/**
+ * \brief write tile data to rest backend
+ *
+ * writes the content of mapcache_tile::data to disk.
+ * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked
+ * \returns MAPCACHE_SUCCESS if the tile has been successfully written to disk
+ * \private \memberof mapcache_cache_rest
+ * \sa mapcache_cache::tile_set()
+ */
+static void _mapcache_cache_rest_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
+{
+  return _mapcache_cache_rest_multi_set(ctx, pcache, tile, 1);
+}
+
+
+static void _mapcache_cache_rest_operation_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_rest_operation *op)
+{
+  ezxml_t cur_node;
+  if ((cur_node = ezxml_child(node,"headers")) != NULL) {
+    ezxml_t header_node;
+    op->headers = apr_table_make(ctx->pool,3);
+    for(header_node = cur_node->child; header_node; header_node = header_node->sibling) {
+      apr_table_set(op->headers, header_node->name, header_node->txt);
+    }
+  }
+}
+
+/**
+ * \private \memberof mapcache_cache_rest
+ */
+static void _mapcache_cache_rest_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
+{
+  ezxml_t cur_node;
+  mapcache_cache_rest *dcache = (mapcache_cache_rest*)cache;
+  if ((cur_node = ezxml_child(node,"url")) != NULL) {
+    dcache->rest.tile_url = apr_pstrdup(ctx->pool,cur_node->txt);
+  }
+  if ((cur_node = ezxml_child(node,"use_redirects")) != NULL) {
+    if(!strcasecmp(cur_node->txt,"true")) {
+      dcache->use_redirects = 1;
+    }
+  }
+  if ((cur_node = ezxml_child(node,"headers")) != NULL) {
+    ezxml_t header_node;
+    dcache->rest.common_headers = apr_table_make(ctx->pool,3);
+    for(header_node = cur_node->child; header_node; header_node = header_node->sibling) {
+      apr_table_set(dcache->rest.common_headers, header_node->name, header_node->txt);
+    }
+  }
+  for(cur_node = ezxml_child(node,"operation"); cur_node; cur_node = cur_node->next) {
+    char *type = (char*)ezxml_attr(cur_node,"type");
+    if(!type) {
+      ctx->set_error(ctx,400,"<operation> with no \"type\" attribute in cache (%s)", cache->name);
+      return;
+    }
+    if(!strcasecmp(type,"put")) {
+      _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.set_tile);
+      GC_CHECK_ERROR(ctx);
+    } else if(!strcasecmp(type,"get")) {
+      _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.get_tile);
+      GC_CHECK_ERROR(ctx);
+    } else if(!strcasecmp(type,"head")) {
+      _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.has_tile);
+      GC_CHECK_ERROR(ctx);
+    } else if(!strcasecmp(type,"delete")) {
+      _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.delete_tile);
+      GC_CHECK_ERROR(ctx);
+    } else {
+      ctx->set_error(ctx,400,"<operation> with unknown \"type\" (%s) attribute in cache (%s) (expecting put, get, head or delete)", type, cache->name);
+      return;
+    }
+  }
+
+}
+
+static void _mapcache_cache_google_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) 
+{
+  ezxml_t cur_node;
+  mapcache_cache_google *google = (mapcache_cache_google*)cache;
+  _mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config);
+  GC_CHECK_ERROR(ctx);
+  if ((cur_node = ezxml_child(node,"access")) != NULL) {
+    google->access = apr_pstrdup(ctx->pool, cur_node->txt);
+  } else {
+    ctx->set_error(ctx,400,"google cache (%s) is missing required <access> child", cache->name);
+    return;
+  }
+  if ((cur_node = ezxml_child(node,"secret")) != NULL) {
+    google->secret = apr_pstrdup(ctx->pool, cur_node->txt);
+  } else {
+    ctx->set_error(ctx,400,"google cache (%s) is missing required <secret> child", cache->name);
+    return;
+  }
+}
+
+static void _mapcache_cache_s3_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) 
+{
+  ezxml_t cur_node;
+  mapcache_cache_s3 *s3 = (mapcache_cache_s3*)cache;
+  _mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config);
+  GC_CHECK_ERROR(ctx);
+  if ((cur_node = ezxml_child(node,"id")) != NULL) {
+    s3->id = apr_pstrdup(ctx->pool, cur_node->txt);
+  } else {
+    ctx->set_error(ctx,400,"s3 cache (%s) is missing required <id> child", cache->name);
+    return;
+  }
+  if ((cur_node = ezxml_child(node,"secret")) != NULL) {
+    s3->secret = apr_pstrdup(ctx->pool, cur_node->txt);
+  } else {
+    ctx->set_error(ctx,400,"s3 cache (%s) is missing required <secret> child", cache->name);
+    return;
+  }
+  if ((cur_node = ezxml_child(node,"region")) != NULL) {
+    s3->region = apr_pstrdup(ctx->pool, cur_node->txt);
+  } else {
+    ctx->set_error(ctx,400,"s3 cache (%s) is missing required <region> child", cache->name);
+    return;
+  }
+}
+
+static void _mapcache_cache_azure_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) 
+{
+  ezxml_t cur_node;
+  mapcache_cache_azure *azure = (mapcache_cache_azure*)cache;
+  _mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config);
+  GC_CHECK_ERROR(ctx);
+  if ((cur_node = ezxml_child(node,"id")) != NULL) {
+    azure->id = apr_pstrdup(ctx->pool, cur_node->txt);
+  } else {
+    ctx->set_error(ctx,400,"azure cache (%s) is missing required <id> child", cache->name);
+    return;
+  }
+  if ((cur_node = ezxml_child(node,"secret")) != NULL) {
+    azure->secret = apr_pstrdup(ctx->pool, cur_node->txt);
+  } else {
+    ctx->set_error(ctx,400,"azure cache (%s) is missing required <secret> child", cache->name);
+    return;
+  }
+  if ((cur_node = ezxml_child(node,"container")) != NULL) {
+    azure->container = apr_pstrdup(ctx->pool, cur_node->txt);
+  } else {
+    ctx->set_error(ctx,400,"azure cache (%s) is missing required <container> child", cache->name);
+    return;
+  }
+}
+
+/**
+ * \private \memberof mapcache_cache_rest
+ */
+static void _mapcache_cache_rest_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
+    mapcache_cfg *cfg)
+{
+  mapcache_cache_rest *dcache = (mapcache_cache_rest*)cache;
+
+
+  if(!dcache->rest.tile_url) {
+    if(!dcache->rest.delete_tile.tile_url) {
+      ctx->set_error(ctx,400, "rest cache (%s) has no global <url> and no <url> for delete_tile operation", cache->name);
+      return;
+    }
+    if(!dcache->rest.get_tile.tile_url) {
+      ctx->set_error(ctx,400, "rest cache (%s) has no global <url> and no <url> for get_tile operation", cache->name);
+      return;
+    }
+    if(!dcache->rest.set_tile.tile_url) {
+      ctx->set_error(ctx,400, "rest cache (%s) has no global <url> and no <url> for set_tile operation", cache->name);
+      return;
+    }
+  }
+}
+
+void mapcache_cache_rest_init(mapcache_context *ctx, mapcache_cache_rest *cache) {
+  cache->retry_count = 0;
+  cache->use_redirects = 0;
+  cache->rest.get_tile.method = MAPCACHE_REST_METHOD_GET;
+  cache->rest.set_tile.method = MAPCACHE_REST_METHOD_PUT;
+  cache->rest.delete_tile.method = MAPCACHE_REST_METHOD_DELETE;
+  cache->rest.multi_set_tile.method = MAPCACHE_REST_METHOD_PUT;
+  cache->rest.has_tile.method = MAPCACHE_REST_METHOD_HEAD;
+  cache->cache.metadata = apr_table_make(ctx->pool,3);
+  cache->cache.type = MAPCACHE_CACHE_REST;
+  cache->cache.tile_delete = _mapcache_cache_rest_delete;
+  cache->cache.tile_get = _mapcache_cache_rest_get;
+  cache->cache.tile_exists = _mapcache_cache_rest_has_tile;
+  cache->cache.tile_set = _mapcache_cache_rest_set;
+  cache->cache.tile_multi_set = _mapcache_cache_rest_multi_set;
+  cache->cache.configuration_post_config = _mapcache_cache_rest_configuration_post_config;
+  cache->cache.configuration_parse_xml = _mapcache_cache_rest_configuration_parse_xml;
+}
+/**
+ * \brief creates and initializes a mapcache_rest_cache
+ */
+mapcache_cache* mapcache_cache_rest_create(mapcache_context *ctx)
+{
+  mapcache_cache_rest *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_rest));
+  if(!cache) {
+    ctx->set_error(ctx, 500, "failed to allocate rest cache");
+    return NULL;
+  }
+  mapcache_cache_rest_init(ctx,cache);
+  cache->provider = MAPCACHE_REST_PROVIDER_NONE;
+  return (mapcache_cache*)cache;
+}
+
+/**
+ * \brief creates and initializes a mapcache_s3_cache
+ */
+mapcache_cache* mapcache_cache_s3_create(mapcache_context *ctx)
+{
+  mapcache_cache_s3 *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_s3));
+  if(!cache) {
+    ctx->set_error(ctx, 500, "failed to allocate s3 cache");
+    return NULL;
+  }
+  mapcache_cache_rest_init(ctx,&cache->cache);
+  cache->cache.provider = MAPCACHE_REST_PROVIDER_S3;
+  cache->cache.cache.configuration_parse_xml = _mapcache_cache_s3_configuration_parse_xml;
+  cache->cache.rest.get_tile.add_headers = _mapcache_cache_s3_get_headers_add;
+  cache->cache.rest.has_tile.add_headers = _mapcache_cache_s3_head_headers_add;
+  cache->cache.rest.set_tile.add_headers = _mapcache_cache_s3_put_headers_add;
+  cache->cache.rest.delete_tile.add_headers = _mapcache_cache_s3_delete_headers_add;
+  return (mapcache_cache*)cache;
+}
+
+/**
+ * \brief creates and initializes a mapcache_azure_cache
+ */
+mapcache_cache* mapcache_cache_azure_create(mapcache_context *ctx)
+{
+  mapcache_cache_azure *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_azure));
+  if(!cache) {
+    ctx->set_error(ctx, 500, "failed to allocate azure cache");
+    return NULL;
+  }
+  mapcache_cache_rest_init(ctx,&cache->cache);
+  cache->cache.provider = MAPCACHE_REST_PROVIDER_AZURE;
+  cache->cache.cache.configuration_parse_xml = _mapcache_cache_azure_configuration_parse_xml;
+  cache->cache.rest.get_tile.add_headers = _mapcache_cache_azure_get_headers_add;
+  cache->cache.rest.has_tile.add_headers = _mapcache_cache_azure_head_headers_add;
+  cache->cache.rest.set_tile.add_headers = _mapcache_cache_azure_put_headers_add;
+  cache->cache.rest.delete_tile.add_headers = _mapcache_cache_azure_delete_headers_add;
+  return (mapcache_cache*)cache;
+}
+
+/**
+ * \brief creates and initializes a mapcache_google_cache
+ */
+mapcache_cache* mapcache_cache_google_create(mapcache_context *ctx)
+{
+  mapcache_cache_google *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_google));
+  if(!cache) {
+    ctx->set_error(ctx, 500, "failed to allocate google cache");
+    return NULL;
+  }
+  mapcache_cache_rest_init(ctx,&cache->cache);
+  cache->cache.provider = MAPCACHE_REST_PROVIDER_GOOGLE;
+  cache->cache.cache.configuration_parse_xml = _mapcache_cache_google_configuration_parse_xml;
+  cache->cache.rest.get_tile.add_headers = _mapcache_cache_google_get_headers_add;
+  cache->cache.rest.has_tile.add_headers = _mapcache_cache_google_head_headers_add;
+  cache->cache.rest.set_tile.add_headers = _mapcache_cache_google_put_headers_add;
+  cache->cache.rest.delete_tile.add_headers = _mapcache_cache_google_delete_headers_add;
+  return (mapcache_cache*)cache;
+}
+
+
+
+/* vim: ts=2 sts=2 et sw=2
+*/
diff --git a/lib/cache_riak.c b/lib/cache_riak.c
new file mode 100644
index 0000000..fba4e24
--- /dev/null
+++ b/lib/cache_riak.c
@@ -0,0 +1,452 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  MapServer
+ * Purpose:  MapCache tile caching support file: riak cache backend.
+ * Author:   Michael Downey and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2013 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include "mapcache-config.h"
+#ifdef USE_RIAK
+
+#include "mapcache.h"
+
+#include <apr_strings.h>
+#include <apr_reslist.h>
+#include <apr_hash.h>
+
+#include <string.h>
+#include <errno.h>
+
+/*
+ * Since we don't construct the connection pool and store it in the cache object
+ * we have to store all the connections in a hash map in case there are multiple
+ * riak caches defined.
+ */
+
+struct riak_conn_params {
+  mapcache_cache_riak *cache;
+};
+
+void mapcache_riak_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *pool) {
+    mapcache_cache_riak *cache = ((struct riak_conn_params*)params)->cache;
+    struct RIACK_CONNECTION_OPTIONS options;
+    struct RIACK_CLIENT *client = riack_new_client(0);
+    
+    if (client == NULL) {
+        ctx->set_error(ctx,500,"failed to riack_new_client(0)");
+        return;
+    }
+
+    options.recv_timeout_ms = 2000;
+    options.send_timeout_ms = 2000;
+    if (riack_connect(client, cache->host, cache->port, &options) != RIACK_SUCCESS) {
+        riack_free(client);
+        ctx->set_error(ctx,500,"failed to riack_connect()");
+        return;
+    }
+
+    if (riack_ping(client) != RIACK_SUCCESS) {
+        riack_free(client);
+        ctx->set_error(ctx,500,"failed to riack_ping()");
+        return;
+    }
+
+    *conn_ = client;
+}
+
+void mapcache_riak_connection_destructor(void *conn_, apr_pool_t *process_pool) {
+    struct RIACK_CLIENT *client = (struct RIACK_CLIENT *)conn_;
+    riack_free(client);
+}
+
+static mapcache_pooled_connection* _riak_get_connection(mapcache_context *ctx, mapcache_cache_riak *cache, mapcache_tile *tile)
+{
+  mapcache_pooled_connection *pc;
+  struct riak_conn_params params;
+
+  params.cache = cache;
+
+  pc = mapcache_connection_pool_get_connection(ctx,cache->cache.name,mapcache_riak_connection_constructor,
+          mapcache_riak_connection_destructor, &params);
+
+  return pc;
+}
+
+static int _mapcache_cache_riak_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
+    int error;
+	  int retries = 3;
+    RIACK_STRING key;
+    struct RIACK_GET_OBJECT obj;
+    struct RIACK_CLIENT *client;
+    mapcache_pooled_connection *pc;
+    mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache;
+
+    key.value = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+    if (GC_HAS_ERROR(ctx)) {
+        return MAPCACHE_FALSE;
+    }
+    key.len = strlen(key.value);
+
+    pc = _riak_get_connection(ctx, cache, tile);
+    if (GC_HAS_ERROR(ctx)) {
+        return MAPCACHE_FALSE;
+    }
+    client = pc->connection;
+
+	do
+    {
+        error = riack_get(client, cache->bucket, key, 0, &obj);
+        if (error != RIACK_SUCCESS) {
+            ctx->log(ctx, MAPCACHE_WARN, "Retry %d in riak_has_tile for tile %s from cache %s due to error %d", (4-retries), key.value, cache->cache.name, error);
+            for (error = riack_reconnect(client);
+                 error != RIACK_SUCCESS && retries > 0;
+                 error = riack_reconnect(client))
+            {
+              --retries;
+            }
+
+            --retries;
+        }
+    }
+    while (error != RIACK_SUCCESS && retries >= 0);
+
+    if (error != RIACK_SUCCESS) {
+        riack_free_get_object(client, &obj);    // riack_get allocates the returned object so we need to deallocate it.
+        mapcache_connection_pool_invalidate_connection(ctx,pc);
+        ctx->set_error(ctx, 500, "riak: failed to get key %s: %d", key, error);
+        return MAPCACHE_FALSE;
+    }
+
+    if (obj.object.content_count < 1 || obj.object.content[0].data_len == 0) {
+      error = MAPCACHE_FALSE;
+    } else {
+      error = MAPCACHE_TRUE;
+    }
+
+    riack_free_get_object(client, &obj);    // riack_get allocates the returned object so we need to deallocate it.
+    mapcache_connection_pool_release_connection(ctx,pc);
+
+    return error;
+}
+
+static void _mapcache_cache_riak_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
+    int error;
+    RIACK_STRING key;
+    struct RIACK_CLIENT *client;
+    struct RIACK_DEL_PROPERTIES properties;
+    mapcache_pooled_connection *pc;
+    mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache;
+
+    memset(&properties, 0, sizeof(struct RIACK_DEL_PROPERTIES));
+
+
+    key.value = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+    GC_CHECK_ERROR(ctx);
+    key.len = strlen(key.value);
+
+    pc = _riak_get_connection(ctx, cache, tile);
+    GC_CHECK_ERROR(ctx);
+    client = pc->connection;
+
+    properties.rw_use = 1;
+    properties.rw = (4294967295 - 3);	// Special value meaning "ALL"
+    error = riack_delete(client, cache->bucket, key, &properties);
+
+    mapcache_connection_pool_release_connection(ctx,pc);
+
+    if (error != RIACK_SUCCESS) {
+        ctx->set_error(ctx, 500, "riak: failed to delete key %s: %d", key, error);
+    }
+}
+
+/**
+ * \brief get content of given tile
+ *
+ * fills the mapcache_tile::data of the given tile with content stored on the riak server
+ * \private \memberof mapcache_cache_riak
+ * \sa mapcache_cache::tile_get()
+ */
+static int _mapcache_cache_riak_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
+    int error;
+    int connect_error = RIACK_SUCCESS;
+    int retries = 3;
+    RIACK_STRING key;
+    struct RIACK_GET_OBJECT obj;
+    struct RIACK_GET_PROPERTIES properties;
+    struct RIACK_CLIENT *client;
+    mapcache_pooled_connection *pc;
+    mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache;
+
+    memset(&properties, 0, sizeof(struct RIACK_GET_PROPERTIES));
+
+	//Use Buckets defaults instead of setting the read/write attributes
+    /*
+	properties.r_use = 1;
+    properties.r = 1;
+	*/
+
+
+    key.value = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+    if (GC_HAS_ERROR(ctx)) {
+        return MAPCACHE_FAILURE;
+    }
+    key.len = strlen(key.value);
+
+    tile->encoded_data = mapcache_buffer_create(0, ctx->pool);
+
+    pc = _riak_get_connection(ctx, cache, tile);
+    if (GC_HAS_ERROR(ctx)) {
+        return MAPCACHE_FAILURE;
+    }
+    client = pc->connection;
+
+    // If we get an error it is advised that we call reconnect.  It also appears
+    // that every now and then we get an error and need to retry once again to
+    // get it to work.
+    do
+    {
+        error = riack_get(client, cache->bucket, key, &properties, &obj);
+        if (error != RIACK_SUCCESS) {
+            ctx->log(ctx, MAPCACHE_WARN, "Retry %d in riak_get for tile %s from cache %s due to error %d", (4-retries), key.value, cache->cache.name, error);
+            for (connect_error = riack_reconnect(client);
+                 connect_error != RIACK_SUCCESS && retries > 0;
+                 connect_error = riack_reconnect(client))
+            {
+              --retries;
+            }
+
+            --retries;
+        }
+    }
+    while (error != RIACK_SUCCESS && retries >= 0);
+
+    if (error != RIACK_SUCCESS)
+    {
+        if (connect_error != RIACK_SUCCESS)
+            mapcache_connection_pool_invalidate_connection(ctx,pc);
+        else
+            mapcache_connection_pool_release_connection(ctx,pc);
+
+        ctx->set_error(ctx, 500, "Failed to get tile %s from cache %s due to error %d", key.value, cache->cache.name, error);
+        return MAPCACHE_FAILURE;
+    }
+
+    // Check if tile exists.  If it doesn't we need to return CACHE_MISS or things go wrong.
+    // Mapcache doesn't appear to use the has_tile function and uses _get instead so we need
+    // to do this sort of test here instead of erroring.
+    if (obj.object.content_count < 1 || obj.object.content[0].data_len == 0) {
+        riack_free_get_object(client, &obj);  // Need to free the object here as well.
+        mapcache_connection_pool_release_connection(ctx,pc);
+        return MAPCACHE_CACHE_MISS;
+    }
+
+    // Copy the data into the buffer
+    mapcache_buffer_append(tile->encoded_data, obj.object.content[0].data_len, obj.object.content[0].data);
+
+    riack_free_get_object(client, &obj);    // riack_get allocates the returned object so we need to deallocate it.
+
+    mapcache_connection_pool_release_connection(ctx,pc);
+
+    return MAPCACHE_SUCCESS;
+}
+
+/**
+ * \brief push tile data to riak
+ *
+ * writes the content of mapcache_tile::data to the configured riak instance(s)
+ * \private \memberof mapcache_cache_riak
+ * \sa mapcache_cache::tile_set()
+ */
+static void _mapcache_cache_riak_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
+    char *key;
+    int error;
+    int connect_error = RIACK_SUCCESS;
+    int retries = 3;
+    struct RIACK_OBJECT object;
+    struct RIACK_CONTENT content;
+    struct RIACK_PUT_PROPERTIES properties;
+    struct RIACK_CLIENT *client;
+    mapcache_pooled_connection *pc;
+    mapcache_cache_riak *cache = (mapcache_cache_riak*)pcache;
+
+    memset(&content, 0, sizeof(struct RIACK_CONTENT));
+    memset(&object, 0, sizeof(struct RIACK_OBJECT));
+    memset(&properties, 0, sizeof(struct RIACK_PUT_PROPERTIES));
+
+	//Use Buckets defaults instead of setting the read/write attributes
+	/* 
+    properties.w_use = 1;
+    properties.w = 1;
+
+    properties.dw_use = 1;
+    properties.dw = 0;*/
+
+
+    key = mapcache_util_get_tile_key(ctx, tile, NULL, " \r\n\t\f\e\a\b", "#");
+    GC_CHECK_ERROR(ctx);
+
+    if (!tile->encoded_data) {
+        tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
+        GC_CHECK_ERROR(ctx);
+    }
+
+    pc = _riak_get_connection(ctx, cache, tile);
+    GC_CHECK_ERROR(ctx);
+    client = pc->connection;
+
+    // Set up the riak object to put.  Need to do this after we get the client connection
+    object.bucket.value = cache->bucket.value;
+    object.bucket.len = cache->bucket.len;
+    object.key.value = key;
+    object.key.len = strlen(key);
+    object.vclock.len = 0;
+    object.content_count = 1;
+    object.content = &content;
+    content.content_type.value = tile->tileset->format->mime_type;
+    content.content_type.len = strlen(tile->tileset->format->mime_type);
+    content.data = (uint8_t*)tile->encoded_data->buf;
+    content.data_len = tile->encoded_data->size;
+
+    // If we get an error it is advised that we call reconnect.  It also appears
+    // that every now and then we get an error and need to retry once again to
+    // get it to work.
+    do
+    {
+        error = riack_put(client, object, 0, &properties);
+        if (error != RIACK_SUCCESS) {
+            ctx->log(ctx, MAPCACHE_WARN, "Retry %d in riak_set for tile %s from cache %s due to eror %d", (4 - retries), key, cache->cache.name, error);
+            for (connect_error = riack_reconnect(client);
+                 connect_error != RIACK_SUCCESS && retries > 0;
+                 connect_error = riack_reconnect(client))
+            {
+                --retries;
+            }
+
+            --retries;
+        }
+    }
+    while (error != RIACK_SUCCESS && retries >= 0);
+
+    if (connect_error != RIACK_SUCCESS)
+      mapcache_connection_pool_invalidate_connection(ctx,pc);
+    else
+      mapcache_connection_pool_release_connection(ctx,pc);
+
+    if (error != RIACK_SUCCESS)
+    {
+        ctx->set_error(ctx, 500, "failed to store tile %s to cache %s due to error %d.", key, cache->cache.name, error);
+    }
+}
+
+/**
+ * \private \memberof mapcache_cache_riak
+ */
+static void _mapcache_cache_riak_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config) {
+    ezxml_t cur_node,xhost,xport,xbucket;
+    mapcache_cache_riak *dcache = (mapcache_cache_riak*)cache;
+    int servercount = 0;
+
+    for (cur_node = ezxml_child(node,"server"); cur_node; cur_node = cur_node->next) {
+        servercount++;
+    }
+
+    if (!servercount) {
+        ctx->set_error(ctx, 400, "riak cache %s has no <server>s configured", cache->name);
+        return;
+    }
+
+    if (servercount > 1) {
+        ctx->set_error(ctx, 400, "riak cache %s has more than 1 server configured", cache->name);
+        return;
+    }
+
+    cur_node = ezxml_child(node, "server");
+    xhost = ezxml_child(cur_node, "host");   /* Host should contain just server */
+    xport = ezxml_child(cur_node, "port");
+    xbucket = ezxml_child(cur_node, "bucket");
+
+    if (!xhost || !xhost->txt || ! *xhost->txt) {
+        ctx->set_error(ctx, 400, "cache %s: <server> with no <host>", cache->name);
+        return;
+    } else {
+        dcache->host = apr_pstrdup(ctx->pool, xhost->txt);
+        if (dcache->host == NULL) {
+            ctx->set_error(ctx, 400, "cache %s: failed to allocate host string!", cache->name);
+            return;
+        }
+    }
+
+    if (!xport || !xport->txt || ! *xport->txt) {
+        ctx->set_error(ctx, 400, "cache %s: <server> with no <port>", cache->name);
+        return;
+    } else {
+        dcache->port = atoi(xport->txt);
+    }
+
+    if (!xbucket || !xbucket->txt || ! *xbucket->txt) {
+        ctx->set_error(ctx, 400, "cache %s: <server> with no <bucket>", cache->name);
+        return;
+    } else {
+        dcache->bucket.value = apr_pstrdup(ctx->pool, xbucket->txt);
+        if (dcache->bucket.value == NULL) {
+            ctx->set_error(ctx, 400, "cache %s: failed to allocate bucket string!", cache->name);
+            return;
+        }
+        dcache->bucket.len = strlen(dcache->bucket.value);
+    }
+}
+
+/**
+ * \private \memberof mapcache_cache_riak
+ */
+static void _mapcache_cache_riak_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache, mapcache_cfg *cfg) {
+    riack_init();
+}
+
+/**
+ * \brief creates and initializes a mapcache_riak_cache
+ */
+mapcache_cache* mapcache_cache_riak_create(mapcache_context *ctx) {
+    mapcache_cache_riak *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_riak));
+    if (!cache) {
+        ctx->set_error(ctx, 500, "failed to allocate riak cache");
+        return NULL;
+    }
+
+    cache->cache.metadata = apr_table_make(ctx->pool, 3);
+    cache->cache.type = MAPCACHE_CACHE_RIAK;
+    cache->cache.tile_get = _mapcache_cache_riak_get;
+    cache->cache.tile_exists = _mapcache_cache_riak_has_tile;
+    cache->cache.tile_set = _mapcache_cache_riak_set;
+    cache->cache.tile_delete = _mapcache_cache_riak_delete;
+    cache->cache.configuration_parse_xml = _mapcache_cache_riak_configuration_parse_xml;
+    cache->cache.configuration_post_config = _mapcache_cache_riak_configuration_post_config;
+    cache->host = NULL;
+    cache->port = 8087;	// Default RIAK port used for protobuf
+
+    return (mapcache_cache*)cache;
+}
+
+#endif
diff --git a/lib/cache_sqlite.c b/lib/cache_sqlite.c
index 602929a..b4dd8dc 100644
--- a/lib/cache_sqlite.c
+++ b/lib/cache_sqlite.c
@@ -47,17 +47,20 @@
 
 #include <sqlite3.h>
 
-static apr_hash_t *ro_connection_pools = NULL;
-static apr_hash_t *rw_connection_pools = NULL;
+struct sqlite_conn_params {
+  mapcache_cache_sqlite *cache;
+  char *dbfile;
+  int readonly;
+};
 
 struct sqlite_conn {
   sqlite3 *handle;
-  int readonly;
   int nstatements;
   sqlite3_stmt **prepared_statements;
-  char *errmsg;
 };
 
+#define SQLITE_CONN(pooled_connection) ((struct sqlite_conn*)((pooled_connection)->connection))
+
 #define HAS_TILE_STMT_IDX 0
 #define GET_TILE_STMT_IDX 1
 #define SQLITE_SET_TILE_STMT_IDX 2
@@ -70,8 +73,9 @@ struct sqlite_conn {
 #define MBTILES_DEL_TILE_STMT1_IDX 7
 #define MBTILES_DEL_TILE_STMT2_IDX 8
 
+static void _mapcache_cache_sqlite_filename_for_tile(mapcache_context *ctx, mapcache_cache_sqlite *dcache, mapcache_tile *tile, char **path);
 
-static int _sqlite_set_pragmas(apr_pool_t *pool, mapcache_cache_sqlite* cache, struct sqlite_conn *conn)
+static void _sqlite_set_pragmas(mapcache_context *ctx, mapcache_cache_sqlite* cache, struct sqlite_conn *conn)
 {
   if (cache->pragmas && !apr_is_empty_table(cache->pragmas)) {
     const apr_array_header_t *elts = apr_table_elts(cache->pragmas);
@@ -80,7 +84,7 @@ static int _sqlite_set_pragmas(apr_pool_t *pool, mapcache_cache_sqlite* cache, s
     char *pragma_stmt;
     for (i = 0; i < elts->nelts; i++) {
       apr_table_entry_t entry = APR_ARRAY_IDX(elts, i, apr_table_entry_t);
-      pragma_stmt = apr_psprintf(pool,"PRAGMA %s=%s",entry.key,entry.val);
+      pragma_stmt = apr_psprintf(ctx->pool,"PRAGMA %s=%s",entry.key,entry.val);
       do {
         ret = sqlite3_exec(conn->handle, pragma_stmt, 0, 0, NULL);
         if (ret != SQLITE_OK && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
@@ -88,78 +92,59 @@ static int _sqlite_set_pragmas(apr_pool_t *pool, mapcache_cache_sqlite* cache, s
         }
       } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
       if (ret != SQLITE_OK) {
-        conn->errmsg = apr_psprintf(pool,"failed to execute pragma statement %s",pragma_stmt);
-        return MAPCACHE_FAILURE;
+        ctx->set_error(ctx, 500, "failed to execute pragma statement %s",pragma_stmt);
+        return;
       }
     }
   }
-  return MAPCACHE_SUCCESS;
+  return;
+}
+
+
+
+static void mapcache_sqlite_release_conn(mapcache_context *ctx, mapcache_pooled_connection *conn) {
+  mapcache_connection_pool_release_connection(ctx,conn);
 }
 
-static apr_status_t _sqlite_reslist_get_rw_connection(void **conn_, void *params, apr_pool_t *pool)
+void mapcache_sqlite_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *process_pool)
 {
   int ret;
   int flags;  
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) params;
-  struct sqlite_conn *conn = apr_pcalloc(pool, sizeof (struct sqlite_conn));
+  struct sqlite_conn_params *sq_params = (struct sqlite_conn_params*)params;
+  struct sqlite_conn *conn = calloc(1, sizeof (struct sqlite_conn));
   *conn_ = conn;
-  flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_CREATE;
-  ret = sqlite3_open_v2(cache->dbfile, &conn->handle, flags, NULL);
+  if(sq_params->readonly) {
+    flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX;
+  } else {
+    flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_CREATE;
+  }
+  ret = sqlite3_open_v2(sq_params->dbfile, &conn->handle, flags, NULL);
   if (ret != SQLITE_OK) {
-    conn->errmsg = apr_psprintf(pool,"sqlite backend failed to open db %s: %s", cache->dbfile, sqlite3_errmsg(conn->handle));
-    return APR_EGENERAL;
+    ctx->set_error(ctx,500,"sqlite backend failed to open db %s: %s", sq_params->dbfile, sqlite3_errmsg(conn->handle));
+    return;
   }
   sqlite3_busy_timeout(conn->handle, 300000);
   do {
-    ret = sqlite3_exec(conn->handle, cache->create_stmt.sql, 0, 0, NULL);
+    ret = sqlite3_exec(conn->handle, sq_params->cache->create_stmt.sql, 0, 0, NULL);
     if (ret != SQLITE_OK && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
       break;
     }
   } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
   if (ret != SQLITE_OK) {
-    conn->errmsg = apr_psprintf(pool, "sqlite backend failed to create db schema on %s: %s", cache->dbfile, sqlite3_errmsg(conn->handle));
-    sqlite3_close(conn->handle);
-    return APR_EGENERAL;
-  }
-  conn->readonly = 0;
-  ret = _sqlite_set_pragmas(pool, cache, conn);
-  if(ret != MAPCACHE_SUCCESS) {
+    ctx->set_error(ctx,500, "sqlite backend failed to create db schema on %s: %s", sq_params->dbfile, sqlite3_errmsg(conn->handle));
     sqlite3_close(conn->handle);
-    return APR_EGENERAL;
-  }
-  conn->prepared_statements = calloc(cache->n_prepared_statements,sizeof(sqlite3_stmt*));
-  conn->nstatements = cache->n_prepared_statements;
-
-  return APR_SUCCESS;
-}
-
-static apr_status_t _sqlite_reslist_get_ro_connection(void **conn_, void *params, apr_pool_t *pool)
-{
-  int ret;
-  int flags;  
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) params;
-  struct sqlite_conn *conn = apr_pcalloc(pool, sizeof (struct sqlite_conn));
-  *conn_ = conn;
-  flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX;
-  ret = sqlite3_open_v2(cache->dbfile, &conn->handle, flags, NULL);
-  
-  if (ret != SQLITE_OK) {
-    return APR_EGENERAL;
+    return;
   }
-  sqlite3_busy_timeout(conn->handle, 300000);
-  conn->readonly = 1;
-
-  ret = _sqlite_set_pragmas(pool,cache, conn);
-  if (ret != MAPCACHE_SUCCESS) {
+  _sqlite_set_pragmas(ctx, sq_params->cache, conn);
+  if(GC_HAS_ERROR(ctx)) {
     sqlite3_close(conn->handle);
-    return APR_EGENERAL;
+    return;
   }
-  conn->prepared_statements = calloc(cache->n_prepared_statements,sizeof(sqlite3_stmt*));
-  conn->nstatements = cache->n_prepared_statements;
-  return APR_SUCCESS;
+  conn->prepared_statements = calloc(sq_params->cache->n_prepared_statements,sizeof(sqlite3_stmt*));
+  conn->nstatements = sq_params->cache->n_prepared_statements;
 }
 
-static apr_status_t _sqlite_reslist_free_connection(void *conn_, void *params, apr_pool_t *pool)
+void mapcache_sqlite_connection_destructor(void *conn_, apr_pool_t *process_pool)
 {
   struct sqlite_conn *conn = (struct sqlite_conn*) conn_;
   int i;
@@ -170,109 +155,106 @@ static apr_status_t _sqlite_reslist_free_connection(void *conn_, void *params, a
   }
   free(conn->prepared_statements);
   sqlite3_close(conn->handle);
-  return APR_SUCCESS;
 }
 
-static struct sqlite_conn* _sqlite_get_conn(mapcache_context *ctx, mapcache_tile* tile, int readonly) {
-  apr_status_t rv;
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache;
-  struct sqlite_conn *conn = NULL;
-  apr_reslist_t *pool = NULL;
-  apr_hash_t *pool_container;
-  if (readonly) {
-    pool_container = ro_connection_pools;
+static mapcache_pooled_connection* mapcache_sqlite_get_conn(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, int readonly) {
+  struct sqlite_conn_params params;
+  mapcache_pooled_connection *pc;
+  char *key;
+  _mapcache_cache_sqlite_filename_for_tile(ctx,cache,tile,&params.dbfile);
+  params.cache = cache;
+  params.readonly = readonly;
+  if(!strstr(cache->dbfile,"{")) {
+    key = apr_pstrcat(ctx->pool,readonly?"ro_":"rw_",cache->cache.name,NULL);
   } else {
-    pool_container = rw_connection_pools;
-  }
-  if(!pool_container || NULL == (pool = apr_hash_get(pool_container,cache->cache.name, APR_HASH_KEY_STRING)) ) {
-#ifdef APR_HAS_THREADS
-    if(ctx->threadlock)
-      apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-    if(!ro_connection_pools) {
-      ro_connection_pools = apr_hash_make(ctx->process_pool);
-      rw_connection_pools = apr_hash_make(ctx->process_pool);
-    }
-
-    /* probably doesn't exist, unless the previous mutex locked us, so we check */
-    pool = apr_hash_get(ro_connection_pools,cache->cache.name, APR_HASH_KEY_STRING);
-    if(!pool) {
-      /* there where no existing connection pools, create them*/
-      rv = apr_reslist_create(&pool,
-                              0 /* min */,
-                              10 /* soft max */,
-                              200 /* hard max */,
-                              60*1000000 /*60 seconds, ttl*/,
-                              _sqlite_reslist_get_ro_connection, /* resource constructor */
-                              _sqlite_reslist_free_connection, /* resource destructor */
-                              cache, ctx->process_pool);
-      if(rv != APR_SUCCESS) {
-        ctx->set_error(ctx,500,"failed to create bdb ro connection pool");
-#ifdef APR_HAS_THREADS
-        if(ctx->threadlock)
-          apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-        return NULL;
-      }
-      apr_hash_set(ro_connection_pools,cache->cache.name,APR_HASH_KEY_STRING,pool);
-      rv = apr_reslist_create(&pool,
-                              0 /* min */,
-                              1 /* soft max */,
-                              1 /* hard max */,
-                              60*1000000 /*60 seconds, ttl*/,
-                              _sqlite_reslist_get_rw_connection, /* resource constructor */
-                              _sqlite_reslist_free_connection, /* resource destructor */
-                              cache, ctx->process_pool);
-      if(rv != APR_SUCCESS) {
-        ctx->set_error(ctx,500,"failed to create bdb rw connection pool");
-#ifdef APR_HAS_THREADS
-        if(ctx->threadlock)
-          apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-        return NULL;
-      }
-      apr_hash_set(rw_connection_pools,cache->cache.name,APR_HASH_KEY_STRING,pool);
-    }
-#ifdef APR_HAS_THREADS
-    if(ctx->threadlock)
-      apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-    if(readonly)
-      pool = apr_hash_get(ro_connection_pools,cache->cache.name, APR_HASH_KEY_STRING);
-    else
-      pool = apr_hash_get(rw_connection_pools,cache->cache.name, APR_HASH_KEY_STRING);
-    assert(pool);
-  }
-  rv = apr_reslist_acquire(pool, (void **) &conn);
-  if (rv != APR_SUCCESS) {
-    ctx->set_error(ctx, 500, "failed to aquire connection to sqlite backend: %s", (conn && conn->errmsg)?conn->errmsg:"unknown error");
-    return NULL;
+    key = apr_pstrcat(ctx->pool,readonly?"ro_":"rw_",params.dbfile,NULL);
   }
-  return conn;
+  pc = mapcache_connection_pool_get_connection(ctx,key,mapcache_sqlite_connection_constructor,mapcache_sqlite_connection_destructor,&params);
+  return pc;
 }
 
-static void _sqlite_release_conn(mapcache_context *ctx, mapcache_tile *tile, struct sqlite_conn *conn)
+
+/**
+ * \brief return sqlite db filename for given tile
+ *
+ * \param tile the tile to get the key from
+ * \param path pointer to a char* that will contain the filename
+ * \param r
+ * \private \memberof mapcache_cache_sqlite
+ */
+static void _mapcache_cache_sqlite_filename_for_tile(mapcache_context *ctx, mapcache_cache_sqlite *dcache, mapcache_tile *tile, char **path)
 {
-  apr_reslist_t *pool;
-  apr_hash_t *pool_container;
-  if(conn->readonly) {
-    pool_container = ro_connection_pools;
-  } else {
-    pool_container = rw_connection_pools;
+  *path = dcache->dbfile;
+
+  if(strstr(*path,"{")) {
+    /*
+     * generic template substitutions
+     */
+    if(strstr(*path,"{tileset}"))
+      *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}",
+              tile->tileset->name);
+    if(strstr(*path,"{grid}"))
+      *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}",
+              tile->grid_link->grid->name);
+    if(tile->dimensions && strstr(*path,"{dim}")) {
+      char *dimstring="";
+      const apr_array_header_t *elts = apr_table_elts(tile->dimensions);
+      int i = elts->nelts;
+      while(i--) {
+        apr_table_entry_t *entry = &(APR_ARRAY_IDX(elts,i,apr_table_entry_t));
+        const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->val,"/.",'#');
+        dimstring = apr_pstrcat(ctx->pool,dimstring,"#",dimval,NULL);
+      }
+      *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
+    }
+    
+    
+    while(strstr(*path,"{z}"))
+      *path = mapcache_util_str_replace(ctx->pool,*path, "{z}",
+              apr_psprintf(ctx->pool,dcache->z_fmt,tile->z));
+    
+    if(dcache->count_x > 0) {
+      while(strstr(*path,"{div_x}"))
+        *path = mapcache_util_str_replace(ctx->pool,*path, "{div_x}",
+                apr_psprintf(ctx->pool,dcache->div_x_fmt,tile->x/dcache->count_x));
+      while(strstr(*path,"{inv_div_x}"))
+        *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_x}",
+                apr_psprintf(ctx->pool,dcache->inv_div_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/dcache->count_x));
+      while(strstr(*path,"{x}"))
+        *path = mapcache_util_str_replace(ctx->pool,*path, "{x}",
+                apr_psprintf(ctx->pool,dcache->x_fmt,tile->x/dcache->count_x*dcache->count_x));
+      while(strstr(*path,"{inv_x}"))
+        *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}",
+                apr_psprintf(ctx->pool,dcache->inv_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/dcache->count_x*dcache->count_x));
+    }
+    
+    if(dcache->count_y > 0) {
+      while(strstr(*path,"{div_y}"))
+        *path = mapcache_util_str_replace(ctx->pool,*path, "{div_y}",
+                apr_psprintf(ctx->pool,dcache->div_y_fmt,tile->y/dcache->count_y));
+      while(strstr(*path,"{inv_div_y}"))
+        *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_y}",
+                apr_psprintf(ctx->pool,dcache->inv_div_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y));
+      while(strstr(*path,"{y}"))
+        *path = mapcache_util_str_replace(ctx->pool,*path, "{y}",
+                apr_psprintf(ctx->pool,dcache->y_fmt,tile->y/dcache->count_y*dcache->count_y));
+      while(strstr(*path,"{inv_y}"))
+        *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}",
+                apr_psprintf(ctx->pool,dcache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y*dcache->count_y));
+      
+    }
   }
-  pool = apr_hash_get(pool_container,tile->tileset->cache->name, APR_HASH_KEY_STRING);
 
-  if (GC_HAS_ERROR(ctx)) {
-    apr_reslist_invalidate(pool, (void*) conn);
-  } else {
-    apr_reslist_release(pool, (void*) conn);
+  if(!*path) {
+    ctx->set_error(ctx,500, "failed to allocate tile key");
   }
 }
 
 
+
 /**
  * \brief apply appropriate tile properties to the sqlite statement */
-static void _bind_sqlite_params(mapcache_context *ctx, void *vstmt, mapcache_tile *tile)
+static void _bind_sqlite_params(mapcache_context *ctx, void *vstmt, mapcache_cache_sqlite *cache, mapcache_tile *tile)
 {
   sqlite3_stmt *stmt = vstmt;
   int paramidx;
@@ -311,8 +293,7 @@ static void _bind_sqlite_params(mapcache_context *ctx, void *vstmt, mapcache_til
   paramidx = sqlite3_bind_parameter_index(stmt, ":data");
   if (paramidx) {
     int written = 0;
-    if(((mapcache_cache_sqlite*)tile->tileset->cache)->detect_blank && tile->grid_link->grid->tile_sx == 256 &&
-            tile->grid_link->grid->tile_sy == 256) {
+    if(cache->detect_blank) {
       if(!tile->raw_image) {
         tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
         GC_CHECK_ERROR(ctx);
@@ -339,7 +320,7 @@ static void _bind_sqlite_params(mapcache_context *ctx, void *vstmt, mapcache_til
   }
 }
 
-static void _bind_mbtiles_params(mapcache_context *ctx, void *vstmt, mapcache_tile *tile)
+static void _bind_mbtiles_params(mapcache_context *ctx, void *vstmt, mapcache_cache_sqlite *cache, mapcache_tile *tile)
 {
   sqlite3_stmt *stmt = vstmt;
   int paramidx;
@@ -389,26 +370,29 @@ static void _bind_mbtiles_params(mapcache_context *ctx, void *vstmt, mapcache_ti
 
 }
 
-static int _mapcache_cache_sqlite_has_tile(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_sqlite_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache;
-  struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 1);
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
+  mapcache_pooled_connection *pc;
+  struct sqlite_conn *conn;
   sqlite3_stmt *stmt;
   int ret;
+  pc = mapcache_sqlite_get_conn(ctx,cache,tile,1);
   if (GC_HAS_ERROR(ctx)) {
-    if(conn) _sqlite_release_conn(ctx, tile, conn);
+    if(pc) mapcache_sqlite_release_conn(ctx, pc);
     if(!tile->tileset->read_only && tile->tileset->source) {
       /* not an error in this case, as the db file may not have been created yet */
       ctx->clear_errors(ctx);
     }
     return MAPCACHE_FALSE;
   }
+  conn = SQLITE_CONN(pc);
   stmt = conn->prepared_statements[HAS_TILE_STMT_IDX];
   if(!stmt) {
     sqlite3_prepare(conn->handle, cache->exists_stmt.sql, -1, &conn->prepared_statements[HAS_TILE_STMT_IDX], NULL);
     stmt = conn->prepared_statements[HAS_TILE_STMT_IDX];
   }
-  cache->bind_stmt(ctx, stmt, tile);
+  cache->bind_stmt(ctx, stmt, cache, tile);
   ret = sqlite3_step(stmt);
   if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
     ctx->set_error(ctx, 500, "sqlite backend failed on has_tile: %s", sqlite3_errmsg(conn->handle));
@@ -419,46 +403,53 @@ static int _mapcache_cache_sqlite_has_tile(mapcache_context *ctx, mapcache_tile
     ret = MAPCACHE_TRUE;
   }
   sqlite3_reset(stmt);
-  _sqlite_release_conn(ctx, tile, conn);
+  mapcache_sqlite_release_conn(ctx, pc);
   return ret;
 }
 
-static void _mapcache_cache_sqlite_delete(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_sqlite_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache;
-  struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0);
-  sqlite3_stmt *stmt = conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX];
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
+  mapcache_pooled_connection *pc;
+  struct sqlite_conn *conn;
+  sqlite3_stmt *stmt;
   int ret;
+  pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
   if (GC_HAS_ERROR(ctx)) {
-    _sqlite_release_conn(ctx, tile, conn);
+    mapcache_sqlite_release_conn(ctx, pc);
     return;
   }
+  conn = SQLITE_CONN(pc);
+  stmt = conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX];
   if(!stmt) {
     sqlite3_prepare(conn->handle, cache->delete_stmt.sql, -1, &conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX], NULL);
     stmt = conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX];
   }
-  cache->bind_stmt(ctx, stmt, tile);
+  cache->bind_stmt(ctx, stmt, cache, tile);
   ret = sqlite3_step(stmt);
   if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
     ctx->set_error(ctx, 500, "sqlite backend failed on delete: %s", sqlite3_errmsg(conn->handle));
   }
   sqlite3_reset(stmt);
-  _sqlite_release_conn(ctx, tile, conn);
+  mapcache_sqlite_release_conn(ctx, pc);
 }
 
 
-static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache;
-  struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0);
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
+  mapcache_pooled_connection *pc;
+  struct sqlite_conn *conn;
   sqlite3_stmt *stmt1,*stmt2,*stmt3;
   int ret;
   const char *tile_id;
   size_t tile_id_size;
+  pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
   if (GC_HAS_ERROR(ctx)) {
-    _sqlite_release_conn(ctx, tile, conn);
+    mapcache_sqlite_release_conn(ctx, pc);
     return;
   }
+  conn = SQLITE_CONN(pc);
   stmt1 = conn->prepared_statements[MBTILES_DEL_TILE_SELECT_STMT_IDX];
   stmt2 = conn->prepared_statements[MBTILES_DEL_TILE_STMT1_IDX];
   stmt3 = conn->prepared_statements[MBTILES_DEL_TILE_STMT2_IDX];
@@ -474,19 +465,19 @@ static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_tile
   /* first extract tile_id from the tile we will delete. We need this because we do not know
    * if the tile is empty or not.
    * If it is empty, we will not delete the image blob data from the images table */
-  cache->bind_stmt(ctx, stmt1, tile);
+  cache->bind_stmt(ctx, stmt1, cache, tile);
   do {
     ret = sqlite3_step(stmt1);
     if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
       ctx->set_error(ctx, 500, "sqlite backend failed on mbtile del 1: %s", sqlite3_errmsg(conn->handle));
       sqlite3_reset(stmt1);
-      _sqlite_release_conn(ctx, tile, conn);
+      mapcache_sqlite_release_conn(ctx, pc);
       return;
     }
   } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
   if (ret == SQLITE_DONE) { /* tile does not exist, ignore */
     sqlite3_reset(stmt1);
-    _sqlite_release_conn(ctx, tile, conn);
+    mapcache_sqlite_release_conn(ctx, pc);
     return;
   } else {
     tile_id = (const char*) sqlite3_column_text(stmt1, 0);
@@ -495,13 +486,13 @@ static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_tile
 
 
   /* delete the tile from the "map" table */
-  cache->bind_stmt(ctx,stmt2, tile);
+  cache->bind_stmt(ctx,stmt2, cache, tile);
   ret = sqlite3_step(stmt2);
   if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
     ctx->set_error(ctx, 500, "sqlite backend failed on mbtile del 2: %s", sqlite3_errmsg(conn->handle));
     sqlite3_reset(stmt1);
     sqlite3_reset(stmt2);
-    _sqlite_release_conn(ctx, tile, conn);
+    mapcache_sqlite_release_conn(ctx, pc);
     return;
   }
 
@@ -517,7 +508,7 @@ static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_tile
       sqlite3_reset(stmt1);
       sqlite3_reset(stmt2);
       sqlite3_reset(stmt3);
-      _sqlite_release_conn(ctx, tile, conn);
+      mapcache_sqlite_release_conn(ctx, pc);
       return;
     }
   }
@@ -525,15 +516,14 @@ static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_tile
   sqlite3_reset(stmt1);
   sqlite3_reset(stmt2);
   sqlite3_reset(stmt3);
-  _sqlite_release_conn(ctx, tile, conn);
+  mapcache_sqlite_release_conn(ctx, pc);
 }
 
 
 
-static void _single_mbtile_set(mapcache_context *ctx, mapcache_tile *tile, struct sqlite_conn *conn)
+static void _single_mbtile_set(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, struct sqlite_conn *conn)
 {
   sqlite3_stmt *stmt1,*stmt2;
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)tile->tileset->cache;
   int ret;
   if(!tile->raw_image) {
     tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
@@ -552,8 +542,8 @@ static void _single_mbtile_set(mapcache_context *ctx, mapcache_tile *tile, struc
       stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX];
       stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX];
     }
-    cache->bind_stmt(ctx, stmt1, tile);
-    cache->bind_stmt(ctx, stmt2, tile);
+    cache->bind_stmt(ctx, stmt1, cache, tile);
+    cache->bind_stmt(ctx, stmt2, cache, tile);
   } else {
     stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX];
     stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX];
@@ -567,8 +557,8 @@ static void _single_mbtile_set(mapcache_context *ctx, mapcache_tile *tile, struc
       stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX];
       stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX];
     }
-    cache->bind_stmt(ctx, stmt1, tile);
-    cache->bind_stmt(ctx, stmt2, tile);
+    cache->bind_stmt(ctx, stmt1, cache, tile);
+    cache->bind_stmt(ctx, stmt2, cache, tile);
   }
   do {
     ret = sqlite3_step(stmt1);
@@ -596,47 +586,49 @@ static void _single_mbtile_set(mapcache_context *ctx, mapcache_tile *tile, struc
   sqlite3_reset(stmt2);
 }
 
-static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache;
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
   struct sqlite_conn *conn;
   sqlite3_stmt *stmt;
   int ret;
-  conn = _sqlite_get_conn(ctx, tile, 1);
+  mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,1);
   if (GC_HAS_ERROR(ctx)) {
-    if(conn) _sqlite_release_conn(ctx, tile, conn);
     if(tile->tileset->read_only || !tile->tileset->source) {
+      mapcache_sqlite_release_conn(ctx, pc);
       return MAPCACHE_FAILURE;
     } else {
       /* not an error in this case, as the db file may not have been created yet */
       ctx->clear_errors(ctx);
+      mapcache_sqlite_release_conn(ctx, pc);
       return MAPCACHE_CACHE_MISS;
     }
   }
+  conn = SQLITE_CONN(pc);
   stmt = conn->prepared_statements[GET_TILE_STMT_IDX];
   if(!stmt) {
     sqlite3_prepare(conn->handle, cache->get_stmt.sql, -1, &conn->prepared_statements[GET_TILE_STMT_IDX], NULL);
     stmt = conn->prepared_statements[GET_TILE_STMT_IDX];
   }
-  cache->bind_stmt(ctx, stmt, tile);
+  cache->bind_stmt(ctx, stmt, cache, tile);
   do {
     ret = sqlite3_step(stmt);
     if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
       ctx->set_error(ctx, 500, "sqlite backend failed on get: %s", sqlite3_errmsg(conn->handle));
       sqlite3_reset(stmt);
-      _sqlite_release_conn(ctx, tile, conn);
+      mapcache_sqlite_release_conn(ctx, pc);
       return MAPCACHE_FAILURE;
     }
   } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
   if (ret == SQLITE_DONE) {
     sqlite3_reset(stmt);
-    _sqlite_release_conn(ctx, tile, conn);
+    mapcache_sqlite_release_conn(ctx, pc);
     return MAPCACHE_CACHE_MISS;
   } else {
     const void *blob = sqlite3_column_blob(stmt, 0);
     int size = sqlite3_column_bytes(stmt, 0);
     if(size>0 && ((char*)blob)[0] == '#') {
-      tile->encoded_data = mapcache_empty_png_decode(ctx,blob,&tile->nodata);
+      tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy ,blob,&tile->nodata);
     } else {
       tile->encoded_data = mapcache_buffer_create(size, ctx->pool);
       memcpy(tile->encoded_data->buf, blob, size);
@@ -647,14 +639,13 @@ static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_tile *tile
       apr_time_ansi_put(&(tile->mtime), mtime);
     }
     sqlite3_reset(stmt);
-    _sqlite_release_conn(ctx, tile, conn);
+    mapcache_sqlite_release_conn(ctx, pc);
     return MAPCACHE_SUCCESS;
   }
 }
 
-static void _single_sqlitetile_set(mapcache_context *ctx, mapcache_tile *tile, struct sqlite_conn *conn)
+static void _single_sqlitetile_set(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, struct sqlite_conn *conn)
 {
-  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) tile->tileset->cache;
   sqlite3_stmt *stmt = conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX];
   int ret;
 
@@ -662,7 +653,7 @@ static void _single_sqlitetile_set(mapcache_context *ctx, mapcache_tile *tile, s
     sqlite3_prepare(conn->handle, cache->set_stmt.sql, -1, &conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX], NULL);
     stmt = conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX];
   }
-  cache->bind_stmt(ctx, stmt, tile);
+  cache->bind_stmt(ctx, stmt, cache, tile);
   do {
     ret = sqlite3_step(stmt);
     if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
@@ -676,29 +667,41 @@ static void _single_sqlitetile_set(mapcache_context *ctx, mapcache_tile *tile, s
   sqlite3_reset(stmt);
 }
 
-static void _mapcache_cache_sqlite_set(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_sqlite_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
-  struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0);
-  GC_CHECK_ERROR(ctx);
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
+  struct sqlite_conn *conn;
+  mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
+  if (GC_HAS_ERROR(ctx)) {
+    mapcache_sqlite_release_conn(ctx, pc);
+    return;
+  }
+  conn = SQLITE_CONN(pc);
   sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
-  _single_sqlitetile_set(ctx,tile,conn);
+  _single_sqlitetile_set(ctx,cache, tile,conn);
   if (GC_HAS_ERROR(ctx)) {
     sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0);
   } else {
     sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
   }
-  _sqlite_release_conn(ctx, tile, conn);
+  mapcache_sqlite_release_conn(ctx, pc);
 }
 
-static void _mapcache_cache_sqlite_multi_set(mapcache_context *ctx, mapcache_tile *tiles, int ntiles)
+static void _mapcache_cache_sqlite_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
 {
-  struct sqlite_conn *conn = _sqlite_get_conn(ctx, &tiles[0], 0);
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
   int i;
-  GC_CHECK_ERROR(ctx);
+  struct sqlite_conn *conn;
+  mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,&tiles[0],0);
+  if (GC_HAS_ERROR(ctx)) {
+    mapcache_sqlite_release_conn(ctx, pc);
+    return;
+  }
+  conn = SQLITE_CONN(pc);
   sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
   for (i = 0; i < ntiles; i++) {
     mapcache_tile *tile = &tiles[i];
-    _single_sqlitetile_set(ctx,tile,conn);
+    _single_sqlitetile_set(ctx,cache, tile,conn);
     if(GC_HAS_ERROR(ctx)) break;
   }
   if (GC_HAS_ERROR(ctx)) {
@@ -706,34 +709,42 @@ static void _mapcache_cache_sqlite_multi_set(mapcache_context *ctx, mapcache_til
   } else {
     sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
   }
-  _sqlite_release_conn(ctx, &tiles[0], conn);
+  mapcache_sqlite_release_conn(ctx, pc);
 }
 
-static void _mapcache_cache_mbtiles_set(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_mbtiles_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
-  struct sqlite_conn *conn = _sqlite_get_conn(ctx, tile, 0);
-  GC_CHECK_ERROR(ctx);
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
+  struct sqlite_conn *conn;
+  mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
+  if (GC_HAS_ERROR(ctx)) {
+    mapcache_sqlite_release_conn(ctx, pc);
+    return;
+  }
+  conn = SQLITE_CONN(pc);
   if(!tile->raw_image) {
     tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
     if(GC_HAS_ERROR(ctx)) {
-      _sqlite_release_conn(ctx, tile, conn);
+      mapcache_sqlite_release_conn(ctx, pc);
       return;
     }
   }
   sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
-  _single_mbtile_set(ctx,tile,conn);
+  _single_mbtile_set(ctx, cache, tile,conn);
   if (GC_HAS_ERROR(ctx)) {
     sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0);
   } else {
     sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
   }
-  _sqlite_release_conn(ctx, tile, conn);
+  mapcache_sqlite_release_conn(ctx, pc);
 }
 
-static void _mapcache_cache_mbtiles_multi_set(mapcache_context *ctx, mapcache_tile *tiles, int ntiles)
+static void _mapcache_cache_mbtiles_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
 {
-  struct sqlite_conn *conn = NULL;
   int i;
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
+  mapcache_pooled_connection *pc;
+  struct sqlite_conn *conn;
 
   /* decode/encode image data before going into the sqlite write lock */
   for (i = 0; i < ntiles; i++) {
@@ -748,12 +759,17 @@ static void _mapcache_cache_mbtiles_multi_set(mapcache_context *ctx, mapcache_ti
       GC_CHECK_ERROR(ctx);
     }
   }
-  conn = _sqlite_get_conn(ctx, &tiles[0], 0);
+  pc = mapcache_sqlite_get_conn(ctx,cache,&tiles[0],0);
+  if (GC_HAS_ERROR(ctx)) {
+    mapcache_sqlite_release_conn(ctx, pc);
+    return;
+  }
+  conn = SQLITE_CONN(pc);
 
   sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
   for (i = 0; i < ntiles; i++) {
     mapcache_tile *tile = &tiles[i];
-    _single_mbtile_set(ctx,tile,conn);
+    _single_mbtile_set(ctx,cache,tile,conn);
     if(GC_HAS_ERROR(ctx)) break;
   }
   if (GC_HAS_ERROR(ctx)) {
@@ -761,32 +777,69 @@ static void _mapcache_cache_mbtiles_multi_set(mapcache_context *ctx, mapcache_ti
   } else {
     sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
   }
-  _sqlite_release_conn(ctx, &tiles[0], conn);
+  mapcache_sqlite_release_conn(ctx, pc);
 }
 
-static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
+static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
 {
   ezxml_t cur_node;
-  mapcache_cache_sqlite *dcache;
+  mapcache_cache_sqlite *cache;
   sqlite3_initialize();
   sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
-  dcache = (mapcache_cache_sqlite*) cache;
+  cache = (mapcache_cache_sqlite*) pcache;
   if ((cur_node = ezxml_child(node, "base")) != NULL) {
     ctx->set_error(ctx, 500, "sqlite config <base> not supported anymore, use <dbfile>");
     return;
   }
   if ((cur_node = ezxml_child(node, "dbname_template")) != NULL) {
-    ctx->set_error(ctx, 500, "sqlite config <dbname_template> not supported anymore, use <dbfile>");
+    ctx->set_error(ctx, 500, "sqlite config <dbname_template> not supported anymore, use a \"multi-sqlite3\" cache type");
     return;
   }
   if ((cur_node = ezxml_child(node, "dbfile")) != NULL) {
-    dcache->dbfile = apr_pstrdup(ctx->pool, cur_node->txt);
+    char *fmt;
+    cache->dbfile = apr_pstrdup(ctx->pool, cur_node->txt);
+    fmt = (char*)ezxml_attr(cur_node,"x_fmt");
+    if(fmt && *fmt) {
+      cache->x_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
+    fmt = (char*)ezxml_attr(cur_node,"y_fmt");
+    if(fmt && *fmt) {
+      cache->y_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
+    fmt = (char*)ezxml_attr(cur_node,"z_fmt");
+    if(fmt && *fmt) {
+      cache->z_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
+    fmt = (char*)ezxml_attr(cur_node,"inv_x_fmt");
+    if(fmt && *fmt) {
+      cache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
+    fmt = (char*)ezxml_attr(cur_node,"inv_y_fmt");
+    if(fmt && *fmt) {
+      cache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
+    fmt = (char*)ezxml_attr(cur_node,"div_x_fmt");
+    if(fmt && *fmt) {
+      cache->div_x_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
+    fmt = (char*)ezxml_attr(cur_node,"div_y_fmt");
+    if(fmt && *fmt) {
+      cache->div_y_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
+    fmt = (char*)ezxml_attr(cur_node,"inv_div_x_fmt");
+    if(fmt && *fmt) {
+      cache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
+    fmt = (char*)ezxml_attr(cur_node,"inv_div_y_fmt");
+    if(fmt && *fmt) {
+      cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt);
+    }
   }
   
-  dcache->detect_blank = 0;
+  cache->detect_blank = 0;
   if ((cur_node = ezxml_child(node, "detect_blank")) != NULL) {
-    if(!strcasecmp(cur_node->txt,"true")) {
-      dcache->detect_blank = 1;
+    if(strcasecmp(cur_node->txt,"false")) {
+      cache->detect_blank = 1;
     }
   }
 
@@ -796,20 +849,53 @@ static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx
     }
   }
   if ((cur_node = ezxml_child(node, "pragma")) != NULL) {
-    dcache->pragmas = apr_table_make(ctx->pool,1);
+    cache->pragmas = apr_table_make(ctx->pool,1);
     while(cur_node) {
       char *name = (char*)ezxml_attr(cur_node,"name");
       if(!name || !cur_node->txt || !strlen(cur_node->txt)) {
         ctx->set_error(ctx,500,"<pragma> missing name attribute");
         return;
       }
-      apr_table_set(dcache->pragmas,name,cur_node->txt);
+      apr_table_set(cache->pragmas,name,cur_node->txt);
       cur_node = cur_node->next;
     }
   }
-  if (!dcache->dbfile) {
-    ctx->set_error(ctx, 500, "sqlite cache \"%s\" is missing <dbfile> entry", cache->name);
-    return;
+  if ((cur_node = ezxml_child(node, "queries")) != NULL) {
+    ezxml_t query_node;
+    if ((query_node = ezxml_child(cur_node, "exists")) != NULL) {
+      cache->exists_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
+    }
+    if ((query_node = ezxml_child(cur_node, "get")) != NULL) {
+      cache->get_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
+    }
+    if ((query_node = ezxml_child(cur_node, "set")) != NULL) {
+      cache->set_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
+    }
+    if ((query_node = ezxml_child(cur_node, "delete")) != NULL) {
+      cache->delete_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
+    }
+    if ((query_node = ezxml_child(cur_node, "create")) != NULL) {
+      cache->create_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
+    }
+  }
+  
+  cur_node = ezxml_child(node,"xcount");
+  if(cur_node && cur_node->txt && *cur_node->txt) {
+    char *endptr;
+    cache->count_x = (int)strtol(cur_node->txt,&endptr,10);
+    if(*endptr != 0) {
+      ctx->set_error(ctx,400,"failed to parse xcount value %s for sqlite cache %s", cur_node->txt,cache->cache.name);
+      return;
+    }
+  }
+  cur_node = ezxml_child(node,"ycount");
+  if(cur_node && cur_node->txt && *cur_node->txt) {
+    char *endptr;
+    cache->count_y = (int)strtol(cur_node->txt,&endptr,10);
+    if(*endptr != 0) {
+      ctx->set_error(ctx,400,"failed to parse ycount value %s for sqlite cache %s", cur_node->txt,cache->cache.name);
+      return;
+    }
   }
 }
 
@@ -817,18 +903,24 @@ static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx
  * \private \memberof mapcache_cache_sqlite
  */
 static void _mapcache_cache_sqlite_configuration_post_config(mapcache_context *ctx,
-    mapcache_cache *cache, mapcache_cfg *cfg)
+    mapcache_cache *pcache, mapcache_cfg *cfg)
 {
+  mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
+  if (!cache->dbfile) {
+    ctx->set_error(ctx, 500, "sqlite cache \"%s\" is missing <dbfile> entry", pcache->name);
+    return;
+  }
 }
 
 /**
  * \private \memberof mapcache_cache_sqlite
  */
 static void _mapcache_cache_mbtiles_configuration_post_config(mapcache_context *ctx,
-    mapcache_cache *cache, mapcache_cfg *cfg)
+    mapcache_cache *pcache, mapcache_cfg *cfg)
 {
   /* check that only one tileset/grid references this cache, as mbtiles does
    not support multiple tilesets/grids per cache */
+#ifdef FIXME
   int nrefs = 0;
   apr_hash_index_t *tileseti = apr_hash_first(ctx->pool,cfg->tilesets);
   while(tileseti) {
@@ -849,12 +941,9 @@ static void _mapcache_cache_mbtiles_configuration_post_config(mapcache_context *
     }
     tileseti = apr_hash_next(tileseti);
   }
-
+#endif
 }
 
-/**
- * \brief creates and initializes a mapcache_sqlite_cache
- */
 mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx)
 {
   mapcache_cache_sqlite *cache = apr_pcalloc(ctx->pool, sizeof (mapcache_cache_sqlite));
@@ -884,9 +973,15 @@ mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx)
   cache->n_prepared_statements = 4;
   cache->bind_stmt = _bind_sqlite_params;
   cache->detect_blank = 1;
-  return (mapcache_cache*) cache;
+  cache->x_fmt = cache->y_fmt = cache->z_fmt
+          = cache->inv_x_fmt = cache->inv_y_fmt
+          = cache->div_x_fmt = cache->div_y_fmt
+          = cache->inv_div_x_fmt = cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,"%d");
+  cache->count_x = cache->count_y = -1;
+  return (mapcache_cache*)cache;
 }
 
+
 /**
  * \brief creates and initializes a mapcache_sqlite_cache
  */
diff --git a/lib/cache_tiff.c b/lib/cache_tiff.c
index 81ac218..cc5bfe0 100644
--- a/lib/cache_tiff.c
+++ b/lib/cache_tiff.c
@@ -62,10 +62,9 @@
  * \param r
  * \private \memberof mapcache_cache_tiff
  */
-static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_tile *tile, char **path)
+static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, char **path)
 {
-  mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)tile->tileset->cache;
-  *path = dcache->filename_template;
+  *path = cache->filename_template;
 
   /*
    * generic template substitutions
@@ -91,23 +90,23 @@ static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_tile *
 
   while(strstr(*path,"{z}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{z}",
-                                      apr_psprintf(ctx->pool,dcache->z_fmt,tile->z));
+                                      apr_psprintf(ctx->pool,cache->z_fmt,tile->z));
   /*
    * x and y replacing, when the tiff files are numbered with an increasing
    * x,y scheme (adjacent tiffs have x-x'=1 or y-y'=1
    */
   while(strstr(*path,"{div_x}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{div_x}",
-                                      apr_psprintf(ctx->pool,dcache->div_x_fmt,tile->x/dcache->count_x));
+                                      apr_psprintf(ctx->pool,cache->div_x_fmt,tile->x/cache->count_x));
   while(strstr(*path,"{div_y}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{div_y}",
-                                      apr_psprintf(ctx->pool,dcache->div_y_fmt,tile->y/dcache->count_y));
+                                      apr_psprintf(ctx->pool,cache->div_y_fmt,tile->y/cache->count_y));
   while(strstr(*path,"{inv_div_y}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_y}",
-                                      apr_psprintf(ctx->pool,dcache->inv_div_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y));
+                                      apr_psprintf(ctx->pool,cache->inv_div_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/cache->count_y));
   while(strstr(*path,"{inv_div_x}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_x}",
-                                      apr_psprintf(ctx->pool,dcache->inv_div_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/dcache->count_x));
+                                      apr_psprintf(ctx->pool,cache->inv_div_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/cache->count_x));
 
   /*
    * x and y replacing, when the tiff files are numbered with the index
@@ -116,25 +115,24 @@ static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_tile *
    */
   while(strstr(*path,"{x}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{x}",
-                                      apr_psprintf(ctx->pool,dcache->x_fmt,tile->x/dcache->count_x*dcache->count_x));
+                                      apr_psprintf(ctx->pool,cache->x_fmt,tile->x/cache->count_x*cache->count_x));
   while(strstr(*path,"{y}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{y}",
-                                      apr_psprintf(ctx->pool,dcache->y_fmt,tile->y/dcache->count_y*dcache->count_y));
+                                      apr_psprintf(ctx->pool,cache->y_fmt,tile->y/cache->count_y*cache->count_y));
   while(strstr(*path,"{inv_y}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}",
-                                      apr_psprintf(ctx->pool,dcache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y*dcache->count_y));
+                                      apr_psprintf(ctx->pool,cache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/cache->count_y*cache->count_y));
   while(strstr(*path,"{inv_x}"))
     *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}",
-                                      apr_psprintf(ctx->pool,dcache->inv_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/dcache->count_x*dcache->count_y));
+                                      apr_psprintf(ctx->pool,cache->inv_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/cache->count_x*cache->count_y));
   if(!*path) {
     ctx->set_error(ctx,500, "failed to allocate tile key");
   }
 }
 
 #ifdef DEBUG
-static void check_tiff_format(mapcache_context *ctx, mapcache_tile *tile, TIFF *hTIFF, const char *filename)
+static void check_tiff_format(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, TIFF *hTIFF, const char *filename)
 {
-  mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)tile->tileset->cache;
   uint32 imwidth,imheight,tilewidth,tileheight;
   int16 planarconfig,orientation;
   uint16 compression;
@@ -192,8 +190,8 @@ static void check_tiff_format(mapcache_context *ctx, mapcache_tile *tile, TIFF *
    *   configured for the cache
    */
   level = tile->grid_link->grid->levels[tile->z];
-  ntilesx = MAPCACHE_MIN(dcache->count_x, level->maxx);
-  ntilesy = MAPCACHE_MIN(dcache->count_y, level->maxy);
+  ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
+  ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
   if( tilewidth != tile->grid_link->grid->tile_sx ||
       tileheight != tile->grid_link->grid->tile_sy ||
       imwidth != tile->grid_link->grid->tile_sx * ntilesx ||
@@ -213,13 +211,12 @@ static void check_tiff_format(mapcache_context *ctx, mapcache_tile *tile, TIFF *
 }
 #endif
 
-static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   char *filename;
   TIFF *hTIFF;
-  mapcache_cache_tiff *dcache;
-  _mapcache_cache_tiff_tile_key(ctx, tile, &filename);
-  dcache = (mapcache_cache_tiff*)tile->tileset->cache;
+  mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
+  _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
   if(GC_HAS_ERROR(ctx)) {
     return MAPCACHE_FALSE;
   }
@@ -246,15 +243,15 @@ static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_tile *t
 
 
 #ifdef DEBUG
-      check_tiff_format(ctx,tile,hTIFF,filename);
+      check_tiff_format(ctx,cache,tile,hTIFF,filename);
       if(GC_HAS_ERROR(ctx)) {
         MyTIFFClose(hTIFF);
         return MAPCACHE_FALSE;
       }
 #endif
       level = tile->grid_link->grid->levels[tile->z];
-      ntilesx = MAPCACHE_MIN(dcache->count_x, level->maxx);
-      ntilesy = MAPCACHE_MIN(dcache->count_y, level->maxy);
+      ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
+      ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
 
       /* x offset of the tile along a row */
       tiff_offx = tile->x % ntilesx;
@@ -286,7 +283,7 @@ static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_tile *t
     return MAPCACHE_FALSE;
 }
 
-static void _mapcache_cache_tiff_delete(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_tiff_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   ctx->set_error(ctx,500,"TIFF cache tile deleting not implemented");
 }
@@ -299,14 +296,13 @@ static void _mapcache_cache_tiff_delete(mapcache_context *ctx, mapcache_tile *ti
  * \private \memberof mapcache_cache_tiff
  * \sa mapcache_cache::tile_get()
  */
-static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_tile *tile)
+static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
   char *filename;
   TIFF *hTIFF = NULL;
   int rv;
-  mapcache_cache_tiff *dcache;
-  _mapcache_cache_tiff_tile_key(ctx, tile, &filename);
-  dcache = (mapcache_cache_tiff*)tile->tileset->cache;
+  mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
+  _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
   if(GC_HAS_ERROR(ctx)) {
     return MAPCACHE_FALSE;
   }
@@ -347,7 +343,7 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_tile *tile)
 
 
 #ifdef DEBUG
-      check_tiff_format(ctx,tile,hTIFF,filename);
+      check_tiff_format(ctx,cache,tile,hTIFF,filename);
       if(GC_HAS_ERROR(ctx)) {
         MyTIFFClose(hTIFF);
         return MAPCACHE_FAILURE;
@@ -359,8 +355,8 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_tile *tile)
        * file for lower zoom levels
        */
       level = tile->grid_link->grid->levels[tile->z];
-      ntilesx = MAPCACHE_MIN(dcache->count_x, level->maxx);
-      ntilesy = MAPCACHE_MIN(dcache->count_y, level->maxy);
+      ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
+      ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
 
       /* x offset of the tile along a row */
       tiff_offx = tile->x % ntilesx;
@@ -508,15 +504,16 @@ static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_tile *tile)
  * \private \memberof mapcache_cache_tiff
  * \sa mapcache_cache::tile_set()
  */
-static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_tile *tile)
+static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
 {
 #ifdef USE_TIFF_WRITE
   char *filename;
   TIFF *hTIFF = NULL;
   int rv;
+  void *lock;
   int create;
   char errmsg[120];
-  mapcache_cache_tiff *dcache;
+  mapcache_cache_tiff *cache;
   mapcache_image_format_jpeg *format;
   char *hackptr1,*hackptr2;
   int tilew;
@@ -530,9 +527,9 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_tile *tile)
   int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */
   int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */
 
-  _mapcache_cache_tiff_tile_key(ctx, tile, &filename);
-  dcache = (mapcache_cache_tiff*)tile->tileset->cache;
-  format = (mapcache_image_format_jpeg*) dcache->format;
+  cache = (mapcache_cache_tiff*)pcache;
+  _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
+  format = (mapcache_image_format_jpeg*) cache->format;
   if(GC_HAS_ERROR(ctx)) {
     return;
   }
@@ -592,7 +589,7 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_tile *tile)
    * aquire a lock on the tiff file.
    */
 
-  while(mapcache_lock_or_wait_for_resource(ctx,filename) == MAPCACHE_FALSE);
+  while(mapcache_lock_or_wait_for_resource(ctx,(cache->locker?cache->locker:ctx->config->locker),filename, &lock) == MAPCACHE_FALSE);
 
   /* check if the tiff file exists already */
   rv = apr_stat(&finfo,filename,0,ctx->pool);
@@ -615,11 +612,12 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_tile *tile)
    * file for lower zoom levels
    */
   level = tile->grid_link->grid->levels[tile->z];
-  ntilesx = MAPCACHE_MIN(dcache->count_x, level->maxx);
-  ntilesy = MAPCACHE_MIN(dcache->count_y, level->maxy);
+  ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
+  ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
   if(create) {
 #ifdef USE_GEOTIFF
-    double  adfPixelScale[3], adfTiePoints[6], bbox[4];
+    double  adfPixelScale[3], adfTiePoints[6];
+    mapcache_extent bbox;
     GTIF *gtif;
     int x,y;
 #endif
@@ -679,16 +677,16 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_tile *tile)
 
 
       /* top left tile x,y */
-      x = (tile->x / dcache->count_x)*(dcache->count_x);
-      y = (tile->y / dcache->count_y)*(dcache->count_y) + ntilesy - 1;
+      x = (tile->x / cache->count_x)*(cache->count_x);
+      y = (tile->y / cache->count_y)*(cache->count_y) + ntilesy - 1;
 
       mapcache_grid_get_extent(ctx, tile->grid_link->grid,
-                               x,y,tile->z,bbox);
+                               x,y,tile->z,&bbox);
       adfTiePoints[0] = 0.0;
       adfTiePoints[1] = 0.0;
       adfTiePoints[2] = 0.0;
-      adfTiePoints[3] = bbox[0];
-      adfTiePoints[4] = bbox[3];
+      adfTiePoints[3] = bbox.minx;
+      adfTiePoints[4] = bbox.maxy;
       adfTiePoints[5] = 0.0;
       TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints );
     }
@@ -737,7 +735,7 @@ static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_tile *tile)
 close_tiff:
   if(hTIFF)
     MyTIFFClose(hTIFF);
-  mapcache_unlock_resource(ctx,filename);
+  mapcache_unlock_resource(ctx,cache->locker?cache->locker:ctx->config->locker,filename, lock);
 #else
   ctx->set_error(ctx,500,"tiff write support disabled by default");
 #endif
@@ -747,76 +745,73 @@ close_tiff:
 /**
  * \private \memberof mapcache_cache_tiff
  */
-static void _mapcache_cache_tiff_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
+static void _mapcache_cache_tiff_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
 {
   ezxml_t cur_node;
-  mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)cache;
-  ezxml_t xcount;
-  ezxml_t ycount;
-  ezxml_t xformat;
+  mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
   char * format_name;
   mapcache_image_format *pformat;
   if ((cur_node = ezxml_child(node,"template")) != NULL) {
     char *fmt;
-    dcache->filename_template = apr_pstrdup(ctx->pool,cur_node->txt);
+    cache->filename_template = apr_pstrdup(ctx->pool,cur_node->txt);
     fmt = (char*)ezxml_attr(cur_node,"x_fmt");
     if(fmt && *fmt) {
-      dcache->x_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->x_fmt = apr_pstrdup(ctx->pool,fmt);
     }
     fmt = (char*)ezxml_attr(cur_node,"y_fmt");
     if(fmt && *fmt) {
-      dcache->y_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->y_fmt = apr_pstrdup(ctx->pool,fmt);
     }
     fmt = (char*)ezxml_attr(cur_node,"z_fmt");
     if(fmt && *fmt) {
-      dcache->z_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->z_fmt = apr_pstrdup(ctx->pool,fmt);
     }
     fmt = (char*)ezxml_attr(cur_node,"inv_x_fmt");
     if(fmt && *fmt) {
-      dcache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt);
     }
     fmt = (char*)ezxml_attr(cur_node,"inv_y_fmt");
     if(fmt && *fmt) {
-      dcache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt);
     }
     fmt = (char*)ezxml_attr(cur_node,"div_x_fmt");
     if(fmt && *fmt) {
-      dcache->div_x_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->div_x_fmt = apr_pstrdup(ctx->pool,fmt);
     }
     fmt = (char*)ezxml_attr(cur_node,"div_y_fmt");
     if(fmt && *fmt) {
-      dcache->div_y_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->div_y_fmt = apr_pstrdup(ctx->pool,fmt);
     }
     fmt = (char*)ezxml_attr(cur_node,"inv_div_x_fmt");
     if(fmt && *fmt) {
-      dcache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt);
     }
     fmt = (char*)ezxml_attr(cur_node,"inv_div_y_fmt");
     if(fmt && *fmt) {
-      dcache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt);
+      cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt);
     }
   }
-  xcount = ezxml_child(node,"xcount");
-  if(xcount && xcount->txt && *xcount->txt) {
+  cur_node = ezxml_child(node,"xcount");
+  if(cur_node && cur_node->txt && *cur_node->txt) {
     char *endptr;
-    dcache->count_x = (int)strtol(xcount->txt,&endptr,10);
+    cache->count_x = (int)strtol(cur_node->txt,&endptr,10);
     if(*endptr != 0) {
-      ctx->set_error(ctx,400,"failed to parse xcount value %s for tiff cache %s", xcount->txt,cache->name);
+      ctx->set_error(ctx,400,"failed to parse xcount value %s for tiff cache %s", cur_node->txt,pcache->name);
       return;
     }
   }
-  ycount = ezxml_child(node,"ycount");
-  if(ycount && ycount->txt && *ycount->txt) {
+  cur_node = ezxml_child(node,"ycount");
+  if(cur_node && cur_node->txt && *cur_node->txt) {
     char *endptr;
-    dcache->count_y = (int)strtol(ycount->txt,&endptr,10);
+    cache->count_y = (int)strtol(cur_node->txt,&endptr,10);
     if(*endptr != 0) {
-      ctx->set_error(ctx,400,"failed to parse ycount value %s for tiff cache %s", ycount->txt,cache->name);
+      ctx->set_error(ctx,400,"failed to parse ycount value %s for tiff cache %s", cur_node->txt,pcache->name);
       return;
     }
   }
-  xformat = ezxml_child(node,"format");
-  if(xformat && xformat->txt && *xformat->txt) {
-    format_name = xformat->txt;
+  cur_node = ezxml_child(node,"format");
+  if(cur_node && cur_node->txt && *cur_node->txt) {
+    format_name = cur_node->txt;
   } else {
     format_name = "JPEG";
   }
@@ -824,31 +819,37 @@ static void _mapcache_cache_tiff_configuration_parse_xml(mapcache_context *ctx,
               config,format_name);
   if(!pformat) {
     ctx->set_error(ctx,500,"TIFF cache %s references unknown image format %s",
-                   cache->name, format_name);
+                   pcache->name, format_name);
     return;
   }
   if(pformat->type != GC_JPEG) {
     ctx->set_error(ctx,500,"TIFF cache %s can only reference a JPEG image format",
-                   cache->name);
+                   pcache->name);
     return;
   }
-  dcache->format = (mapcache_image_format_jpeg*)pformat;
+  cache->format = (mapcache_image_format_jpeg*)pformat;
+
+  cur_node = ezxml_child(node,"locker");
+  if(cur_node) {
+    mapcache_config_parse_locker(ctx, cur_node, &cache->locker);
+  }
+  
 }
 
 /**
  * \private \memberof mapcache_cache_tiff
  */
-static void _mapcache_cache_tiff_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
+static void _mapcache_cache_tiff_configuration_post_config(mapcache_context *ctx, mapcache_cache *pcache,
     mapcache_cfg *cfg)
 {
-  mapcache_cache_tiff *dcache = (mapcache_cache_tiff*)cache;
+  mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
   /* check all required parameters are configured */
-  if((!dcache->filename_template || !strlen(dcache->filename_template))) {
-    ctx->set_error(ctx, 400, "tiff cache %s has no template pattern",dcache->cache.name);
+  if((!cache->filename_template || !strlen(cache->filename_template))) {
+    ctx->set_error(ctx, 400, "tiff cache %s has no template pattern",cache->cache.name);
     return;
   }
-  if(dcache->count_x <= 0 || dcache->count_y <= 0) {
-    ctx->set_error(ctx, 400, "tiff cache %s has invalid count (%d,%d)",dcache->count_x,dcache->count_y);
+  if(cache->count_x <= 0 || cache->count_y <= 0) {
+    ctx->set_error(ctx, 400, "tiff cache %s has invalid count (%d,%d)",cache->count_x,cache->count_y);
     return;
   }
 }
diff --git a/lib/configuration.c b/lib/configuration.c
index 30301b2..778c684 100644
--- a/lib/configuration.c
+++ b/lib/configuration.c
@@ -35,47 +35,12 @@
 
 void mapcache_configuration_parse(mapcache_context *ctx, const char *filename, mapcache_cfg *config, int cgi)
 {
-  apr_dir_t *lockdir;
-  apr_status_t rv;
-  char errmsg[120];
   char *url;
 
   mapcache_configuration_parse_xml(ctx,filename,config);
 
-
   GC_CHECK_ERROR(ctx);
 
-  if(!config->lockdir || !strlen(config->lockdir)) {
-    config->lockdir = apr_pstrdup(ctx->pool, "/tmp");
-  }
-  rv = apr_dir_open(&lockdir,config->lockdir,ctx->pool);
-  if(rv != APR_SUCCESS) {
-    ctx->set_error(ctx,500, "failed to open lock directory %s: %s"
-                   ,config->lockdir,apr_strerror(rv,errmsg,120));
-    return;
-  }
-
-  /* only remove lockfiles if we're not in cgi mode */
-  if(!cgi) {
-    apr_finfo_t finfo;
-    while ((apr_dir_read(&finfo, APR_FINFO_DIRENT|APR_FINFO_TYPE|APR_FINFO_NAME, lockdir)) == APR_SUCCESS) {
-      if(finfo.filetype == APR_REG) {
-        if(!strncmp(finfo.name, MAPCACHE_LOCKFILE_PREFIX, strlen(MAPCACHE_LOCKFILE_PREFIX))) {
-          ctx->log(ctx,MAPCACHE_WARN,"found old lockfile %s/%s, deleting it",config->lockdir,
-                   finfo.name);
-          rv = apr_file_remove(apr_psprintf(ctx->pool,"%s/%s",config->lockdir, finfo.name),ctx->pool);
-          if(rv != APR_SUCCESS) {
-            ctx->set_error(ctx,500, "failed to remove lockfile %s: %s",finfo.name,apr_strerror(rv,errmsg,120));
-            return;
-          }
-
-        }
-
-      }
-    }
-  }
-  apr_dir_close(lockdir);
-
   /* if we were suppplied with an onlineresource, make sure it ends with a / */
   if(NULL != (url = (char*)apr_table_get(config->metadata,"url"))) {
     char *urlend = url + strlen(url)-1;
@@ -254,8 +219,6 @@ mapcache_cfg* mapcache_configuration_create(apr_pool_t *pool)
   }
   mapcache_configuration_add_grid(cfg,grid,"g");
 
-  /* default retry interval is 1/100th of a second, i.e. 10000 microseconds */
-  cfg->lock_retry_interval = 10000;
 
   cfg->loglevel = MAPCACHE_WARN;
   cfg->autoreload = 0;
diff --git a/lib/configuration_xml.c b/lib/configuration_xml.c
index 2b728ac..a681de7 100644
--- a/lib/configuration_xml.c
+++ b/lib/configuration_xml.c
@@ -96,6 +96,7 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
     char *name = (char*)ezxml_attr(dimension_node,"name");
     char *type = (char*)ezxml_attr(dimension_node,"type");
     char *unit = (char*)ezxml_attr(dimension_node,"unit");
+    char *skip_validation = (char*)ezxml_attr(dimension_node,"skip_validation");
     char *default_value = (char*)ezxml_attr(dimension_node,"default");
 
     mapcache_dimension *dimension = NULL;
@@ -112,6 +113,8 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
         dimension = mapcache_dimension_regex_create(ctx->pool);
       } else if(!strcmp(type,"intervals")) {
         dimension = mapcache_dimension_intervals_create(ctx->pool);
+      } else if(!strcmp(type,"sqlite")) {
+        dimension = mapcache_dimension_sqlite_create(ctx->pool);
       } else if(!strcmp(type,"time")) {
         ctx->set_error(ctx,501,"time dimension type not implemented yet");
         return;
@@ -130,6 +133,10 @@ void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tile
     if(unit && *unit) {
       dimension->unit = apr_pstrdup(ctx->pool,unit);
     }
+    
+    if(skip_validation && !strcmp(skip_validation,"true")) {
+      dimension->skip_validation = MAPCACHE_TRUE;
+    }
 
     if(default_value && *default_value) {
       dimension->default_value = apr_pstrdup(ctx->pool,default_value);
@@ -387,6 +394,8 @@ void parseFormat(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
         compression = MAPCACHE_COMPRESSION_FAST;
       } else if(!strcmp(cur_node->txt, "best")) {
         compression = MAPCACHE_COMPRESSION_BEST;
+      } else if(!strcmp(cur_node->txt, "none")) {
+        compression = MAPCACHE_COMPRESSION_DISABLE;
       } else {
         ctx->set_error(ctx, 400, "unknown compression type %s for format \"%s\"", cur_node->txt, name);
         return;
@@ -496,6 +505,20 @@ void parseCache(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
   }
   if(!strcmp(type,"disk")) {
     cache = mapcache_cache_disk_create(ctx);
+  } else if(!strcmp(type,"fallback")) {
+    cache = mapcache_cache_fallback_create(ctx);
+  } else if(!strcmp(type,"multitier")) {
+    cache = mapcache_cache_multitier_create(ctx);
+  } else if(!strcmp(type,"composite")) {
+    cache = mapcache_cache_composite_create(ctx);
+  } else if(!strcmp(type,"rest")) {
+    cache = mapcache_cache_rest_create(ctx);
+  } else if(!strcmp(type,"s3")) {
+    cache = mapcache_cache_s3_create(ctx);
+  } else if(!strcmp(type,"azure")) {
+    cache = mapcache_cache_azure_create(ctx);
+  } else if(!strcmp(type,"google")) {
+    cache = mapcache_cache_google_create(ctx);
   } else if(!strcmp(type,"bdb")) {
 #ifdef USE_BDB
     cache = mapcache_cache_bdb_create(ctx);
@@ -538,6 +561,18 @@ void parseCache(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
     ctx->set_error(ctx,400, "failed to add cache \"%s\": tiff support is not available on this build",name);
     return;
 #endif
+  } else if(!strcmp(type,"couchbase")) {
+#ifdef USE_COUCHBASE
+    cache = mapcache_cache_couchbase_create(ctx);
+#else
+    ctx->set_error(ctx, 400, "failed to add cache \"%s\": couchbase support is not available on this build", name);
+#endif
+  } else if(!strcmp(type,"riak")) {
+#ifdef USE_RIAK
+    cache = mapcache_cache_riak_create(ctx);
+#else
+    ctx->set_error(ctx, 400, "failed to add cache \"%s\": riak support is not available on this build", name);
+#endif
   } else {
     ctx->set_error(ctx, 400, "unknown cache type %s for cache \"%s\"", type, name);
     return;
@@ -632,6 +667,7 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
     gridlink->maxz = grid->nlevels;
     gridlink->grid_limits = (mapcache_extent_i*)apr_pcalloc(ctx->pool,grid->nlevels*sizeof(mapcache_extent_i));
     gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_NOTCONFIGURED;
+    gridlink->intermediate_grids = apr_array_make(ctx->pool,1,sizeof(mapcache_grid_link*));
 
     restrictedExtent = (char*)ezxml_attr(cur_node,"restricted_extent");
     if(restrictedExtent) {
@@ -667,6 +703,11 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
         return;
       }
     }
+    sTolerance = (char*)ezxml_attr(cur_node,"use_wms_intermediate_resolutions");
+    if(sTolerance && !strcmp(sTolerance,"true")) {
+      mapcache_grid_link *intermediate_gridlink = apr_pcalloc(ctx->pool,sizeof(mapcache_grid_link));
+      APR_ARRAY_PUSH(gridlink->intermediate_grids,mapcache_grid_link*) = intermediate_gridlink;
+    }
 
 
     mapcache_grid_compute_limits(grid,extent,gridlink->grid_limits,tolerance);
@@ -741,6 +782,40 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
     if(!havewgs84bbox && !strcasecmp(grid->srs,"EPSG:4326")) {
       tileset->wgs84bbox = *extent;
     }
+
+    if(gridlink->intermediate_grids->nelts > 0) {
+      double factor = 0.5, unitheight,unitwidth;
+      int i;
+      mapcache_grid_link *igl = APR_ARRAY_IDX(gridlink->intermediate_grids, 0, mapcache_grid_link*);
+      igl->restricted_extent = gridlink->restricted_extent;
+      igl->minz = gridlink->minz;
+      igl->max_cached_zoom = gridlink->max_cached_zoom - 1;
+      igl->maxz = gridlink->maxz - 1;
+      igl->outofzoom_strategy = gridlink->outofzoom_strategy;
+      igl->grid = mapcache_grid_create(ctx->pool);
+      igl->grid->extent = gridlink->grid->extent;
+      igl->grid->name = apr_psprintf(ctx->pool,"%s_intermediate_%g",gridlink->grid->name,factor);
+      igl->grid->nlevels = gridlink->grid->nlevels - 1;
+      igl->grid->origin = gridlink->grid->origin;
+      igl->grid->srs = gridlink->grid->srs;
+      igl->grid->srs_aliases = gridlink->grid->srs_aliases;
+      igl->grid->unit = gridlink->grid->unit;
+      igl->grid->tile_sx = gridlink->grid->tile_sx + gridlink->grid->tile_sx * factor;
+      igl->grid->tile_sy = gridlink->grid->tile_sy + gridlink->grid->tile_sy * factor;
+      igl->grid->levels = (mapcache_grid_level**)apr_pcalloc(ctx->pool, igl->grid->nlevels*sizeof(mapcache_grid_level*));
+      for(i=0; i<igl->grid->nlevels; i++) {
+        mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(ctx->pool,sizeof(mapcache_grid_level));
+        level->resolution = gridlink->grid->levels[i]->resolution + (gridlink->grid->levels[i+1]->resolution - gridlink->grid->levels[i]->resolution) * factor;
+        unitheight = igl->grid->tile_sy * level->resolution;
+        unitwidth = igl->grid->tile_sx * level->resolution;
+        
+        level->maxy = ceil((igl->grid->extent.maxy-igl->grid->extent.miny - 0.01* unitheight)/unitheight);
+        level->maxx = ceil((igl->grid->extent.maxx-igl->grid->extent.minx - 0.01* unitwidth)/unitwidth);
+        igl->grid->levels[i] = level;
+      }
+      igl->grid_limits = (mapcache_extent_i*)apr_pcalloc(ctx->pool,igl->grid->nlevels*sizeof(mapcache_extent_i));
+      mapcache_grid_compute_limits(igl->grid,extent,igl->grid_limits,tolerance);
+    }
     APR_ARRAY_PUSH(tileset->grid_links,mapcache_grid_link*) = gridlink;
   }
 
@@ -762,7 +837,7 @@ void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
                      " but it is not configured", name, cur_node->txt);
       return;
     }
-    tileset->cache = cache;
+    tileset->_cache = cache;
   }
 
   if ((cur_node = ezxml_child(node,"source")) != NULL) {
@@ -910,6 +985,7 @@ void parseServices(mapcache_context *ctx, ezxml_t root, mapcache_cfg *config)
 }
 
 
+
 void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filename, mapcache_cfg *config)
 {
   ezxml_t doc, node;
@@ -1078,20 +1154,35 @@ void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filenam
     }
   }
 
-  if((node = ezxml_child(doc,"lock_dir")) != NULL) {
-    config->lockdir = apr_pstrdup(ctx->pool, node->txt);
+  if((node = ezxml_child(doc,"locker")) != NULL) {
+    mapcache_config_parse_locker(ctx,node,&config->locker);
+    GC_CHECK_ERROR(ctx);
   } else {
-    config->lockdir = apr_pstrdup(ctx->pool,"/tmp");
-  }
+    /* backwards compatibility */
+    int micro_retry;
+    mapcache_locker_disk *ldisk;
+    config->locker = mapcache_locker_disk_create(ctx);
+    ldisk = (mapcache_locker_disk*)config->locker;
+    if((node = ezxml_child(doc,"lock_dir")) != NULL) {
+      ldisk->dir = apr_pstrdup(ctx->pool, node->txt);
+    } else {
+      ldisk->dir = apr_pstrdup(ctx->pool,"/tmp");
+    }
 
-  if((node = ezxml_child(doc,"lock_retry")) != NULL) {
-    char *endptr;
-    config->lock_retry_interval = (unsigned int)strtol(node->txt,&endptr,10);
-    if(*endptr != 0 || config->lock_retry_interval < 0) {
-      ctx->set_error(ctx, 400, "failed to parse lock_retry microseconds \"%s\". Expecting a positive integer",
-                     node->txt);
-      return;
+    if((node = ezxml_child(doc,"lock_retry")) != NULL) {
+      char *endptr;
+      micro_retry = strtol(node->txt,&endptr,10);
+      if(*endptr != 0 || micro_retry <= 0) {
+        ctx->set_error(ctx, 400, "failed to parse lock_retry microseconds \"%s\". Expecting a positive integer",
+            node->txt);
+        return;
+      }
+    } else {
+      /* default retry interval is 1/100th of a second, i.e. 10000 microseconds */
+      micro_retry = 10000;
     }
+    config->locker->retry_interval = micro_retry / 1000000.0;
+    config->locker->timeout=120;
   }
 
   if((node = ezxml_child(doc,"threaded_fetching")) != NULL) {
diff --git a/lib/connection_pool.c b/lib/connection_pool.c
new file mode 100644
index 0000000..1999d8e
--- /dev/null
+++ b/lib/connection_pool.c
@@ -0,0 +1,190 @@
+/******************************************************************************
+ *
+ * Project:  MapServer
+ * Purpose:  MapCache connection pooling
+ * Author:   Thomas Bonfort and the MapServer team.
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2011 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#include <apr_reslist.h>
+#include "mapcache.h"
+
+struct mapcache_connection_pool {
+    apr_pool_t *server_pool;
+    apr_reslist_t *connexions;
+};
+
+
+struct mapcache_pooled_connection_container {
+  mapcache_pooled_connection *head;
+  apr_pool_t *pool;
+  unsigned int max_list_size;
+};
+
+
+
+struct mapcache_pooled_connection_private_data {
+  char *key;
+  mapcache_connection_destructor destructor; 
+  mapcache_pooled_connection *next;
+  mapcache_pooled_connection_container *pcc;
+};
+
+static apr_status_t mapcache_connection_container_creator(void **conn_, void *params, apr_pool_t *pool) {
+  mapcache_pooled_connection_container *pcc;
+  pcc = calloc(1, sizeof(mapcache_pooled_connection_container));
+  pcc->max_list_size = 10;
+  pcc->pool = pool;
+  *conn_ = pcc;
+  return APR_SUCCESS;
+}
+
+static apr_status_t mapcache_connection_container_destructor(void *conn_, void *params, apr_pool_t *pool) {
+  mapcache_pooled_connection_container *pcc = (mapcache_pooled_connection_container*)conn_;
+  mapcache_pooled_connection *pc = pcc->head;
+  while(pc) {
+    mapcache_pooled_connection *this = pc;
+    this->private->destructor(this->connection, pcc->pool);
+    free(this->private->key);
+    pc = this->private->next;
+    free(this);
+  }
+  free(pcc);
+  return MAPCACHE_SUCCESS;
+}
+
+apr_status_t mapcache_connection_pool_create(mapcache_connection_pool **cp, apr_pool_t *server_pool) {
+  apr_status_t rv;
+  *cp = apr_pcalloc(server_pool, sizeof(mapcache_connection_pool));
+  (*cp)->server_pool = server_pool;
+  rv = apr_reslist_create(&((*cp)->connexions), 1, 5, 200, 60*1000000,
+      mapcache_connection_container_creator,
+      mapcache_connection_container_destructor,
+      NULL,
+      server_pool);
+  return rv;
+}
+
+mapcache_pooled_connection* mapcache_connection_pool_get_connection(mapcache_context *ctx, char *key,
+        mapcache_connection_constructor constructor, mapcache_connection_destructor destructor,
+        void *params) {
+  apr_status_t rv;
+  int count = 0;
+  mapcache_pooled_connection_container *pcc;
+  mapcache_pooled_connection *pc,*pred=NULL;
+  rv = apr_reslist_acquire(ctx->connection_pool->connexions, (void**)&pcc);
+  if(rv != APR_SUCCESS || !pcc) {
+    char errmsg[120];
+    ctx->set_error(ctx,500, "failed to acquire connection from mapcache connection pool: (%s)", apr_strerror(rv, errmsg,120));
+    return NULL;
+  }
+
+  /* loop through existing connections to see if we find one matching the given key */
+  pc = pcc->head;
+  while(pc) {
+    count++;
+    if(!strcmp(key,pc->private->key)) {
+      /* move current connection to head of list, and return it. We only move the connection
+         to the front of the list if it wasn't in the first 2 connections, as in the seeding
+         case we are always alternating between read and write operations (i.e. potentially
+         2 different connections and in that cas we end up switching connections each time
+         there's an access */
+      if(pc != pcc->head && count>2) {
+        assert(pred);
+        pred->private->next = pc->private->next;
+        pc->private->next = pcc->head;
+        pcc->head = pc;
+      }
+      return pc;
+    }
+    pred = pc;
+    pc = pc->private->next;
+  }
+  
+  /* connection not found in pool */
+  pc = calloc(1,sizeof(mapcache_pooled_connection));
+  /*
+  ctx->log(ctx, MAPCACHE_DEBUG, "calling constructor for pooled connection (%s)", key);
+  */
+  constructor(ctx, &pc->connection, params, pcc->pool);
+  if(GC_HAS_ERROR(ctx)) {
+    free(pc);
+    apr_reslist_release(ctx->connection_pool->connexions, pcc);
+    return NULL;
+  }
+  
+  pc->private = calloc(1,sizeof(mapcache_pooled_connection_private_data));
+  pc->private->key = strdup(key);
+  pc->private->destructor = destructor;
+  pc->private->next = pcc->head;
+  pc->private->pcc = pcc;
+  
+  if(count == pcc->max_list_size) {
+    /* max number of connections atained, we must destroy the last one that was used */
+    mapcache_pooled_connection *opc;
+    opc = pcc->head;
+    count = 1;
+    while(count < pcc->max_list_size) {
+      pred = opc;
+      opc = opc->private->next;
+      count++;
+    }
+    ctx->log(ctx, MAPCACHE_DEBUG, "tearing down pooled connection (%s) to make room", opc->private->key);
+    opc->private->destructor(opc->connection, pcc->pool);
+    free(opc->private->key);
+    free(opc->private);
+    free(opc);
+    if(pred) {
+      pred->private->next = NULL;
+    }
+  }
+  pcc->head = pc;
+  return pc;
+
+}
+void mapcache_connection_pool_invalidate_connection(mapcache_context *ctx, mapcache_pooled_connection *connection) {
+  mapcache_pooled_connection_container *pcc = connection->private->pcc;
+  mapcache_pooled_connection *pc = pcc->head, *pred=NULL;
+  while(pc) {
+    if(pc == connection) {
+      if(pred) {
+        pred->private->next = pc->private->next;
+      } else {
+        pcc->head = pc->private->next;
+      }
+      pc->private->destructor(pc->connection, pcc->pool);
+      free(pc->private->key);
+      free(pc);
+    }
+    pred = pc;
+    pc = pc->private->next;
+  }
+}
+
+void mapcache_connection_pool_release_connection(mapcache_context *ctx, mapcache_pooled_connection *connection) {
+  if(connection) {
+    mapcache_pooled_connection_container *pcc = connection->private->pcc;
+    apr_reslist_release(ctx->connection_pool->connexions,(void*)pcc);
+  }
+}
+
diff --git a/lib/core.c b/lib/core.c
index 27108e7..de2befd 100644
--- a/lib/core.c
+++ b/lib/core.c
@@ -196,7 +196,7 @@ mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_r
 {
   int expires = 0;
   mapcache_http_response *response;
-  int i,is_empty=1 /* response image is initially empty */;
+  int i,is_empty=1; /* response image is initially empty */;
   char *timestr;
   mapcache_image *base=NULL;
   mapcache_image_format *format = NULL;
@@ -208,12 +208,23 @@ mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_r
   }
 #endif
   response = mapcache_http_response_create(ctx->pool);
+  
+  if(ctx->supports_redirects && req_tile->ntiles == 1) {
+    req_tile->tiles[0]->allow_redirect = 1;
+  }
 
 
   mapcache_prefetch_tiles(ctx,req_tile->tiles,req_tile->ntiles);
   if(GC_HAS_ERROR(ctx))
     return NULL;
 
+  if(req_tile->tiles[0]->redirect) {
+    response->code = 302;
+    apr_table_set(response->headers,"Location",req_tile->tiles[0]->redirect);
+    response->data = mapcache_buffer_create(0, ctx->pool);
+    return response;
+  }
+
   /* loop through tiles, and eventually merge them vertically together */
   for(i=0; i<req_tile->ntiles; i++) {
     mapcache_tile *tile = req_tile->tiles[i]; /* shortcut */
@@ -283,8 +294,8 @@ mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_r
   if(!response->data) {
     /* we need to encode the raw image data*/
     if(base) {
-      if(req_tile->format) {
-        format = req_tile->format;
+      if(req_tile->image_request.format) {
+        format = req_tile->image_request.format;
       } else {
         format = req_tile->tiles[0]->tileset->format;
         if(!format) {
@@ -303,7 +314,7 @@ mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_r
       }
 #endif
       unsigned char empty[5] = {'#',0,0,0,0};
-      response->data = mapcache_empty_png_decode(ctx,empty,&is_empty); /* is_empty is unchanged and left to 1 */
+      response->data = mapcache_empty_png_decode(ctx,req_tile->tiles[0]->grid_link->grid->tile_sx, req_tile->tiles[0]->grid_link->grid->tile_sy, empty,&is_empty); /* is_empty is unchanged and left to 1 */
       format = mapcache_configuration_get_image_format(ctx->config,"PNG8");
     }
   }
@@ -339,10 +350,12 @@ mapcache_map* mapcache_assemble_maps(mapcache_context *ctx, mapcache_map **maps,
   int i;
   maptiles = apr_pcalloc(ctx->pool,nmaps*sizeof(mapcache_tile**));
   nmaptiles = apr_pcalloc(ctx->pool,nmaps*sizeof(int));
+  mapcache_grid_link **effectively_used_grid_links = apr_pcalloc(ctx->pool,nmaps*sizeof(mapcache_grid_link*));
   for(i=0; i<nmaps; i++) {
     mapcache_tileset_get_map_tiles(ctx,maps[i]->tileset,maps[i]->grid_link,
                                    &maps[i]->extent, maps[i]->width, maps[i]->height,
-                                   &(nmaptiles[i]), &(maptiles[i]));
+                                   &(nmaptiles[i]), &(maptiles[i]), &(effectively_used_grid_links[i]));
+    if(GC_HAS_ERROR(ctx)) return NULL;
     ntiles += nmaptiles[i];
   }
   tiles = apr_pcalloc(ctx->pool,ntiles * sizeof(mapcache_tile*));
@@ -379,7 +392,7 @@ mapcache_map* mapcache_assemble_maps(mapcache_context *ctx, mapcache_map **maps,
       }
     }
     if(hasdata) {
-      maps[i]->raw_image = mapcache_tileset_assemble_map_tiles(ctx,maps[i]->tileset,maps[i]->grid_link,
+      maps[i]->raw_image = mapcache_tileset_assemble_map_tiles(ctx,maps[i]->tileset,effectively_used_grid_links[i],
                            &maps[i]->extent, maps[i]->width, maps[i]->height,
                            nmaptiles[i], maptiles[i],
                            mode);
@@ -468,7 +481,7 @@ mapcache_http_response *mapcache_core_get_map(mapcache_context *ctx, mapcache_re
   }
 
   if(basemap->raw_image) {
-    format = req_map->getmap_format; /* always defined, defaults to JPEG */
+    format = req_map->image_request.format; /* always defined, defaults to JPEG */
     response->data = format->write(ctx,basemap->raw_image,format);
     if(GC_HAS_ERROR(ctx)) {
       return NULL;
@@ -516,16 +529,21 @@ mapcache_http_response *mapcache_core_proxy_request(mapcache_context *ctx, mapca
   mapcache_http *http;
   mapcache_http_response *response = mapcache_http_response_create(ctx->pool);
   response->data = mapcache_buffer_create(30000,ctx->pool);
-  http = req_proxy->http;
+  http = mapcache_http_clone(ctx, req_proxy->rule->http);
   if(req_proxy->pathinfo) {
-    http = mapcache_http_clone(ctx,http);
     if( (*(req_proxy->pathinfo)) == '/' ||
         http->url[strlen(http->url)-1] == '/')
       http->url = apr_pstrcat(ctx->pool,http->url,req_proxy->pathinfo,NULL);
     else
       http->url = apr_pstrcat(ctx->pool,http->url,"/",req_proxy->pathinfo,NULL);
   }
-  mapcache_http_do_request_with_params(ctx,http,req_proxy->params,response->data,response->headers,&response->code);
+  http->url = mapcache_http_build_url(ctx,http->url,req_proxy->params);
+  http->post_body = req_proxy->post_buf;
+  http->post_len = req_proxy->post_len;
+  if(req_proxy->headers) {
+    apr_table_overlap(http->headers, req_proxy->headers, APR_OVERLAP_TABLES_SET);
+  }
+  mapcache_http_do_request(ctx,http, response->data,response->headers,&response->code);
   if(response->code !=0 && GC_HAS_ERROR(ctx)) {
     /* the http request was successful, but the server returned an error */
     ctx->clear_errors(ctx);
diff --git a/lib/dimension.c b/lib/dimension.c
index 6590368..037aab3 100644
--- a/lib/dimension.c
+++ b/lib/dimension.c
@@ -35,11 +35,7 @@
 #include <time.h>
 #ifdef USE_SQLITE
 #include <sqlite3.h>
-#include <apr_reslist.h>
-#include <apr_hash.h>
-#ifdef APR_HAS_THREADS
-#include <apr_thread_mutex.h>
-#endif
+#include <float.h>
 #endif
 
 
@@ -69,16 +65,15 @@ static int _mapcache_dimension_intervals_validate(mapcache_context *ctx, mapcach
   return MAPCACHE_FAILURE;
 }
 
-static const char** _mapcache_dimension_intervals_print(mapcache_context *ctx, mapcache_dimension *dim)
+static apr_array_header_t* _mapcache_dimension_intervals_print(mapcache_context *ctx, mapcache_dimension *dim)
 {
   mapcache_dimension_intervals *dimension = (mapcache_dimension_intervals*)dim;
-  const char **ret = (const char**)apr_pcalloc(ctx->pool,(dimension->nintervals+1)*sizeof(const char*));
+  apr_array_header_t *ret = apr_array_make(ctx->pool,dimension->nintervals,sizeof(char*));
   int i;
   for(i=0; i<dimension->nintervals; i++) {
     mapcache_interval *interval = &dimension->intervals[i];
-    ret[i] = apr_psprintf(ctx->pool,"%g/%g/%g",interval->start,interval->end,interval->resolution);
+    APR_ARRAY_PUSH(ret,char*) = apr_psprintf(ctx->pool,"%g/%g/%g",interval->start,interval->end,interval->resolution);
   }
-  ret[i]=NULL;
   return ret;
 }
 
@@ -149,12 +144,11 @@ static int _mapcache_dimension_regex_validate(mapcache_context *ctx, mapcache_di
   return MAPCACHE_FAILURE;
 }
 
-static const char** _mapcache_dimension_regex_print(mapcache_context *ctx, mapcache_dimension *dim)
+static apr_array_header_t* _mapcache_dimension_regex_print(mapcache_context *ctx, mapcache_dimension *dim)
 {
   mapcache_dimension_regex *dimension = (mapcache_dimension_regex*)dim;
-  const char **ret = (const char**)apr_pcalloc(ctx->pool,2*sizeof(const char*));
-  ret[0]=dimension->regex_string;
-  ret[1]=NULL;
+  apr_array_header_t *ret = apr_array_make(ctx->pool,1,sizeof(char*));
+  APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,dimension->regex_string);
   return ret;
 }
 
@@ -212,15 +206,14 @@ static int _mapcache_dimension_values_validate(mapcache_context *ctx, mapcache_d
   return MAPCACHE_FAILURE;
 }
 
-static const char** _mapcache_dimension_values_print(mapcache_context *ctx, mapcache_dimension *dim)
+static apr_array_header_t* _mapcache_dimension_values_print(mapcache_context *ctx, mapcache_dimension *dim)
 {
   mapcache_dimension_values *dimension = (mapcache_dimension_values*)dim;
-  const char **ret = (const char**)apr_pcalloc(ctx->pool,(dimension->nvalues+1)*sizeof(const char*));
+  apr_array_header_t *ret = apr_array_make(ctx->pool,dimension->nvalues,sizeof(char*));
   int i;
   for(i=0; i<dimension->nvalues; i++) {
-    ret[i] = dimension->values[i];
+    APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,dimension->values[i]);
   }
-  ret[i]=NULL;
   return ret;
 }
 
@@ -308,105 +301,202 @@ mapcache_dimension* mapcache_dimension_regex_create(apr_pool_t *pool)
 
 #ifdef USE_SQLITE
 
-static apr_hash_t *time_connection_pools = NULL;
-
-struct sqlite_time_conn {
+struct sqlite_dimension_conn {
   sqlite3 *handle;
-  sqlite3_stmt *prepared_statement;
-  char *errmsg;
+  sqlite3_stmt **prepared_statements;
+  int n_statements;
 };
 
-static apr_status_t _sqlite_time_reslist_get_ro_connection(void **conn_, void *params, apr_pool_t *pool)
+void mapcache_sqlite_dimension_connection_constructor(mapcache_context *ctx, void **conn_, void *params, apr_pool_t *pool)
 {
   int ret;
   int flags;  
-  mapcache_timedimension_sqlite *dim = (mapcache_timedimension_sqlite*) params;
-  struct sqlite_time_conn *conn = apr_pcalloc(pool, sizeof (struct sqlite_time_conn));
+  char *dbfile = (char*) params;
+  struct sqlite_dimension_conn *conn = calloc(1, sizeof (struct sqlite_dimension_conn));
   *conn_ = conn;
   flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX;
-  ret = sqlite3_open_v2(dim->dbfile, &conn->handle, flags, NULL);
+  ret = sqlite3_open_v2(dbfile, &conn->handle, flags, NULL);
   
   if (ret != SQLITE_OK) {
-    return APR_EGENERAL;
+    ctx->set_error(ctx,500,"failed to open sqlite dimension dbfile (%s): %s",dbfile,sqlite3_errmsg(conn->handle));
+    sqlite3_close(conn->handle);
+    *conn_=NULL;
+    return;
   }
   sqlite3_busy_timeout(conn->handle, 300000);
-  return APR_SUCCESS;
 }
 
-static apr_status_t _sqlite_time_reslist_free_connection(void *conn_, void *params, apr_pool_t *pool)
+void mapcache_sqlite_dimension_connection_destructor(void *conn_, apr_pool_t *pool)
 {
-  struct sqlite_time_conn *conn = (struct sqlite_time_conn*) conn_;
-  if(conn->prepared_statement) {
-    sqlite3_finalize(conn->prepared_statement);
+  struct sqlite_dimension_conn *conn = (struct sqlite_dimension_conn*) conn_;
+  while(conn->n_statements) {
+    conn->n_statements--;
+    if(conn->prepared_statements[conn->n_statements]) {
+      sqlite3_finalize(conn->prepared_statements[conn->n_statements]);
+    }
   }
+  free(conn->prepared_statements);
   sqlite3_close(conn->handle);
-  return APR_SUCCESS;
+  free(conn);
 }
 
-static struct sqlite_time_conn* _sqlite_time_get_conn(mapcache_context *ctx, mapcache_timedimension_sqlite *dim) {
-  apr_status_t rv;
-  struct sqlite_time_conn *conn = NULL;
-  apr_reslist_t *pool = NULL;
-  if(!time_connection_pools || NULL == (pool = apr_hash_get(time_connection_pools,dim->timedimension.key, APR_HASH_KEY_STRING)) ) {
-    
-#ifdef APR_HAS_THREADS
-    if(ctx->threadlock)
-      apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-    
-    if(!time_connection_pools) {
-      time_connection_pools = apr_hash_make(ctx->process_pool);
-    }
+static mapcache_pooled_connection* _sqlite_time_dimension_get_conn(mapcache_context *ctx, mapcache_timedimension_sqlite *dim) {
+  mapcache_pooled_connection *pc = mapcache_connection_pool_get_connection(ctx,dim->timedimension.key,
+        mapcache_sqlite_dimension_connection_constructor,
+        mapcache_sqlite_dimension_connection_destructor, dim->dbfile);
+  return pc;
+}
+static mapcache_pooled_connection* _sqlite_dimension_get_conn(mapcache_context *ctx, mapcache_dimension_sqlite *dim) {
+  mapcache_pooled_connection *pc = mapcache_connection_pool_get_connection(ctx,dim->dimension.name,
+        mapcache_sqlite_dimension_connection_constructor,
+        mapcache_sqlite_dimension_connection_destructor, dim->dbfile);
+  return pc;
+}
 
-    /* probably doesn't exist, unless the previous mutex locked us, so we check */
-    pool = apr_hash_get(time_connection_pools,dim->timedimension.key, APR_HASH_KEY_STRING);
-    if(!pool) {
-      /* there where no existing connection pools, create them*/
-      rv = apr_reslist_create(&pool,
-                              0 /* min */,
-                              10 /* soft max */,
-                              200 /* hard max */,
-                              60*1000000 /*60 seconds, ttl*/,
-                              _sqlite_time_reslist_get_ro_connection, /* resource constructor */
-                              _sqlite_time_reslist_free_connection, /* resource destructor */
-                              dim, ctx->process_pool);
-      if(rv != APR_SUCCESS) {
-        ctx->set_error(ctx,500,"failed to create sqlite time connection pool");
-#ifdef APR_HAS_THREADS
-        if(ctx->threadlock)
-          apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-        return NULL;
+static void _sqlite_dimension_release_conn(mapcache_context *ctx, mapcache_pooled_connection *pc)
+{
+  if(GC_HAS_ERROR(ctx)) {
+    mapcache_connection_pool_invalidate_connection(ctx,pc);
+  } else {
+    mapcache_connection_pool_release_connection(ctx,pc);
+  }
+}
+
+
+static int _mapcache_dimension_sqlite_validate(mapcache_context *ctx, mapcache_dimension *dim, char **value)
+{
+  mapcache_dimension_sqlite *dimension = (mapcache_dimension_sqlite*)dim;
+  struct sqlite_dimension_conn *conn = NULL;
+  int sqliteret,paramidx,ret=MAPCACHE_FAILURE;
+  mapcache_pooled_connection *pc;
+  pc = _sqlite_dimension_get_conn(ctx,dimension);
+  if (GC_HAS_ERROR(ctx)) {
+    return ret;
+  }
+  conn = pc->connection;
+  if(!conn->prepared_statements) {
+    conn->prepared_statements = calloc(2,sizeof(sqlite3_stmt*));
+    conn->n_statements = 2;
+  }
+  if(!conn->prepared_statements[0]) {
+    sqliteret = sqlite3_prepare_v2(conn->handle, dimension->validate_query, -1, &conn->prepared_statements[0], NULL);
+    if(sqliteret != SQLITE_OK) {
+      ctx->set_error(ctx, 500, "sqlite dimension backend failed on preparing query: %s", sqlite3_errmsg(conn->handle));
+      goto cleanup;
+    }
+  }
+  
+  paramidx = sqlite3_bind_parameter_index(conn->prepared_statements[0], ":dim");
+  if (paramidx) {
+    sqliteret = sqlite3_bind_text(conn->prepared_statements[0], paramidx, *value, -1, SQLITE_STATIC);
+    if(sqliteret != SQLITE_OK) {
+      ctx->set_error(ctx,400, "sqlite dimension failed to bind :dim : %s", sqlite3_errmsg(conn->handle));
+      goto cleanup;
+    }
+  }
+  do {
+    sqliteret = sqlite3_step(conn->prepared_statements[0]);
+    if (sqliteret != SQLITE_DONE && sqliteret != SQLITE_ROW && sqliteret != SQLITE_BUSY && sqliteret != SQLITE_LOCKED) {
+      ctx->set_error(ctx, 500, "sqlite dimension backend failed on query : %s (%d)", sqlite3_errmsg(conn->handle), sqliteret);
+      goto cleanup;
+    }
+    if(sqliteret == SQLITE_ROW) {
+      const char* dim_modified = (const char*) sqlite3_column_text(conn->prepared_statements[0], 0);
+      if(strcmp(dim_modified, *value)) {
+        *value = apr_pstrdup(ctx->pool, dim_modified);
       }
-      apr_hash_set(time_connection_pools,dim->timedimension.key,APR_HASH_KEY_STRING,pool);
+      ret = MAPCACHE_SUCCESS;
     }
-#ifdef APR_HAS_THREADS
-    if(ctx->threadlock)
-      apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
-#endif
-    pool = apr_hash_get(time_connection_pools,dim->timedimension.key, APR_HASH_KEY_STRING);
-    assert(pool);
+  } while (sqliteret == SQLITE_ROW || sqliteret == SQLITE_BUSY || sqliteret == SQLITE_LOCKED);
+
+cleanup:
+  if(conn->prepared_statements[0]) {
+    sqlite3_reset(conn->prepared_statements[0]);
   }
-  rv = apr_reslist_acquire(pool, (void **) &conn);
-  if (rv != APR_SUCCESS) {
-    ctx->set_error(ctx, 500, "failed to aquire connection to time dimension sqlite backend: %s", (conn && conn->errmsg)?conn->errmsg:"unknown error");
-    return NULL;
+  _sqlite_dimension_release_conn(ctx,pc);
+      
+  return ret;
+}
+
+static apr_array_header_t* _mapcache_dimension_sqlite_print(mapcache_context *ctx, mapcache_dimension *dim)
+{
+  mapcache_dimension_sqlite *dimension = (mapcache_dimension_sqlite*)dim;
+  struct sqlite_dimension_conn *conn = NULL;
+  int sqliteret;
+  apr_array_header_t *ret = apr_array_make(ctx->pool,0,sizeof(char*));
+  mapcache_pooled_connection *pc;
+  pc = _sqlite_dimension_get_conn(ctx,dimension);
+  if (GC_HAS_ERROR(ctx)) {
+    return ret;
+  }
+  conn = pc->connection;
+  if(!conn->prepared_statements) {
+    conn->prepared_statements = calloc(2,sizeof(sqlite3_stmt*));
+    conn->n_statements = 2;
+  }
+ 
+  if(!conn->prepared_statements[1]) {
+    sqliteret = sqlite3_prepare_v2(conn->handle, dimension->list_query, -1, &conn->prepared_statements[1], NULL);
+    if(sqliteret != SQLITE_OK) {
+      ctx->set_error(ctx, 500, "sqlite dimension backend failed on preparing query: %s", sqlite3_errmsg(conn->handle));
+      goto cleanup;
+    }
   }
-  return conn;
+  do {
+    sqliteret = sqlite3_step(conn->prepared_statements[1]);
+    if (sqliteret != SQLITE_DONE && sqliteret != SQLITE_ROW && sqliteret != SQLITE_BUSY && sqliteret != SQLITE_LOCKED) {
+      ctx->set_error(ctx, 500, "sqlite dimension backend failed on query : %s (%d)", sqlite3_errmsg(conn->handle), sqliteret);
+      goto cleanup;
+    }
+    if(sqliteret == SQLITE_ROW) {
+      const char* sqdim = (const char*) sqlite3_column_text(conn->prepared_statements[1], 0);
+      APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,sqdim);
+    }
+  } while (sqliteret == SQLITE_ROW || sqliteret == SQLITE_BUSY || sqliteret == SQLITE_LOCKED);
+
+cleanup:
+  if(conn->prepared_statements[1]) {
+    sqlite3_reset(conn->prepared_statements[1]);
+  }
+  _sqlite_dimension_release_conn(ctx,pc);
+      
+  return ret;
 }
 
-static void _sqlite_time_release_conn(mapcache_context *ctx, mapcache_timedimension_sqlite *sdim, struct sqlite_time_conn *conn)
+
+static void _mapcache_dimension_sqlite_parse_xml(mapcache_context *ctx, mapcache_dimension *dim,
+    ezxml_t node)
 {
-  apr_reslist_t *pool;
-  pool = apr_hash_get(time_connection_pools,sdim->timedimension.key, APR_HASH_KEY_STRING);
+  mapcache_dimension_sqlite *dimension;
+  ezxml_t child;
+  
+  dimension = (mapcache_dimension_sqlite*)dim;
 
-  if (GC_HAS_ERROR(ctx)) {
-    apr_reslist_invalidate(pool, (void*) conn);
+  child = ezxml_child(node,"dbfile");
+  if(child) {
+    dimension->dbfile = apr_pstrdup(ctx->pool, child->txt);
+  } else {
+    ctx->set_error(ctx,400,"sqlite dimension \"%s\" has no <dbfile> node", dim->name);
+    return;
+  }
+  child = ezxml_child(node,"validate_query");
+  if(child) {
+    dimension->validate_query = apr_pstrdup(ctx->pool, child->txt);
   } else {
-    apr_reslist_release(pool, (void*) conn);
+    ctx->set_error(ctx,400,"sqlite dimension \"%s\" has no <validate_query> node", dim->name);
+    return;
   }
+  child = ezxml_child(node,"list_query");
+  if(child) {
+    dimension->list_query = apr_pstrdup(ctx->pool, child->txt);
+  } else {
+    ctx->set_error(ctx,400,"sqlite dimension \"%s\" has no <list_query> node", dim->name);
+    return;
+  }
+  
 }
 
+
 static void _bind_sqlite_timedimension_params(mapcache_context *ctx, sqlite3_stmt *stmt,
         sqlite3 *handle, mapcache_tileset *tileset, mapcache_grid *grid, mapcache_extent *extent,
         time_t start, time_t end)
@@ -432,41 +522,39 @@ static void _bind_sqlite_timedimension_params(mapcache_context *ctx, sqlite3_stm
     }
   }
 
-  if(extent) {
-    paramidx = sqlite3_bind_parameter_index(stmt, ":minx");
-    if (paramidx) {
-      ret = sqlite3_bind_double(stmt, paramidx, extent->minx);
-      if(ret != SQLITE_OK) {
-        ctx->set_error(ctx,400, "failed to bind :minx %s", sqlite3_errmsg(handle));
-        return;
-      }
+  paramidx = sqlite3_bind_parameter_index(stmt, ":minx");
+  if (paramidx) {
+    ret = sqlite3_bind_double(stmt, paramidx, extent?extent->minx:-DBL_MAX);
+    if(ret != SQLITE_OK) {
+      ctx->set_error(ctx,400, "failed to bind :minx %s", sqlite3_errmsg(handle));
+      return;
     }
-    paramidx = sqlite3_bind_parameter_index(stmt, ":miny");
-    if (paramidx) {
-      ret = sqlite3_bind_double(stmt, paramidx, extent->miny);
-      if(ret != SQLITE_OK) {
-        ctx->set_error(ctx,400, "failed to bind :miny %s", sqlite3_errmsg(handle));
-        return;
-      }
+  }
+  paramidx = sqlite3_bind_parameter_index(stmt, ":miny");
+  if (paramidx) {
+    ret = sqlite3_bind_double(stmt, paramidx, extent?extent->miny:-DBL_MAX);
+    if(ret != SQLITE_OK) {
+      ctx->set_error(ctx,400, "failed to bind :miny %s", sqlite3_errmsg(handle));
+      return;
     }
-    paramidx = sqlite3_bind_parameter_index(stmt, ":maxx");
-    if (paramidx) {
-      ret = sqlite3_bind_double(stmt, paramidx, extent->maxx);
-      if(ret != SQLITE_OK) {
-        ctx->set_error(ctx,400, "failed to bind :maxx %s", sqlite3_errmsg(handle));
-        return;
-      }
+  }
+  paramidx = sqlite3_bind_parameter_index(stmt, ":maxx");
+  if (paramidx) {
+    ret = sqlite3_bind_double(stmt, paramidx, extent?extent->maxx:DBL_MAX);
+    if(ret != SQLITE_OK) {
+      ctx->set_error(ctx,400, "failed to bind :maxx %s", sqlite3_errmsg(handle));
+      return;
     }
-    paramidx = sqlite3_bind_parameter_index(stmt, ":maxy");
-    if (paramidx) {
-      ret = sqlite3_bind_double(stmt, paramidx, extent->maxy);
-      if(ret != SQLITE_OK) {
-        ctx->set_error(ctx,400, "failed to bind :maxy %s", sqlite3_errmsg(handle));
-        return;
-      }
+  }
+  paramidx = sqlite3_bind_parameter_index(stmt, ":maxy");
+  if (paramidx) {
+    ret = sqlite3_bind_double(stmt, paramidx, extent?extent->maxy:DBL_MAX);
+    if(ret != SQLITE_OK) {
+      ctx->set_error(ctx,400, "failed to bind :maxy %s", sqlite3_errmsg(handle));
+      return;
     }
   }
-
+  
   paramidx = sqlite3_bind_parameter_index(stmt, ":start_timestamp");
   if (paramidx) {
     ret = sqlite3_bind_int64(stmt, paramidx, start);
@@ -490,47 +578,48 @@ apr_array_header_t *_mapcache_timedimension_sqlite_get_entries(mapcache_context
         mapcache_tileset *tileset, mapcache_grid *grid, mapcache_extent *extent, time_t start, time_t end) {
   mapcache_timedimension_sqlite *sdim = (mapcache_timedimension_sqlite*)dim;
   int ret;
-  sqlite3_stmt *stmt;
   apr_array_header_t *time_ids = NULL;
-  struct sqlite_time_conn *conn = _sqlite_time_get_conn(ctx, sdim);
+  mapcache_pooled_connection *pc;
+  struct sqlite_dimension_conn *conn;
+  pc = _sqlite_time_dimension_get_conn(ctx,sdim);
   if (GC_HAS_ERROR(ctx)) {
-    if(conn) _sqlite_time_release_conn(ctx, sdim, conn);
     return NULL;
   }
-  stmt = conn->prepared_statement;
-  if(!stmt) {
-    ret = sqlite3_prepare_v2(conn->handle, sdim->query, -1, &conn->prepared_statement, NULL);
+  conn = pc->connection;
+  if(!conn->prepared_statements) {
+    conn->prepared_statements = calloc(1,sizeof(sqlite3_stmt*));
+    conn->n_statements = 1;
+  }
+  if(!conn->prepared_statements[0]) {
+    ret = sqlite3_prepare_v2(conn->handle, sdim->query, -1, &conn->prepared_statements[0], NULL);
     if(ret != SQLITE_OK) {
       ctx->set_error(ctx, 500, "time sqlite backend failed on preparing query: %s", sqlite3_errmsg(conn->handle));
-      _sqlite_time_release_conn(ctx, sdim, conn);
+      _sqlite_dimension_release_conn(ctx, pc);
       return NULL;
     }
-    stmt = conn->prepared_statement;
   }
   
-  _bind_sqlite_timedimension_params(ctx,stmt,conn->handle,tileset,grid,extent,start,end);
+  _bind_sqlite_timedimension_params(ctx,conn->prepared_statements[0],conn->handle,tileset,grid,extent,start,end);
   if(GC_HAS_ERROR(ctx)) {
-    sqlite3_reset(stmt);
-    _sqlite_time_release_conn(ctx, sdim, conn);
+    _sqlite_dimension_release_conn(ctx, pc);
     return NULL;
   }
   
   time_ids = apr_array_make(ctx->pool,0,sizeof(char*));
   do {
-    ret = sqlite3_step(stmt);
+    ret = sqlite3_step(conn->prepared_statements[0]);
     if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
       ctx->set_error(ctx, 500, "sqlite backend failed on timedimension query : %s (%d)", sqlite3_errmsg(conn->handle), ret);
-      sqlite3_reset(stmt);
-      _sqlite_time_release_conn(ctx, sdim, conn);
+      _sqlite_dimension_release_conn(ctx, pc);
       return NULL;
     }
     if(ret == SQLITE_ROW) {
-      const char* time_id = (const char*) sqlite3_column_text(stmt, 0);
+      const char* time_id = (const char*) sqlite3_column_text(conn->prepared_statements[0], 0);
       APR_ARRAY_PUSH(time_ids,char*) = apr_pstrdup(ctx->pool,time_id);
     }
   } while (ret == SQLITE_ROW || ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
-  sqlite3_reset(stmt);
-  _sqlite_time_release_conn(ctx, sdim, conn);
+  sqlite3_reset(conn->prepared_statements[0]);
+  _sqlite_dimension_release_conn(ctx, pc);
   return time_ids;
 }
 
@@ -612,9 +701,14 @@ apr_array_header_t* mapcache_timedimension_get_entries_for_value(mapcache_contex
     return NULL;
   }
   
-  if(*valueptr == '/') {
+  if(*valueptr == '/' || (*valueptr == '-' && *(valueptr+1) == '-')) {
     /* we have a second (end) time */
-    valueptr++;
+    if (*valueptr == '/') {
+      valueptr++;
+    }
+    else {
+      valueptr += 2;
+    }
     valueptr = mapcache_ogc_strptime(valueptr,&tm_end,&tie);
     if(!valueptr) {
       ctx->set_error(ctx,400,"failed to parse end time in %s",value);
@@ -657,6 +751,22 @@ apr_array_header_t* mapcache_timedimension_get_entries_for_value(mapcache_contex
   /* end loop */
 }
 
+mapcache_dimension* mapcache_dimension_sqlite_create(apr_pool_t *pool)
+{
+#ifdef USE_SQLITE
+  mapcache_dimension_sqlite *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_sqlite));
+  dimension->dimension.type = MAPCACHE_DIMENSION_SQLITE;
+  dimension->dbfile = NULL;
+  dimension->dimension.validate = _mapcache_dimension_sqlite_validate;
+  dimension->dimension.configuration_parse_xml = _mapcache_dimension_sqlite_parse_xml;
+  dimension->dimension.print_ogc_formatted_values = _mapcache_dimension_sqlite_print;
+  return (mapcache_dimension*)dimension;
+#else
+  return NULL;
+#endif
+}
+
+
 #ifdef USE_SQLITE
 mapcache_timedimension* mapcache_timedimension_sqlite_create(apr_pool_t *pool) {
   mapcache_timedimension_sqlite *dim = apr_pcalloc(pool, sizeof(mapcache_timedimension_sqlite));
diff --git a/lib/grid.c b/lib/grid.c
index 8eb26cc..d3e92a3 100644
--- a/lib/grid.c
+++ b/lib/grid.c
@@ -160,10 +160,11 @@ int mapcache_grid_get_level(mapcache_context *ctx, mapcache_grid *grid, double *
   return MAPCACHE_FAILURE;
 }
 
-void mapcache_grid_get_closest_level(mapcache_context *ctx, mapcache_grid_link *grid_link, double resolution, int *level)
+mapcache_grid_link* mapcache_grid_get_closest_wms_level(mapcache_context *ctx, mapcache_grid_link *grid_link, double resolution, int *level)
 {
   double dst = fabs(grid_link->grid->levels[grid_link->minz]->resolution - resolution);
-  int i;
+  int i,g;
+  mapcache_grid_link *ret = grid_link;
   *level = 0;
 
   for(i=grid_link->minz + 1; i<grid_link->maxz; i++) {
@@ -173,6 +174,20 @@ void mapcache_grid_get_closest_level(mapcache_context *ctx, mapcache_grid_link *
       *level = i;
     }
   }
+  if(grid_link->intermediate_grids) {
+    for(g=0; g<grid_link->intermediate_grids->nelts; g++) {
+      mapcache_grid_link *igl = APR_ARRAY_IDX(grid_link->intermediate_grids, g, mapcache_grid_link*);
+      for(i=igl->minz; i<igl->maxz; i++) {
+        double curdst = fabs(igl->grid->levels[i]->resolution - resolution);
+        if(curdst<dst) {
+          dst = curdst;
+          *level = i;
+          ret = igl;
+        }
+      }
+    }
+  }
+  return ret;
 }
 
 /*
diff --git a/lib/hmac-sha.c b/lib/hmac-sha.c
new file mode 100644
index 0000000..187433f
--- /dev/null
+++ b/lib/hmac-sha.c
@@ -0,0 +1,413 @@
+/*
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 02/02/2007
+ * Issue date:  04/30/2005
+ *
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay at a3.epfl.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mapcache.h"
+#include <string.h>
+#include <stdio.h>
+
+#define SHA256_DIGEST_SIZE ( 256 / 8)
+#define SHA256_BLOCK_SIZE  ( 512 / 8)
+
+typedef unsigned char uint8;
+typedef unsigned int  uint32;
+typedef unsigned long long uint64;
+
+typedef struct {
+    unsigned int tot_len;
+    unsigned int len;
+    unsigned char block[2 * SHA256_BLOCK_SIZE];
+    uint32 h[8];
+} sha256_ctx;
+
+typedef struct {
+    sha256_ctx ctx_inside;
+    sha256_ctx ctx_outside;
+
+    /* for hmac_reinit */
+    sha256_ctx ctx_inside_reinit;
+    sha256_ctx ctx_outside_reinit;
+
+    unsigned char block_ipad[SHA256_BLOCK_SIZE];
+    unsigned char block_opad[SHA256_BLOCK_SIZE];
+} hmac_sha256_ctx;
+
+
+#define SHFR(x, n)    (x >> n)
+#define ROTR(x, n)   ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define ROTL(x, n)   ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define CH(x, y, z)  ((x & y) ^ (~x & z))
+#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+
+#define SHA256_F1(x) (ROTR(x,  2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define SHA256_F2(x) (ROTR(x,  6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define SHA256_F3(x) (ROTR(x,  7) ^ ROTR(x, 18) ^ SHFR(x,  3))
+#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
+
+#define UNPACK32(x, str)                      \
+{                                             \
+    *((str) + 3) = (uint8) ((x)      );       \
+    *((str) + 2) = (uint8) ((x) >>  8);       \
+    *((str) + 1) = (uint8) ((x) >> 16);       \
+    *((str) + 0) = (uint8) ((x) >> 24);       \
+}
+
+#define PACK32(str, x)                        \
+{                                             \
+    *(x) =   ((uint32) *((str) + 3)      )    \
+           | ((uint32) *((str) + 2) <<  8)    \
+           | ((uint32) *((str) + 1) << 16)    \
+           | ((uint32) *((str) + 0) << 24);   \
+}
+
+#define UNPACK64(x, str)                      \
+{                                             \
+    *((str) + 7) = (uint8) ((x)      );       \
+    *((str) + 6) = (uint8) ((x) >>  8);       \
+    *((str) + 5) = (uint8) ((x) >> 16);       \
+    *((str) + 4) = (uint8) ((x) >> 24);       \
+    *((str) + 3) = (uint8) ((x) >> 32);       \
+    *((str) + 2) = (uint8) ((x) >> 40);       \
+    *((str) + 1) = (uint8) ((x) >> 48);       \
+    *((str) + 0) = (uint8) ((x) >> 56);       \
+}
+
+#define PACK64(str, x)                        \
+{                                             \
+    *(x) =   ((uint64) *((str) + 7)      )    \
+           | ((uint64) *((str) + 6) <<  8)    \
+           | ((uint64) *((str) + 5) << 16)    \
+           | ((uint64) *((str) + 4) << 24)    \
+           | ((uint64) *((str) + 3) << 32)    \
+           | ((uint64) *((str) + 2) << 40)    \
+           | ((uint64) *((str) + 1) << 48)    \
+           | ((uint64) *((str) + 0) << 56);   \
+}
+
+#define SHA256_SCR(i)                         \
+{                                             \
+    w[i] =  SHA256_F4(w[i -  2]) + w[i -  7]  \
+          + SHA256_F3(w[i - 15]) + w[i - 16]; \
+}
+
+uint32 sha256_h0[8] =
+            {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+             0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
+
+uint32 sha256_k[64] =
+            {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+             0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+             0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+             0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+             0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+             0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+             0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+             0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+             0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+             0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+             0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+             0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+             0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+             0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+             0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+             0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
+
+void sha256_transf(sha256_ctx *ctx, const unsigned char *message,
+                   unsigned int block_nb)
+{
+    uint32 w[64];
+    uint32 wv[8];
+    uint32 t1, t2;
+    const unsigned char *sub_block;
+    int i,j;
+
+    for (i = 0; i < (int) block_nb; i++) {
+        sub_block = message + (i << 6);
+
+        for (j = 0; j < 16; j++) {
+            PACK32(&sub_block[j << 2], &w[j]);
+        }
+
+        for (j = 16; j < 64; j++) {
+            SHA256_SCR(j);
+        }
+
+        for (j = 0; j < 8; j++) {
+            wv[j] = ctx->h[j];
+        }
+
+        for (j = 0; j < 64; j++) {
+            t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6])
+                + sha256_k[j] + w[j];
+            t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
+            wv[7] = wv[6];
+            wv[6] = wv[5];
+            wv[5] = wv[4];
+            wv[4] = wv[3] + t1;
+            wv[3] = wv[2];
+            wv[2] = wv[1];
+            wv[1] = wv[0];
+            wv[0] = t1 + t2;
+        }
+
+        for (j = 0; j < 8; j++) {
+            ctx->h[j] += wv[j];
+        }
+    }
+}
+
+
+void sha256_init(sha256_ctx *ctx)
+{
+    int i;
+    for (i = 0; i < 8; i++) {
+        ctx->h[i] = sha256_h0[i];
+    }
+    ctx->len = 0;
+    ctx->tot_len = 0;
+}
+
+void sha256_update(sha256_ctx *ctx, const unsigned char *message,
+                   unsigned int len)
+{
+    unsigned int block_nb;
+    unsigned int new_len, rem_len, tmp_len;
+    const unsigned char *shifted_message;
+
+    tmp_len = SHA256_BLOCK_SIZE - ctx->len;
+    rem_len = len < tmp_len ? len : tmp_len;
+
+    memcpy(&ctx->block[ctx->len], message, rem_len);
+
+    if (ctx->len + len < SHA256_BLOCK_SIZE) {
+        ctx->len += len;
+        return;
+    }
+
+    new_len = len - rem_len;
+    block_nb = new_len / SHA256_BLOCK_SIZE;
+
+    shifted_message = message + rem_len;
+
+    sha256_transf(ctx, ctx->block, 1);
+    sha256_transf(ctx, shifted_message, block_nb);
+
+    rem_len = new_len % SHA256_BLOCK_SIZE;
+
+    memcpy(ctx->block, &shifted_message[block_nb << 6],
+           rem_len);
+
+    ctx->len = rem_len;
+    ctx->tot_len += (block_nb + 1) << 6;
+}
+
+void sha256_final(sha256_ctx *ctx, unsigned char *digest)
+{
+    unsigned int block_nb;
+    unsigned int pm_len;
+    unsigned int len_b;
+    int i;
+
+    block_nb = (1 + ((SHA256_BLOCK_SIZE - 9)
+                     < (ctx->len % SHA256_BLOCK_SIZE)));
+
+    len_b = (ctx->tot_len + ctx->len) << 3;
+    pm_len = block_nb << 6;
+
+    memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+    ctx->block[ctx->len] = 0x80;
+    UNPACK32(len_b, ctx->block + pm_len - 4);
+
+    sha256_transf(ctx, ctx->block, block_nb);
+
+    for (i = 0 ; i < 8; i++) {
+        UNPACK32(ctx->h[i], &digest[i << 2]);
+    }
+}
+
+void sha256(const unsigned char *message, unsigned int len, unsigned char *digest)
+{
+    sha256_ctx ctx;
+
+    sha256_init(&ctx);
+    sha256_update(&ctx, message, len);
+    sha256_final(&ctx, digest);
+}
+
+void hmac_sha256_init(hmac_sha256_ctx *ctx, const unsigned char *key,
+                      unsigned int key_size)
+{
+    unsigned int fill;
+    unsigned int num;
+
+    const unsigned char *key_used;
+    unsigned char key_temp[SHA256_DIGEST_SIZE];
+    int i;
+
+    if (key_size == SHA256_BLOCK_SIZE) {
+        key_used = key;
+        num = SHA256_BLOCK_SIZE;
+    } else {
+        if (key_size > SHA256_BLOCK_SIZE){
+            num = SHA256_DIGEST_SIZE;
+            sha256(key, key_size, key_temp);
+            key_used = key_temp;
+        } else { /* key_size > SHA256_BLOCK_SIZE */
+            key_used = key;
+            num = key_size;
+        }
+        fill = SHA256_BLOCK_SIZE - num;
+
+        memset(ctx->block_ipad + num, 0x36, fill);
+        memset(ctx->block_opad + num, 0x5c, fill);
+    }
+
+    for (i = 0; i < (int) num; i++) {
+        ctx->block_ipad[i] = key_used[i] ^ 0x36;
+        ctx->block_opad[i] = key_used[i] ^ 0x5c;
+    }
+
+    sha256_init(&ctx->ctx_inside);
+    sha256_update(&ctx->ctx_inside, ctx->block_ipad, SHA256_BLOCK_SIZE);
+
+    sha256_init(&ctx->ctx_outside);
+    sha256_update(&ctx->ctx_outside, ctx->block_opad,
+                  SHA256_BLOCK_SIZE);
+
+    /* for hmac_reinit */
+    memcpy(&ctx->ctx_inside_reinit, &ctx->ctx_inside,
+           sizeof(sha256_ctx));
+    memcpy(&ctx->ctx_outside_reinit, &ctx->ctx_outside,
+           sizeof(sha256_ctx));
+}
+
+void hmac_sha256_reinit(hmac_sha256_ctx *ctx)
+{
+    memcpy(&ctx->ctx_inside, &ctx->ctx_inside_reinit,
+           sizeof(sha256_ctx));
+    memcpy(&ctx->ctx_outside, &ctx->ctx_outside_reinit,
+           sizeof(sha256_ctx));
+}
+
+void hmac_sha256_update(hmac_sha256_ctx *ctx, const unsigned char *message,
+                        unsigned int message_len)
+{
+    sha256_update(&ctx->ctx_inside, message, message_len);
+}
+
+void hmac_sha256_final(hmac_sha256_ctx *ctx, unsigned char *mac,
+                       unsigned int mac_size)
+{
+    unsigned char digest_inside[SHA256_DIGEST_SIZE];
+    unsigned char mac_temp[SHA256_DIGEST_SIZE];
+
+    sha256_final(&ctx->ctx_inside, digest_inside);
+    sha256_update(&ctx->ctx_outside, digest_inside, SHA256_DIGEST_SIZE);
+    sha256_final(&ctx->ctx_outside, mac_temp);
+    memcpy(mac, mac_temp, mac_size);
+}
+
+void hmac_sha256(const unsigned char *message, unsigned int message_len,
+          const unsigned char *key, unsigned int key_size,
+          unsigned char *mac, unsigned mac_size)
+{
+    hmac_sha256_ctx ctx;
+
+    hmac_sha256_init(&ctx, key, key_size);
+    hmac_sha256_update(&ctx, message, message_len);
+    hmac_sha256_final(&ctx, mac, mac_size);
+}
+
+void sha_hex_encode(unsigned char *sha, unsigned int sha_size) {
+  int i = sha_size;
+  while(i--) {
+    char hex[3];
+    sprintf(hex, "%02x", sha[i]);
+    memcpy(sha+2*i,hex,2);
+  }
+}
+
+
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5c
+#define HMAC_BLOCKSIZE 64
+#include <apr_sha1.h>
+
+static void mxor(void *dst, const void *src, size_t len) {
+    char const *s = src;
+    char *d = dst;
+    for (; len > 0; len--)
+        *d++ ^= *s++;
+    return;
+}
+
+void hmac_sha1(const char *message, unsigned int message_len,
+          const unsigned char *key, unsigned int key_size,
+          void *mac) {
+
+  apr_sha1_ctx_t inner;
+  apr_sha1_ctx_t outer;
+  unsigned char keypad[HMAC_BLOCKSIZE];
+  unsigned char inner_digest[APR_SHA1_DIGESTSIZE];
+  unsigned char long_digest[APR_SHA1_DIGESTSIZE];
+
+  /* Shorten the key down to the blocksize, anything more is useless */
+  if (key_size > HMAC_BLOCKSIZE) {
+    apr_sha1_ctx_t context;
+    apr_sha1_init(&context);
+    apr_sha1_update_binary(&context, key, key_size);
+    apr_sha1_final(long_digest, &context);
+    key = long_digest;
+    key_size = APR_SHA1_DIGESTSIZE;
+  }
+
+  /* Prepare and mask the inner portion of the key */
+  memset(keypad, HMAC_IPAD, HMAC_BLOCKSIZE);
+  mxor(keypad, key, key_size);
+
+  /* Compute the inner hash */
+  apr_sha1_init(&inner);
+  apr_sha1_update_binary(&inner, keypad, HMAC_BLOCKSIZE);
+  apr_sha1_update(&inner, message, message_len);
+  apr_sha1_final(inner_digest, &inner);
+
+  /* Prepare and mask the outer portion of the key */
+  memset(keypad, HMAC_OPAD, HMAC_BLOCKSIZE);
+  mxor(keypad, key, key_size);
+
+  /* Compute the outer hash */
+  apr_sha1_init(&outer);
+  apr_sha1_update_binary(&outer, keypad, HMAC_BLOCKSIZE);
+  apr_sha1_update_binary(&outer, inner_digest, APR_SHA1_DIGESTSIZE);
+  apr_sha1_final(mac, &outer);
+}
+
diff --git a/lib/http.c b/lib/http.c
index 407eef4..dd24965 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -73,6 +73,32 @@ size_t _mapcache_curl_header_callback( void *ptr, size_t size, size_t nmemb,  vo
   return size*nmemb;
 }
 
+/*update val replacing {foo_header} with the Value of foo_header from the orginal request */
+static void _header_replace_str(mapcache_context *ctx, apr_table_t *headers, char **val) {
+  char *value = *val;
+  char *start_tag, *end_tag;
+  size_t start_tag_offset;
+  start_tag = strchr(value,'{');
+  while(start_tag) {
+    start_tag_offset = start_tag - value; /*record where we found the '{' so we can look for the next one after that spot
+                                            (avoids infinite loop if tag was not found/replaced) */
+    *start_tag=0;
+    end_tag = strchr(start_tag+1,'}');
+    if(end_tag) {
+      const char *header_value;
+      *end_tag=0;
+      header_value = apr_table_get(headers,start_tag+1);
+      if(header_value) {
+        value = apr_pstrcat(ctx->pool,value,header_value,end_tag+1,NULL);
+      }
+      *end_tag='}';
+    }
+    *start_tag='{';
+    start_tag = strchr(value+start_tag_offset+1,'{');
+  }
+  *val = value;
+}
+
 void mapcache_http_do_request(mapcache_context *ctx, mapcache_http *req, mapcache_buffer *data, apr_table_t *headers, long *http_code)
 {
   CURL *curl_handle;
@@ -115,13 +141,21 @@ void mapcache_http_do_request(mapcache_context *ctx, mapcache_http *req, mapcach
     apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
     int i;
     for (i = 0; i < array->nelts; i++) {
-      curl_headers = curl_slist_append(curl_headers, apr_pstrcat(ctx->pool,elts[i].key,": ",elts[i].val,NULL));
+      char *val = elts[i].val;
+      if(strchr(val,'{') && ctx->headers_in) {
+        _header_replace_str(ctx,ctx->headers_in,&val);
+      }
+      curl_headers = curl_slist_append(curl_headers, apr_pstrcat(ctx->pool,elts[i].key,": ",val,NULL));
     }
   }
   if(!req->headers || !apr_table_get(req->headers,"User-Agent")) {
     curl_headers = curl_slist_append(curl_headers, "User-Agent: "MAPCACHE_USERAGENT);
   }
   curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, curl_headers);
+
+  if(req->post_body && req->post_len>0) {
+    curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, req->post_body);
+  }
   /* get it! */
   ret = curl_easy_perform(curl_handle);
   if(http_code)
@@ -144,16 +178,31 @@ void mapcache_http_do_request_with_params(mapcache_context *ctx, mapcache_http *
   mapcache_http_do_request(ctx,request,data,headers, http_code);
 }
 
-/* calculate the length of the string formed by key=value&, and add it to cnt */
-#ifdef _WIN32
-static int _mapcache_key_value_strlen_callback(void *cnt, const char *key, const char *value)
-{
-#else
-static APR_DECLARE_NONSTD(int) _mapcache_key_value_strlen_callback(void *cnt, const char *key, const char *value)
-{
-#endif
-  *((int*)cnt) += strlen(key) + 2 + ((value && *value) ? strlen(value) : 0);
-  return 1;
+typedef struct header_cb_struct{
+  apr_pool_t *pool;
+  char *str;
+} header_cb_struct;
+
+/* Converts an integer value to its hex character*/
+char to_hex(char code) {
+  static char hex[] = "0123456789abcdef";
+  return hex[code & 15];
+}
+
+/* Returns a url-encoded version of str */
+char *url_encode(apr_pool_t *p, const char *str) {
+  char *buf = apr_pcalloc(p, strlen(str) * 3 + 1), *pbuf = buf;
+  while (*str) {
+    if (isalnum(*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~') 
+      *pbuf++ = *str;
+    else if (*str == ' ') 
+      *pbuf++ = '+';
+    else 
+      *pbuf++ = '%', *pbuf++ = to_hex(*str >> 4), *pbuf++ = to_hex(*str & 15);
+    str++;
+  }
+  *pbuf = '\0';
+  return buf;
 }
 
 #ifdef _WIN32
@@ -163,13 +212,15 @@ static int _mapcache_key_value_append_callback(void *cnt, const char *key, const
 static APR_DECLARE_NONSTD(int) _mapcache_key_value_append_callback(void *cnt, const char *key, const char *value)
 {
 #endif
-#define _mystr *((char**)cnt)
-  _mystr = apr_cpystrn(_mystr,key,MAX_STRING_LEN);
-  *((_mystr)++) = '=';
+#define _mystr (((header_cb_struct*)cnt)->str)
+  header_cb_struct *hcs = (header_cb_struct*)cnt;
+  hcs->str = apr_pstrcat(hcs->pool, hcs->str, key, "=", NULL);
   if(value && *value) {
-    _mystr = apr_cpystrn(_mystr,value,MAX_STRING_LEN);
+    hcs->str = apr_pstrcat(hcs->pool, hcs->str, url_encode(hcs->pool, value), "&", NULL);
+  }
+  else {
+    hcs->str = apr_pstrcat(hcs->pool, hcs->str, "&", NULL);
   }
-  *((_mystr)++) = '&';
   return 1;
 #undef _mystr
 }
@@ -230,38 +281,29 @@ int _mapcache_unescape_url(char *url)
 
 
 
-char* mapcache_http_build_url(mapcache_context *r, char *base, apr_table_t *params)
+char* mapcache_http_build_url(mapcache_context *ctx, char *base, apr_table_t *params)
 {
   if(!apr_is_empty_table(params)) {
-    int stringLength = 0, baseLength;
-    char *builtUrl,*builtUrlPtr;
-    char charToAppend=0;
+    int baseLength;
+    header_cb_struct hcs;
     baseLength = strlen(base);
-
-    /*calculate the length of the param string we are going to build */
-    apr_table_do(_mapcache_key_value_strlen_callback, (void*)&stringLength, params, NULL);
+    hcs.pool = ctx->pool;
+    hcs.str = base;
 
     if(strchr(base,'?')) {
       /* base already contains a '?' , shall we be adding a '&' to the end */
       if(base[baseLength-1] != '?' && base[baseLength-1] != '&') {
-        charToAppend = '&';
+        hcs.str = apr_pstrcat(ctx->pool, hcs.str, "&", NULL);
       }
     } else {
       /* base does not contain a '?', we will be adding it */
-      charToAppend='?';
+      hcs.str = apr_pstrcat(ctx->pool, hcs.str, "?", NULL);
     }
 
-    /* add final \0 and eventual separator to add ('?' or '&') */
-    stringLength += baseLength + ((charToAppend)?2:1);
-
-    builtUrl = builtUrlPtr = apr_palloc(r->pool, stringLength);
-
-    builtUrlPtr = apr_cpystrn(builtUrlPtr,base,MAX_STRING_LEN);
-    if(charToAppend)
-      *(builtUrlPtr++)=charToAppend;
-    apr_table_do(_mapcache_key_value_append_callback, (void*)&builtUrlPtr, params, NULL);
-    *(builtUrlPtr-1) = '\0'; /*replace final '&' by a \0 */
-    return builtUrl;
+    apr_table_do(_mapcache_key_value_append_callback, (void*)&hcs, params, NULL);
+    baseLength = strlen(hcs.str);
+    hcs.str[baseLength-1] = '\0';
+    return hcs.str;
   } else {
     return base;
   }
diff --git a/lib/image.c b/lib/image.c
index 00395ed..4d228b5 100644
--- a/lib/image.c
+++ b/lib/image.c
@@ -359,5 +359,24 @@ int mapcache_image_blank_color(mapcache_image* image)
     return MAPCACHE_FALSE;
 }
 
+
+void mapcache_image_fill(mapcache_context *ctx, mapcache_image *image, const unsigned char *fill_color) {
+#if 0 && defined(USE_PIXMAN)
+  pixman_fill((uint32_t*)image->data, image->stride, 32, 0, 0, image->w, image->h, *((int*)fill_color) );
+#else
+  int r,c;
+  unsigned char *pixptr;
+  for(r=0;r<image->h;r++) {
+    pixptr = image->data + image->stride * r;
+    for(c=0;c<image->w;c++) {
+      pixptr[0]=fill_color[0];
+      pixptr[1]=fill_color[1];
+      pixptr[2]=fill_color[2];
+      pixptr[3]=fill_color[3];
+      pixptr+=4;
+    }
+  }
+#endif
+}
 /* vim: ts=2 sts=2 et sw=2
 */
diff --git a/lib/imageio_png.c b/lib/imageio_png.c
index d4cdadd..2b5de6d 100644
--- a/lib/imageio_png.c
+++ b/lib/imageio_png.c
@@ -45,6 +45,9 @@ typedef unsigned long int uint64_t;
 #ifndef Z_BEST_COMPRESSION
 #define Z_BEST_COMPRESSION 9
 #endif
+#ifndef Z_NO_COMPRESSION
+#define Z_NO_COMPRESSION 0
+#endif
 
 /* Table of CRCs of all 8-bit messages. */
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@@ -146,48 +149,152 @@ static unsigned long crc(unsigned char *buf, int len)
   return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
 }
 
-static unsigned char empty_png[] = {
+static unsigned char empty_png_256[] = {
   0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52
   ,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x03,0x00,0x00,0x00,0x66,0xbc,0x3a
-  ,0x25,0x00,0x00,0x00,0x03,0x50,0x4c,0x54,0x45,0x73,0x91,0xad,0x31,0xf0,0x8f,0xdd
-  ,0x00,0x00,0x00,0x01,0x74,0x52,0x4e,0x53,0xff,0x6d,0xe4,0x37,0xeb,0x00,0x00,0x00
+  ,0x25,0x00,0x00,0x00,0x03,
+  /*PLTE*/0x50,0x4c,0x54,0x45,
+  /*RGB*/0x73,0x91,0xad,
+  /*PLTE-CRC*/0x31,0xf0,0x8f,0xdd
+  ,0x00,0x00,0x00,0x01,
+  /*tRNS*/0x74,0x52,0x4e,0x53,
+  /*A (fully opaque by default)*/0xff,
+  /*tRNS-CRC*/0x6d,0xe4,0x37,0xeb,
+  0x00,0x00,0x00
   ,0x1f,0x49,0x44,0x41,0x54,0x68,0xde,0xed,0xc1,0x01,0x0d,0x00,0x00,0x00,0xc2,0xa0
   ,0xf7,0x4f,0x6d,0x0e,0x37,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xbe,0x0d
   ,0x21,0x00,0x00,0x01,0x7f,0x19,0x9c,0xa7,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44
   ,0xae,0x42,0x60,0x82
 };
+static size_t plte_offset_256= 0x25;
+static size_t trns_offset_256 = 0x34;
+
+
+unsigned char empty_png_384[] = {
+  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+  0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80,
+  0x01, 0x03, 0x00, 0x00, 0x00, 0x9e, 0x09, 0x27, 0xfc, 0x00, 0x00, 0x00,
+  0x03, 
+  /*PLTE*/0x50,0x4c,0x54,0x45,
+  /*RGB*/0xcd,0x44,0x44,
+  /*PLTE-CRC*/0xdb,0xa9,0x37,0x41
+  ,0x00,0x00,0x00,0x01,
+  /*tRNS*/0x74,0x52,0x4e,0x53,
+  /*A (fully opaque by default)*/0xff,
+  /*tRNS-CRC*/0x6d,0xe4,0x37,0xeb,
+  0x00,0x00,0x00,
+  0x28, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0xed,
+  0xc1, 0x01, 0x01, 0x00, 0x00, 0x00, 0x82, 0x20, 0xff, 0xaf, 0x6e, 0x48,
+  0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x19, 0x49, 0x80, 0x00,
+  0x01, 0x28, 0x77, 0xfb, 0x61, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
+  0x44, 0xae, 0x42, 0x60, 0x82
+};
+static size_t plte_offset_384= 0x25;
+static size_t trns_offset_384 = 0x34;
+
+
+static unsigned char empty_png_512[] = {
+  0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52
+  ,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x01,0x03,0x00,0x00,0x00,0xce,0xb6,0x46
+  ,0xb9,0x00,0x00,0x00,0x03,
+  /*PLTE*/0x50,0x4c,0x54,0x45,
+  /*RGB*/0xcd,0x44,0x44,
+  /*PLTE-CRC*/0xdb,0xa9,0x37,0x41
+  ,0x00,0x00,0x00,0x01,
+  /*tRNS*/0x74,0x52,0x4e,0x53,
+  /*A (fully opaque by default)*/0xff,
+  /*tRNS-CRC*/0x6d,0xe4,0x37,0xeb,
+  0x00,0x00,0x00
+  ,0x36,0x49,0x44,0x41,0x54,0x78,0x5e,0xed,0xc1,0x01,0x01,0x00,0x00,0x00,0x82,0x20
+  ,0xff,0xaf,0x6e,0x48,0x40,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0x00,0x00,0x7c,0x1b,0x82,0x00,0x00,0x01,0x9a,0x38,0x6a,0xc7,0x00
+  ,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
+};
+static size_t plte_offset_512= 0x25;
+static size_t trns_offset_512 = 0x34;
 
-static size_t plte_offset = 0x25;
-static size_t trns_offset = 0x34;
 
-mapcache_buffer* mapcache_empty_png_decode(mapcache_context *ctx, const unsigned char *hex_color, int *is_empty) {
+mapcache_buffer* mapcache_empty_png_decode(mapcache_context *ctx, int width, int height, const unsigned char *hex_color, int *is_empty) {
   int chunkcrc;
   unsigned char *dd;
-  mapcache_buffer *encoded_data = mapcache_buffer_create(sizeof(empty_png)+4,ctx->pool);
-  dd = encoded_data->buf;
-  memcpy(dd,empty_png,sizeof(empty_png));
-  dd[plte_offset+4] = hex_color[3]; //r
-  dd[plte_offset+5] = hex_color[2]; //g
-  dd[plte_offset+6] = hex_color[1]; //b
-  chunkcrc = crc(dd+plte_offset,7);
-  dd[plte_offset+7] = (unsigned char)((chunkcrc >> 24) & 0xff);
-  dd[plte_offset+8] = (unsigned char)((chunkcrc >> 16) & 0xff);
-  dd[plte_offset+9] = (unsigned char)((chunkcrc >> 8) & 0xff);
-  dd[plte_offset+10] = (unsigned char)(chunkcrc & 0xff);
-  if(hex_color[4] != 255) {
-    dd[trns_offset+4] = hex_color[4];
-    chunkcrc = crc(dd+trns_offset,5);
-    dd[trns_offset+5] = (unsigned char)((chunkcrc >> 24) & 0xff);
-    dd[trns_offset+6] = (unsigned char)((chunkcrc >> 16) & 0xff);
-    dd[trns_offset+7] = (unsigned char)((chunkcrc >> 8) & 0xff);
-    dd[trns_offset+8] = (unsigned char)(chunkcrc & 0xff);
+  mapcache_buffer *encoded_data;
+  if(width == 256 && height == 256) {
+    encoded_data = mapcache_buffer_create(sizeof(empty_png_256)+4,ctx->pool);
+    dd = encoded_data->buf;
+    memcpy(dd,empty_png_256,sizeof(empty_png_256));
+    dd[plte_offset_256+4] = hex_color[3]; //r
+    dd[plte_offset_256+5] = hex_color[2]; //g
+    dd[plte_offset_256+6] = hex_color[1]; //b
+    chunkcrc = crc(dd+plte_offset_256,7);
+    dd[plte_offset_256+7] = (unsigned char)((chunkcrc >> 24) & 0xff);
+    dd[plte_offset_256+8] = (unsigned char)((chunkcrc >> 16) & 0xff);
+    dd[plte_offset_256+9] = (unsigned char)((chunkcrc >> 8) & 0xff);
+    dd[plte_offset_256+10] = (unsigned char)(chunkcrc & 0xff);
+    if(hex_color[4] != 255) {
+      dd[trns_offset_256+4] = hex_color[4];
+      chunkcrc = crc(dd+trns_offset_256,5);
+      dd[trns_offset_256+5] = (unsigned char)((chunkcrc >> 24) & 0xff);
+      dd[trns_offset_256+6] = (unsigned char)((chunkcrc >> 16) & 0xff);
+      dd[trns_offset_256+7] = (unsigned char)((chunkcrc >> 8) & 0xff);
+      dd[trns_offset_256+8] = (unsigned char)(chunkcrc & 0xff);
+    }
+    encoded_data->size = sizeof(empty_png_256);
+  } else if(width == 384 && height == 384) {
+    encoded_data = mapcache_buffer_create(sizeof(empty_png_384)+4,ctx->pool);
+    dd = encoded_data->buf;
+    memcpy(dd,empty_png_384,sizeof(empty_png_384));
+    dd[plte_offset_384+4] = hex_color[3]; //r
+    dd[plte_offset_384+5] = hex_color[2]; //g
+    dd[plte_offset_384+6] = hex_color[1]; //b
+    chunkcrc = crc(dd+plte_offset_384,7);
+    dd[plte_offset_384+7] = (unsigned char)((chunkcrc >> 24) & 0xff);
+    dd[plte_offset_384+8] = (unsigned char)((chunkcrc >> 16) & 0xff);
+    dd[plte_offset_384+9] = (unsigned char)((chunkcrc >> 8) & 0xff);
+    dd[plte_offset_384+10] = (unsigned char)(chunkcrc & 0xff);
+    if(hex_color[4] != 255) {
+      dd[trns_offset_384+4] = hex_color[4];
+      chunkcrc = crc(dd+trns_offset_384,5);
+      dd[trns_offset_384+5] = (unsigned char)((chunkcrc >> 24) & 0xff);
+      dd[trns_offset_384+6] = (unsigned char)((chunkcrc >> 16) & 0xff);
+      dd[trns_offset_384+7] = (unsigned char)((chunkcrc >> 8) & 0xff);
+      dd[trns_offset_384+8] = (unsigned char)(chunkcrc & 0xff);
+    }
+    encoded_data->size = sizeof(empty_png_384);
+  } else if(width == 512 && height == 512) {
+    encoded_data = mapcache_buffer_create(sizeof(empty_png_512)+4,ctx->pool);
+    dd = encoded_data->buf;
+    memcpy(dd,empty_png_512,sizeof(empty_png_512));
+    dd[plte_offset_512+4] = hex_color[3]; //r
+    dd[plte_offset_512+5] = hex_color[2]; //g
+    dd[plte_offset_512+6] = hex_color[1]; //b
+    chunkcrc = crc(dd+plte_offset_512,7);
+    dd[plte_offset_512+7] = (unsigned char)((chunkcrc >> 24) & 0xff);
+    dd[plte_offset_512+8] = (unsigned char)((chunkcrc >> 16) & 0xff);
+    dd[plte_offset_512+9] = (unsigned char)((chunkcrc >> 8) & 0xff);
+    dd[plte_offset_512+10] = (unsigned char)(chunkcrc & 0xff);
+    if(hex_color[4] != 255) {
+      dd[trns_offset_512+4] = hex_color[4];
+      chunkcrc = crc(dd+trns_offset_512,5);
+      dd[trns_offset_512+5] = (unsigned char)((chunkcrc >> 24) & 0xff);
+      dd[trns_offset_512+6] = (unsigned char)((chunkcrc >> 16) & 0xff);
+      dd[trns_offset_512+7] = (unsigned char)((chunkcrc >> 8) & 0xff);
+      dd[trns_offset_512+8] = (unsigned char)(chunkcrc & 0xff);
+    }
+    encoded_data->size = sizeof(empty_png_512);
+  } else {
+    /* here for compatibility reasons, although this should not be used in production as it is cpu-heavy */
+    mapcache_image *rgba = mapcache_image_create_with_data(ctx,width,height);
+    mapcache_image_format *format = mapcache_configuration_get_image_format(ctx->config,"PNG8");
+    mapcache_image_fill(ctx,rgba,hex_color+1);
+    encoded_data = format->write(ctx,rgba,format);
   }
   if(hex_color[4] == 0) {
     *is_empty = 1;
   } else {
     *is_empty = 0;
   }
-  encoded_data->size = sizeof(empty_png);
   return encoded_data;
 }
 
@@ -410,6 +517,9 @@ mapcache_buffer* _mapcache_imageio_png_encode(mapcache_context *ctx, mapcache_im
     png_set_compression_level (png_ptr, Z_BEST_COMPRESSION);
   else if(compression == MAPCACHE_COMPRESSION_FAST)
     png_set_compression_level (png_ptr, Z_BEST_SPEED);
+  else if(compression == MAPCACHE_COMPRESSION_DISABLE)
+    png_set_compression_level (png_ptr, Z_NO_COMPRESSION);
+    
   png_set_filter(png_ptr,0,PNG_FILTER_NONE);
 
   info_ptr = png_create_info_struct(png_ptr);
@@ -1314,6 +1424,8 @@ mapcache_buffer* _mapcache_imageio_png_q_encode( mapcache_context *ctx, mapcache
     png_set_compression_level (png_ptr, Z_BEST_COMPRESSION);
   else if(compression == MAPCACHE_COMPRESSION_FAST)
     png_set_compression_level (png_ptr, Z_BEST_SPEED);
+  else if(compression == MAPCACHE_COMPRESSION_DISABLE)
+    png_set_compression_level (png_ptr, Z_NO_COMPRESSION);
   png_set_filter(png_ptr,0,PNG_FILTER_NONE);
   info_ptr = png_create_info_struct(png_ptr);
   if (!info_ptr) {
@@ -1413,7 +1525,7 @@ mapcache_image_format* mapcache_imageio_create_png_q_format(apr_pool_t *pool, ch
   mapcache_image_format_png_q *format = apr_pcalloc(pool, sizeof(mapcache_image_format_png_q));
   format->format.format.name = name;
   format->format.format.extension = apr_pstrdup(pool,"png");
-  format->format.format.mime_type = apr_pstrdup(pool,"image/png");
+  format->format.format.mime_type = apr_pstrdup(pool,"image/png; mode=8bit");
   format->format.compression_level = compression;
   format->format.format.write = _mapcache_imageio_png_q_encode;
   format->format.format.create_empty_image = _mapcache_imageio_png_create_empty;
diff --git a/lib/lock.c b/lib/lock.c
index 7b87f92..2cef016 100644
--- a/lib/lock.c
+++ b/lib/lock.c
@@ -31,8 +31,11 @@
 #include <apr_file_io.h>
 #include <apr_strings.h>
 #include <apr_time.h>
+#include <unistd.h>
 
-char* lock_filename_for_resource(mapcache_context *ctx, const char *resource)
+#define MAPCACHE_LOCKFILE_PREFIX "_gc_lock"
+
+char* lock_filename_for_resource(mapcache_context *ctx, mapcache_locker_disk *ldisk, const char *resource)
 {
   char *saferes = apr_pstrdup(ctx->pool,resource);
   char *safeptr = saferes;
@@ -43,45 +46,368 @@ char* lock_filename_for_resource(mapcache_context *ctx, const char *resource)
     safeptr++;
   }
   return apr_psprintf(ctx->pool,"%s/"MAPCACHE_LOCKFILE_PREFIX"%s.lck",
-                      ctx->config->lockdir,saferes);
+                      ldisk->dir,saferes);
 }
 
-int mapcache_lock_or_wait_for_resource(mapcache_context *ctx, char *resource)
+int mapcache_lock_or_wait_for_resource(mapcache_context *ctx, mapcache_locker *locker, char *resource, void **lock)
 {
-  char *lockname = lock_filename_for_resource(ctx,resource);
+  mapcache_lock_result rv = locker->aquire_lock(ctx, locker, resource, lock);
+  if(GC_HAS_ERROR(ctx)) {
+    return MAPCACHE_FAILURE;
+  }
+  if(rv == MAPCACHE_LOCK_AQUIRED)
+    return MAPCACHE_TRUE;
+  else {
+    apr_time_t start_wait = apr_time_now();
+    rv = MAPCACHE_LOCK_LOCKED;
+    
+    while(rv != MAPCACHE_LOCK_NOENT) {
+      unsigned int waited = apr_time_as_msec(apr_time_now()-start_wait);
+      if(waited > locker->timeout*1000) {
+        mapcache_unlock_resource(ctx,locker,resource, *lock);
+        ctx->log(ctx,MAPCACHE_ERROR,"deleting a possibly stale lock after waiting on it for %g seconds",waited/1000.0);
+        return MAPCACHE_FALSE;
+      }
+      apr_sleep(locker->retry_interval * 1000000);
+      rv = locker->ping_lock(ctx,locker,resource, *lock);
+    }
+    return MAPCACHE_FALSE;
+  }
+}
+
+
+mapcache_lock_result mapcache_locker_disk_aquire_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void **lock) {
+  char *lockname, errmsg[120];
+  mapcache_locker_disk *ldisk;
   apr_file_t *lockfile;
   apr_status_t rv;
+  
+  assert(self->type == MAPCACHE_LOCKER_DISK);
+  ldisk = (mapcache_locker_disk*)self;
+  *lock = NULL; /*unused*/
+
+  lockname = lock_filename_for_resource(ctx,ldisk,resource);
   /* create the lockfile */
   rv = apr_file_open(&lockfile,lockname,APR_WRITE|APR_CREATE|APR_EXCL|APR_XTHREAD,APR_OS_DEFAULT,ctx->pool);
 
   /* if the file already exists, wait for it to disappear */
   /* TODO: check the lock isn't stale (i.e. too old) */
   if( rv != APR_SUCCESS ) {
-    apr_finfo_t info;
-    rv = apr_stat(&info,lockname,0,ctx->pool);
-#ifdef DEBUG
-    if(!APR_STATUS_IS_ENOENT(rv)) {
-      ctx->log(ctx, MAPCACHE_DEBUG, "waiting on resource lock %s", resource);
+    if( !APR_STATUS_IS_EEXIST(rv) ) {
+      ctx->set_error(ctx, 500, "failed to create lockfile %s: %s", lockname, apr_strerror(rv,errmsg,120));
+      return MAPCACHE_LOCK_NOENT;
     }
-#endif
-    while(!APR_STATUS_IS_ENOENT(rv)) {
-      /* sleep for the configured number of micro-seconds (default is 1/100th of a second) */
-      apr_sleep(ctx->config->lock_retry_interval);
-      rv = apr_stat(&info,lockname,0,ctx->pool);
-    }
-    return MAPCACHE_FALSE;
+    return MAPCACHE_LOCK_LOCKED;
   } else {
     /* we acquired the lock */
+    char *pid_s;
+    pid_t pid;
+    apr_size_t pid_s_len;
+    pid = getpid();
+    pid_s = apr_psprintf(ctx->pool,"%"APR_PID_T_FMT,pid);
+    pid_s_len = strlen(pid_s);
+    apr_file_write(lockfile,pid_s,&pid_s_len);
     apr_file_close(lockfile);
-    return MAPCACHE_TRUE;
+    return MAPCACHE_LOCK_AQUIRED;
+  }
+}
+
+mapcache_lock_result mapcache_locker_disk_ping_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+  apr_finfo_t info;
+  apr_status_t rv;
+  char *lockname;
+  mapcache_locker_disk *ldisk = (mapcache_locker_disk*)self;
+  lockname = lock_filename_for_resource(ctx,ldisk,resource);
+  rv = apr_stat(&info,lockname,0,ctx->pool);
+  if(APR_STATUS_IS_ENOENT(rv)) {
+    return MAPCACHE_LOCK_NOENT;
+  } else {
+    return MAPCACHE_LOCK_LOCKED;
   }
 }
 
-void mapcache_unlock_resource(mapcache_context *ctx, char *resource)
+void mapcache_locker_disk_release_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock)
 {
-  char *lockname = lock_filename_for_resource(ctx,resource);
+  mapcache_locker_disk *ld = (mapcache_locker_disk*)self;
+  char *lockname = lock_filename_for_resource(ctx,ld,resource);
   apr_file_remove(lockname,ctx->pool);
 }
 
+void mapcache_unlock_resource(mapcache_context *ctx, mapcache_locker *locker, char *resource, void *lock) {
+  locker->release_lock(ctx, locker, resource, lock);
+}
+
+void mapcache_locker_disk_parse_xml(mapcache_context *ctx, mapcache_locker *self, ezxml_t doc) {
+  mapcache_locker_disk *ldisk = (mapcache_locker_disk*)self;
+  ezxml_t node;
+  if((node = ezxml_child(doc,"directory")) != NULL) {
+    ldisk->dir = apr_pstrdup(ctx->pool, node->txt);
+  } else {
+    ldisk->dir = apr_pstrdup(ctx->pool,"/tmp");
+  }
+}
+
+mapcache_locker* mapcache_locker_disk_create(mapcache_context *ctx) {
+  mapcache_locker_disk *ld = (mapcache_locker_disk*)apr_pcalloc(ctx->pool, sizeof(mapcache_locker_disk));
+  mapcache_locker *l = (mapcache_locker*)ld;
+  l->type = MAPCACHE_LOCKER_DISK;
+  l->aquire_lock = mapcache_locker_disk_aquire_lock;
+  l->parse_xml = mapcache_locker_disk_parse_xml;
+  l->release_lock = mapcache_locker_disk_release_lock;
+  l->ping_lock = mapcache_locker_disk_ping_lock;
+  return l;
+}
+
+struct mapcache_locker_fallback_lock {
+  mapcache_locker *locker; /*the locker that actually acquired the lock*/
+  void *lock; /*the opaque lock returned by the locker*/
+};
+
+void mapcache_locker_fallback_release_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+  struct mapcache_locker_fallback_lock *flock = lock;
+  flock->locker->release_lock(ctx,flock->locker,resource,flock->lock);
+}
+
+mapcache_lock_result mapcache_locker_fallback_ping_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+  struct mapcache_locker_fallback_lock *flock = lock;
+  return flock->locker->ping_lock(ctx,flock->locker,resource,flock->lock);
+}
+
+mapcache_lock_result mapcache_locker_fallback_aquire_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void **lock) {
+  int i;
+  mapcache_locker_fallback *locker = (mapcache_locker_fallback*)self;
+  struct mapcache_locker_fallback_lock *fallback_lock = apr_pcalloc(ctx->pool, sizeof(struct mapcache_locker_fallback_lock));
+  *lock = fallback_lock;
+  for(i=0;i<locker->lockers->nelts;i++) {
+    mapcache_lock_result lock_result;
+    mapcache_locker *child_locker = APR_ARRAY_IDX(locker->lockers, i, mapcache_locker*);
+    void *error;
+    ctx->pop_errors(ctx,&error);
+    lock_result = child_locker->aquire_lock(ctx, child_locker, resource, &(fallback_lock->lock));
+    if(!GC_HAS_ERROR(ctx)) {
+      fallback_lock->locker = child_locker;
+      ctx->push_errors(ctx,error);
+      return lock_result;
+    }
+    ctx->push_errors(ctx,error);
+  }
+  return MAPCACHE_LOCK_NOENT;
+}
+
+
+void mapcache_locker_fallback_parse_xml(mapcache_context *ctx, mapcache_locker *self, ezxml_t doc) {
+  mapcache_locker_fallback *lm = (mapcache_locker_fallback*)self;
+  ezxml_t node;
+  lm->lockers = apr_array_make(ctx->pool,2,sizeof(mapcache_locker*));
+  for(node = ezxml_child(doc,"locker"); node; node = node->next) {
+    mapcache_locker *child_locker;
+    mapcache_config_parse_locker(ctx,node,&child_locker);
+    GC_CHECK_ERROR(ctx);
+    APR_ARRAY_PUSH(lm->lockers,mapcache_locker*) = child_locker;
+  }
+}
+
+#ifdef USE_MEMCACHE
+void mapcache_locker_memcache_parse_xml(mapcache_context *ctx, mapcache_locker *self, ezxml_t doc) {
+  mapcache_locker_memcache *lm = (mapcache_locker_memcache*)self;
+  ezxml_t node,server_node;
+  char *endptr;
+  for(server_node = ezxml_child(doc,"server"); server_node; server_node = server_node->next) {
+    lm->nservers++;
+  }
+  lm->servers = apr_pcalloc(ctx->pool, lm->nservers * sizeof(mapcache_locker_memcache_server));
+  lm->nservers = 0;
+  for(server_node = ezxml_child(doc,"server"); server_node; server_node = server_node->next) {
+    if((node = ezxml_child(server_node,"host")) != NULL) {
+      lm->servers[lm->nservers].host = apr_pstrdup(ctx->pool, node->txt);
+    } else {
+      ctx->set_error(ctx, 400, "memcache locker: no <host> provided");
+      return;
+    }
+
+    if((node = ezxml_child(server_node,"port")) != NULL) {
+      lm->servers[lm->nservers].port = (unsigned int)strtol(node->txt,&endptr,10);
+      if(*endptr != 0 || lm->servers[lm->nservers].port <= 0) {
+        ctx->set_error(ctx, 400, "failed to parse memcache locker port \"%s\". Expecting a positive integer",
+            node->txt);
+        return;
+      }
+    } else {
+      /* default memcached port */
+      lm->servers[lm->nservers].port = 11211;
+    }
+    lm->nservers++;
+  }
+}
+
+static char* memcache_key_for_resource(mapcache_context *ctx, mapcache_locker_memcache *lm, const char *resource)
+{
+  char *saferes = apr_pstrdup(ctx->pool,resource);
+  char *safeptr = saferes;
+  while(*safeptr) {
+    if(*safeptr==' ' || *safeptr == '/' || *safeptr == '~' || *safeptr == '.' || 
+        *safeptr == '\r' || *safeptr == '\n' || *safeptr == '\t' || *safeptr == '\f' || *safeptr == '\e' || *safeptr == '\a' || *safeptr == '\b') {
+      *safeptr = '#';
+    }
+    safeptr++;
+  }
+  return apr_psprintf(ctx->pool,MAPCACHE_LOCKFILE_PREFIX"%s.lck",saferes);
+}
+
+apr_memcache_t* create_memcache(mapcache_context *ctx, mapcache_locker_memcache *lm) {
+  apr_status_t rv;
+  apr_memcache_t *memcache;
+  char errmsg[120];
+  int i;
+  if(APR_SUCCESS != apr_memcache_create(ctx->pool, lm->nservers, 0, &memcache)) {
+    ctx->set_error(ctx,500,"memcache locker: failed to create memcache backend");
+    return NULL;
+  }
+
+  for(i=0;i<lm->nservers;i++) {
+    apr_memcache_server_t *server;
+    rv = apr_memcache_server_create(ctx->pool,lm->servers[i].host,lm->servers[i].port,1,1,1,10000,&server);
+    if(APR_SUCCESS != rv) {
+      ctx->set_error(ctx,500,"memcache locker: failed to create server %s:%d: %s",lm->servers[i].host,lm->servers[i].port, apr_strerror(rv,errmsg,120));
+      return NULL;
+    }
+
+    rv = apr_memcache_add_server(memcache,server);
+    if(APR_SUCCESS != rv) {
+      ctx->set_error(ctx,500,"memcache locker: failed to add server %s:%d: %s",lm->servers[i].host,lm->servers[i].port, apr_strerror(rv,errmsg,120));
+      return NULL;
+    }
+  }
+  return memcache;
+}
+
+mapcache_lock_result mapcache_locker_memcache_ping_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+  apr_status_t rv;
+  char *one;
+  size_t ione;
+  mapcache_locker_memcache *lm = (mapcache_locker_memcache*)self;
+  char *key = memcache_key_for_resource(ctx, lm, resource);
+  apr_memcache_t *memcache = (apr_memcache_t*)lock;  
+  if(!memcache)
+    return MAPCACHE_LOCK_NOENT;
+  rv = apr_memcache_getp(memcache,ctx->pool,key,&one,&ione,NULL);
+  if(rv == APR_SUCCESS)
+    return MAPCACHE_LOCK_LOCKED;
+  else
+    return MAPCACHE_LOCK_NOENT;
+}
+  
+
+mapcache_lock_result mapcache_locker_memcache_aquire_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void **lock) {
+  apr_status_t rv;
+  mapcache_locker_memcache *lm = (mapcache_locker_memcache*)self;
+  char errmsg[120];
+  char *key = memcache_key_for_resource(ctx, lm, resource);
+  apr_memcache_t *memcache = create_memcache(ctx,lm);  
+  if(GC_HAS_ERROR(ctx)) {
+    return MAPCACHE_LOCK_NOENT;
+  }
+  *lock = memcache;
+  rv = apr_memcache_add(memcache,key,"1",1,self->timeout,0);
+  if( rv == APR_SUCCESS) {
+    return MAPCACHE_LOCK_AQUIRED;
+  } else if ( rv == APR_EEXIST ) {
+    return MAPCACHE_LOCK_LOCKED;
+  } else {
+    ctx->set_error(ctx,500,"failed to lock resource %s to memcache locker: %s",resource, apr_strerror(rv,errmsg,120));
+    return MAPCACHE_LOCK_NOENT;
+  }
+}
+
+void mapcache_locker_memcache_release_lock(mapcache_context *ctx, mapcache_locker *self, char *resource, void *lock) {
+  apr_status_t rv;
+  mapcache_locker_memcache *lm = (mapcache_locker_memcache*)self;
+  char errmsg[120];
+  char *key = memcache_key_for_resource(ctx, lm, resource);
+  apr_memcache_t *memcache = (apr_memcache_t*)lock;  
+  if(!memcache) {
+    /*error*/
+    return;
+  }
+  
+  rv = apr_memcache_delete(memcache,key,0);
+  if(rv != APR_SUCCESS && rv!= APR_NOTFOUND) {
+    ctx->set_error(ctx,500,"memcache: failed to delete key %s: %s", key, apr_strerror(rv,errmsg,120));
+  }
+
+}
+
+mapcache_locker* mapcache_locker_memcache_create(mapcache_context *ctx) {
+  mapcache_locker_memcache *lm = (mapcache_locker_memcache*)apr_pcalloc(ctx->pool, sizeof(mapcache_locker_memcache));
+  mapcache_locker *l = (mapcache_locker*)lm;
+  l->type = MAPCACHE_LOCKER_MEMCACHE;
+  l->aquire_lock = mapcache_locker_memcache_aquire_lock;
+  l->ping_lock = mapcache_locker_memcache_ping_lock;
+  l->parse_xml = mapcache_locker_memcache_parse_xml;
+  l->release_lock = mapcache_locker_memcache_release_lock;
+  lm->nservers = 0;
+  lm->servers = NULL;
+  return l;
+}
+
+#endif
+
+mapcache_locker* mapcache_locker_fallback_create(mapcache_context *ctx) {
+  mapcache_locker_fallback *lm = (mapcache_locker_fallback*)apr_pcalloc(ctx->pool, sizeof(mapcache_locker_fallback));
+  mapcache_locker *l = (mapcache_locker*)lm;
+  l->type = MAPCACHE_LOCKER_FALLBACK;
+  l->aquire_lock = mapcache_locker_fallback_aquire_lock;
+  l->ping_lock = mapcache_locker_fallback_ping_lock;
+  l->parse_xml = mapcache_locker_fallback_parse_xml;
+  l->release_lock = mapcache_locker_fallback_release_lock;
+  return l;
+}
+
+void mapcache_config_parse_locker(mapcache_context *ctx, ezxml_t node, mapcache_locker **locker) {
+  ezxml_t cur_node;
+  const char *ltype = ezxml_attr(node, "type");
+  if(!ltype) ltype = "disk";
+  if(!strcmp(ltype,"disk")) {
+    *locker = mapcache_locker_disk_create(ctx);
+  } else if(!strcmp(ltype,"fallback")) {
+    *locker = mapcache_locker_fallback_create(ctx);
+  } else if(!strcmp(ltype,"memcache")) {
+#ifdef USE_MEMCACHE
+    *locker = mapcache_locker_memcache_create(ctx);
+#else
+    ctx->set_error(ctx,400,"<locker>: type \"memcache\" cannot be used as memcache support is not compiled in");
+    return;
+#endif
+  } else {
+    ctx->set_error(ctx,400,"<locker>: unknown type \"%s\" (allowed are disk and memcache)",ltype);
+    return;
+  }
+  (*locker)->parse_xml(ctx, *locker, node);
+  
+  if((cur_node = ezxml_child(node,"retry")) != NULL) {
+    char *endptr;
+    (*locker)->retry_interval = strtod(cur_node->txt,&endptr);
+    if(*endptr != 0 || (*locker)->retry_interval <= 0) {
+      ctx->set_error(ctx, 400, "failed to locker parse retry seconds \"%s\". Expecting a positive floating point number",
+              cur_node->txt);
+      return;
+    }
+  } else {
+    /* default retry interval is 1/10th of a second */
+    (*locker)->retry_interval = 0.1;
+  }
+  if((cur_node = ezxml_child(node,"timeout")) != NULL) {
+    char *endptr;
+    (*locker)->timeout = strtod(cur_node->txt,&endptr);
+    if(*endptr != 0 || (*locker)->timeout <= 0) {
+      ctx->set_error(ctx, 400, "failed to parse locker timeout seconds \"%s\". Expecting a positive floating point number",
+              cur_node->txt);
+      return;
+    }
+  } else {
+    /* default timeout is 2 minutes */
+    (*locker)->timeout = 120;
+  }
+}
 /* vim: ts=2 sts=2 et sw=2
 */
diff --git a/lib/service_mapguide.c b/lib/service_mapguide.c
index 040f58e..f1e72e8 100644
--- a/lib/service_mapguide.c
+++ b/lib/service_mapguide.c
@@ -124,7 +124,7 @@ void _mapcache_service_mg_parse_request(mapcache_context *ctx, mapcache_service
   if(index == 5) {
     char *gridname;
     mapcache_request_get_tile *req = (mapcache_request_get_tile*)apr_pcalloc(ctx->pool,sizeof(mapcache_request_get_tile));
-    req->request.type = MAPCACHE_REQUEST_GET_TILE;
+    ((mapcache_request*)req)->type = MAPCACHE_REQUEST_GET_TILE;
     gridname = sTileset;  /*hijack the char* pointer while counting the number of commas */
     while(*gridname) {
       if(*gridname == ';') req->ntiles++;
diff --git a/lib/service_tms.c b/lib/service_tms.c
index 6adbedc..3de8d8a 100644
--- a/lib/service_tms.c
+++ b/lib/service_tms.c
@@ -240,7 +240,7 @@ void _mapcache_service_tms_parse_request(mapcache_context *ctx, mapcache_service
   if(index == 5) {
     char *gridname;
     mapcache_request_get_tile *req = (mapcache_request_get_tile*)apr_pcalloc(ctx->pool,sizeof(mapcache_request_get_tile));
-    req->request.type = MAPCACHE_REQUEST_GET_TILE;
+    ((mapcache_request*)req)->type = MAPCACHE_REQUEST_GET_TILE;
     gridname = sTileset;  /*hijack the char* pointer while counting the number of commas */
     while(*gridname) {
       if(*gridname == ';') req->ntiles++;
diff --git a/lib/service_ve.c b/lib/service_ve.c
index 708f62e..0bcdb95 100644
--- a/lib/service_ve.c
+++ b/lib/service_ve.c
@@ -131,7 +131,7 @@ void _mapcache_service_ve_parse_request(mapcache_context *ctx, mapcache_service
 
 
   req = (mapcache_request_get_tile*) apr_pcalloc(ctx->pool, sizeof (mapcache_request_get_tile));
-  req->request.type = MAPCACHE_REQUEST_GET_TILE;
+  ((mapcache_request*)req)->type = MAPCACHE_REQUEST_GET_TILE;
   req->ntiles = 1;
   req->tiles = (mapcache_tile**) apr_pcalloc(ctx->pool, sizeof (mapcache_tile*));
   req->tiles[0] = tile;
diff --git a/lib/service_wms.c b/lib/service_wms.c
index cfabf18..1fc643e 100644
--- a/lib/service_wms.c
+++ b/lib/service_wms.c
@@ -228,8 +228,9 @@ void _create_capabilities_wms(mapcache_context *ctx, mapcache_request_get_capabi
 
     if(tileset->dimensions) {
       for(i=0; i<tileset->dimensions->nelts; i++) {
-        const char **value;
-        char *dimval;
+        apr_array_header_t *values;
+        int value_idx;
+        char *dimval = NULL;
         mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
         ezxml_t dimxml = ezxml_add_child(layerxml,"Dimension",0);
         ezxml_set_attr(dimxml,"name",dimension->name);
@@ -238,12 +239,14 @@ void _create_capabilities_wms(mapcache_context *ctx, mapcache_request_get_capabi
         if(dimension->unit) {
           ezxml_set_attr(dimxml,"units",dimension->unit);
         }
-        value = dimension->print_ogc_formatted_values(ctx,dimension);
-        dimval = apr_pstrdup(ctx->pool,*value);
-        value++;
-        while(*value) {
-          dimval = apr_pstrcat(ctx->pool,dimval,",",*value,NULL);
-          value++;
+        values = dimension->print_ogc_formatted_values(ctx,dimension);
+        for(value_idx=0;value_idx<values->nelts;value_idx++) {
+          char *idval = APR_ARRAY_IDX(values,value_idx,char*);
+          if(dimval) {
+            dimval = apr_pstrcat(ctx->pool,dimval,",",idval,NULL);
+          } else {
+            dimval = apr_pstrdup(ctx->pool,idval);
+          }
         }
         ezxml_set_txt(dimxml,dimval);
       }
@@ -483,6 +486,7 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
       mapcache_grid_link *main_grid_link = NULL;
       mapcache_tileset *main_tileset = NULL;
       mapcache_request_type type;
+      mapcache_image_format *imf;
 
       /* count the number of layers that are requested.
        * if we are in combined-mirror mode, then there is
@@ -545,24 +549,44 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
         type = MAPCACHE_REQUEST_GET_MAP;
       }
 
+      imf = wms_service->getmap_format;
+      if(wms_service->allow_format_override) {
+        str = apr_table_get(params,"FORMAT");
+        if(strcmp(str,imf->name) && strcmp(str,imf->mime_type)) {
+          apr_hash_index_t *hi;
+          for (hi = apr_hash_first(ctx->pool, ctx->config->image_formats); hi; hi = apr_hash_next(hi)) {
+            apr_hash_this(hi, NULL, NULL, (void**)&imf);
+            if(!strcmp(imf->name, str) || (imf->mime_type && !strcmp(imf->mime_type, str))) {
+              break;
+            }
+          }
+          if(!hi) { /* did not find any matching format for given mimetype or name */
+            errcode = 404;
+            errmsg = apr_psprintf(ctx->pool,"received wms request with invalid format %s", str);
+            goto proxies;
+          }
+        }
+      }
+
 
       if(type == MAPCACHE_REQUEST_GET_TILE) {
         tile_req = apr_pcalloc(ctx->pool, sizeof(mapcache_request_get_tile));
-        tile_req->request.type = MAPCACHE_REQUEST_GET_TILE;
         tile_req->tiles = apr_pcalloc(ctx->pool, count*sizeof(mapcache_tile*));
-        tile_req->format = wms_service->getmap_format;
+        tile_req->image_request.format = imf;
         *request = (mapcache_request*)tile_req;
+        (*request)->type = MAPCACHE_REQUEST_GET_TILE;
       } else {
         map_req = apr_pcalloc(ctx->pool, sizeof(mapcache_request_get_map));
-        map_req->request.type = MAPCACHE_REQUEST_GET_MAP;
         map_req->maps = apr_pcalloc(ctx->pool, count*sizeof(mapcache_map*));
         map_req->getmap_strategy = wms_service->getmap_strategy;
         map_req->resample_mode = wms_service->resample_mode;
-        map_req->getmap_format = wms_service->getmap_format;
+        map_req->image_request.format = imf;
         *request = (mapcache_request*)map_req;
+        (*request)->type = MAPCACHE_REQUEST_GET_MAP;
       }
       nallocated = count;
 
+
       /*
        * loop through all the layers to verify that they reference the requested grid,
        * and to extract any dimensions if configured
@@ -636,17 +660,36 @@ void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service
           const char *value;
           if(tileset->dimensions) {
             for(i=0; i<tileset->dimensions->nelts; i++) {
+              char *dim_name;
               mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
-              if((value = (char*)apr_table_get(params,dimension->name)) != NULL) {
+              if(!strcasecmp(dimension->name,"TIME") || !strcasecmp(dimension->name,"ELEVATION")) {
+                dim_name = dimension->name;
+              } else {
+                dim_name = apr_pstrcat(ctx->pool, "dim_", dimension->name, NULL);
+              }
+              if((value = (char*)apr_table_get(params,dim_name)) == NULL) {
+                if(strcasecmp(dimension->name,"TIME") && strcasecmp(dimension->name,"ELEVATION")) {
+                  /* also test for the dimension without the DIM_ prefix if the latter was not found in the KVP params */
+                  dim_name = dimension->name;
+                  value = (char*)apr_table_get(params,dimension->name);
+                }
+              }
+
+              if(value) {
                 char *tmpval = apr_pstrdup(ctx->pool,value);
-                int ok = dimension->validate(ctx,dimension,&tmpval);
-                GC_CHECK_ERROR(ctx);
+                int ok;
+                if(dimension->skip_validation) {
+                  ok = MAPCACHE_SUCCESS;
+                } else {
+                  ok = dimension->validate(ctx,dimension,&tmpval);
+                  GC_CHECK_ERROR(ctx);
+                }
                 if(ok == MAPCACHE_SUCCESS)
                   apr_table_setn(dimtable,dimension->name,tmpval);
                 else {
                   errcode = 400;
                   errmsg = apr_psprintf(ctx->pool, "dimension \"%s\" value \"%s\" fails to validate",
-                                        dimension->name, value);
+                                        dim_name, value);
                   goto proxies;
                 }
               }
@@ -876,7 +919,7 @@ proxies:
         *request = (mapcache_request*)req_proxy;
         (*request)->service = this;
         (*request)->type = MAPCACHE_REQUEST_PROXY;
-        req_proxy->http = rule->http;
+        req_proxy->rule = rule;
         req_proxy->params = params;
         if(rule->append_pathinfo) {
           req_proxy->pathinfo = pathinfo;
@@ -916,32 +959,42 @@ void _configuration_parse_wms_xml(mapcache_context *ctx, ezxml_t node, mapcache_
 
   for( rule_node = ezxml_child(node,"forwarding_rule"); rule_node; rule_node = rule_node->next) {
     mapcache_forwarding_rule *rule;
-    ezxml_t http_node;
-    ezxml_t pathinfo_node;
-    ezxml_t param_node;
+    ezxml_t node;
     char *name = (char*)ezxml_attr(rule_node,"name");
     if(!name) name = "(null)";
     rule = apr_pcalloc(ctx->pool, sizeof(mapcache_forwarding_rule));
     rule->name = apr_pstrdup(ctx->pool,name);
     rule->match_params = apr_array_make(ctx->pool,1,sizeof(mapcache_dimension*));
+    rule->max_post_len = 10485760; /* 10 megabytes by default */ 
 
-    pathinfo_node = ezxml_child(rule_node,"append_pathinfo");
-    if(pathinfo_node && !strcasecmp(pathinfo_node->txt,"true")) {
+    node = ezxml_child(rule_node,"append_pathinfo");
+    if(node && !strcasecmp(node->txt,"true")) {
       rule->append_pathinfo = 1;
     } else {
       rule->append_pathinfo = 0;
     }
-    http_node = ezxml_child(rule_node,"http");
-    if(!http_node) {
+    
+    node = ezxml_child(rule_node,"max_post_length");
+    if(node) {
+      char *endptr;
+      rule->max_post_len= (size_t)strtol(node->txt,&endptr,10);
+      if(*endptr != 0 || rule->max_post_len <= 0) {
+        ctx->set_error(ctx,500,"rule \"%s\" cannot have a negative or null <max_post_length>",name);
+        return;
+      }
+    }
+    
+    node = ezxml_child(rule_node,"http");
+    if(!node) {
       ctx->set_error(ctx,500,"rule \"%s\" does not contain an <http> block",name);
       return;
     }
-    rule->http = mapcache_http_configuration_parse_xml(ctx,http_node);
+    rule->http = mapcache_http_configuration_parse_xml(ctx,node);
     GC_CHECK_ERROR(ctx);
 
-    for(param_node = ezxml_child(rule_node,"param"); param_node; param_node = param_node->next) {
-      char *name = (char*)ezxml_attr(param_node,"name");
-      char *type = (char*)ezxml_attr(param_node,"type");
+    for(node = ezxml_child(rule_node,"param"); node; node = node->next) {
+      char *name = (char*)ezxml_attr(node,"name");
+      char *type = (char*)ezxml_attr(node,"type");
 
       mapcache_dimension *dimension = NULL;
 
@@ -966,7 +1019,7 @@ void _configuration_parse_wms_xml(mapcache_context *ctx, ezxml_t node, mapcache_
 
       dimension->name = apr_pstrdup(ctx->pool,name);
 
-      dimension->configuration_parse_xml(ctx,dimension,param_node);
+      dimension->configuration_parse_xml(ctx,dimension,node);
       GC_CHECK_ERROR(ctx);
 
       APR_ARRAY_PUSH(rule->match_params,mapcache_dimension*) = dimension;
@@ -986,11 +1039,16 @@ void _configuration_parse_wms_xml(mapcache_context *ctx, ezxml_t node, mapcache_
 
   wms->getmap_format = mapcache_configuration_get_image_format(cfg,"JPEG");
   if ((rule_node = ezxml_child(node,"format")) != NULL) {
+    const char *attr;
     wms->getmap_format = mapcache_configuration_get_image_format(cfg,rule_node->txt);
     if(!wms->getmap_format) {
       ctx->set_error(ctx,400, "unknown <format> %s for wms service", rule_node->txt);
       return;
     }
+    attr = ezxml_attr(rule_node,"allow_client_override");
+    if(attr && !strcmp(attr,"true")) {
+      wms->allow_format_override = 1;
+    }
   }
 
   if ((rule_node = ezxml_child(node,"resample_mode")) != NULL) {
@@ -1064,6 +1122,7 @@ mapcache_service* mapcache_service_wms_create(mapcache_context *ctx)
   service->getmap_strategy = MAPCACHE_GETMAP_ASSEMBLE;
   service->resample_mode = MAPCACHE_RESAMPLE_BILINEAR;
   service->getmap_format = NULL;
+  service->allow_format_override = 0;
   return (mapcache_service*)service;
 }
 
diff --git a/lib/service_wmts.c b/lib/service_wmts.c
index df87a7f..541f9fb 100644
--- a/lib/service_wmts.c
+++ b/lib/service_wmts.c
@@ -348,6 +348,15 @@ void _create_capabilities_wmts(mapcache_context *ctx, mapcache_request_get_capab
       ezxml_set_txt(ezxml_add_child(layer,"ows:Abstract",0),abstract);
     }
 
+    if(tileset->wgs84bbox.minx != tileset->wgs84bbox.maxx) {
+      ezxml_t bbox = ezxml_add_child(layer,"ows:WGS84BoundingBox",0);
+      ezxml_set_txt(ezxml_add_child(bbox,"ows:LowerCorner",0),
+                    apr_psprintf(ctx->pool,"%f %f",tileset->wgs84bbox.minx, tileset->wgs84bbox.miny));
+      ezxml_set_txt(ezxml_add_child(bbox,"ows:UpperCorner",0),
+                    apr_psprintf(ctx->pool,"%f %f",tileset->wgs84bbox.maxx, tileset->wgs84bbox.maxy));
+    }
+
+
     ezxml_set_txt(ezxml_add_child(layer,"ows:Identifier",0),tileset->name);
 
     style = ezxml_add_child(layer,"Style",0);
@@ -363,8 +372,8 @@ void _create_capabilities_wmts(mapcache_context *ctx, mapcache_request_get_capab
 
     if(tileset->dimensions) {
       for(i=0; i<tileset->dimensions->nelts; i++) {
-        const char **values;
-        const char **value;
+        apr_array_header_t *values;
+        int value_idx;
         mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
         ezxml_t dim = ezxml_add_child(layer,"Dimension",0);
         ezxml_set_txt(ezxml_add_child(dim,"ows:Identifier",0),dimension->name);
@@ -374,10 +383,9 @@ void _create_capabilities_wmts(mapcache_context *ctx, mapcache_request_get_capab
           ezxml_set_txt(ezxml_add_child(dim,"UOM",0),dimension->unit);
         }
         values = dimension->print_ogc_formatted_values(ctx,dimension);
-        value = values;
-        while(*value) {
-          ezxml_set_txt(ezxml_add_child(dim,"Value",0),*value);
-          value++;
+        for(value_idx=0;value_idx<values->nelts;value_idx++) {
+          char *idval = APR_ARRAY_IDX(values,value_idx,char*);
+          ezxml_set_txt(ezxml_add_child(dim,"Value",0),idval);
         }
         dimensionstemplate = apr_pstrcat(ctx->pool,dimensionstemplate,"{",dimension->name,"}/",NULL);
       }
@@ -390,13 +398,6 @@ void _create_capabilities_wmts(mapcache_context *ctx, mapcache_request_get_capab
       }
     }
 
-    if(tileset->wgs84bbox.minx != tileset->wgs84bbox.maxx) {
-      ezxml_t bbox = ezxml_add_child(layer,"ows:WGS84BoundingBox",0);
-      ezxml_set_txt(ezxml_add_child(bbox,"ows:LowerCorner",0),
-                    apr_psprintf(ctx->pool,"%f %f",tileset->wgs84bbox.minx, tileset->wgs84bbox.miny));
-      ezxml_set_txt(ezxml_add_child(bbox,"ows:UpperCorner",0),
-                    apr_psprintf(ctx->pool,"%f %f",tileset->wgs84bbox.maxx, tileset->wgs84bbox.maxy));
-    }
 
     for(i=0; i<tileset->grid_links->nelts; i++) {
       mapcache_grid_link *grid_link = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
@@ -701,7 +702,7 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
           continue;
         }
       }
-      if(tileset->timedimension) {
+      if(!timedim && tileset->timedimension) {
         timedim = key;
         continue;
       }
@@ -779,19 +780,20 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
     for(i=0; i<tileset->dimensions->nelts; i++) {
       char *tmpval;
       int ok;
+      const char *value;
       mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
-      const char *value = apr_table_get(dimtable,dimension->name);
+      if(dimension->skip_validation) continue;
+      value = apr_table_get(dimtable,dimension->name);
       if(value) {
         tmpval = apr_pstrdup(ctx->pool,value);
         ok = dimension->validate(ctx,dimension,&tmpval);
         GC_CHECK_ERROR(ctx);
         if(ok != MAPCACHE_SUCCESS) {
           ctx->set_error(ctx,404,"dimension \"%s\" value \"%s\" fails to validate",
-                         dimension->name, value);
+                  dimension->name, value);
           if(kvp) ctx->set_exception(ctx,"InvalidParameterValue","%s",dimension->name);
           return;
         }
-
         /* re-set the eventually modified value in the dimension table */
         apr_table_set(dimtable,dimension->name,tmpval);
       }
@@ -913,7 +915,7 @@ void _mapcache_service_wmts_parse_request(mapcache_context *ctx, mapcache_servic
     mapcache_request_get_tile *req = (mapcache_request_get_tile*)apr_pcalloc(
                                        ctx->pool,sizeof(mapcache_request_get_tile));
     
-    req->request.type = MAPCACHE_REQUEST_GET_TILE;
+    ((mapcache_request*)req)->type = MAPCACHE_REQUEST_GET_TILE;
     if(timedim) {
       timedim_selected = mapcache_timedimension_get_entries_for_value(ctx,
               tileset->timedimension, tileset, grid_link->grid, extent, timedim);
diff --git a/lib/source_mapserver.c b/lib/source_mapserver.c
index 7d3048f..4321e49 100644
--- a/lib/source_mapserver.c
+++ b/lib/source_mapserver.c
@@ -278,6 +278,7 @@ mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx)
 mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx)
 {
   ctx->set_error(ctx, 500, "mapserver source not configured for this build");
+  return NULL;
 }
 #endif
 
diff --git a/lib/source_wms.c b/lib/source_wms.c
index ca735e3..250a440 100644
--- a/lib/source_wms.c
+++ b/lib/source_wms.c
@@ -40,6 +40,7 @@
 void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_map *map)
 {
   mapcache_source_wms *wms = (mapcache_source_wms*)map->tileset->source;
+  mapcache_http *http;
   apr_table_t *params = apr_table_clone(ctx->pool,wms->wms_default_params);
   apr_table_setn(params,"BBOX",apr_psprintf(ctx->pool,"%f,%f,%f,%f",
                  map->extent.minx,map->extent.miny,map->extent.maxx,map->extent.maxy));
@@ -54,7 +55,12 @@ void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_map *map)
     int i;
     for(i=0; i<elts->nelts; i++) {
       apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t);
+      /* set both DIM_key=val and key=val KVP params */
       apr_table_setn(params,entry.key,entry.val);
+      if(strcasecmp(entry.key,"TIME") && strcasecmp(entry.key,"ELEVATION")) {
+        char *dim_name = apr_pstrcat(ctx->pool,"DIM_",entry.key,NULL);
+        apr_table_setn(params,dim_name,entry.val);
+      }
     }
 
   }
@@ -68,7 +74,9 @@ void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_map *map)
   }
 
   map->encoded_data = mapcache_buffer_create(30000,ctx->pool);
-  mapcache_http_do_request_with_params(ctx,wms->http,params,map->encoded_data,NULL,NULL);
+  http = mapcache_http_clone(ctx, wms->http);
+  http->url = mapcache_http_build_url(ctx,http->url,params);
+  mapcache_http_do_request(ctx,http,map->encoded_data,NULL,NULL);
   GC_CHECK_ERROR(ctx);
 
   if(!mapcache_imageio_is_valid_format(ctx,map->encoded_data)) {
@@ -81,6 +89,7 @@ void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_map *map)
 void _mapcache_source_wms_query(mapcache_context *ctx, mapcache_feature_info *fi)
 {
   mapcache_map *map = (mapcache_map*)fi;
+  mapcache_http *http;
   mapcache_source_wms *wms = (mapcache_source_wms*)map->tileset->source;
 
   apr_table_t *params = apr_table_clone(ctx->pool,wms->wms_default_params);
@@ -107,7 +116,9 @@ void _mapcache_source_wms_query(mapcache_context *ctx, mapcache_feature_info *fi
   }
 
   fi->data = mapcache_buffer_create(30000,ctx->pool);
-  mapcache_http_do_request_with_params(ctx,wms->http,params,fi->data,NULL,NULL);
+  http = mapcache_http_clone(ctx, wms->http);
+  http->url = mapcache_http_build_url(ctx,http->url,params);
+  mapcache_http_do_request(ctx,http,fi->data,NULL,NULL);
   GC_CHECK_ERROR(ctx);
 
 }
diff --git a/lib/tileset.c b/lib/tileset.c
index e8a283c..8677d48 100644
--- a/lib/tileset.c
+++ b/lib/tileset.c
@@ -71,7 +71,7 @@ void mapcache_tileset_configuration_check(mapcache_context *ctx, mapcache_tilese
 {
 
   /* check we have all we want */
-  if(tileset->cache == NULL) {
+  if(tileset->_cache == NULL) {
     /* TODO: we should allow tilesets with no caches */
     ctx->set_error(ctx, 400, "tileset \"%s\" has no cache configured.", tileset->name);
     return;
@@ -176,7 +176,8 @@ void mapcache_tileset_get_map_tiles(mapcache_context *ctx, mapcache_tileset *til
                                     mapcache_grid_link *grid_link,
                                     mapcache_extent *bbox, int width, int height,
                                     int *ntiles,
-                                    mapcache_tile ***tiles)
+                                    mapcache_tile ***tiles,
+                                    mapcache_grid_link **effectively_used_grid_link)
 {
   double resolution;
   int level;
@@ -185,25 +186,25 @@ void mapcache_tileset_get_map_tiles(mapcache_context *ctx, mapcache_tileset *til
   int x,y;
   int i=0;
   resolution = mapcache_grid_get_resolution(bbox, width, height);
-  mapcache_grid_get_closest_level(ctx,grid_link,resolution,&level);
-  
+  *effectively_used_grid_link = mapcache_grid_get_closest_wms_level(ctx,grid_link,resolution,&level);
+
   /* we don't want to assemble tiles that have already been reassembled from a lower level */
-  if(grid_link->outofzoom_strategy == MAPCACHE_OUTOFZOOM_REASSEMBLE && level > grid_link->max_cached_zoom) {
-    level = grid_link->max_cached_zoom;
+  if((*effectively_used_grid_link)->outofzoom_strategy == MAPCACHE_OUTOFZOOM_REASSEMBLE && level > (*effectively_used_grid_link)->max_cached_zoom) {
+    level = (*effectively_used_grid_link)->max_cached_zoom;
   }
 
-  mapcache_grid_get_xy(ctx,grid_link->grid,bbox->minx,bbox->miny,level,&bl_x,&bl_y);
-  mapcache_grid_get_xy(ctx,grid_link->grid,bbox->maxx,bbox->maxy,level,&tr_x,&tr_y);
-  Mx = MAPCACHE_MAX(tr_x,bl_x);
-  My = MAPCACHE_MAX(tr_y,bl_y);
-  mx = MAPCACHE_MIN(tr_x,bl_x);
-  my = MAPCACHE_MIN(tr_y,bl_y);
+  mapcache_grid_get_xy(ctx,(*effectively_used_grid_link)->grid,bbox->minx,bbox->miny,level,&bl_x,&bl_y);
+  mapcache_grid_get_xy(ctx,(*effectively_used_grid_link)->grid,bbox->maxx,bbox->maxy,level,&tr_x,&tr_y);
+  Mx = MAPCACHE_MAX(MAPCACHE_MIN(MAPCACHE_MAX(tr_x,bl_x),(*effectively_used_grid_link)->grid_limits[level].maxx),(*effectively_used_grid_link)->grid_limits[level].minx);
+  My = MAPCACHE_MAX(MAPCACHE_MIN(MAPCACHE_MAX(tr_y,bl_y),(*effectively_used_grid_link)->grid_limits[level].maxy),(*effectively_used_grid_link)->grid_limits[level].miny);
+  mx = MAPCACHE_MIN(MAPCACHE_MAX(MAPCACHE_MIN(tr_x,bl_x),(*effectively_used_grid_link)->grid_limits[level].minx),(*effectively_used_grid_link)->grid_limits[level].maxx);
+  my = MAPCACHE_MIN(MAPCACHE_MAX(MAPCACHE_MIN(tr_y,bl_y),(*effectively_used_grid_link)->grid_limits[level].miny),(*effectively_used_grid_link)->grid_limits[level].maxy);
   *ntiles = (Mx-mx+1)*(My-my+1);
   i=0;
   *tiles = (mapcache_tile**)apr_pcalloc(ctx->pool, *ntiles*sizeof(mapcache_tile*));
   for(x=mx; x<=Mx; x++) {
     for(y=my; y<=My; y++) {
-      mapcache_tile *tile = mapcache_tileset_tile_create(ctx->pool,tileset, grid_link);
+      mapcache_tile *tile = mapcache_tileset_tile_create(ctx->pool,tileset, (*effectively_used_grid_link));
       tile->x = x;
       tile->y = y;
       tile->z = level;
@@ -459,12 +460,12 @@ void mapcache_tileset_render_metatile(mapcache_context *ctx, mapcache_metatile *
   GC_CHECK_ERROR(ctx);
   mapcache_image_metatile_split(ctx, mt);
   GC_CHECK_ERROR(ctx);
-  if(mt->map.tileset->cache->tile_multi_set) {
-    mt->map.tileset->cache->tile_multi_set(ctx, mt->tiles, mt->ntiles);
+  if(mt->map.tileset->_cache->tile_multi_set) {
+    mt->map.tileset->_cache->tile_multi_set(ctx, mt->map.tileset->_cache, mt->tiles, mt->ntiles);
   } else {
     for(i=0; i<mt->ntiles; i++) {
       mapcache_tile *tile = &(mt->tiles[i]);
-      mt->map.tileset->cache->tile_set(ctx, tile);
+      mt->map.tileset->_cache->tile_set(ctx, mt->map.tileset->_cache, tile);
       GC_CHECK_ERROR(ctx);
     }
   }
@@ -504,7 +505,7 @@ mapcache_tileset* mapcache_tileset_clone(mapcache_context *ctx, mapcache_tileset
   dst->grid_links = src->grid_links;
   dst->config = src->config;
   dst->name = src->name;
-  dst->cache = src->cache;
+  dst->_cache = src->_cache;
   dst->source = src->source;
   dst->watermark = src->watermark;
   dst->wgs84bbox = src->wgs84bbox;
@@ -554,6 +555,7 @@ mapcache_tile* mapcache_tileset_tile_clone(apr_pool_t *pool, mapcache_tile *src)
   tile->x = src->x;
   tile->y = src->y;
   tile->z = src->z;
+  tile->allow_redirect = src->allow_redirect;
   return tile;
 }
 
@@ -761,14 +763,14 @@ void mapcache_tileset_outofzoom_get(mapcache_context *ctx, mapcache_tile *tile)
  */
 void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
 {
-  int isLocked,ret;
+  int ret;
   mapcache_metatile *mt=NULL;
   if(tile->grid_link->outofzoom_strategy != MAPCACHE_OUTOFZOOM_NOTCONFIGURED &&
           tile->z > tile->grid_link->max_cached_zoom) {
     mapcache_tileset_outofzoom_get(ctx, tile);
     return;
   }
-  ret = tile->tileset->cache->tile_get(ctx, tile);
+  ret = tile->tileset->_cache->tile_get(ctx, tile->tileset->_cache, tile);
   GC_CHECK_ERROR(ctx);
 
   if(ret == MAPCACHE_SUCCESS && tile->tileset->auto_expire && tile->mtime && tile->tileset->source && !tile->tileset->read_only) {
@@ -778,14 +780,12 @@ void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
     apr_time_t now = apr_time_now();
     apr_time_t stale = tile->mtime + apr_time_from_sec(tile->tileset->auto_expire);
     if(stale<now) {
-      mapcache_tileset_tile_delete(ctx,tile,MAPCACHE_TRUE);
-      GC_CHECK_ERROR(ctx);
-      ret = MAPCACHE_CACHE_MISS;
+      /* Indicate that we need to re-render the tile */
+      ret = MAPCACHE_CACHE_RELOAD;
     }
   }
 
-  if(ret == MAPCACHE_CACHE_MISS) {
-
+  if (ret == MAPCACHE_CACHE_MISS) {
     /* bail out straight away if the tileset has no source or is read-only */
     if(tile->tileset->read_only || !tile->tileset->source) {
       /* there is no source configured for this tile. not an error, let caller now*/
@@ -802,45 +802,71 @@ void mapcache_tileset_tile_get(mapcache_context *ctx, mapcache_tile *tile)
       ctx->set_error(ctx,404,"tile not in cache, and configured for readonly mode");
       return;
     }
+  }
+  
+  
+  if (ret == MAPCACHE_CACHE_MISS || ret == MAPCACHE_CACHE_RELOAD) {
+    int isLocked;
+    void *lock;
 
-    /* the tile does not exist, we must take action before re-asking for it */
-    /*
-     * is the tile already being rendered by another thread ?
-     * the call is protected by the same mutex that sets the lock on the tile,
-     * so we can assure that:
-     * - if the lock does not exist, then this thread should do the rendering
-     * - if the lock exists, we should wait for the other thread to finish
-     */
+    /* If the tile does not exist or stale, we must take action before re-asking for it */
+    if( !tile->tileset->read_only && tile->tileset->source && !ctx->config->non_blocking) { 
+      /*
+       * is the tile already being rendered by another thread ?
+       * the call is protected by the same mutex that sets the lock on the tile,
+       * so we can assure that:
+       * - if the lock does not exist, then this thread should do the rendering
+       * - if the lock exists, we should wait for the other thread to finish
+       */
 
-    /* aquire a lock on the metatile */
-    mt = mapcache_tileset_metatile_get(ctx, tile);
-    isLocked = mapcache_lock_or_wait_for_resource(ctx, mapcache_tileset_metatile_resource_key(ctx,mt));
+      /* aquire a lock on the metatile */
+      mt = mapcache_tileset_metatile_get(ctx, tile);
+      isLocked = mapcache_lock_or_wait_for_resource(ctx, ctx->config->locker, mapcache_tileset_metatile_resource_key(ctx,mt), &lock);
+      GC_CHECK_ERROR(ctx);
 
 
-    if(isLocked == MAPCACHE_TRUE) {
-      /* no other thread is doing the rendering, do it ourselves */
+      if(isLocked == MAPCACHE_TRUE) {
+         /* no other thread is doing the rendering, do it ourselves */
 #ifdef DEBUG
-      ctx->log(ctx, MAPCACHE_DEBUG, "cache miss: tileset %s - tile %d %d %d",
-               tile->tileset->name,tile->x, tile->y,tile->z);
+        ctx->log(ctx, MAPCACHE_DEBUG, "cache miss/reload: tileset %s - tile %d %d %d",
+             tile->tileset->name,tile->x, tile->y,tile->z);
 #endif
-      /* this will query the source to create the tiles, and save them to the cache */
-      mapcache_tileset_render_metatile(ctx, mt);
-
-      mapcache_unlock_resource(ctx, mapcache_tileset_metatile_resource_key(ctx,mt));
+        /* this will query the source to create the tiles, and save them to the cache */
+        mapcache_tileset_render_metatile(ctx, mt);
+
+        if(GC_HAS_ERROR(ctx)) {
+          /* temporarily clear error state so we don't mess up with error handling in the locker */
+          void *error;
+          ctx->pop_errors(ctx,&error);
+          mapcache_unlock_resource(ctx, ctx->config->locker, mapcache_tileset_metatile_resource_key(ctx,mt), lock);
+          ctx->push_errors(ctx,error);
+        } else {
+          mapcache_unlock_resource(ctx, ctx->config->locker, mapcache_tileset_metatile_resource_key(ctx,mt), lock);
+        }
+      }
     }
-    GC_CHECK_ERROR(ctx);
 
-    /* the previous step has successfully finished, we can now query the cache to return the tile content */
-    ret = tile->tileset->cache->tile_get(ctx, tile);
-    GC_CHECK_ERROR(ctx);
+    if (ret == MAPCACHE_CACHE_RELOAD && GC_HAS_ERROR(ctx)) 
+      /* If we tried to reload a stale tile but failed, we know we have already 
+       * fetched it from the cache. We can then ignore errors and just use old tile. 
+       */
+      ctx->clear_errors(ctx);
 
-    if(ret != MAPCACHE_SUCCESS) {
-      if(isLocked == MAPCACHE_FALSE) {
-        ctx->set_error(ctx, 500, "tileset %s: unknown error (another thread/process failed to create the tile I was waiting for)",
-                       tile->tileset->name);
-      } else {
-        /* shouldn't really happen, as the error ought to have been caught beforehand */
-        ctx->set_error(ctx, 500, "tileset %s: failed to re-get tile %d %d %d from cache after set", tile->tileset->name,tile->x,tile->y,tile->z);
+    else {
+      /* Else, check for errors and try to fetch the tile from the cache. 
+      */ 
+      GC_CHECK_ERROR(ctx);   
+      ret = tile->tileset->_cache->tile_get(ctx, tile->tileset->_cache, tile);
+      GC_CHECK_ERROR(ctx);
+
+      if(ret != MAPCACHE_SUCCESS) {
+        if(isLocked == MAPCACHE_FALSE) {
+          ctx->set_error(ctx, 500, "tileset %s: unknown error (another thread/process failed to create the tile I was waiting for)",
+              tile->tileset->name);
+        } else {
+          /* shouldn't really happen, as the error ought to have been caught beforehand */
+          ctx->set_error(ctx, 500, "tileset %s: failed to re-get tile %d %d %d from cache after set", tile->tileset->name,tile->x,tile->y,tile->z);
+        }
       }
     }
   }
@@ -856,7 +882,7 @@ void mapcache_tileset_tile_delete(mapcache_context *ctx, mapcache_tile *tile, in
 {
   int i;
   /*delete the tile itself*/
-  tile->tileset->cache->tile_delete(ctx,tile);
+  tile->tileset->_cache->tile_delete(ctx,tile->tileset->_cache, tile);
   GC_CHECK_ERROR(ctx);
 
   if(whole_metatile) {
@@ -865,7 +891,7 @@ void mapcache_tileset_tile_delete(mapcache_context *ctx, mapcache_tile *tile, in
       mapcache_tile *subtile = &mt->tiles[i];
       /* skip deleting the actual tile */
       if(subtile->x == tile->x && subtile->y == tile->y) continue;
-      subtile->tileset->cache->tile_delete(ctx,subtile);
+      subtile->tileset->_cache->tile_delete(ctx,subtile->tileset->_cache,subtile);
       /* silently pass failure if the tile was not found */
       if(ctx->get_error(ctx) == 404) {
         ctx->clear_errors(ctx);
diff --git a/lib/util.c b/lib/util.c
index 1822b6e..590532a 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -43,6 +43,49 @@
 #endif
 const double mapcache_meters_per_unit[MAPCACHE_UNITS_COUNT] = {1.0,6378137.0 * 2.0 * M_PI / 360,0.3048};
 
+
+static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+                                'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+                                'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+                                'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+                                'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+                                'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+                                'w', 'x', 'y', 'z', '0', '1', '2', '3',
+                                '4', '5', '6', '7', '8', '9', '+', '/'};
+static int mod_table[] = {0, 2, 1};
+
+
+char *base64_encode(apr_pool_t *pool, const unsigned char *data, size_t input_length) {
+  int i,j;
+  char *encoded_data;
+  size_t output_length = 4 * ((input_length + 2) / 3) + 1;
+
+  encoded_data = (char*)apr_pcalloc(pool,output_length*sizeof(char));
+  if (encoded_data == NULL) return NULL;
+
+  for (i = 0, j = 0; i < input_length;) {
+
+    uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
+    uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
+    uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
+
+    uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
+
+    encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
+    encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
+    encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
+    encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
+  }
+
+  for (i = 0; i < mod_table[input_length % 3]; i++)
+    encoded_data[output_length - 2 - i] = '=';
+  
+  encoded_data[output_length-1]=0;
+
+  return encoded_data;
+}
+
+
 int mapcache_util_extract_int_list(mapcache_context *ctx, const char* cargs,
                                    const char *sdelim, int **numbers, int *numbers_count)
 {
@@ -172,18 +215,18 @@ void _mapcache_context_set_exception_default(mapcache_context *ctx, char *key, c
 
 void _mapcache_context_set_error_default(mapcache_context *ctx, int code, char *msg, ...)
 {
-  char *fmt;
+  char *new_msg;
   va_list args;
   va_start(args,msg);
+  new_msg = apr_pvsprintf(ctx->pool,msg,args);
+  va_end(args);
 
   if(ctx->_errmsg) {
-    fmt=apr_psprintf(ctx->pool,"%s\n%s",ctx->_errmsg,msg);
+    ctx->_errmsg = apr_pstrcat(ctx->pool, ctx->_errmsg, "\n", new_msg, NULL);
   } else {
-    fmt=msg;
+    ctx->_errmsg = new_msg;
     ctx->_errcode = code;
   }
-  ctx->_errmsg = apr_pvsprintf(ctx->pool,fmt,args);
-  va_end(args);
 }
 
 void _mapcache_context_clear_error_default(mapcache_context *ctx)
@@ -196,6 +239,46 @@ void _mapcache_context_clear_error_default(mapcache_context *ctx)
 }
 
 
+struct _error_log {
+  int _errcode;
+  char *_errmsg;
+  apr_table_t *exceptions;
+};
+
+void _mapcache_context_pop_errors(mapcache_context *ctx, void **error)
+{
+  struct _error_log *e = (struct _error_log*)apr_pcalloc(ctx->pool, sizeof(struct _error_log));
+  e->_errcode = ctx->_errcode;
+  e->_errmsg = ctx->_errmsg;
+  e->exceptions = ctx->exceptions;
+  ctx->_errcode = 0;
+  ctx->_errmsg = NULL;
+  ctx->exceptions = NULL;
+  *error = e;
+}
+
+
+void _mapcache_context_push_errors(mapcache_context *ctx, void *error)
+{
+  struct _error_log *e = (struct _error_log*)error;
+  ctx->_errcode = e->_errcode;
+  if(e->_errmsg) {
+    if(ctx->_errmsg) {
+      ctx->_errmsg = apr_psprintf(ctx->pool,"%s\n%s",ctx->_errmsg,e->_errmsg);
+    } else {
+      ctx->_errmsg = e->_errmsg;
+    }
+  }
+  if(e->exceptions) {
+    if(ctx->exceptions) {
+      apr_table_overlap(ctx->exceptions, e->exceptions, APR_OVERLAP_TABLES_SET);
+    } else {
+      ctx->exceptions = e->exceptions;
+    }
+  }
+}
+
+
 void mapcache_context_init(mapcache_context *ctx)
 {
   ctx->_errcode = 0;
@@ -205,6 +288,9 @@ void mapcache_context_init(mapcache_context *ctx)
   ctx->set_error = _mapcache_context_set_error_default;
   ctx->set_exception = _mapcache_context_set_exception_default;
   ctx->clear_errors = _mapcache_context_clear_error_default;
+  ctx->pop_errors = _mapcache_context_pop_errors;
+  ctx->push_errors = _mapcache_context_push_errors;
+  ctx->headers_in = NULL;
 }
 
 void mapcache_context_copy(mapcache_context *src, mapcache_context *dst)
@@ -225,7 +311,11 @@ void mapcache_context_copy(mapcache_context *src, mapcache_context *dst)
   dst->service = src->service;
   dst->exceptions = src->exceptions;
   dst->threadlock = src->threadlock;
-  dst->process_pool = src->process_pool;
+  dst->supports_redirects = src->supports_redirects;
+  dst->pop_errors = src->pop_errors;
+  dst->push_errors = src->push_errors;
+  dst->connection_pool = src->connection_pool;
+  dst->headers_in = src->headers_in;
 }
 
 char* mapcache_util_get_tile_dimkey(mapcache_context *ctx, mapcache_tile *tile, char* sanitized_chars, char *sanitize_to)
diff --git a/mapcache.xml b/mapcache.xml
index 53af095..0007b77 100644
--- a/mapcache.xml
+++ b/mapcache.xml
@@ -7,6 +7,10 @@
       <base>/tmp</base>
       <symlink_blank/>
    </cache>
+   <cache name="sqlite" type="sqlite3">
+     <dbfile>/tmp/{tileset}-{z}-{grid}.db</dbfile>
+      <detect_blank/>
+   </cache>
 
    <source name="vmap0" type="wms">
       <getmap>
@@ -23,9 +27,9 @@
    
    <tileset name="test">
       <source>vmap0</source>
-      <cache>disk</cache>
+      <cache>sqlite</cache>
       <grid>WGS84</grid>
-      <grid>g</grid>
+      <grid>GoogleMapsCompatible</grid>
       <format>PNG</format>
       <metatile>5 5</metatile>
       <metabuffer>10</metabuffer>
@@ -49,6 +53,9 @@
    <service type="demo" enabled="true"/>
 
    <errors>report</errors>
-   <lock_dir>/tmp</lock_dir>
+   <locker type="disk">
+     <directory>/tmp</directory>
+     <timeout>300</timeout>
+    </locker>
 
 </mapcache>
diff --git a/mapcache.xml.sample b/mapcache.xml.sample
index 26c45f4..4e7a976 100644
--- a/mapcache.xml.sample
+++ b/mapcache.xml.sample
@@ -73,6 +73,16 @@
    </grid>
 
    <!--
+   <grid name="worldwind">
+     <extent>-180 -90 180 90</extent>
+     <srs>EPSG:4326</srs>
+     <units>dd</units>
+     <size>512 512</size>
+     <resolutions>0.0703125000000000 0.0351562500000000 0.0175781250000000 8.78906250000000e-3 4.39453125000000e-3 2.19726562500000e-3 1.09863281250000e-3 5.49316406250000e-4 2.74658203125000e-4 1.37329101562500e-4 6.86645507812500e-5 3.43322753906250e-5 1.71661376953125e-5 8.58306884765625e-6 4.29153442382812e-6 2.14576721191406e-6 1.07288360595703e-6 5.36441802978516e-7</resolutions>
+   </grid>
+   -->
+
+   <!--
          there are three preconfigured grids you can use in <tileset>s without having to
          explicitely define them here:
          <grid name="WGS84">
@@ -406,6 +416,17 @@
 
       -->
       <pragma name="key">value</pragma>
+      <!-- queries
+            SQL to be sent to sqlite backend for operations on tiles. The default queries that are
+            sent are listed below
+      --> 
+      <queries>
+        <create>create table if not exists tiles(tileset text, grid text, x integer, y integer, z integer, data blob, dim text, ctime datetime, primary key(tileset,grid,x,y,z,dim))</create>
+        <exists>select 1 from tiles where x=:x and y=:y and z=:z and dim=:dim and tileset=:tileset and grid=:grid</exists>
+        <get>select data,strftime("%s",ctime) from tiles where tileset=:tileset and grid=:grid and x=:x and y=:y and z=:z and dim=:dim</get>
+        <set>insert or replace into tiles(tileset,grid,x,y,z,data,dim,ctime) values (:tileset,:grid,:x,:y,:z,:data,:dim,datetime('now'))</set>
+        <delete>delete from tiles where x=:x and y=:y and z=:z and dim=:dim and tileset=:tileset and grid=:grid</delete>
+      </queries>
    </cache>
    <!--
    <cache name="mbtiles" type="mbtiles">
@@ -439,6 +460,69 @@
       <base>/tmp/</base> <!-- will create /tmp/tokyocabinet.tcb , not configurable yet -->
       <key_template>{tileset}-{grid}-{dim}-{z}-{y}-{x}.{ext}</key_template>
    </cache>
+   
+   <cache name="myrestcache" type="rest">
+     <url>https://myserver/webdav/{tileset}/{grid}/{z}/{x}/{y}.{ext}</url>
+     <headers>
+       <Host>my-virtualhost-alias.domain.com</Host>
+     </headers>
+     <operation type="put">
+       <headers>
+         <X-my-specific-put-header>foo</X-my-specific-put-header>
+       </headers>
+     </operation>
+     <operation type="get">
+       <headers>
+         <X-my-specific-get-header>foo</X-my-specific-get-header>
+       </headers>
+     </operation>
+     <operation type="head">
+       <headers>
+         <X-my-specific-head-header>foo</X-my-specific-head-header>
+       </headers>
+     </operation>
+     <operation type="delete">
+       <headers>
+         <X-my-specific-delete-header>foo</X-my-specific-delete-header>
+       </headers>
+     </operation>
+   </cache>
+   <cache name="s3" type="s3">
+     <url>https://foo.s3.amazonaws.com/tiles/{tileset}/{grid}/{z}/{x}/{y}/{ext}</url>
+     <headers>
+       <Host>foo.s3.amazonaws.com</Host>
+     </headers>
+     <id>foo</id>
+     <secret>foo/sdsvd</secret>
+     <region>eu-west-1</region>
+     <operation type="put">
+       <headers>
+         <x-amz-storage-class>REDUCED_REDUNDANCY</x-amz-storage-class>
+         <x-amz-acl>public-read</x-amz-acl>
+       </headers>
+   </operation>
+   </cache>
+   <cache name="azure" type="azure">
+     <url>https://foo.blob.core.windows.net/tiles/{tileset}/{grid}/{z}/{x}/{y}/{ext}</url>
+     <headers>
+       <Host>foo.blob.core.windows.net</Host>
+     </headers>
+     <id>foo</id>
+     <secret>foobarcccccccccccccccccccccyA==</secret>
+     <container>tiles</container>
+   </cache>
+
+   <cache name="google" type="google">
+     <url>https://storage.googleapis.com/mytiles-mapcache/{tileset}/{grid}/{z}/{x}/{y}.{ext}</url>
+     <access>GOOGPGDWFDG345SDFGSD</access>
+     <secret>sdfgsdSDFwedfwefr2345324dfsGdsfgs</secret>
+     <operation type="put">
+       <headers>
+         <x-amz-acl>public-read</x-amz-acl>
+       </headers>
+     </operation>
+   </cache>
+
 
    <!-- format
 
@@ -876,12 +960,33 @@
    <errors>report</errors>
 
    
+   
+   <locker type="disk">  <!-- this is the default -->
    <!--
         location to put lockfiles (to block other clients while a metatile is being rendered.
         defaults to /tmp
         this location should be writable by the apache user
    -->
-   <lock_dir>/tmp</lock_dir>
+     <directory>/tmp</directory>
+     <retry>0.01</retry> <!-- check back every .01 seconds -->
+   </locker>
+
+   <lock_dir>/tmp</lock_dir>  <!-- deprecated, use <locker type="disk"> -->
+
+
+   <locker type="memcache">
+     <server>
+       <host>localhost</host>
+       <port>11211</port>
+     </server>
+     <server>
+       <host>memcache-host</host>
+       <port>11212</port>
+     </server>
+     <retry>0.3</retry> <!-- check back every .3 seconds if the lock has been released -->
+   </locker>
+
+
 
    <!-- use multiple threads when fetching multiple tiles (used for wms tile assembling -->
    <threaded_fetching>true</threaded_fetching>
diff --git a/nginx/ngx_http_mapcache_module.c b/nginx/ngx_http_mapcache_module.c
index 754aefb..36bd7a6 100644
--- a/nginx/ngx_http_mapcache_module.c
+++ b/nginx/ngx_http_mapcache_module.c
@@ -62,7 +62,7 @@ ngx_http_mapcache_create_conf(ngx_conf_t *cf)
   apr_pool_create(&process_pool,NULL);
   mapcache_context *ctx = apr_pcalloc(process_pool, sizeof(mapcache_ngx_context));
   ctx->pool = process_pool;
-  ctx->process_pool = process_pool;
+  ctx->connection_pool = NULL;
   ctx->threadlock = NULL;
   mapcache_context_init(ctx);
   ctx->log = ngx_mapcache_context_log;
@@ -211,7 +211,6 @@ ngx_http_mapcache_handler(ngx_http_request_t *r)
   mapcache_ngx_context *ngctx = ngx_http_get_module_loc_conf(r, ngx_http_mapcache_module);
   mapcache_context *ctx = (mapcache_context*)ngctx;
   apr_pool_create(&(ctx->pool),process_pool);
-  ctx->process_pool = process_pool;
   ngctx->r = r;
   mapcache_request *request = NULL;
   mapcache_http_response *http_response;
@@ -298,6 +297,7 @@ ngx_http_mapcache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,ctx->get_error_message(ctx));
     return NGX_CONF_ERROR;
   }
+  mapcache_connection_pool_create(&ctx->connection_pool,ctx->pool);
   ctx->config->non_blocking = 1;
 
   ngx_http_core_loc_conf_t  *clcf;
diff --git a/scripts/vagrant/mapcache.sh b/scripts/vagrant/mapcache.sh
new file mode 100755
index 0000000..b47445e
--- /dev/null
+++ b/scripts/vagrant/mapcache.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+NUMTHREADS=2 # we have 2 cpus configured
+export NUMTHREADS
+
+cd /vagrant
+
+mkdir build_vagrant
+cd build_vagrant
+cmake -DWITH_MEMCACHE=1 ..
+
+make -j $NUMTHREADS
+make install
diff --git a/scripts/vagrant/packages.sh b/scripts/vagrant/packages.sh
new file mode 100755
index 0000000..7ee162b
--- /dev/null
+++ b/scripts/vagrant/packages.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+sed -i 's#deb http://us.archive.ubuntu.com/ubuntu/#deb mirror://mirrors.ubuntu.com/mirrors.txt#' /etc/apt/sources.list
+
+export DEBIAN_FRONTEND=noninteractive
+
+apt-get update
+apt-get install -y python-software-properties
+add-apt-repository -y ppa:ubuntugis/ubuntugis-unstable
+apt-get update
+apt-get -y upgrade
+
+# install packages we need
+apt-get install -q -y build-essential pkg-config cmake libgeos-dev rake vim \
+    bison flex libgdal1-dev libproj-dev libpng12-dev libjpeg-dev libfcgi-dev \
+    libcurl4-gnutls-dev apache2-prefork-dev libtiff4-dev libpixman-1-dev \
+    libsqlite3-dev libmemcached-dev
diff --git a/scripts/vagrant/virtualbox-fix.sh b/scripts/vagrant/virtualbox-fix.sh
new file mode 100755
index 0000000..4e0cf7e
--- /dev/null
+++ b/scripts/vagrant/virtualbox-fix.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ ! -e "/usr/lib/VBoxGuestAdditions" ]; then
+    ln -s /opt/VBoxGuestAdditions-4.3.10/lib/VBoxGuestAdditions /usr/lib/VBoxGuestAdditions
+fi
diff --git a/util/mapcache_seed.c b/util/mapcache_seed.c
index 5f1e6c9..f483122 100644
--- a/util/mapcache_seed.c
+++ b/util/mapcache_seed.c
@@ -53,6 +53,7 @@ int msqid;
 
 #include <apr_queue.h>
 apr_queue_t *work_queue;
+apr_queue_t *log_queue;
 
 #if defined(USE_OGR) && defined(USE_GEOS)
 #define USE_CLIPPERS
@@ -80,10 +81,13 @@ int verbose = 0;
 int force = 0;
 int sig_int_received = 0;
 int error_detected = 0;
+double percent_failed_allowed = 1.0;
+int n_metatiles_tot=0;
+FILE *failed_log = NULL, *retry_log = NULL;
+#define FAIL_BACKLOG_COUNT 1000
 
 apr_time_t age_limit = 0;
-int seededtilestot=0, seededtiles=0, queuedtilestot=0;
-struct mctimeval lastlogtime,starttime;
+struct mctimeval starttime;
 
 typedef enum {
   MAPCACHE_CMD_SEED,
@@ -96,7 +100,8 @@ typedef enum {
 typedef enum {
   MAPCACHE_ITERATION_UNSET,
   MAPCACHE_ITERATION_DEPTH_FIRST,
-  MAPCACHE_ITERATION_LEVEL_FIRST
+  MAPCACHE_ITERATION_LEVEL_FIRST,
+  MAPCACHE_ITERATION_LOG
 } mapcache_iteration_mode;
 
 mapcache_iteration_mode iteration_mode = MAPCACHE_ITERATION_UNSET;
@@ -108,6 +113,18 @@ struct seed_cmd {
   int z;
 };
 
+typedef enum {
+  MAPCACHE_STATUS_OK,
+  MAPCACHE_STATUS_FAIL,
+  MAPCACHE_STATUS_FINISHED
+} s_status;
+
+struct seed_status {
+  s_status status;
+  int x,y,z;
+  char *msg;
+};
+
 #ifdef USE_FORK
 struct msg_cmd {
   long mtype;
@@ -191,6 +208,7 @@ int trypop_queue(struct seed_cmd *cmd)
 static const apr_getopt_option_t seed_options[] = {
   /* long-option, short-option, has-arg flag, description */
   { "config", 'c', TRUE, "configuration file (/path/to/mapcache.xml)"},
+  { "cache", 'C', TRUE, "override cache used by selected tileset (useful for selectively seeding fallback/multitier caches)"},
 #ifdef USE_CLIPPERS
   { "ogr-datasource", 'd', TRUE, "ogr datasource to get features from"},
 #endif
@@ -203,12 +221,15 @@ static const apr_getopt_option_t seed_options[] = {
 #ifdef USE_CLIPPERS
   { "ogr-layer", 'l', TRUE, "layer inside datasource"},
 #endif
+  { "log-failed", 'L', TRUE, "log failed tiles to [file]"},
   { "mode", 'm', TRUE, "mode: seed (default), delete or transfer" },
   { "metasize", 'M', TRUE, "override metatile size while seeding, eg 8,8" },
   { "nthreads", 'n', TRUE, "number of parallel threads to use (incompatible with -p/--nprocesses)" },
   { "older", 'o', TRUE, "reseed tiles older than supplied date (format: year/month/day hour:minute, eg: 2011/01/31 20:45" },
   { "nprocesses", 'p', TRUE, "number of parallel processes to use (incompatible with -n/--nthreads)" },
+  { "percent", 'P', TRUE, "percent of failed requests allowed from the last 1000 before we abort (default: 1(%), set to 0 to abort on first error)" },
   { "quiet", 'q', FALSE, "don't show progress info" },
+  { "retry-failed", 'R', TRUE, "retry failed requests logged to [file] by --log-failed" },
 #ifdef USE_CLIPPERS
   { "ogr-sql", 's', TRUE, "sql to filter inside layer"},
 #endif
@@ -286,80 +307,16 @@ int ogr_features_intersect_tile(mapcache_context *ctx, mapcache_tile *tile)
 
 #endif
 
-int lastmsglen = 0;
-void progresslog(int x, int y, int z)
-{
-  char msg[1024];
-  int nworkers;
-  if(quiet) return;
-  nworkers = nthreads;
-  if(nprocesses >= 1) nworkers = nprocesses;
-
-  sprintf(msg,"seeding tile %d %d %d",x,y,z);
-  if(lastmsglen) {
-    char erasestring[1024];
-    int len = MAPCACHE_MIN(1023,lastmsglen);
-    memset(erasestring,' ',len);
-    erasestring[len+1]='\0';
-    sprintf(erasestring,"\r%%%ds\r",lastmsglen);
-    printf(erasestring," ");
-  }
-  lastmsglen = strlen(msg);
-  printf("%s",msg);
-  fflush(NULL);
-  return;
-
-  if(queuedtilestot>nworkers) {
-    struct mctimeval now_t;
-    float duration;
-    float totalduration;
-    seededtilestot = queuedtilestot - nworkers;
-
-    mapcache_gettimeofday(&now_t,NULL);
-    duration = ((now_t.tv_sec-lastlogtime.tv_sec)*1000000+(now_t.tv_usec-lastlogtime.tv_usec))/1000000.0;
-    totalduration = ((now_t.tv_sec-starttime.tv_sec)*1000000+(now_t.tv_usec-starttime.tv_usec))/1000000.0;
-    if(duration>=5) {
-      int Nx, Ny, Ntot, Ncur, ntilessincelast;
-      Nx = (grid_link->grid_limits[z].maxx-grid_link->grid_limits[z].minx)/tileset->metasize_x;
-      Ny = (grid_link->grid_limits[z].maxy-grid_link->grid_limits[z].miny)/tileset->metasize_y;
-      Ntot = Nx*Ny;
-      Ncur = ((y-grid_link->grid_limits[z].miny)/tileset->metasize_y ) * Nx +
-          (x-grid_link->grid_limits[z].minx+1)/tileset->metasize_x;
-      ntilessincelast = seededtilestot-seededtiles;
-      sprintf(msg,"seeding level %d [%d/%d]: %f metatiles/sec (avg since start: %f)",z,Ncur,Ntot,ntilessincelast/duration,
-              seededtilestot/totalduration);
-      lastlogtime=now_t;
-      seededtiles=seededtilestot;
-    } else {
-      return;
-    }
-  } else {
-    sprintf(msg,"seeding level %d",z);
-
-  }
-  if(lastmsglen) {
-    char erasestring[1024];
-    int len = MAPCACHE_MIN(1023,lastmsglen);
-    memset(erasestring,' ',len);
-    erasestring[len+1]='\0';
-    sprintf(erasestring,"\r%%%ds\r",lastmsglen);
-    printf(erasestring," ");
-  }
-  lastmsglen = strlen(msg);
-  printf("%s",msg);
-  fflush(NULL);
-}
-
 cmd examine_tile(mapcache_context *ctx, mapcache_tile *tile)
 {
   int action = MAPCACHE_CMD_SKIP;
   int intersects = -1;
-  int tile_exists = force?0:tileset->cache->tile_exists(ctx,tile);
+  int tile_exists = force?0:tileset->_cache->tile_exists(ctx,tileset->_cache,tile);
 
   /* if the tile exists and a time limit was specified, check the tile modification date */
   if(tile_exists) {
     if(age_limit) {
-      if(tileset->cache->tile_get(ctx,tile) == MAPCACHE_SUCCESS) {
+      if(tileset->_cache->tile_get(ctx,tileset->_cache, tile) == MAPCACHE_SUCCESS) {
         if(tile->mtime && tile->mtime<age_limit) {
           /* the tile modification time is older than the specified limit */
 #ifdef USE_CLIPPERS
@@ -375,7 +332,7 @@ cmd examine_tile(mapcache_context *ctx, mapcache_tile *tile)
               /* if we are in mode transfer, delete it from the dst tileset */
               if (mode == MAPCACHE_CMD_TRANSFER) {
                 tile->tileset = tileset_transfer;
-                if (tileset_transfer->cache->tile_exists(ctx,tile)) {
+                if (tile->tileset->_cache->tile_exists(ctx,tile->tileset->_cache, tile)) {
                   mapcache_tileset_tile_delete(ctx,tile,MAPCACHE_TRUE);
                 }
                 tile->tileset = tileset;
@@ -401,7 +358,7 @@ cmd examine_tile(mapcache_context *ctx, mapcache_tile *tile)
         /* the tile exists in the source tileset,
            check if the tile exists in the destination cache */
         tile->tileset = tileset_transfer;
-        if (tileset_transfer->cache->tile_exists(ctx,tile)) {
+        if (tile->tileset->_cache->tile_exists(ctx,tile->tileset->_cache, tile)) {
           action = MAPCACHE_CMD_SKIP;
         } else {
           action = MAPCACHE_CMD_TRANSFER;
@@ -450,9 +407,7 @@ void cmd_recurse(mapcache_context *cmd_ctx, mapcache_tile *tile)
   if(sig_int_received || error_detected) { //stop if we were asked to stop by hitting ctrl-c
     //remove all items from the queue
     struct seed_cmd entry;
-    while (trypop_queue(&entry)!=APR_EAGAIN) {
-      queuedtilestot--;
-    }
+    while (trypop_queue(&entry)!=APR_EAGAIN) /*do nothing*/;
     return;
   }
 
@@ -466,8 +421,6 @@ void cmd_recurse(mapcache_context *cmd_ctx, mapcache_tile *tile)
     cmd.z = tile->z;
     cmd.command = action;
     push_queue(cmd);
-    queuedtilestot++;
-    progresslog(tile->x,tile->y,tile->z);
   }
 
   //recurse into our 4 child metatiles
@@ -559,11 +512,16 @@ void cmd_worker()
       if(sig_int_received || error_detected) { //stop if we were asked to stop by hitting ctrl-c
         //remove all items from the queue
         struct seed_cmd entry;
-        while (trypop_queue(&entry)!=APR_EAGAIN) {
-          queuedtilestot--;
-        }
+        while (trypop_queue(&entry)!=APR_EAGAIN) /* do nothing */;
         break;
       }
+      if(iteration_mode == MAPCACHE_ITERATION_LOG) {
+        if(3 != fscanf(retry_log,"%d,%d,%d\n",&x,&y,&z)) {
+          break;
+        } else {
+          printf("from log: %d %d %d\n",x,y,z);
+        }
+      }
       tile->x = x;
       tile->y = y;
       tile->z = z;
@@ -577,8 +535,6 @@ void cmd_worker()
         cmd.z = z;
         cmd.command = action;
         push_queue(cmd);
-        queuedtilestot++;
-        progresslog(x,y,z);
       }
 
       //compute next x,y,z
@@ -603,10 +559,6 @@ void cmd_worker()
     cmd.command = MAPCACHE_CMD_STOP;
     push_queue(cmd);
   }
-
-  if(error_detected && ctx.get_error_message(&ctx)) {
-    printf("%s\n",ctx.get_error_message(&ctx));
-  }
 }
 
 
@@ -633,11 +585,20 @@ void seed_worker()
     if(cmd.command == MAPCACHE_CMD_SEED) {
       /* aquire a lock on the metatile ?*/
       mapcache_metatile *mt = mapcache_tileset_metatile_get(&seed_ctx, tile);
-      int isLocked = mapcache_lock_or_wait_for_resource(&seed_ctx, mapcache_tileset_metatile_resource_key(&seed_ctx,mt));
+      void *lock;
+      int isLocked = mapcache_lock_or_wait_for_resource(&seed_ctx, seed_ctx.config->locker, mapcache_tileset_metatile_resource_key(&seed_ctx,mt), &lock);
       if(isLocked == MAPCACHE_TRUE) {
         /* this will query the source to create the tiles, and save them to the cache */
         mapcache_tileset_render_metatile(&seed_ctx, mt);
-        mapcache_unlock_resource(&seed_ctx, mapcache_tileset_metatile_resource_key(&seed_ctx,mt));
+        if(GC_HAS_ERROR(&seed_ctx)) {
+          /* temporarily clear error state so we don't mess up with error handling in the locker */
+          void *error;
+          seed_ctx.pop_errors(&seed_ctx,&error);
+          mapcache_unlock_resource(&seed_ctx, seed_ctx.config->locker, mapcache_tileset_metatile_resource_key(&seed_ctx,mt), lock);
+          seed_ctx.push_errors(&seed_ctx,error);
+        } else {
+          mapcache_unlock_resource(&seed_ctx, seed_ctx.config->locker, mapcache_tileset_metatile_resource_key(&seed_ctx,mt), lock);
+        }
       }
     } else if (cmd.command == MAPCACHE_CMD_TRANSFER) {
       int i;
@@ -646,14 +607,30 @@ void seed_worker()
         mapcache_tile *subtile = &mt->tiles[i];
         mapcache_tileset_tile_get(&seed_ctx, subtile);
         subtile->tileset = tileset_transfer;
-        tileset_transfer->cache->tile_set(&seed_ctx, subtile);
+        subtile->tileset->_cache->tile_set(&seed_ctx, subtile->tileset->_cache, subtile);
       }
     } else { //CMD_DELETE
       mapcache_tileset_tile_delete(&seed_ctx,tile,MAPCACHE_TRUE);
     }
-    if(seed_ctx.get_error(&seed_ctx)) {
-      error_detected++;
-      ctx.log(&ctx,MAPCACHE_INFO,seed_ctx.get_error_message(&seed_ctx));
+    
+    {
+      struct seed_status *st = calloc(1,sizeof(struct seed_status));
+      st->x=tile->x;
+      st->y=tile->y;
+      st->z=tile->z;
+      if(seed_ctx.get_error(&seed_ctx)) {
+        st->status = MAPCACHE_STATUS_FAIL;
+        st->msg = strdup(seed_ctx.get_error_message(&seed_ctx));
+        seed_ctx.clear_errors(&seed_ctx);
+      } else {
+        st->status = MAPCACHE_STATUS_OK;
+      }
+      ret = apr_queue_push(log_queue,(void*)st);
+      if(ret != APR_SUCCESS)
+      {
+        printf("FATAL ERROR: unable to log progress\n");
+        break;
+      }
     }
   }
 }
@@ -669,6 +646,58 @@ static void* APR_THREAD_FUNC seed_thread(apr_thread_t *thread, void *data) {
   return NULL;
 }
 
+static void* APR_THREAD_FUNC log_thread_fn(apr_thread_t *thread, void *data) {
+  char failed[FAIL_BACKLOG_COUNT];
+  memset(failed,-1,FAIL_BACKLOG_COUNT);
+  size_t cur=0;
+  double last_time=0, now_time;
+  while(1) {
+    struct seed_status *st;
+    apr_status_t ret = apr_queue_pop(log_queue, (void**)&st);
+    if(ret != APR_SUCCESS || !st) break;
+    if(st->status == MAPCACHE_STATUS_FINISHED)
+      return NULL;
+    if(st->status == MAPCACHE_STATUS_OK) {
+      failed[cur]=0;
+      n_metatiles_tot++;
+      if(!quiet) {
+        struct mctimeval now;
+        mapcache_gettimeofday(&now,NULL);
+        now_time = now.tv_sec + now.tv_usec / 1000000.0;
+        if((now_time - last_time) > 1.0) {
+          printf("                                                                                               \r");
+          printf("seeded %d tiles, now at z%d x%d y%d\r",n_metatiles_tot*tileset->metasize_x*tileset->metasize_y, st->z,st->x,st->y);
+          fflush(stdout);
+          last_time = now_time;
+        }
+      }
+    } else {
+      /* count how many errors and successes we have */
+      failed[cur]=1;
+      int i,nfailed=0,ntotal=0;
+      if(failed_log) {
+        fprintf(failed_log,"%d,%d,%d\n",st->x,st->y,st->z);
+      }
+      for(i=0; i<FAIL_BACKLOG_COUNT; i++) {
+        if(failed[i]>=0) ntotal++;
+        if(failed[i]==1) nfailed++; 
+      }
+      ctx.log(&ctx, MAPCACHE_WARN, "failed to seed tile z%d,x%d,y%d:\n%s\n", st->z,st->x,st->y,st->msg);
+      double pct = ((double)nfailed / (double)ntotal) * 100;
+      if(pct > percent_failed_allowed) {
+        ctx.log(&ctx, MAPCACHE_ERROR, "aborting seed as %.1f%% of the last %d requests failed\n", pct, FAIL_BACKLOG_COUNT);
+        error_detected = 1;
+        return NULL;
+      }
+    }
+    if(st->msg) free(st->msg);
+    free(st);
+    cur++;
+    cur %= FAIL_BACKLOG_COUNT;
+  }
+  return NULL;
+}
+
 void
 notice(const char *fmt, ...)
 {
@@ -734,10 +763,12 @@ int main(int argc, const char **argv)
   apr_getopt_t *opt;
   const char *configfile=NULL;
   apr_thread_t **threads;
+  apr_thread_t *log_thread;
   apr_threadattr_t *thread_attrs;
   const char *tileset_name=NULL;
   const char *tileset_transfer_name=NULL;
   const char *grid_name = NULL;
+  const char *cache_override = NULL;
   int *zooms = NULL;//[2];
   mapcache_extent *extent = NULL;//[4];
   int optch;
@@ -762,15 +793,12 @@ int main(int argc, const char **argv)
   (void) signal(SIGINT,handle_sig_int);
   apr_pool_create(&ctx.pool,NULL);
   mapcache_context_init(&ctx);
-  ctx.process_pool = ctx.pool;
   cfg = mapcache_configuration_create(ctx.pool);
   ctx.config = cfg;
   ctx.log= mapcache_context_seeding_log;
   apr_getopt_init(&opt, ctx.pool, argc, argv);
 
-  seededtiles=seededtilestot=queuedtilestot=0;
   mapcache_gettimeofday(&starttime,NULL);
-  lastlogtime=starttime;
   argdimensions = apr_table_make(ctx.pool,3);
 
 
@@ -792,6 +820,9 @@ int main(int argc, const char **argv)
       case 'c':
         configfile = optarg;
         break;
+      case 'C':
+        cache_override = optarg;
+        break;
       case 'g':
         grid_name = optarg;
         break;
@@ -810,6 +841,18 @@ int main(int argc, const char **argv)
           return usage(argv[0],"invalid iteration mode, expecting \"drill-down\" or \"level-by-level\"");
         }
         break;
+      case 'L':
+        failed_log = fopen(optarg,"w");
+        if(!failed_log) {
+          return usage(argv[0],"failed to open -L|--log-failed file for writing");
+        }
+        break;
+      case 'R':
+        retry_log = fopen(optarg,"r");
+        if(!retry_log) {
+          return usage(argv[0],"failed to open -R|--retry_failed file for writing");
+        }
+        break;
       case 'm':
         if(!strcmp(optarg,"delete")) {
           mode = MAPCACHE_CMD_DELETE;
@@ -835,6 +878,12 @@ int main(int argc, const char **argv)
 #else
         return usage(argv[0], "multi process seeding not available on this platform");
 #endif
+      case 'P':
+        percent_failed_allowed = (double)strtol(optarg, NULL, 10);
+        if(percent_failed_allowed<0 || percent_failed_allowed>100 )
+          return usage(argv[0], "failed to parse percent, expecting number between 0 and 100");
+        break;
+
       case 'e':
         if ( MAPCACHE_SUCCESS != mapcache_util_extract_double_list(&ctx, (char*)optarg, ",", &extent_array, &n) ||
              n != 4 || extent_array[0] >= extent_array[2] || extent_array[1] >= extent_array[3] ) {
@@ -914,6 +963,7 @@ int main(int argc, const char **argv)
     mapcache_configuration_post_config(&ctx,cfg);
     if(ctx.get_error(&ctx))
       return usage(argv[0],ctx.get_error_message(&ctx));
+    mapcache_connection_pool_create(&ctx.connection_pool, ctx.pool);
   }
 
 #ifdef USE_CLIPPERS
@@ -1044,6 +1094,10 @@ int main(int argc, const char **argv)
         iteration_mode = MAPCACHE_ITERATION_LEVEL_FIRST;
       }
     }
+    if(retry_log) {
+      iteration_mode = MAPCACHE_ITERATION_LOG;
+    }
+
     if(minzoom == -1 && maxzoom == -1) {
       minzoom = grid_link->minz;
       maxzoom = grid_link->maxz - 1;
@@ -1067,6 +1121,14 @@ int main(int argc, const char **argv)
       }
     }
 
+    if(cache_override) {
+      mapcache_cache *co = mapcache_configuration_get_cache(cfg, cache_override);
+      if(!co) {
+        return usage(argv[0], "overrided cache\"%s\" to not found in configuration", cache_override);
+      } else {
+        tileset->_cache = co;
+      }
+    }
 
   }
 
@@ -1164,6 +1226,17 @@ int main(int argc, const char **argv)
       }
     }
   }
+  
+  {
+  /* start the logging thread */
+    //create the queue where the seeding statuses will be put
+    apr_threadattr_t *log_thread_attrs;
+    apr_queue_create(&log_queue,2,ctx.pool);
+
+    //start the rendering threads.
+    apr_threadattr_create(&log_thread_attrs, ctx.pool);
+    apr_thread_create(&log_thread, log_thread_attrs, log_thread_fn, NULL, ctx.pool);
+  }
 
   if(nthreads == 0 && nprocesses == 0) {
     nthreads = 1;
@@ -1238,16 +1311,25 @@ int main(int argc, const char **argv)
       apr_thread_join(&rv, threads[n]);
     }
   }
-  if(ctx.get_error(&ctx)) {
-    printf("%s",ctx.get_error_message(&ctx));
+  {
+    struct seed_status *st = calloc(1,sizeof(struct seed_status));
+    st->status = MAPCACHE_STATUS_FINISHED;
+    apr_queue_push(log_queue,(void*)st);
+    apr_thread_join(&rv, log_thread);
   }
 
-  if(seededtilestot>0) {
+  if(n_metatiles_tot>0) {
     struct mctimeval now_t;
     float duration;
+    int ntilestot = n_metatiles_tot*tileset->metasize_x*tileset->metasize_y;
     mapcache_gettimeofday(&now_t,NULL);
     duration = ((now_t.tv_sec-starttime.tv_sec)*1000000+(now_t.tv_usec-starttime.tv_usec))/1000000.0;
-    printf("\nseeded %d metatiles at %g tiles/sec\n",seededtilestot, seededtilestot/duration);
+
+    printf("\nseeded %d metatiles (%d tiles) in %.1f seconds at %.1f tiles/sec\n",n_metatiles_tot, ntilestot, duration, ntilestot/duration);
+  } else {
+    if(!error_detected) {
+      printf("0 tiles needed to be seeded, exiting\n");
+    }
   }
   apr_terminate();
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/mapcache.git



More information about the Pkg-grass-devel mailing list