source: subversion/applications/utils/mod_tile/mod_tile.c @ 29283

Last change on this file since 29283 was 29196, checked in by apmon, 7 years ago

[mod_tile] report atime in /status as well.

Add a warning to the status page that neither create time nor access time might be accurate.

Create time is manipulated by the tile expiry process, and atime is often disabled on the filesytem via noatime
for performance reasons.

File size: 89.4 KB
Line 
1#include "apr.h"
2#include "apr_strings.h"
3#include "apr_thread_proc.h"    /* for RLIMIT stuff */
4#include "apr_optional.h"
5#include "apr_buckets.h"
6#include "apr_lib.h"
7#include "apr_poll.h"
8
9#define APR_WANT_STRFUNC
10#define APR_WANT_MEMFUNC
11#include "apr_want.h"
12
13#include "util_filter.h"
14#include "ap_config.h"
15#include "httpd.h"
16#include "http_config.h"
17#include "http_request.h"
18#include "http_core.h"
19#include "http_protocol.h"
20#include "http_main.h"
21#include "http_log.h"
22#include "util_script.h"
23#include "ap_mpm.h"
24#include "mod_core.h"
25#include "mod_cgi.h"
26#include "util_md5.h"
27
28module AP_MODULE_DECLARE_DATA tile_module;
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <strings.h>
34#include <stdarg.h>
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <sys/un.h>
38#include <sys/time.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <errno.h>
42#include <limits.h>
43#include <time.h>
44#include <netinet/in.h>
45#include <arpa/inet.h>
46
47
48#include "gen_tile.h"
49#include "protocol.h"
50#include "render_config.h"
51#include "store.h"
52#include "dir_utils.h"
53#include "mod_tile.h"
54#include "sys_utils.h"
55
56
57#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
58#include "unixd.h"
59#define MOD_TILE_SET_MUTEX_PERMS /* XXX Apache should define something */
60#endif
61
62apr_time_t *last_check = 0;
63apr_time_t *planet_timestamp = 0;
64
65apr_shm_t *stats_shm;
66apr_shm_t *delaypool_shm;
67char *shmfilename;
68char *shmfilename_delaypool;
69apr_global_mutex_t *stats_mutex;
70apr_global_mutex_t *delay_mutex;
71char *mutexfilename;
72int layerCount = 0;
73int global_max_zoom = 0;
74
75static int error_message(request_rec *r, const char *format, ...)
76                 __attribute__ ((format (printf, 2, 3)));
77
78static int error_message(request_rec *r, const char *format, ...)
79{
80    va_list ap;
81    va_start(ap, format);
82    int len;
83    char *msg = malloc(1000*sizeof(char));
84
85    if (msg) {
86        len = vsnprintf(msg, 1000, format, ap);
87        //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", msg);
88        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "%s", msg);
89        r->content_type = "text/plain";
90        if (!r->header_only)
91            ap_rputs(msg, r);
92        free(msg);
93    }
94    va_end(ap);
95    return OK;
96}
97
98int socket_init(request_rec *r)
99{
100    ap_conf_vector_t *sconf = r->server->module_config;
101    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
102
103    int fd;
104    struct sockaddr_un addr;
105
106    fd = socket(PF_UNIX, SOCK_STREAM, 0);
107    if (fd < 0) {
108        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "failed to create unix socket");
109        return FD_INVALID;
110    }
111
112    bzero(&addr, sizeof(addr));
113    addr.sun_family = AF_UNIX;
114    strncpy(addr.sun_path, scfg->renderd_socket_name, sizeof(addr.sun_path) - sizeof(char));
115
116    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
117        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "socket connect failed for: %s with reason: %s", scfg->renderd_socket_name, strerror(errno));
118        close(fd);
119        return FD_INVALID;
120    }
121    return fd;
122}
123
124int request_tile(request_rec *r, struct protocol *cmd, int renderImmediately)
125{
126    int fd;
127    int ret = 0;
128    int retry = 1;
129    struct protocol resp;
130
131    ap_conf_vector_t *sconf = r->server->module_config;
132    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
133
134    fd = socket_init(r);
135
136    if (fd == FD_INVALID) {
137        ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Failed to connect to renderer");
138        return 0;
139    }
140
141    // cmd has already been partial filled, fill in the rest
142    cmd->ver = PROTO_VER;
143    switch (renderImmediately) {
144    case 0: { cmd->cmd = cmdDirty; break;}
145    case 1: { cmd->cmd = cmdRender; break;}
146    case 2: { cmd->cmd = cmdRenderPrio; break;}
147    }
148
149    if (scfg->bulkMode) cmd->cmd = cmdRenderBulk; 
150
151    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Requesting style(%s) z(%d) x(%d) y(%d) from renderer with priority %d", cmd->xmlname, cmd->z, cmd->x, cmd->y, cmd->cmd);
152    do {
153        ret = send(fd, cmd, sizeof(struct protocol), 0);
154
155        if (ret == sizeof(struct protocol))
156            break;
157       
158        if (errno != EPIPE) {
159            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "request_tile: Failed to send request to renderer: %s", strerror(errno));
160            close(fd);
161            return 0;
162        }
163        close(fd);
164
165        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "request_tile: Reconnecting to rendering socket after failed request due to sigpipe");
166
167        fd = socket_init(r);
168        if (fd == FD_INVALID)
169            return 0;
170    } while (retry--);
171
172    if (renderImmediately) {
173        struct timeval tv = {(renderImmediately > 1?scfg->request_timeout_priority:scfg->request_timeout), 0 };
174        fd_set rx;
175        int s;
176
177        while (1) {
178            FD_ZERO(&rx);
179            FD_SET(fd, &rx);
180            s = select(fd+1, &rx, NULL, NULL, &tv);
181            if (s == 1) {
182                bzero(&resp, sizeof(struct protocol));
183                ret = recv(fd, &resp, sizeof(struct protocol), 0);
184                if (ret != sizeof(struct protocol)) {
185                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "request_tile: Failed to read response from rendering socket %s",
186                                  strerror(errno));
187                    break;
188                }
189
190                if (cmd->x == resp.x && cmd->y == resp.y && cmd->z == resp.z && !strcmp(cmd->xmlname, resp.xmlname)) {
191                    close(fd);
192                    if (resp.cmd == cmdDone)
193                        return 1;
194                    else
195                        return 0;
196                } else {
197                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
198                       "Response does not match request: xml(%s,%s) z(%d,%d) x(%d,%d) y(%d,%d)", cmd->xmlname,
199                       resp.xmlname, cmd->z, resp.z, cmd->x, resp.x, cmd->y, resp.y);
200                }
201            } else {
202                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
203                              "request_tile: Request xml(%s) z(%d) x(%d) y(%d) could not be rendered in %i seconds",
204                              cmd->xmlname, cmd->z, cmd->x, cmd->y,
205                              (renderImmediately > 1?scfg->request_timeout_priority:scfg->request_timeout));
206                break;
207            }
208        }
209    }
210
211    close(fd);
212    return 0;
213}
214
215static apr_time_t getPlanetTime(request_rec *r)
216{
217    static pthread_mutex_t planet_lock = PTHREAD_MUTEX_INITIALIZER;
218    apr_time_t now = r->request_time;
219    struct apr_finfo_t s;
220
221    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
222    if (rdata == NULL) {
223        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "No per request configuration data");
224        return planet_timestamp[0];
225    }
226    struct protocol * cmd = rdata->cmd;
227
228
229    pthread_mutex_lock(&planet_lock);
230    // Only check for updates periodically
231    if (now < last_check[rdata->layerNumber] + apr_time_from_sec(300)) {
232        pthread_mutex_unlock(&planet_lock);
233        return planet_timestamp[rdata->layerNumber];
234    }
235
236    ap_conf_vector_t *sconf = r->server->module_config;
237    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
238
239    char filename[PATH_MAX];
240    snprintf(filename, PATH_MAX-1, "%s/%s%s", scfg->tile_dir, cmd->xmlname, PLANET_TIMESTAMP);
241
242    last_check[rdata->layerNumber] = now;
243    if (apr_stat(&s, filename, APR_FINFO_MIN, r->pool) != APR_SUCCESS) {
244        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "per tile style planet time stamp (%s) missing, trying global one", filename);
245        snprintf(filename, PATH_MAX-1, "%s/%s", scfg->tile_dir, PLANET_TIMESTAMP);
246        if (apr_stat(&s, filename, APR_FINFO_MIN, r->pool) != APR_SUCCESS) {
247            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Global planet time stamp file (%s) is missing. Assuming 3 days old.", filename);
248            // Make something up
249            planet_timestamp[rdata->layerNumber] = now - apr_time_from_sec(3 * 24 * 60 * 60);
250        } else {
251            if (s.mtime != planet_timestamp[rdata->layerNumber]) {
252                planet_timestamp[rdata->layerNumber] = s.mtime;
253                char * timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
254                    apr_rfc822_date(timestr, (planet_timestamp[rdata->layerNumber]));
255                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Global planet file time stamp (%s) updated to %s", filename, timestr);
256            }
257        }
258    } else {
259        if (s.mtime != planet_timestamp[rdata->layerNumber]) {
260            planet_timestamp[rdata->layerNumber] = s.mtime;
261            char * timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
262            apr_rfc822_date(timestr, (planet_timestamp[rdata->layerNumber]));
263            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Per style planet file time stamp (%s) updated to %s", filename, timestr);
264        }
265    }
266    pthread_mutex_unlock(&planet_lock);
267    return planet_timestamp[rdata->layerNumber];
268}
269
270static enum tileState tile_state_once(request_rec *r)
271{
272    apr_status_t rv;
273    apr_finfo_t *finfo = &r->finfo;
274
275    if (!(finfo->valid & APR_FINFO_MTIME)) {
276        rv = apr_stat(finfo, r->filename, APR_FINFO_MIN, r->pool);
277        if (rv != APR_SUCCESS) {
278            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_state_once: File %s is missing", r->filename);
279            return tileMissing;
280        }
281    }
282
283    if (finfo->mtime < getPlanetTime(r)) {
284        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_state_once: File %s is old", r->filename);
285        return tileOld;
286    }
287
288    return tileCurrent;
289}
290
291static enum tileState tile_state(request_rec *r, struct protocol *cmd)
292{
293    enum tileState state = tile_state_once(r);
294#ifdef METATILEFALLBACK
295    if (state == tileMissing) {
296        ap_conf_vector_t *sconf = r->server->module_config;
297        tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
298
299        // Try fallback to plain PNG
300        char path[PATH_MAX];
301        xyz_to_path(path, sizeof(path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
302        r->filename = apr_pstrdup(r->pool, path);
303        state = tile_state_once(r);
304        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "png fallback %d/%d/%d",x,y,z);
305
306        if (state == tileMissing) {
307            // PNG not available either, if it gets rendered, it'll now be a .meta
308            xyz_to_meta(path, sizeof(path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
309            r->filename = apr_pstrdup(r->pool, path);
310        }
311    }
312#endif
313    return state;
314}
315
316/**
317 * Add CORS ( Cross-origin resource sharing ) headers. http://www.w3.org/TR/cors/
318 * CORS allows requests that would otherwise be forbidden under the same origin policy.
319 */
320static int add_cors(request_rec *r, const char * cors) {
321    const char* origin = apr_table_get(r->headers_in,"Origin");
322    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Checking if CORS headers need to be added: Origin: %s Policy: %s", origin, cors);       
323    if (!origin) return DONE;
324    else {
325        if ((strcmp(cors,"*") == 0) || strstr(cors, origin)) {
326            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Origin header is allowed under the CORS policy. Adding Access-Control-Allow-Origin");
327            if (strcmp(cors,"*") == 0) {
328                apr_table_setn(r->headers_out, "Access-Control-Allow-Origin",
329                               apr_psprintf(r->pool, "%s", cors));
330            } else {
331                apr_table_setn(r->headers_out, "Access-Control-Allow-Origin",
332                               apr_psprintf(r->pool, "%s", origin));
333                apr_table_setn(r->headers_out, "Vary",
334                               apr_psprintf(r->pool, "%s", "Origin"));
335               
336            }
337            const char* headers = apr_table_get(r->headers_in,"Access-Control-Request-Headers");
338            apr_table_setn(r->headers_out, "Access-Control-Allow-Headers",
339                           apr_psprintf(r->pool, "%s", headers));
340            if (headers) {
341                apr_table_setn(r->headers_out, "Access-Control-Max-Age",
342                               apr_psprintf(r->pool, "%i", 604800));
343            }
344            //If this is an OPTIONS cors pre-flight request, no need to return the body as the actual request will follow
345            if (strcmp(r->method, "OPTIONS") == 0)
346                return OK;
347            else return DONE;
348        } else {
349            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Origin header (%s)is NOT allowed under the CORS policy(%s). Rejecting request", origin, cors);
350            return HTTP_FORBIDDEN;
351        }
352    }
353}
354
355static void add_expiry(request_rec *r, struct protocol * cmd)
356{
357    apr_time_t holdoff;
358    apr_table_t *t = r->headers_out;
359    enum tileState state = tile_state(r, cmd);
360    apr_finfo_t *finfo = &r->finfo;
361    char *timestr;
362    long int planetTimestamp, maxAge, minCache, lastModified;
363
364    ap_conf_vector_t *sconf = r->server->module_config;
365    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
366    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
367    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
368    tile_config_rec *tile_config = &tile_configs[rdata->layerNumber];
369
370    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "expires(%s), uri(%s), filename(%s), path_info(%s)\n",
371                  r->handler, r->uri, r->filename, r->path_info);
372
373    /* If the hostname matches the "extended caching hostname" then set the cache age accordingly */
374    if ((scfg->cache_extended_hostname[0] != 0) && (strstr(r->hostname,
375            scfg->cache_extended_hostname) != NULL)) {
376        maxAge = scfg->cache_extended_duration;
377    } else {
378
379        /* Test if the tile we are serving is out of date, then set a low maxAge*/
380        if (state == tileOld) {
381            holdoff = (scfg->cache_duration_dirty / 2) * (rand() / (RAND_MAX
382                    + 1.0));
383            maxAge = scfg->cache_duration_dirty + holdoff;
384        } else {
385            // cache heuristic based on zoom level
386            if (cmd->z > tile_config->maxzoom) {
387                minCache = 0;
388                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
389                        "z (%i) is larger than MAXZOOM %i\n", cmd->z, tile_config->maxzoom);
390            } else {
391                minCache = scfg->mincachetime[cmd->z];
392            }
393            // Time to the next known complete rerender
394            planetTimestamp = apr_time_sec(getPlanetTime(r)
395                    + apr_time_from_sec(PLANET_INTERVAL) - r->request_time);
396            // Time since the last render of this tile
397            lastModified = (int) (((double) apr_time_sec(r->request_time
398                    - finfo->mtime))
399                    * scfg->cache_duration_last_modified_factor);
400            // Add a random jitter of 3 hours to space out cache expiry
401            holdoff = (3 * 60 * 60) * (rand() / (RAND_MAX + 1.0));
402
403            maxAge = MAX(minCache, planetTimestamp);
404            maxAge = MAX(maxAge, lastModified);
405            maxAge += holdoff;
406
407            ap_log_rerror(
408                    APLOG_MARK,
409                    APLOG_DEBUG,
410                    0,
411                    r,
412                    "caching heuristics: next planet render %ld; zoom level based %ld; last modified %ld\n",
413                    planetTimestamp, minCache, lastModified);
414        }
415
416        maxAge = MIN(maxAge, scfg->cache_duration_max);
417    }
418
419    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Setting tiles maxAge to %ld\n", maxAge);
420
421    apr_table_mergen(t, "Cache-Control",
422                     apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
423                     maxAge));
424    timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
425    apr_rfc822_date(timestr, (apr_time_from_sec(maxAge) + r->request_time));
426    apr_table_setn(t, "Expires", timestr);
427}
428
429
430
431static int get_global_lock(request_rec *r, apr_global_mutex_t * mutex) {
432    apr_status_t rs;
433    int camped;
434
435    for (camped = 0; camped < MAXCAMP; camped++) {
436        rs = apr_global_mutex_trylock(mutex);
437        if (APR_STATUS_IS_EBUSY(rs)) {
438            apr_sleep(CAMPOUT);
439        } else if (rs == APR_SUCCESS) {
440            return 1;
441        } else if (APR_STATUS_IS_ENOTIMPL(rs)) {
442            /* If it's not implemented, just hang in the mutex. */
443            rs = apr_global_mutex_lock(mutex);
444            if (rs == APR_SUCCESS) {
445                return 1;
446            } else {
447                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Could not get hardlock");
448                return 0;
449            }
450        } else {
451            ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, "Unknown return status from trylock");
452            return 0;
453        }
454    }
455    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Timedout trylock");
456    return 0;
457}
458
459static int incRespCounter(int resp, request_rec *r, struct protocol * cmd, int layerNumber) {
460    stats_data *stats;
461
462    ap_conf_vector_t *sconf = r->server->module_config;
463    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
464
465    if (!scfg->enableGlobalStats) {
466        /* If tile stats reporting is not enable
467         * pretend we correctly updated the counter to
468         * not fill the logs with warnings about failed
469         * stats
470         */
471        return 1;
472    }
473
474    if (get_global_lock(r, stats_mutex) != 0) {
475        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
476        switch (resp) {
477        case OK: {
478            stats->noResp200++;
479            if (cmd != NULL) {
480                stats->noRespZoom[cmd->z]++;
481                stats->noResp200Layer[layerNumber]++;
482            }
483            break;
484        }
485        case HTTP_NOT_MODIFIED: {
486            stats->noResp304++;
487            if (cmd != NULL) {
488                stats->noRespZoom[cmd->z]++;
489                stats->noResp200Layer[layerNumber]++;
490            }
491            break;
492        }
493        case HTTP_NOT_FOUND: {
494            stats->noResp404++;
495            stats->noResp404Layer[layerNumber]++;
496            break;
497        }
498        case HTTP_SERVICE_UNAVAILABLE: {
499            stats->noResp503++;
500            break;
501        }
502        case HTTP_INTERNAL_SERVER_ERROR: {
503            stats->noResp5XX++;
504            break;
505        }
506        default: {
507            stats->noRespOther++;
508        }
509
510        }
511        apr_global_mutex_unlock(stats_mutex);
512        /* Swallowing the result because what are we going to do with it at
513         * this stage?
514         */
515        return 1;
516    } else {
517        return 0;
518    }
519}
520
521static int incFreshCounter(int status, request_rec *r) {
522    stats_data *stats;
523
524    ap_conf_vector_t *sconf = r->server->module_config;
525    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
526
527    if (!scfg->enableGlobalStats) {
528        /* If tile stats reporting is not enable
529         * pretend we correctly updated the counter to
530         * not fill the logs with warnings about failed
531         * stats
532         */
533        return 1;
534    }
535
536    if (get_global_lock(r, stats_mutex) != 0) {
537        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
538        switch (status) {
539        case FRESH: {
540            stats->noFreshCache++;
541            break;
542        }
543        case FRESH_RENDER: {
544            stats->noFreshRender++;
545            break;
546        }
547        case OLD: {
548            stats->noOldCache++;
549            break;
550        }
551        case OLD_RENDER: {
552            stats->noOldRender++;
553            break;
554        }
555        }
556        apr_global_mutex_unlock(stats_mutex);
557        /* Swallowing the result because what are we going to do with it at
558         * this stage?
559         */
560        return 1;
561    } else {
562        return 0;
563    }
564}
565
566static int incTimingCounter(apr_uint64_t duration, int z, request_rec *r) {
567    stats_data *stats;
568
569    ap_conf_vector_t *sconf = r->server->module_config;
570    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
571
572    if (!scfg->enableGlobalStats) {
573        /* If tile stats reporting is not enable
574         * pretend we correctly updated the counter to
575         * not fill the logs with warnings about failed
576         * stats
577         */
578        return 1;
579    }
580
581    if (get_global_lock(r, stats_mutex) != 0) {
582        stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
583        stats->totalBufferRetrievalTime += duration;
584        stats->zoomBufferRetrievalTime[z] += duration;
585        stats->noTotalBufferRetrieval++;
586        stats->noZoomBufferRetrieval[z]++;
587        apr_global_mutex_unlock(stats_mutex);
588        /* Swallowing the result because what are we going to do with it at
589         * this stage?
590         */
591        return 1;
592    } else {
593        return 0;
594    }
595}
596
597static int delay_allowed(request_rec *r, enum tileState state) {
598    delaypool * delayp;
599    int delay = 0;
600    int i,j;
601    int hashkey;
602    const char * ip_addr = NULL;
603
604    ap_conf_vector_t *sconf = r->server->module_config;
605    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
606    delayp = (delaypool *)apr_shm_baseaddr_get(delaypool_shm);
607
608    ip_addr = r->connection->remote_ip;
609    if (scfg->enableTileThrottlingXForward){
610        const char * ip_addrs = apr_table_get(r->headers_in,"X-Forwarded-For");
611        if (ip_addrs) {
612            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Checking throttling delays: Found X-Forward-For header \"%s\", forwarded by %s", ip_addrs, r->connection->remote_ip);
613            //X-Forwarded-For can be a chain of proxies deliminated by , The first entry in the list is the client, the last entry is the remote address seen by the proxy
614            //closest to the tileserver.
615            char ** state = NULL;
616            const char * tmp = apr_strtok(ip_addrs,", ",state);
617            ip_addr = tmp; 
618           
619            //Use the last entry in the chain of X-Forwarded-For instead of the client, i.e. the entry added by the proxy closest to the tileserver
620            //If this is a reverse proxy under our control, its X-Forwarded-For can be trusted.
621            if (scfg->enableTileThrottlingXForward == 2) {
622                while (tmp = apr_strtok(NULL,", ",state)) {
623                    ip_addr = tmp;
624                }
625            }
626           
627            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Checking throttling delays for IP %s, forwarded by %s", ip_addr, r->connection->remote_ip);
628        }
629    }
630
631    struct in_addr sin_addr;
632    struct in6_addr ip;
633    if (inet_pton(AF_INET,ip_addr,&sin_addr) > 0) {
634        //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Checking delays: for IP %s appears to be an IPv4 address", ip_addr);
635        memset(ip.s6_addr,0,16);
636        memcpy(&(ip.s6_addr[12]), &(sin_addr.s_addr), 4);
637        uint32_t hashkey = sin_addr.s_addr % DELAY_HASHTABLE_WHITELIST_SIZE;
638        if (delayp->whitelist[hashkey] == sin_addr.s_addr) {
639            return 1;
640        }
641    } else {
642        if (inet_pton(AF_INET6,ip_addr,&ip) <= 0) {
643            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Checking delays: for IP %s. Don't know what it is", ip_addr);
644            return 0;
645        }
646    }
647
648    hashkey = (*((uint32_t *)(&ip.s6_addr[0])) ^ *((uint32_t *)(&ip.s6_addr[4])) ^ *((uint32_t *)(&ip.s6_addr[8])) ^ *((uint32_t *)(&ip.s6_addr[12]))) % DELAY_HASHTABLE_SIZE;
649   
650    /* If a delaypool fillup is ongoing, just skip accounting to not block on a lock */
651    if (delayp->locked) {
652        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "skipping delay pool accounting, during fillup procedure\n");
653        return 1;
654    }
655   
656    if (get_global_lock(r,delay_mutex) == 0) {
657        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Could not acquire lock, skipping delay pool accounting\n");
658        return 1;
659    };
660
661    if (memcmp(&(delayp->users[hashkey].ip_addr), &ip, sizeof(struct in6_addr)) == 0) {
662        /* Repeat the process to determine if we have tockens in the bucket, as the fillup only runs once a client hits an empty bucket,
663           so in the mean time, the bucket might have been filled */
664        for (j = 0; j < 3; j++) {
665            //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Checking delays: Current poolsize: %i tiles and %i renders\n", delayp->users[hashkey].available_tiles, delayp->users[hashkey].available_render_req);
666            delay = 0;
667            if (delayp->users[hashkey].available_tiles > 0) {
668                delayp->users[hashkey].available_tiles--;
669            } else {
670                delay = 1;
671            }
672            if (state == tileMissing) {
673                if (delayp->users[hashkey].available_render_req > 0) {
674                    delayp->users[hashkey].available_render_req--;
675                } else {
676                    delay = 2;
677                }
678            }
679
680            if (delay > 0) {
681                /* If we are on the second round, we really  hit an empty delaypool, timeout for a while to slow down clients */
682                if (j > 0) {
683                    apr_global_mutex_unlock(delay_mutex);
684                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Delaypool: Client %s has hit its limits, throttling (%i)\n", ip_addr, delay);
685                    sleep(CLIENT_PENALTY);
686                    if (get_global_lock(r,delay_mutex) == 0) {
687                        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Could not acquire lock, but had to delay\n");
688                        return 0;
689                    };
690                }
691                /* We hit an empty bucket, so run the bucket fillup procedure to check if new tokens should have arrived in the mean time. */
692                apr_time_t now = apr_time_now();
693                int tiles_topup = (now - delayp->last_tile_fillup) / scfg->delaypoolTileRate;
694                int render_topup = (now - delayp->last_render_fillup) / scfg->delaypoolRenderRate;
695                //ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Filling up pools with %i tiles and %i renders\n", tiles_topup, render_topup);
696                if ((tiles_topup > 0) || (render_topup > 0)) {
697                    delayp->locked = 1;
698                    for (i = 0; i < DELAY_HASHTABLE_SIZE; i++) {
699                        delayp->users[i].available_tiles += tiles_topup;
700                        delayp->users[i].available_render_req += render_topup;
701                        if (delayp->users[i].available_tiles > scfg->delaypoolTileSize) {
702                            delayp->users[i].available_tiles = scfg->delaypoolTileSize;
703                        }
704                        if (delayp->users[i].available_render_req > scfg->delaypoolRenderSize) {
705                            delayp->users[i].available_render_req = scfg->delaypoolRenderSize;
706                        }
707                    }
708                    delayp->locked = 0;
709                }
710                delayp->last_tile_fillup += scfg->delaypoolTileRate*tiles_topup;
711                delayp->last_render_fillup += scfg->delaypoolRenderRate*render_topup;               
712               
713            } else {
714                break;
715            }
716        }
717    } else {
718        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Creating a new delaypool for ip %s\n", ip_addr);
719        memcpy(&(delayp->users[hashkey].ip_addr), &ip, sizeof(struct in6_addr));
720        delayp->users[hashkey].available_tiles = scfg->delaypoolTileSize;
721        delayp->users[hashkey].available_render_req = scfg->delaypoolRenderSize;
722        delay = 0;
723    }
724    apr_global_mutex_unlock(delay_mutex);
725
726    if (delay > 0) {
727        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Delaypool: Client %s has hit its limits, rejecting (%i)\n", ip_addr, delay);
728        return 0;
729    } else {
730        return 1;
731    }
732}
733
734static int tile_handler_dirty(request_rec *r)
735{
736    if(strcmp(r->handler, "tile_dirty"))
737        return DECLINED;
738
739    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
740    struct protocol * cmd = rdata->cmd;
741    if (cmd == NULL)
742        return DECLINED;
743
744    ap_conf_vector_t *sconf = r->server->module_config;
745    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
746    if (scfg->bulkMode) return OK;
747
748    request_tile(r, cmd, 0);
749    return error_message(r, "Tile submitted for rendering\n");
750}
751
752static int tile_storage_hook(request_rec *r)
753{
754//    char abs_path[PATH_MAX];
755    double avg;
756    int renderPrio = 0;
757    enum tileState state;
758
759    if (!r->handler)
760        return DECLINED;
761
762    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "tile_storage_hook: handler(%s), uri(%s), filename(%s), path_info(%s)",
763                  r->handler, r->uri, r->filename, r->path_info);
764
765    // Any status request is OK. tile_dirty also doesn't need to be handled, as tile_handler_dirty will take care of it
766    if (!strcmp(r->handler, "tile_status") || !strcmp(r->handler, "tile_dirty") || !strcmp(r->handler, "tile_mod_stats")
767            || !(strcmp(r->handler, "tile_json")))
768        return OK;
769
770    if (strcmp(r->handler, "tile_serve"))
771        return DECLINED;
772
773    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
774    struct protocol * cmd = rdata->cmd;
775    if (cmd == NULL)
776        return DECLINED;
777
778/*
779should already be done
780    // Generate the tile filename
781#ifdef METATILE
782    ap_conf_vector_t *sconf = r->server->module_config;
783    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
784    xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
785#else
786    xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
787#endif
788    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "abs_path(%s)", abs_path);
789    r->filename = apr_pstrdup(r->pool, abs_path);
790*/
791    avg = get_load_avg();
792    state = tile_state(r, cmd);
793
794    ap_conf_vector_t *sconf = r->server->module_config;
795    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
796
797    if (scfg->enableTileThrottling && !delay_allowed(r, state)) {
798        if (!incRespCounter(HTTP_SERVICE_UNAVAILABLE, r, cmd, rdata->layerNumber)) {
799                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
800                        "Failed to increase response stats counter");
801        }
802        return HTTP_SERVICE_UNAVAILABLE;       
803    }
804
805    switch (state) {
806        case tileCurrent:
807            if (!incFreshCounter(FRESH, r)) {
808                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
809                    "Failed to increase fresh stats counter");
810            }
811            return OK;
812            break;
813        case tileOld:
814            if (scfg->bulkMode) {
815                return OK;
816            } else if (avg > scfg->max_load_old) {
817               // Too much load to render it now, mark dirty but return old tile
818               request_tile(r, cmd, 0);
819               ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Load (%f) larger max_load_old (%d). Mark dirty and deliver from cache.", avg, scfg->max_load_old);
820               if (!incFreshCounter(OLD, r)) {
821                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
822                        "Failed to increase fresh stats counter");
823               }
824               return OK;
825            }
826            renderPrio = 1;
827            break;
828        case tileMissing:
829            if (avg > scfg->max_load_missing) {
830               request_tile(r, cmd, 0);
831               ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Load (%f) larger max_load_missing (%d). Return HTTP_NOT_FOUND.", avg, scfg->max_load_missing);
832               if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
833                   ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
834                        "Failed to increase response stats counter");
835               }
836               return HTTP_NOT_FOUND;
837            }
838            renderPrio = 2;
839            break;
840    }
841
842    if (request_tile(r, cmd, renderPrio)) {
843        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Update file info abs_path(%s)", r->filename);
844        // Need to update fileinfo for new rendered tile
845        apr_stat(&r->finfo, r->filename, APR_FINFO_MIN, r->pool);
846        if (!incFreshCounter(FRESH_RENDER, r)) {
847            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
848                    "Failed to increase fresh stats counter");
849        }
850        return OK;
851    }
852
853    if (state == tileOld) {
854        if (!incFreshCounter(OLD_RENDER, r)) {
855            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
856                    "Failed to increase fresh stats counter");
857        }
858        return OK;
859    }
860    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_storage_hook: Missing tile was not rendered in time. Returning File Not Found");
861    if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
862        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
863                "Failed to increase response stats counter");
864    }
865
866    return HTTP_NOT_FOUND;
867}
868
869static int tile_handler_status(request_rec *r)
870{
871    enum tileState state;
872    char mtime_str[APR_CTIME_LEN];
873    char atime_str[APR_CTIME_LEN];
874
875    if(strcmp(r->handler, "tile_status"))
876        return DECLINED;
877
878    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
879    struct protocol * cmd = rdata->cmd;
880    if (cmd == NULL){
881        sleep(CLIENT_PENALTY);
882        return HTTP_NOT_FOUND;
883    }
884
885    state = tile_state(r, cmd);
886    if (state == tileMissing)
887        return error_message(r, "Unable to find a tile at %s\n", r->filename);
888    apr_ctime(mtime_str, r->finfo.mtime);
889    apr_ctime(atime_str, r->finfo.atime);
890
891    return error_message(r, "Tile is %s. Last rendered at %s. Last accessed at %s\n\n(Dates might not be accurate. Rendering time might be reset to an old date for tile expiry. Access times might not be updated on all filesystems)\n", (state == tileOld) ? "due to be rendered" : "clean", mtime_str, atime_str);
892}
893
894/**
895 * Implement a tilejson description page for the tile layer.
896 * This follows the tilejson specification of mapbox ( https://github.com/mapbox/tilejson-spec/tree/master/2.0.0 )
897 */
898static int tile_handler_json(request_rec *r)
899{
900    unsigned char *buf;
901    int len;
902    enum tileState state;
903    char *timestr;
904    long int maxAge = 7*24*60*60;
905    apr_table_t *t = r->headers_out;
906    int i;
907
908    if(strcmp(r->handler, "tile_json"))
909        return DECLINED;
910
911    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Handling tile json request\n");
912
913    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
914    ap_conf_vector_t *sconf = r->server->module_config;
915    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
916    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
917    tile_config_rec *tile_config = &tile_configs[rdata->layerNumber];
918    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Handling tile json request for layer %s\n", tile_config->xmlname);
919
920    if (tile_config->cors) {
921        int resp = add_cors(r, tile_config->cors);
922        if (resp != DONE) return resp;
923    }
924
925    buf = malloc(8*1024);
926
927    snprintf(buf, 8*1024,
928            "{\n"
929            "\t\"tilejson\": \"2.0.0\",\n"
930            "\t\"schema\": \"xyz\",\n"
931            "\t\"name\": \"%s\",\n"
932            "\t\"description\": \"%s\",\n"
933            "\t\"attribution\": \"%s\",\n"
934            "\t\"minzoom\": %i,\n"
935            "\t\"maxzoom\": %i,\n"
936            "\t\"tiles\": [\n",
937            tile_config->xmlname, (tile_config->description?tile_config->description:""), tile_config->attribution, tile_config->minzoom, tile_config->maxzoom);
938    for (i = 0; i < tile_config->noHostnames; i++) {
939        strncat(buf,"\t\t\"", 8*1024-strlen(buf)-1);
940        strncat(buf,tile_config->hostnames[i], 8*1024-strlen(buf)-1);
941        strncat(buf,tile_config->baseuri,8*1024-strlen(buf)-1);
942        strncat(buf,"{z}/{x}/{y}.",8*1024-strlen(buf)-1);
943        strncat(buf,tile_config->fileExtension, 8*1024-strlen(buf)-1);
944        strncat(buf,"\"", 8*1024-strlen(buf)-1);
945        if (i < tile_config->noHostnames - 1) strncat(buf,",", 8*1024-strlen(buf)-1);
946        strncat(buf,"\n", 8*1024-strlen(buf)-1);
947    }
948    strncat(buf,"\t]\n}\n", 8*1024-strlen(buf)-1);
949    len = strlen(buf);
950
951    /*
952     * Add HTTP headers. Make this file cachable for 1 week
953     */
954    char *md5 = ap_md5_binary(r->pool, buf, len);
955    apr_table_setn(r->headers_out, "ETag",
956            apr_psprintf(r->pool, "\"%s\"", md5));
957    ap_set_content_type(r, "application/json");
958    ap_set_content_length(r, len);
959    apr_table_mergen(t, "Cache-Control",
960            apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
961                    maxAge));
962    timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
963    apr_rfc822_date(timestr, (apr_time_from_sec(maxAge) + r->request_time));
964    apr_table_setn(t, "Expires", timestr);
965    ap_rwrite(buf, len, r);
966    free(buf);
967
968    return OK;
969}
970
971static int tile_handler_mod_stats(request_rec *r)
972{
973    stats_data * stats;
974    stats_data local_stats;
975    int i;
976
977    if (strcmp(r->handler, "tile_mod_stats"))
978        return DECLINED;
979
980    ap_conf_vector_t *sconf = r->server->module_config;
981    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
982    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
983
984    if (!scfg->enableGlobalStats) {
985        return error_message(r, "Stats are not enabled for this server");
986    }
987
988    if (get_global_lock(r,stats_mutex) != 0) {
989        //Copy over the global counter variable into
990        //local variables, that we can immediately
991        //release the lock again
992        stats = (stats_data *) apr_shm_baseaddr_get(stats_shm);
993        memcpy(&local_stats, stats, sizeof(stats_data));
994        local_stats.noResp200Layer = malloc(sizeof(apr_uint64_t) * scfg->configs->nelts);
995        memcpy(local_stats.noResp200Layer, stats->noResp200Layer, sizeof(apr_uint64_t) * scfg->configs->nelts);
996        local_stats.noResp404Layer = malloc(sizeof(apr_uint64_t) * scfg->configs->nelts);
997        memcpy(local_stats.noResp404Layer, stats->noResp404Layer, sizeof(apr_uint64_t) * scfg->configs->nelts);
998        apr_global_mutex_unlock(stats_mutex);
999    } else {
1000        return error_message(r, "Failed to acquire lock, can't display stats");
1001    }
1002
1003    ap_rprintf(r, "NoResp200: %li\n", local_stats.noResp200);
1004    ap_rprintf(r, "NoResp304: %li\n", local_stats.noResp304);
1005    ap_rprintf(r, "NoResp404: %li\n", local_stats.noResp404);
1006    ap_rprintf(r, "NoResp503: %li\n", local_stats.noResp503);
1007    ap_rprintf(r, "NoResp5XX: %li\n", local_stats.noResp5XX);
1008    ap_rprintf(r, "NoRespOther: %li\n", local_stats.noRespOther);
1009    ap_rprintf(r, "NoFreshCache: %li\n", local_stats.noFreshCache);
1010    ap_rprintf(r, "NoOldCache: %li\n", local_stats.noOldCache);
1011    ap_rprintf(r, "NoFreshRender: %li\n", local_stats.noFreshRender);
1012    ap_rprintf(r, "NoOldRender: %li\n", local_stats.noOldRender);
1013    for (i = 0; i <= global_max_zoom; i++) {
1014        ap_rprintf(r, "NoRespZoom%02i: %li\n", i, local_stats.noRespZoom[i]);
1015    }
1016    ap_rprintf(r, "NoTileBufferReads: %li\n", local_stats.noTotalBufferRetrieval);
1017    ap_rprintf(r, "DurationTileBufferReads: %li\n", local_stats.totalBufferRetrievalTime);
1018    for (i = 0; i <= global_max_zoom; i++) {
1019        ap_rprintf(r, "NoTileBufferReadZoom%02i: %li\n", i, local_stats.noZoomBufferRetrieval[i]);
1020        ap_rprintf(r, "DurationTileBufferReadZoom%02i: %li\n", i, local_stats.zoomBufferRetrievalTime[i]);
1021    }
1022
1023    for (i = 0; i < scfg->configs->nelts; ++i) {
1024        tile_config_rec *tile_config = &tile_configs[i];
1025        ap_rprintf(r,"NoRes200Layer%s: %li\n", tile_config->baseuri, local_stats.noResp200Layer[i]);
1026        ap_rprintf(r,"NoRes404Layer%s: %li\n", tile_config->baseuri, local_stats.noResp404Layer[i]);
1027    }
1028    free(local_stats.noResp200Layer);
1029    free(local_stats.noResp404Layer);
1030    return OK;
1031}
1032
1033static int tile_handler_serve(request_rec *r)
1034{
1035    const int tile_max = MAX_SIZE;
1036    unsigned char err_msg[4096];
1037    unsigned char *buf;
1038    int len;
1039    int compressed;
1040    apr_status_t errstatus;
1041    struct timeval start, end;
1042
1043    ap_conf_vector_t *sconf = r->server->module_config;
1044    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
1045
1046    if(strcmp(r->handler, "tile_serve"))
1047        return DECLINED;
1048
1049    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
1050    struct protocol * cmd = rdata->cmd;
1051    if (cmd == NULL){
1052        sleep(CLIENT_PENALTY);
1053        if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
1054            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1055                    "Failed to increase response stats counter");
1056        }
1057        return HTTP_NOT_FOUND;
1058    }
1059
1060    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_handler_serve: xml(%s) z(%d) x(%d) y(%d)", cmd->xmlname, cmd->z, cmd->x, cmd->y);
1061
1062    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
1063
1064    if (tile_configs[rdata->layerNumber].cors) {
1065        int resp = add_cors(r, tile_configs[rdata->layerNumber].cors);
1066        if (resp != DONE) return resp;
1067    }
1068
1069    gettimeofday(&start,NULL);
1070
1071    // FIXME: It is a waste to do the malloc + read if we are fulfilling a HEAD or returning a 304.
1072    buf = malloc(tile_max);
1073    if (!buf) {
1074        if (!incRespCounter(HTTP_INTERNAL_SERVER_ERROR, r, cmd, rdata->layerNumber)) {
1075            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1076                    "Failed to increase response stats counter");
1077        }
1078        return HTTP_INTERNAL_SERVER_ERROR;
1079    }
1080
1081    err_msg[0] = 0;
1082    len = tile_read(scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z, buf, tile_max, &compressed, err_msg);
1083    if (len > 0) {
1084        if (compressed) {
1085            const char* accept_encoding = apr_table_get(r->headers_in,"Accept-Encoding");
1086            if (accept_encoding && strstr(accept_encoding,"gzip")) {
1087                r->content_encoding = "gzip";
1088            } else {
1089                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1090                              "Tile data is compressed, but user agent doesn't support Content-Encoding and we don't know how to decompress it server side");
1091                //TODO: decompress the output stream before sending it to client
1092            }
1093        }
1094       
1095#if 0
1096        // Set default Last-Modified and Etag headers
1097        ap_update_mtime(r, r->finfo.mtime);
1098        ap_set_last_modified(r);
1099        ap_set_etag(r);
1100#else
1101        // Use MD5 hash as only cache attribute.
1102        // If a tile is re-rendered and produces the same output
1103        // then we can continue to use the previous cached copy
1104        char *md5 = ap_md5_binary(r->pool, buf, len);
1105        apr_table_setn(r->headers_out, "ETag",
1106                        apr_psprintf(r->pool, "\"%s\"", md5));
1107#endif
1108        ap_set_content_type(r, tile_configs[rdata->layerNumber].mimeType);
1109        ap_set_content_length(r, len);
1110        add_expiry(r, cmd);
1111
1112        gettimeofday(&end,NULL);
1113        incTimingCounter((end.tv_sec*1000000 + end.tv_usec) - (start.tv_sec*1000000 + start.tv_usec) , cmd->z, r);
1114       
1115        if ((errstatus = ap_meets_conditions(r)) != OK) {
1116            free(buf);
1117            if (!incRespCounter(errstatus, r, cmd, rdata->layerNumber)) {
1118                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1119                        "Failed to increase response stats counter");
1120            }
1121            return errstatus;
1122        } else {
1123            ap_rwrite(buf, len, r);
1124            free(buf);
1125            if (!incRespCounter(errstatus, r, cmd, rdata->layerNumber)) {
1126                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1127                        "Failed to increase response stats counter");
1128            }
1129            return OK;
1130        }
1131    }
1132    free(buf);
1133    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Failed to read tile from disk: %s", err_msg);
1134    if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
1135        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1136                "Failed to increase response stats counter");
1137    }
1138    return DECLINED;
1139}
1140
1141static int tile_translate(request_rec *r)
1142{
1143    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: uri(%s)", r->uri);
1144
1145    int i,n,limit,oob;
1146    char option[11];
1147
1148    ap_conf_vector_t *sconf = r->server->module_config;
1149    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
1150
1151    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
1152
1153    /*
1154     * The page /mod_tile returns global stats about the number of tiles
1155     * handled and in what state those tiles were.
1156     * This should probably not be hard coded
1157     */
1158    if (!strncmp("/mod_tile", r->uri, strlen("/mod_tile"))) {
1159        r->handler = "tile_mod_stats";
1160        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1161                "tile_translate: retrieving global mod_tile stats");
1162        return OK;
1163    }
1164
1165    for (i = 0; i < scfg->configs->nelts; ++i) {
1166        tile_config_rec *tile_config = &tile_configs[i];
1167
1168        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: testing baseuri(%s) name(%s) extension(%s)",
1169                tile_config->baseuri, tile_config->xmlname, tile_config->fileExtension );
1170
1171
1172        if (!strncmp(tile_config->baseuri, r->uri, strlen(tile_config->baseuri))) {
1173
1174            struct tile_request_data * rdata = (struct tile_request_data *) apr_pcalloc(r->pool, sizeof(struct tile_request_data));
1175            struct protocol * cmd = (struct protocol *) apr_pcalloc(r->pool, sizeof(struct protocol));
1176            bzero(cmd, sizeof(struct protocol));
1177            bzero(rdata, sizeof(struct tile_request_data));
1178            if (!strncmp(r->uri + strlen(tile_config->baseuri),"tile-layer.json", strlen("tile-layer.json"))) {
1179                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Requesting tileJSON for tilelayer %s", tile_config->xmlname);
1180                r->handler = "tile_json";
1181                rdata->layerNumber = i;
1182                ap_set_module_config(r->request_config, &tile_module, rdata);
1183                return OK;
1184            }
1185            char extension[256];
1186            n = sscanf(r->uri+strlen(tile_config->baseuri),"%d/%d/%d.%[a-z]/%10s", &(cmd->z), &(cmd->x), &(cmd->y), extension, option);
1187            if (n < 4) {
1188                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid URL for tilelayer %s", tile_config->xmlname);
1189                return DECLINED;
1190            }
1191            if (strcmp(extension, tile_config->fileExtension) != 0) {
1192                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid file extension (%s) for tilelayer %s, required %s",
1193                        extension, tile_config->xmlname, tile_config->fileExtension);
1194                return DECLINED;
1195            }
1196
1197            oob = (cmd->z < tile_config->minzoom || cmd->z > tile_config->maxzoom);
1198            if (!oob) {
1199                 // valid x/y for tiles are 0 ... 2^zoom-1
1200                 limit = (1 << cmd->z) - 1;
1201                 oob =  (cmd->x < 0 || cmd->x > limit || cmd->y < 0 || cmd->y > limit);
1202            }
1203
1204            if (oob) {
1205                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: request for %s was outside of allowed bounds", tile_config->xmlname);
1206                sleep(CLIENT_PENALTY);
1207                //Don't increase stats counter here,
1208                //As we are interested in valid tiles only
1209                return HTTP_NOT_FOUND;
1210            }
1211
1212            strcpy(cmd->xmlname, tile_config->xmlname);
1213
1214            // Store a copy for later
1215            rdata->cmd = cmd;
1216            rdata->layerNumber = i;
1217            ap_set_module_config(r->request_config, &tile_module, rdata);
1218
1219            // Generate the tile filename?
1220            char abs_path[PATH_MAX];
1221#ifdef METATILE
1222            xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
1223#else
1224            xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
1225#endif
1226            r->filename = apr_pstrdup(r->pool, abs_path);
1227
1228            if (n == 5) {
1229                if (!strcmp(option, "status")) r->handler = "tile_status";
1230                else if (!strcmp(option, "dirty")) r->handler = "tile_dirty";
1231                else return DECLINED;
1232            } else {
1233                r->handler = "tile_serve";
1234            }
1235
1236            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: op(%s) xml(%s) mime(%s) z(%d) x(%d) y(%d)",
1237                    r->handler , cmd->xmlname, tile_config->mimeType, cmd->z, cmd->x, cmd->y);
1238
1239            return OK;
1240        }
1241    }
1242    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: No suitable tile layer found");
1243    return DECLINED;
1244}
1245
1246/*
1247 * This routine is called in the parent, so we'll set up the shared
1248 * memory segment and mutex here.
1249 */
1250
1251static int mod_tile_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1252    apr_pool_t *ptemp, server_rec *s)
1253{
1254    void *data; /* These two help ensure that we only init once. */
1255    const char *userdata_key = "mod_tile_init_module";
1256    apr_status_t rs;
1257    stats_data *stats;
1258    delaypool *delayp;
1259    int i;
1260
1261    /*
1262     * The following checks if this routine has been called before.
1263     * This is necessary because the parent process gets initialized
1264     * a couple of times as the server starts up, and we don't want
1265     * to create any more mutexes and shared memory segments than
1266     * we're actually going to use.
1267     */
1268    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1269    if (!data) {
1270        apr_pool_userdata_set((const void *) 1, userdata_key,
1271                              apr_pool_cleanup_null, s->process->pool);
1272        return OK;
1273    } /* Kilroy was here */
1274
1275    /* Create the shared memory segment */
1276
1277    /*
1278     * Create a unique filename using our pid. This information is
1279     * stashed in the global variable so the children inherit it.
1280     * TODO get the location from the environment $TMPDIR or somesuch.
1281     */
1282    shmfilename = apr_psprintf(pconf, "/tmp/httpd_shm.%ld", (long int)getpid());
1283    shmfilename_delaypool = apr_psprintf(pconf, "/tmp/httpd_shm_delay.%ld", (long int)getpid());
1284
1285    /* Now create that segment
1286     * would prefer to use scfg->configs->nelts here but that does
1287     * not seem to be set at this stage, so rely on previously set layerCount */
1288
1289    rs = apr_shm_create(&stats_shm, sizeof(stats_data) + layerCount * 2 * sizeof(apr_uint64_t),
1290                        (const char *) shmfilename, pconf);
1291    if (rs != APR_SUCCESS) {
1292        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1293                     "Failed to create shared memory segment on file %s",
1294                     shmfilename);
1295        return HTTP_INTERNAL_SERVER_ERROR;
1296    }
1297
1298    rs = apr_shm_create(&delaypool_shm, sizeof(delaypool),
1299                        (const char *) shmfilename_delaypool, pconf);
1300    if (rs != APR_SUCCESS) {
1301        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1302                     "Failed to create shared memory segment on file %s",
1303                     shmfilename_delaypool);
1304        return HTTP_INTERNAL_SERVER_ERROR;
1305    }
1306
1307    /* Created it, now let's zero it out */
1308    stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
1309    stats->noResp200 = 0;
1310    stats->noResp304 = 0;
1311    stats->noResp404 = 0;
1312    stats->noResp503 = 0;
1313    stats->noResp5XX = 0;
1314    for (i = 0; i <= global_max_zoom; i++) {
1315        stats->noRespZoom[i] = 0;
1316    }
1317    stats->totalBufferRetrievalTime = 0;
1318    stats->noTotalBufferRetrieval = 0;
1319    for (i = 0; i <= global_max_zoom; i++) {
1320        stats->zoomBufferRetrievalTime[i] = 0;
1321        stats->noZoomBufferRetrieval[i] = 0;
1322    }
1323    stats->noRespOther = 0;
1324    stats->noFreshCache = 0;
1325    stats->noFreshRender = 0;
1326    stats->noOldCache = 0;
1327    stats->noOldRender = 0;
1328
1329    /* the "stats" block does not have a fixed size; it is a fixed-size struct
1330     * followed by two arrays with one element each per layer. All of this sits
1331     * in one shared memory block, and for ease of use, pointers from inside the
1332     * struct point to the arrays. */
1333    stats->noResp404Layer = (apr_uint64_t *) ((char *) stats + sizeof(stats_data));
1334    stats->noResp200Layer = (apr_uint64_t *) ((char *) stats + sizeof(stats_data) + sizeof(apr_uint64_t) * layerCount);
1335
1336    /* the last_check and planet_timestamp arrays have a variable size as well,
1337     * however they are not in shared memory. */
1338    last_check = (apr_time_t *) apr_pcalloc(pconf, sizeof(apr_time_t) * layerCount);
1339    planet_timestamp = (apr_time_t *) apr_pcalloc(pconf, sizeof(apr_time_t) * layerCount);
1340
1341    /* zero out all the non-fixed-length stuff */
1342    for (i=0; i<layerCount; i++) {
1343        stats->noResp404Layer[i] = 0;
1344        stats->noResp200Layer[i] = 0;
1345        last_check[i] = 0;
1346        planet_timestamp[i] = 0;
1347    }
1348
1349    delayp = (delaypool *)apr_shm_baseaddr_get(delaypool_shm);
1350   
1351    delayp->last_tile_fillup = apr_time_now();
1352    delayp->last_render_fillup = apr_time_now();
1353
1354    for (i = 0; i < DELAY_HASHTABLE_SIZE; i++) {
1355        memset(&(delayp->users[i].ip_addr),0, sizeof(struct in6_addr));
1356        delayp->users[i].available_tiles = 0;
1357        delayp->users[i].available_render_req = 0;
1358    }
1359    for (i = 0; i < DELAY_HASHTABLE_WHITELIST_SIZE; i++) {
1360        delayp->whitelist[i] = (in_addr_t)0;
1361    }
1362    /* TODO: need a way to initialise the delaypool whitelist */
1363
1364
1365    /* Create global mutex */
1366
1367    /*
1368     * Create another unique filename to lock upon. Note that
1369     * depending on OS and locking mechanism of choice, the file
1370     * may or may not be actually created.
1371     */
1372    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex.%ld",
1373                                 (long int) getpid());
1374
1375    rs = apr_global_mutex_create(&stats_mutex, (const char *) mutexfilename,
1376                                 APR_LOCK_DEFAULT, pconf);
1377    if (rs != APR_SUCCESS) {
1378        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1379                     "Failed to create mutex on file %s",
1380                     mutexfilename);
1381        return HTTP_INTERNAL_SERVER_ERROR;
1382    }
1383
1384#ifdef MOD_TILE_SET_MUTEX_PERMS
1385    rs = unixd_set_global_mutex_perms(stats_mutex);
1386    if (rs != APR_SUCCESS) {
1387        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1388                     "Parent could not set permissions on mod_tile "
1389                     "mutex: check User and Group directives");
1390        return HTTP_INTERNAL_SERVER_ERROR;
1391    }
1392#endif /* MOD_TILE_SET_MUTEX_PERMS */
1393
1394    /*
1395     * Create another unique filename to lock upon. Note that
1396     * depending on OS and locking mechanism of choice, the file
1397     * may or may not be actually created.
1398     */
1399    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex_delay.%ld",
1400                                 (long int) getpid());
1401
1402    rs = apr_global_mutex_create(&delay_mutex, (const char *) mutexfilename,
1403                                 APR_LOCK_DEFAULT, pconf);
1404    if (rs != APR_SUCCESS) {
1405        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1406                     "Failed to create mutex on file %s",
1407                     mutexfilename);
1408        return HTTP_INTERNAL_SERVER_ERROR;
1409    }
1410
1411#ifdef MOD_TILE_SET_MUTEX_PERMS
1412    rs = unixd_set_global_mutex_perms(delay_mutex);
1413    if (rs != APR_SUCCESS) {
1414        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1415                     "Parent could not set permissions on mod_tile "
1416                     "mutex: check User and Group directives");
1417        return HTTP_INTERNAL_SERVER_ERROR;
1418    }
1419#endif /* MOD_TILE_SET_MUTEX_PERMS */
1420
1421    return OK;
1422}
1423
1424
1425/*
1426 * This routine gets called when a child inits. We use it to attach
1427 * to the shared memory segment, and reinitialize the mutex.
1428 */
1429
1430static void mod_tile_child_init(apr_pool_t *p, server_rec *s)
1431{
1432    apr_status_t rs;
1433
1434     /*
1435      * Re-open the mutex for the child. Note we're reusing
1436      * the mutex pointer global here.
1437      */
1438     rs = apr_global_mutex_child_init(&stats_mutex,
1439                                      (const char *) mutexfilename,
1440                                      p);
1441     if (rs != APR_SUCCESS) {
1442         ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1443                     "Failed to reopen mutex on file %s",
1444                     shmfilename);
1445         /* There's really nothing else we can do here, since
1446          * This routine doesn't return a status. */
1447         exit(1); /* Ugly, but what else? */
1448     }
1449}
1450
1451static void register_hooks(__attribute__((unused)) apr_pool_t *p)
1452{
1453    ap_hook_post_config(mod_tile_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1454    ap_hook_child_init(mod_tile_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1455    ap_hook_handler(tile_handler_serve, NULL, NULL, APR_HOOK_MIDDLE);
1456    ap_hook_handler(tile_handler_dirty, NULL, NULL, APR_HOOK_MIDDLE);
1457    ap_hook_handler(tile_handler_status, NULL, NULL, APR_HOOK_MIDDLE);
1458    ap_hook_handler(tile_handler_json, NULL, NULL, APR_HOOK_MIDDLE);
1459    ap_hook_handler(tile_handler_mod_stats, NULL, NULL, APR_HOOK_MIDDLE);
1460    ap_hook_translate_name(tile_translate, NULL, NULL, APR_HOOK_MIDDLE);
1461    ap_hook_map_to_storage(tile_storage_hook, NULL, NULL, APR_HOOK_FIRST);
1462}
1463
1464static const char *_add_tile_config(cmd_parms *cmd, void *mconfig,
1465                                    const char *baseuri, const char *name, int minzoom, int maxzoom,
1466                                    const char * fileExtension, const char *mimeType, const char *description, const char * attribution,
1467                                    int noHostnames, char ** hostnames, char * cors)
1468{
1469    if (strlen(name) == 0) {
1470        return "ConfigName value must not be null";
1471    }
1472
1473    if (hostnames == NULL) {
1474        hostnames = malloc(sizeof(char *));
1475        /* FIXME: wouldn't be allocationg 7+len+1 bytes be enough? */
1476        hostnames[0] = malloc(PATH_MAX);
1477        strncpy(hostnames[0],"http://", PATH_MAX);
1478        if (cmd->server->server_hostname == NULL) {
1479            ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, cmd->server,
1480                         "Could not determine host name of server to configure tile-json request. Using localhost instead");
1481
1482            strncat(hostnames[0],"localhost",PATH_MAX-10);
1483        } else 
1484            strncat(hostnames[0],cmd->server->server_hostname,PATH_MAX-strlen(hostnames[0])-1);
1485        noHostnames = 1;
1486    }
1487
1488    if ((minzoom < 0) || (maxzoom > MAX_ZOOM_SERVER)) {
1489        return "The configured zoom level lies outside of the range supported by this server";
1490    }
1491    if (maxzoom > global_max_zoom) global_max_zoom = maxzoom;
1492
1493
1494    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1495    tile_config_rec *tilecfg = apr_array_push(scfg->configs);
1496
1497    // Ensure URI string ends with a trailing slash
1498    int urilen = strlen(baseuri);
1499
1500    if (urilen==0)
1501      snprintf(tilecfg->baseuri, PATH_MAX, "/");
1502    else if (baseuri[urilen-1] != '/')
1503      snprintf(tilecfg->baseuri, PATH_MAX, "%s/", baseuri);
1504    else
1505      snprintf(tilecfg->baseuri, PATH_MAX, "%s", baseuri);
1506
1507    strncpy(tilecfg->xmlname, name, XMLCONFIG_MAX-1);
1508    strncpy(tilecfg->fileExtension, fileExtension, XMLCONFIG_MAX-1);
1509    strncpy(tilecfg->mimeType, mimeType, XMLCONFIG_MAX-1);
1510    tilecfg->xmlname[XMLCONFIG_MAX-1] = 0;
1511    tilecfg->minzoom = minzoom;
1512    tilecfg->maxzoom = maxzoom;
1513    tilecfg->description = description;
1514    tilecfg->attribution = attribution;
1515    tilecfg->noHostnames = noHostnames;
1516    tilecfg->hostnames = hostnames;
1517    tilecfg->cors = cors;
1518
1519    ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, cmd->server,
1520                    "Loading tile config %s at %s for zooms %i - %i from tile directory %s with extension .%s and mime type %s",
1521                 name, baseuri, minzoom, maxzoom, scfg->tile_dir, fileExtension, mimeType);
1522
1523    layerCount++;
1524    return NULL;
1525}
1526
1527static const char *add_tile_mime_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name, const char * fileExtension)
1528{
1529    if (strcmp(fileExtension,"png") == 0) {
1530        return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "image/png",NULL,DEFAULT_ATTRIBUTION,0,NULL,NULL);
1531    }
1532    if (strcmp(fileExtension,"js") == 0) {
1533        return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "text/javascript",NULL,DEFAULT_ATTRIBUTION,0,NULL,"*");
1534    }
1535    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "image/png",NULL,DEFAULT_ATTRIBUTION,0,NULL,NULL);
1536}
1537
1538static const char *add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name)
1539{
1540    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, "png", "image/png",NULL,DEFAULT_ATTRIBUTION,0,NULL,NULL);
1541}
1542
1543static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *conffile)
1544{
1545    FILE * hini ;
1546    char filename[PATH_MAX];
1547    char url[PATH_MAX];
1548    char xmlname[XMLCONFIG_MAX];
1549    char line[INILINE_MAX];
1550    char key[INILINE_MAX];
1551    char value[INILINE_MAX];
1552    const char * result;
1553    char fileExtension[INILINE_MAX];
1554    char mimeType[INILINE_MAX];
1555    char * description = NULL;
1556    char * attribution = NULL;
1557    char * cors = NULL;
1558    char **hostnames = NULL;
1559    char **hostnames_tmp;
1560    int noHostnames = 0;
1561    int tilelayer = 0;
1562    int minzoom = 0;
1563    int maxzoom = MAX_ZOOM;
1564
1565    if (strlen(conffile) == 0) {
1566        strcpy(filename, RENDERD_CONFIG);
1567    } else {
1568        strcpy(filename, conffile);
1569    }
1570
1571    // Load the config
1572    if ((hini=fopen(filename, "r"))==NULL) {
1573        return "Unable to open config file";
1574    }
1575
1576    while (fgets(line, INILINE_MAX, hini)!=NULL) {
1577        if (line[0] == '#') continue;
1578        if (line[0] == ';') continue;
1579        if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
1580        if (line[0] == '[') {
1581            /*Add the previous section to the configuration */
1582            if (tilelayer == 1) {
1583                result = _add_tile_config(cmd, mconfig, url, xmlname, minzoom, maxzoom, fileExtension, mimeType,
1584                                          description,attribution,noHostnames,hostnames, cors);
1585                if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1586                if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1587                if (result != NULL) {
1588                    fclose(hini);
1589                    return result;
1590                }
1591            }
1592            if (strlen(line) >= XMLCONFIG_MAX) {
1593                if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1594                if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1595                fclose(hini);
1596                return "XML name too long";
1597            }
1598            sscanf(line, "[%[^]]", xmlname);
1599            if ((strcmp(xmlname,"mapnik") == 0) || (strcmp(xmlname,"renderd") == 0)) {
1600                /* These aren't tile layers but configuration sections for renderd */
1601                tilelayer = 0;
1602            } else {
1603                tilelayer = 1;
1604            }
1605            /* Initialise default values for tile layer */
1606            strcpy(url,"");
1607            strcpy(fileExtension,"png");
1608            strcpy(mimeType,"image/png");
1609            description = NULL;
1610            cors = NULL;
1611            if (attribution) free(attribution);
1612            attribution = malloc(sizeof(char)*(strlen(DEFAULT_ATTRIBUTION) + 1));
1613            strcpy(attribution,DEFAULT_ATTRIBUTION);
1614            hostnames = NULL;
1615            noHostnames = 0;
1616            minzoom = 0;
1617            maxzoom = MAX_ZOOM;
1618        } else if (sscanf(line, "%[^=]=%[^;#]", key, value) == 2
1619               ||  sscanf(line, "%[^=]=\"%[^\"]\"", key, value) == 2) {
1620
1621            if (!strcmp(key, "URI")){
1622                if (strlen(value) >= PATH_MAX){
1623                    if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1624                    if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1625                    fclose(hini);
1626                    return "URI too long";
1627                }
1628                strcpy(url, value);
1629            }
1630            if (!strcmp(key, "TYPE")){
1631                if (strlen(value) >= PATH_MAX){
1632                    if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1633                    if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1634                    fclose(hini);
1635                    return "TYPE too long";
1636                }
1637                if (sscanf(value, "%[^ ] %[^;#]", fileExtension, mimeType) != 2) {
1638                    if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1639                    if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1640                    fclose(hini);
1641                    return "TYPE is not correctly parsable";
1642                }
1643            }
1644            if (!strcmp(key, "DESCRIPTION")){
1645                if (description) free(description);
1646                description = malloc(sizeof(char) * (strlen(value) + 1));
1647                strcpy(description, value);
1648            }
1649            if (!strcmp(key, "ATTRIBUTION")){
1650                if (attribution) free(attribution);
1651                attribution = malloc(sizeof(char) * (strlen(value) + 1));
1652                strcpy(attribution, value);
1653            }
1654            if (!strcmp(key, "CORS")){
1655                if (cors) free(cors);
1656                cors = malloc(sizeof(char) * (strlen(value) + 1));
1657                strcpy(cors, value);
1658            }
1659            if (!strcmp(key, "SERVER_ALIAS")){
1660                if (hostnames == NULL) {
1661                    noHostnames = 1;
1662                    hostnames = malloc((noHostnames + 1) * sizeof(char *));
1663                } else {
1664                    hostnames_tmp = hostnames;
1665                    hostnames = malloc((noHostnames + 1) * sizeof(char *));
1666                    memcpy(hostnames, hostnames_tmp,sizeof(char *) * noHostnames);
1667                    free(hostnames_tmp);
1668                    noHostnames++;
1669                }
1670                hostnames[noHostnames - 1] = malloc(sizeof(char)*(strlen(value) + 1));
1671                strcpy(hostnames[noHostnames - 1], value);
1672            }
1673            if (!strcmp(key, "MINZOOM")){
1674                minzoom = atoi(value);
1675            }
1676            if (!strcmp(key, "MAXZOOM")){
1677                maxzoom = atoi(value);
1678            }
1679        }
1680    }
1681    if (tilelayer == 1) {
1682        ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, cmd->server,
1683                "Committing tile config %s", xmlname);
1684        result = _add_tile_config(cmd, mconfig, url, xmlname, minzoom, maxzoom, fileExtension, mimeType,
1685                                  description,attribution,noHostnames,hostnames, cors);
1686        if (description) free(description);
1687        if (attribution) free(attribution);
1688        if (hostnames) free(hostnames);
1689        if (cors) free(cors);
1690        if (result != NULL) {
1691            fclose(hini);
1692            return result;
1693        }
1694    }
1695    fclose(hini);
1696    return NULL;
1697}
1698
1699static const char *mod_tile_request_timeout_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1700{
1701    int request_timeout;
1702
1703    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1704        return "ModTileRequestTimeout needs integer argument";
1705    }
1706
1707    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1708    scfg->request_timeout = request_timeout;
1709    return NULL;
1710}
1711
1712static const char *mod_tile_request_timeout_missing_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1713{
1714    int request_timeout;
1715
1716    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1717        return "ModTileMissingRequestTimeout needs integer argument";
1718    }
1719
1720    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1721    scfg->request_timeout_priority = request_timeout;
1722    return NULL;
1723}
1724
1725static const char *mod_tile_max_load_old_config(cmd_parms *cmd, void *mconfig, const char *max_load_old_string)
1726{
1727    int max_load_old;
1728
1729    if (sscanf(max_load_old_string, "%d", &max_load_old) != 1) {
1730        return "ModTileMaxLoadOld needs integer argument";
1731    }
1732
1733    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1734    scfg->max_load_old = max_load_old;
1735    return NULL;
1736}
1737
1738static const char *mod_tile_max_load_missing_config(cmd_parms *cmd, void *mconfig, const char *max_load_missing_string)
1739{
1740    int max_load_missing;
1741
1742    if (sscanf(max_load_missing_string, "%d", &max_load_missing) != 1) {
1743        return "ModTileMaxLoadMissing needs integer argument";
1744    }
1745
1746    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1747    scfg->max_load_missing = max_load_missing;
1748    return NULL;
1749}
1750
1751static const char *mod_tile_renderd_socket_name_config(cmd_parms *cmd, void *mconfig, const char *renderd_socket_name_string)
1752{
1753    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1754    strncpy(scfg->renderd_socket_name, renderd_socket_name_string, PATH_MAX-1);
1755    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1756    return NULL;
1757}
1758
1759static const char *mod_tile_tile_dir_config(cmd_parms *cmd, void *mconfig, const char *tile_dir_string)
1760{
1761    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1762    strncpy(scfg->tile_dir, tile_dir_string, PATH_MAX-1);
1763    scfg->tile_dir[PATH_MAX-1] = 0;
1764    return NULL;
1765}
1766
1767static const char *mod_tile_cache_extended_host_name_config(cmd_parms *cmd, void *mconfig, const char *cache_extended_hostname)
1768{
1769    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1770    strncpy(scfg->cache_extended_hostname, cache_extended_hostname, PATH_MAX-1);
1771    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1772
1773    return NULL;
1774}
1775
1776static const char *mod_tile_cache_extended_duration_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1777{
1778    int cache_duration;
1779    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1780    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1781            return "ModTileCacheExtendedDuration needs integer argument";
1782    }
1783    scfg->cache_extended_duration = cache_duration;
1784
1785    return NULL;
1786}
1787
1788static const char *mod_tile_cache_lastmod_factor_config(cmd_parms *cmd, void *mconfig, const char *modified_factor_string)
1789{
1790    float modified_factor;
1791    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1792            &tile_module);
1793    if (sscanf(modified_factor_string, "%f", &modified_factor) != 1) {
1794        return "ModTileCacheLastModifiedFactor needs float argument";
1795    }
1796    scfg->cache_duration_last_modified_factor = modified_factor;
1797    return NULL;
1798}
1799
1800static const char *mod_tile_cache_duration_max_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1801{
1802    int cache_duration;
1803    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1804            &tile_module);
1805    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1806        return "ModTileCacheDurationMax needs integer argument";
1807    }
1808    scfg->cache_duration_max = cache_duration;
1809    return NULL;
1810}
1811
1812static const char *mod_tile_cache_duration_dirty_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1813{
1814    int cache_duration;
1815    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1816            &tile_module);
1817    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1818        return "ModTileCacheDurationDirty needs integer argument";
1819    }
1820    scfg->cache_duration_dirty = cache_duration;
1821    return NULL;
1822}
1823
1824static const char *mod_tile_cache_duration_minimum_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1825{
1826    int cache_duration;
1827    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1828            &tile_module);
1829    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1830        return "ModTileCacheDurationMinimum needs integer argument";
1831    }
1832    scfg->cache_duration_minimum = cache_duration;
1833    return NULL;
1834}
1835
1836static const char *mod_tile_cache_duration_low_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1837{
1838    int zoom_level;
1839    int cache_duration;
1840    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1841    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1842            return "ModTileCacheDurationLowZoom needs integer argument";
1843    }
1844    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1845            return "ModTileCacheDurationLowZoom needs integer argument";
1846    }
1847    scfg->cache_level_low_zoom = zoom_level;
1848    scfg->cache_duration_low_zoom = cache_duration;
1849
1850    return NULL;
1851}
1852static const char *mod_tile_cache_duration_medium_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1853{
1854    int zoom_level;
1855    int cache_duration;
1856    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1857    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1858            return "ModTileCacheDurationMediumZoom needs integer argument";
1859    }
1860    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1861            return "ModTileCacheDurationMediumZoom needs integer argument";
1862    }
1863    scfg->cache_level_medium_zoom = zoom_level;
1864    scfg->cache_duration_medium_zoom = cache_duration;
1865
1866    return NULL;
1867}
1868
1869static const char *mod_tile_enable_stats(cmd_parms *cmd, void *mconfig, int enableStats)
1870{
1871    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1872    scfg->enableGlobalStats = enableStats;
1873    return NULL;
1874}
1875
1876static const char *mod_tile_enable_throttling(cmd_parms *cmd, void *mconfig, int enableThrottling)
1877{
1878    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1879    scfg->enableTileThrottling = enableThrottling;
1880    return NULL;
1881}
1882
1883static const char *mod_tile_enable_throttling_xforward(cmd_parms *cmd, void *mconfig, const char * enableThrottlingXForward)
1884{
1885    int throttle_xforward;
1886    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1887    if (sscanf(enableThrottlingXForward, "%d", &throttle_xforward) != 1) {
1888        return "ModTileEnableTileThrottlingXForward needs integer argument between 0 and 2";
1889    }
1890    if ((throttle_xforward < 0) || (throttle_xforward > 2)) {
1891        return "ModTileEnableTileThrottlingXForward needs integer argument between 0 and 2 (0 => off; 1 => use client; 2 => use last entry in chain";
1892    }
1893    scfg->enableTileThrottlingXForward = throttle_xforward;
1894    return NULL;
1895}
1896
1897static const char *mod_tile_bulk_mode(cmd_parms *cmd, void *mconfig, int bulkMode)
1898{
1899    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1900    scfg->bulkMode = bulkMode;
1901    return NULL;
1902}
1903
1904static const char *mod_tile_delaypool_tiles_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1905{
1906    int bucketsize;
1907    float topuprate;
1908
1909    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1910    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1911            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1912    }
1913    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1914            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1915    }
1916    scfg->delaypoolTileSize = bucketsize;
1917
1918    /*Convert topup rate into microseconds per tile */
1919    scfg->delaypoolTileRate = (long)(1000000.0/topuprate);
1920
1921    return NULL;
1922}
1923
1924static const char *mod_tile_delaypool_render_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1925{
1926    int bucketsize;
1927    float topuprate;
1928
1929    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1930    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1931            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1932    }
1933    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1934            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1935    }
1936    scfg->delaypoolRenderSize = bucketsize;
1937
1938    /*Convert topup rate into microseconds per tile */
1939    scfg->delaypoolRenderRate = (long)(1000000.0/topuprate);
1940
1941    return NULL;
1942}
1943
1944static void *create_tile_config(apr_pool_t *p, server_rec *s)
1945{
1946    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1947
1948    scfg->configs = apr_array_make(p, 4, sizeof(tile_config_rec));
1949    scfg->request_timeout = REQUEST_TIMEOUT;
1950    scfg->request_timeout_priority = REQUEST_TIMEOUT;
1951    scfg->max_load_old = MAX_LOAD_OLD;
1952    scfg->max_load_missing = MAX_LOAD_MISSING;
1953    strncpy(scfg->renderd_socket_name, RENDER_SOCKET, PATH_MAX-1);
1954    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1955    strncpy(scfg->tile_dir, HASH_PATH, PATH_MAX-1);
1956    scfg->tile_dir[PATH_MAX-1] = 0;
1957    memset(&(scfg->cache_extended_hostname),0,PATH_MAX);
1958    scfg->cache_extended_duration = 0;
1959    scfg->cache_duration_dirty = 15*60;
1960    scfg->cache_duration_last_modified_factor = 0.0;
1961    scfg->cache_duration_max = 7*24*60*60;
1962    scfg->cache_duration_minimum = 3*60*60;
1963    scfg->cache_duration_low_zoom = 6*24*60*60;
1964    scfg->cache_duration_medium_zoom = 1*24*60*60;
1965    scfg->cache_level_low_zoom = 0;
1966    scfg->cache_level_medium_zoom = 0;
1967    scfg->enableGlobalStats = 1;
1968    scfg->enableTileThrottling = 0;
1969    scfg->enableTileThrottlingXForward = 0;
1970    scfg->delaypoolTileSize = AVAILABLE_TILE_BUCKET_SIZE;
1971    scfg->delaypoolTileRate = RENDER_TOPUP_RATE;
1972    scfg->delaypoolRenderSize = AVAILABLE_RENDER_BUCKET_SIZE;
1973    scfg->delaypoolRenderRate = RENDER_TOPUP_RATE;
1974    scfg->bulkMode = 0;
1975
1976
1977    return scfg;
1978}
1979
1980static void *merge_tile_config(apr_pool_t *p, void *basev, void *overridesv)
1981{
1982    int i;
1983    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1984    tile_server_conf * scfg_base = (tile_server_conf *) basev;
1985    tile_server_conf * scfg_over = (tile_server_conf *) overridesv;
1986
1987    scfg->configs = apr_array_append(p, scfg_base->configs, scfg_over->configs);
1988    scfg->request_timeout = scfg_over->request_timeout;
1989    scfg->request_timeout_priority = scfg_over->request_timeout_priority;
1990    scfg->max_load_old = scfg_over->max_load_old;
1991    scfg->max_load_missing = scfg_over->max_load_missing;
1992    strncpy(scfg->renderd_socket_name, scfg_over->renderd_socket_name, PATH_MAX-1);
1993    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1994    strncpy(scfg->tile_dir, scfg_over->tile_dir, PATH_MAX-1);
1995    scfg->tile_dir[PATH_MAX-1] = 0;
1996    strncpy(scfg->cache_extended_hostname, scfg_over->cache_extended_hostname, PATH_MAX-1);
1997    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1998    scfg->cache_extended_duration = scfg_over->cache_extended_duration;
1999    scfg->cache_duration_dirty = scfg_over->cache_duration_dirty;
2000    scfg->cache_duration_last_modified_factor = scfg_over->cache_duration_last_modified_factor;
2001    scfg->cache_duration_max = scfg_over->cache_duration_max;
2002    scfg->cache_duration_minimum = scfg_over->cache_duration_minimum;
2003    scfg->cache_duration_low_zoom = scfg_over->cache_duration_low_zoom;
2004    scfg->cache_duration_medium_zoom = scfg_over->cache_duration_medium_zoom;
2005    scfg->cache_level_low_zoom = scfg_over->cache_level_low_zoom;
2006    scfg->cache_level_medium_zoom = scfg_over->cache_level_medium_zoom;
2007    scfg->enableGlobalStats = scfg_over->enableGlobalStats;
2008    scfg->enableTileThrottling = scfg_over->enableTileThrottling;
2009    scfg->enableTileThrottlingXForward = scfg_over->enableTileThrottlingXForward;
2010    scfg->delaypoolTileSize = scfg_over->delaypoolTileSize;
2011    scfg->delaypoolTileRate = scfg_over->delaypoolTileRate;
2012    scfg->delaypoolRenderSize = scfg_over->delaypoolRenderSize;
2013    scfg->delaypoolRenderRate = scfg_over->delaypoolRenderRate;
2014    scfg->bulkMode = scfg_over->bulkMode;
2015
2016    //Construct a table of minimum cache times per zoom level
2017    for (i = 0; i <= MAX_ZOOM_SERVER; i++) {
2018        if (i <= scfg->cache_level_low_zoom) {
2019            scfg->mincachetime[i] = scfg->cache_duration_low_zoom;
2020        } else if (i <= scfg->cache_level_medium_zoom) {
2021            scfg->mincachetime[i] = scfg->cache_duration_medium_zoom;
2022        } else {
2023            scfg->mincachetime[i] = scfg->cache_duration_minimum;
2024        }
2025    }
2026
2027    return scfg;
2028}
2029
2030static const command_rec tile_cmds[] =
2031{
2032    AP_INIT_TAKE1(
2033        "LoadTileConfigFile",            /* directive name */
2034        load_tile_config,                /* config action routine */
2035        NULL,                            /* argument to include in call */
2036        OR_OPTIONS,                      /* where available */
2037        "load an entire renderd config file"  /* directive description */
2038    ),
2039    AP_INIT_TAKE2(
2040        "AddTileConfig",                 /* directive name */
2041        add_tile_config,                 /* config action routine */
2042        NULL,                            /* argument to include in call */
2043        OR_OPTIONS,                      /* where available */
2044        "path and name of renderd config to use"  /* directive description */
2045    ),
2046    AP_INIT_TAKE3(
2047            "AddTileMimeConfig",         /* directive name */
2048            add_tile_mime_config,        /* config action routine */
2049            NULL,                        /* argument to include in call */
2050            OR_OPTIONS,                  /* where available */
2051            "path, name and file extension of renderd config to use"  /* directive description */
2052        ),
2053    AP_INIT_TAKE1(
2054        "ModTileRequestTimeout",         /* directive name */
2055        mod_tile_request_timeout_config, /* config action routine */
2056        NULL,                            /* argument to include in call */
2057        OR_OPTIONS,                      /* where available */
2058        "Set timeout in seconds on mod_tile requests"  /* directive description */
2059    ),
2060    AP_INIT_TAKE1(
2061        "ModTileMissingRequestTimeout",         /* directive name */
2062        mod_tile_request_timeout_missing_config, /* config action routine */
2063        NULL,                            /* argument to include in call */
2064        OR_OPTIONS,                      /* where available */
2065        "Set timeout in seconds on missing mod_tile requests"  /* directive description */
2066    ),
2067    AP_INIT_TAKE1(
2068        "ModTileMaxLoadOld",             /* directive name */
2069        mod_tile_max_load_old_config,    /* config action routine */
2070        NULL,                            /* argument to include in call */
2071        OR_OPTIONS,                      /* where available */
2072        "Set max load for rendering old tiles"  /* directive description */
2073    ),
2074    AP_INIT_TAKE1(
2075        "ModTileMaxLoadMissing",         /* directive name */
2076        mod_tile_max_load_missing_config, /* config action routine */
2077        NULL,                            /* argument to include in call */
2078        OR_OPTIONS,                      /* where available */
2079        "Set max load for rendering missing tiles"  /* directive description */
2080    ),
2081    AP_INIT_TAKE1(
2082        "ModTileRenderdSocketName",      /* directive name */
2083        mod_tile_renderd_socket_name_config, /* config action routine */
2084        NULL,                            /* argument to include in call */
2085        OR_OPTIONS,                      /* where available */
2086        "Set name of unix domain socket for connecting to rendering daemon"  /* directive description */
2087    ),
2088    AP_INIT_TAKE1(
2089        "ModTileTileDir",                /* directive name */
2090        mod_tile_tile_dir_config,        /* config action routine */
2091        NULL,                            /* argument to include in call */
2092        OR_OPTIONS,                      /* where available */
2093        "Set name of tile cache directory"  /* directive description */
2094    ),
2095    AP_INIT_TAKE1(
2096        "ModTileCacheExtendedHostName",                /* directive name */
2097        mod_tile_cache_extended_host_name_config,        /* config action routine */
2098        NULL,                            /* argument to include in call */
2099        OR_OPTIONS,                      /* where available */
2100        "set hostname for extended period caching"  /* directive description */
2101    ),
2102    AP_INIT_TAKE1(
2103        "ModTileCacheExtendedDuration",                /* directive name */
2104        mod_tile_cache_extended_duration_config,        /* config action routine */
2105        NULL,                            /* argument to include in call */
2106        OR_OPTIONS,                      /* where available */
2107        "set length of extended period caching"  /* directive description */
2108    ),
2109    AP_INIT_TAKE1(
2110        "ModTileCacheDurationMax",                /* directive name */
2111        mod_tile_cache_duration_max_config,        /* config action routine */
2112        NULL,                            /* argument to include in call */
2113        OR_OPTIONS,                      /* where available */
2114        "Set the maximum cache expiry in seconds"  /* directive description */
2115    ),
2116    AP_INIT_TAKE1(
2117        "ModTileCacheDurationDirty",                    /* directive name */
2118        mod_tile_cache_duration_dirty_config,           /* config action routine */
2119        NULL,                                           /* argument to include in call */
2120        OR_OPTIONS,                                     /* where available */
2121        "Set the cache expiry for serving dirty tiles"  /* directive description */
2122    ),
2123    AP_INIT_TAKE1(
2124        "ModTileCacheDurationMinimum",          /* directive name */
2125        mod_tile_cache_duration_minimum_config, /* config action routine */
2126        NULL,                                   /* argument to include in call */
2127        OR_OPTIONS,                             /* where available */
2128        "Set the minimum cache expiry"          /* directive description */
2129    ),
2130    AP_INIT_TAKE1(
2131        "ModTileCacheLastModifiedFactor",       /* directive name */
2132        mod_tile_cache_lastmod_factor_config,   /* config action routine */
2133        NULL,                                   /* argument to include in call */
2134        OR_OPTIONS,                             /* where available */
2135        "Set the factor by which the last modified determins cache expiry" /* directive description */
2136    ),
2137    AP_INIT_TAKE2(
2138        "ModTileCacheDurationLowZoom",       /* directive name */
2139        mod_tile_cache_duration_low_config,                 /* config action routine */
2140        NULL,                            /* argument to include in call */
2141        OR_OPTIONS,                      /* where available */
2142        "Set the minimum cache duration and zoom level for low zoom tiles"  /* directive description */
2143    ),
2144    AP_INIT_TAKE2(
2145        "ModTileCacheDurationMediumZoom", /* directive name */
2146        mod_tile_cache_duration_medium_config,                 /* config action routine */
2147        NULL,                            /* argument to include in call */
2148        OR_OPTIONS,                      /* where available */
2149        "Set the minimum cache duration and zoom level for medium zoom tiles"  /* directive description */
2150    ),
2151    AP_INIT_FLAG(
2152        "ModTileEnableStats",            /* directive name */
2153        mod_tile_enable_stats,           /* config action routine */
2154        NULL,                            /* argument to include in call */
2155        OR_OPTIONS,                      /* where available */
2156        "On Off - enable of keeping stats about what mod_tile is serving"  /* directive description */
2157    ),
2158    AP_INIT_FLAG(
2159        "ModTileEnableTileThrottling",   /* directive name */
2160        mod_tile_enable_throttling,      /* config action routine */
2161        NULL,                            /* argument to include in call */
2162        OR_OPTIONS,                      /* where available */
2163        "On Off - enable of throttling of IPs that excessively download tiles such as scrapers"  /* directive description */
2164    ),
2165    AP_INIT_TAKE1(
2166        "ModTileEnableTileThrottlingXForward",   /* directive name */
2167        mod_tile_enable_throttling_xforward,      /* config action routine */
2168        NULL,                            /* argument to include in call */
2169        OR_OPTIONS,                      /* where available */
2170        "0 1 2 - use X-Forwarded-For http header to determin IP for throttling when available. 0 => off, 1 => use first entry, 2 => use last entry of the caching chain"  /* directive description */
2171    ),
2172    AP_INIT_TAKE2(
2173        "ModTileThrottlingTiles",        /* directive name */
2174        mod_tile_delaypool_tiles_config, /* config action routine */
2175        NULL,                            /* argument to include in call */
2176        OR_OPTIONS,                      /* where available */
2177        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
2178    ),
2179    AP_INIT_TAKE2(
2180        "ModTileThrottlingRenders",      /* directive name */
2181        mod_tile_delaypool_render_config,/* config action routine */
2182        NULL,                            /* argument to include in call */
2183        OR_OPTIONS,                      /* where available */
2184        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
2185    ),
2186    AP_INIT_FLAG(
2187        "ModTileBulkMode",               /* directive name */
2188        mod_tile_bulk_mode,              /* config action routine */
2189        NULL,                            /* argument to include in call */
2190        OR_OPTIONS,                      /* where available */
2191        "On Off - make all requests to renderd with bulk render priority, never mark tiles dirty"  /* directive description */
2192    ),
2193    {NULL}
2194};
2195
2196module AP_MODULE_DECLARE_DATA tile_module =
2197{
2198    STANDARD20_MODULE_STUFF,
2199    NULL,                                /* dir config creater */
2200    NULL,                                /* dir merger --- default is to override */
2201    create_tile_config,                  /* server config */
2202    merge_tile_config,                   /* merge server config */
2203    tile_cmds,                           /* command apr_table_t */
2204    register_hooks                       /* register hooks */
2205};
2206
Note: See TracBrowser for help on using the repository browser.