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

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

[mod_tile] log and graph time to retrieve tiles from disk

Adds a munin graph to monitor the average time it takes to retrieve a served tile
from disk. This can be useful to check if the underlying disk system is fast enough
to handle the load of serving tiles. It does not include time required for pontential
rendering on the fly, nor the time to deliver the tile over the network.

File size: 89.1 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 time_str[APR_CTIME_LEN];
873
874    if(strcmp(r->handler, "tile_status"))
875        return DECLINED;
876
877    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
878    struct protocol * cmd = rdata->cmd;
879    if (cmd == NULL){
880        sleep(CLIENT_PENALTY);
881        return HTTP_NOT_FOUND;
882    }
883
884    state = tile_state(r, cmd);
885    if (state == tileMissing)
886        return error_message(r, "Unable to find a tile at %s\n", r->filename);
887    apr_ctime(time_str, r->finfo.mtime);
888
889    return error_message(r, "Tile is %s. Last rendered at %s\n", (state == tileOld) ? "due to be rendered" : "clean", time_str);
890}
891
892/**
893 * Implement a tilejson description page for the tile layer.
894 * This follows the tilejson specification of mapbox ( https://github.com/mapbox/tilejson-spec/tree/master/2.0.0 )
895 */
896static int tile_handler_json(request_rec *r)
897{
898    unsigned char *buf;
899    int len;
900    enum tileState state;
901    char *timestr;
902    long int maxAge = 7*24*60*60;
903    apr_table_t *t = r->headers_out;
904    int i;
905
906    if(strcmp(r->handler, "tile_json"))
907        return DECLINED;
908
909    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Handling tile json request\n");
910
911    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
912    ap_conf_vector_t *sconf = r->server->module_config;
913    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
914    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
915    tile_config_rec *tile_config = &tile_configs[rdata->layerNumber];
916    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Handling tile json request for layer %s\n", tile_config->xmlname);
917
918    if (tile_config->cors) {
919        int resp = add_cors(r, tile_config->cors);
920        if (resp != DONE) return resp;
921    }
922
923    buf = malloc(8*1024);
924
925    snprintf(buf, 8*1024,
926            "{\n"
927            "\t\"tilejson\": \"2.0.0\",\n"
928            "\t\"schema\": \"xyz\",\n"
929            "\t\"name\": \"%s\",\n"
930            "\t\"description\": \"%s\",\n"
931            "\t\"attribution\": \"%s\",\n"
932            "\t\"minzoom\": %i,\n"
933            "\t\"maxzoom\": %i,\n"
934            "\t\"tiles\": [\n",
935            tile_config->xmlname, (tile_config->description?tile_config->description:""), tile_config->attribution, tile_config->minzoom, tile_config->maxzoom);
936    for (i = 0; i < tile_config->noHostnames; i++) {
937        strncat(buf,"\t\t\"", 8*1024-strlen(buf)-1);
938        strncat(buf,tile_config->hostnames[i], 8*1024-strlen(buf)-1);
939        strncat(buf,tile_config->baseuri,8*1024-strlen(buf)-1);
940        strncat(buf,"{z}/{x}/{y}.",8*1024-strlen(buf)-1);
941        strncat(buf,tile_config->fileExtension, 8*1024-strlen(buf)-1);
942        strncat(buf,"\"", 8*1024-strlen(buf)-1);
943        if (i < tile_config->noHostnames - 1) strncat(buf,",", 8*1024-strlen(buf)-1);
944        strncat(buf,"\n", 8*1024-strlen(buf)-1);
945    }
946    strncat(buf,"\t]\n}\n", 8*1024-strlen(buf)-1);
947    len = strlen(buf);
948
949    /*
950     * Add HTTP headers. Make this file cachable for 1 week
951     */
952    char *md5 = ap_md5_binary(r->pool, buf, len);
953    apr_table_setn(r->headers_out, "ETag",
954            apr_psprintf(r->pool, "\"%s\"", md5));
955    ap_set_content_type(r, "application/json");
956    ap_set_content_length(r, len);
957    apr_table_mergen(t, "Cache-Control",
958            apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
959                    maxAge));
960    timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
961    apr_rfc822_date(timestr, (apr_time_from_sec(maxAge) + r->request_time));
962    apr_table_setn(t, "Expires", timestr);
963    ap_rwrite(buf, len, r);
964    free(buf);
965
966    return OK;
967}
968
969static int tile_handler_mod_stats(request_rec *r)
970{
971    stats_data * stats;
972    stats_data local_stats;
973    int i;
974
975    if (strcmp(r->handler, "tile_mod_stats"))
976        return DECLINED;
977
978    ap_conf_vector_t *sconf = r->server->module_config;
979    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
980    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
981
982    if (!scfg->enableGlobalStats) {
983        return error_message(r, "Stats are not enabled for this server");
984    }
985
986    if (get_global_lock(r,stats_mutex) != 0) {
987        //Copy over the global counter variable into
988        //local variables, that we can immediately
989        //release the lock again
990        stats = (stats_data *) apr_shm_baseaddr_get(stats_shm);
991        memcpy(&local_stats, stats, sizeof(stats_data));
992        local_stats.noResp200Layer = malloc(sizeof(apr_uint64_t) * scfg->configs->nelts);
993        memcpy(local_stats.noResp200Layer, stats->noResp200Layer, sizeof(apr_uint64_t) * scfg->configs->nelts);
994        local_stats.noResp404Layer = malloc(sizeof(apr_uint64_t) * scfg->configs->nelts);
995        memcpy(local_stats.noResp404Layer, stats->noResp404Layer, sizeof(apr_uint64_t) * scfg->configs->nelts);
996        apr_global_mutex_unlock(stats_mutex);
997    } else {
998        return error_message(r, "Failed to acquire lock, can't display stats");
999    }
1000
1001    ap_rprintf(r, "NoResp200: %li\n", local_stats.noResp200);
1002    ap_rprintf(r, "NoResp304: %li\n", local_stats.noResp304);
1003    ap_rprintf(r, "NoResp404: %li\n", local_stats.noResp404);
1004    ap_rprintf(r, "NoResp503: %li\n", local_stats.noResp503);
1005    ap_rprintf(r, "NoResp5XX: %li\n", local_stats.noResp5XX);
1006    ap_rprintf(r, "NoRespOther: %li\n", local_stats.noRespOther);
1007    ap_rprintf(r, "NoFreshCache: %li\n", local_stats.noFreshCache);
1008    ap_rprintf(r, "NoOldCache: %li\n", local_stats.noOldCache);
1009    ap_rprintf(r, "NoFreshRender: %li\n", local_stats.noFreshRender);
1010    ap_rprintf(r, "NoOldRender: %li\n", local_stats.noOldRender);
1011    for (i = 0; i <= global_max_zoom; i++) {
1012        ap_rprintf(r, "NoRespZoom%02i: %li\n", i, local_stats.noRespZoom[i]);
1013    }
1014    ap_rprintf(r, "NoTileBufferReads: %li\n", local_stats.noTotalBufferRetrieval);
1015    ap_rprintf(r, "DurationTileBufferReads: %li\n", local_stats.totalBufferRetrievalTime);
1016    for (i = 0; i <= global_max_zoom; i++) {
1017        ap_rprintf(r, "NoTileBufferReadZoom%02i: %li\n", i, local_stats.noZoomBufferRetrieval[i]);
1018        ap_rprintf(r, "DurationTileBufferReadZoom%02i: %li\n", i, local_stats.zoomBufferRetrievalTime[i]);
1019    }
1020
1021    for (i = 0; i < scfg->configs->nelts; ++i) {
1022        tile_config_rec *tile_config = &tile_configs[i];
1023        ap_rprintf(r,"NoRes200Layer%s: %li\n", tile_config->baseuri, local_stats.noResp200Layer[i]);
1024        ap_rprintf(r,"NoRes404Layer%s: %li\n", tile_config->baseuri, local_stats.noResp404Layer[i]);
1025    }
1026    free(local_stats.noResp200Layer);
1027    free(local_stats.noResp404Layer);
1028    return OK;
1029}
1030
1031static int tile_handler_serve(request_rec *r)
1032{
1033    const int tile_max = MAX_SIZE;
1034    unsigned char err_msg[4096];
1035    unsigned char *buf;
1036    int len;
1037    int compressed;
1038    apr_status_t errstatus;
1039    struct timeval start, end;
1040
1041    ap_conf_vector_t *sconf = r->server->module_config;
1042    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
1043
1044    if(strcmp(r->handler, "tile_serve"))
1045        return DECLINED;
1046
1047    struct tile_request_data * rdata = (struct tile_request_data *)ap_get_module_config(r->request_config, &tile_module);
1048    struct protocol * cmd = rdata->cmd;
1049    if (cmd == NULL){
1050        sleep(CLIENT_PENALTY);
1051        if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
1052            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1053                    "Failed to increase response stats counter");
1054        }
1055        return HTTP_NOT_FOUND;
1056    }
1057
1058    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);
1059
1060    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
1061
1062    if (tile_configs[rdata->layerNumber].cors) {
1063        int resp = add_cors(r, tile_configs[rdata->layerNumber].cors);
1064        if (resp != DONE) return resp;
1065    }
1066
1067    gettimeofday(&start,NULL);
1068
1069    // FIXME: It is a waste to do the malloc + read if we are fulfilling a HEAD or returning a 304.
1070    buf = malloc(tile_max);
1071    if (!buf) {
1072        if (!incRespCounter(HTTP_INTERNAL_SERVER_ERROR, r, cmd, rdata->layerNumber)) {
1073            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1074                    "Failed to increase response stats counter");
1075        }
1076        return HTTP_INTERNAL_SERVER_ERROR;
1077    }
1078
1079    err_msg[0] = 0;
1080    len = tile_read(scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z, buf, tile_max, &compressed, err_msg);
1081    if (len > 0) {
1082        if (compressed) {
1083            const char* accept_encoding = apr_table_get(r->headers_in,"Accept-Encoding");
1084            if (accept_encoding && strstr(accept_encoding,"gzip")) {
1085                r->content_encoding = "gzip";
1086            } else {
1087                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1088                              "Tile data is compressed, but user agent doesn't support Content-Encoding and we don't know how to decompress it server side");
1089                //TODO: decompress the output stream before sending it to client
1090            }
1091        }
1092       
1093#if 0
1094        // Set default Last-Modified and Etag headers
1095        ap_update_mtime(r, r->finfo.mtime);
1096        ap_set_last_modified(r);
1097        ap_set_etag(r);
1098#else
1099        // Use MD5 hash as only cache attribute.
1100        // If a tile is re-rendered and produces the same output
1101        // then we can continue to use the previous cached copy
1102        char *md5 = ap_md5_binary(r->pool, buf, len);
1103        apr_table_setn(r->headers_out, "ETag",
1104                        apr_psprintf(r->pool, "\"%s\"", md5));
1105#endif
1106        ap_set_content_type(r, tile_configs[rdata->layerNumber].mimeType);
1107        ap_set_content_length(r, len);
1108        add_expiry(r, cmd);
1109
1110        gettimeofday(&end,NULL);
1111        incTimingCounter((end.tv_sec*1000000 + end.tv_usec) - (start.tv_sec*1000000 + start.tv_usec) , cmd->z, r);
1112       
1113        if ((errstatus = ap_meets_conditions(r)) != OK) {
1114            free(buf);
1115            if (!incRespCounter(errstatus, r, cmd, rdata->layerNumber)) {
1116                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1117                        "Failed to increase response stats counter");
1118            }
1119            return errstatus;
1120        } else {
1121            ap_rwrite(buf, len, r);
1122            free(buf);
1123            if (!incRespCounter(errstatus, r, cmd, rdata->layerNumber)) {
1124                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1125                        "Failed to increase response stats counter");
1126            }
1127            return OK;
1128        }
1129    }
1130    free(buf);
1131    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "Failed to read tile from disk: %s", err_msg);
1132    if (!incRespCounter(HTTP_NOT_FOUND, r, cmd, rdata->layerNumber)) {
1133        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1134                "Failed to increase response stats counter");
1135    }
1136    return DECLINED;
1137}
1138
1139static int tile_translate(request_rec *r)
1140{
1141    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: uri(%s)", r->uri);
1142
1143    int i,n,limit,oob;
1144    char option[11];
1145
1146    ap_conf_vector_t *sconf = r->server->module_config;
1147    tile_server_conf *scfg = ap_get_module_config(sconf, &tile_module);
1148
1149    tile_config_rec *tile_configs = (tile_config_rec *) scfg->configs->elts;
1150
1151    /*
1152     * The page /mod_tile returns global stats about the number of tiles
1153     * handled and in what state those tiles were.
1154     * This should probably not be hard coded
1155     */
1156    if (!strncmp("/mod_tile", r->uri, strlen("/mod_tile"))) {
1157        r->handler = "tile_mod_stats";
1158        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1159                "tile_translate: retrieving global mod_tile stats");
1160        return OK;
1161    }
1162
1163    for (i = 0; i < scfg->configs->nelts; ++i) {
1164        tile_config_rec *tile_config = &tile_configs[i];
1165
1166        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: testing baseuri(%s) name(%s) extension(%s)",
1167                tile_config->baseuri, tile_config->xmlname, tile_config->fileExtension );
1168
1169
1170        if (!strncmp(tile_config->baseuri, r->uri, strlen(tile_config->baseuri))) {
1171
1172            struct tile_request_data * rdata = (struct tile_request_data *) apr_pcalloc(r->pool, sizeof(struct tile_request_data));
1173            struct protocol * cmd = (struct protocol *) apr_pcalloc(r->pool, sizeof(struct protocol));
1174            bzero(cmd, sizeof(struct protocol));
1175            bzero(rdata, sizeof(struct tile_request_data));
1176            if (!strncmp(r->uri + strlen(tile_config->baseuri),"tile-layer.json", strlen("tile-layer.json"))) {
1177                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Requesting tileJSON for tilelayer %s", tile_config->xmlname);
1178                r->handler = "tile_json";
1179                rdata->layerNumber = i;
1180                ap_set_module_config(r->request_config, &tile_module, rdata);
1181                return OK;
1182            }
1183            char extension[256];
1184            n = sscanf(r->uri+strlen(tile_config->baseuri),"%d/%d/%d.%[a-z]/%10s", &(cmd->z), &(cmd->x), &(cmd->y), extension, option);
1185            if (n < 4) {
1186                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid URL for tilelayer %s", tile_config->xmlname);
1187                return DECLINED;
1188            }
1189            if (strcmp(extension, tile_config->fileExtension) != 0) {
1190                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: Invalid file extension (%s) for tilelayer %s, required %s",
1191                        extension, tile_config->xmlname, tile_config->fileExtension);
1192                return DECLINED;
1193            }
1194
1195            oob = (cmd->z < tile_config->minzoom || cmd->z > tile_config->maxzoom);
1196            if (!oob) {
1197                 // valid x/y for tiles are 0 ... 2^zoom-1
1198                 limit = (1 << cmd->z) - 1;
1199                 oob =  (cmd->x < 0 || cmd->x > limit || cmd->y < 0 || cmd->y > limit);
1200            }
1201
1202            if (oob) {
1203                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: request for %s was outside of allowed bounds", tile_config->xmlname);
1204                sleep(CLIENT_PENALTY);
1205                //Don't increase stats counter here,
1206                //As we are interested in valid tiles only
1207                return HTTP_NOT_FOUND;
1208            }
1209
1210            strcpy(cmd->xmlname, tile_config->xmlname);
1211
1212            // Store a copy for later
1213            rdata->cmd = cmd;
1214            rdata->layerNumber = i;
1215            ap_set_module_config(r->request_config, &tile_module, rdata);
1216
1217            // Generate the tile filename?
1218            char abs_path[PATH_MAX];
1219#ifdef METATILE
1220            xyz_to_meta(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
1221#else
1222            xyz_to_path(abs_path, sizeof(abs_path), scfg->tile_dir, cmd->xmlname, cmd->x, cmd->y, cmd->z);
1223#endif
1224            r->filename = apr_pstrdup(r->pool, abs_path);
1225
1226            if (n == 5) {
1227                if (!strcmp(option, "status")) r->handler = "tile_status";
1228                else if (!strcmp(option, "dirty")) r->handler = "tile_dirty";
1229                else return DECLINED;
1230            } else {
1231                r->handler = "tile_serve";
1232            }
1233
1234            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: op(%s) xml(%s) mime(%s) z(%d) x(%d) y(%d)",
1235                    r->handler , cmd->xmlname, tile_config->mimeType, cmd->z, cmd->x, cmd->y);
1236
1237            return OK;
1238        }
1239    }
1240    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "tile_translate: No suitable tile layer found");
1241    return DECLINED;
1242}
1243
1244/*
1245 * This routine is called in the parent, so we'll set up the shared
1246 * memory segment and mutex here.
1247 */
1248
1249static int mod_tile_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1250    apr_pool_t *ptemp, server_rec *s)
1251{
1252    void *data; /* These two help ensure that we only init once. */
1253    const char *userdata_key = "mod_tile_init_module";
1254    apr_status_t rs;
1255    stats_data *stats;
1256    delaypool *delayp;
1257    int i;
1258
1259    /*
1260     * The following checks if this routine has been called before.
1261     * This is necessary because the parent process gets initialized
1262     * a couple of times as the server starts up, and we don't want
1263     * to create any more mutexes and shared memory segments than
1264     * we're actually going to use.
1265     */
1266    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1267    if (!data) {
1268        apr_pool_userdata_set((const void *) 1, userdata_key,
1269                              apr_pool_cleanup_null, s->process->pool);
1270        return OK;
1271    } /* Kilroy was here */
1272
1273    /* Create the shared memory segment */
1274
1275    /*
1276     * Create a unique filename using our pid. This information is
1277     * stashed in the global variable so the children inherit it.
1278     * TODO get the location from the environment $TMPDIR or somesuch.
1279     */
1280    shmfilename = apr_psprintf(pconf, "/tmp/httpd_shm.%ld", (long int)getpid());
1281    shmfilename_delaypool = apr_psprintf(pconf, "/tmp/httpd_shm_delay.%ld", (long int)getpid());
1282
1283    /* Now create that segment
1284     * would prefer to use scfg->configs->nelts here but that does
1285     * not seem to be set at this stage, so rely on previously set layerCount */
1286
1287    rs = apr_shm_create(&stats_shm, sizeof(stats_data) + layerCount * 2 * sizeof(apr_uint64_t),
1288                        (const char *) shmfilename, pconf);
1289    if (rs != APR_SUCCESS) {
1290        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1291                     "Failed to create shared memory segment on file %s",
1292                     shmfilename);
1293        return HTTP_INTERNAL_SERVER_ERROR;
1294    }
1295
1296    rs = apr_shm_create(&delaypool_shm, sizeof(delaypool),
1297                        (const char *) shmfilename_delaypool, pconf);
1298    if (rs != APR_SUCCESS) {
1299        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1300                     "Failed to create shared memory segment on file %s",
1301                     shmfilename_delaypool);
1302        return HTTP_INTERNAL_SERVER_ERROR;
1303    }
1304
1305    /* Created it, now let's zero it out */
1306    stats = (stats_data *)apr_shm_baseaddr_get(stats_shm);
1307    stats->noResp200 = 0;
1308    stats->noResp304 = 0;
1309    stats->noResp404 = 0;
1310    stats->noResp503 = 0;
1311    stats->noResp5XX = 0;
1312    for (i = 0; i <= global_max_zoom; i++) {
1313        stats->noRespZoom[i] = 0;
1314    }
1315    stats->totalBufferRetrievalTime = 0;
1316    stats->noTotalBufferRetrieval = 0;
1317    for (i = 0; i <= global_max_zoom; i++) {
1318        stats->zoomBufferRetrievalTime[i] = 0;
1319        stats->noZoomBufferRetrieval[i] = 0;
1320    }
1321    stats->noRespOther = 0;
1322    stats->noFreshCache = 0;
1323    stats->noFreshRender = 0;
1324    stats->noOldCache = 0;
1325    stats->noOldRender = 0;
1326
1327    /* the "stats" block does not have a fixed size; it is a fixed-size struct
1328     * followed by two arrays with one element each per layer. All of this sits
1329     * in one shared memory block, and for ease of use, pointers from inside the
1330     * struct point to the arrays. */
1331    stats->noResp404Layer = (apr_uint64_t *) ((char *) stats + sizeof(stats_data));
1332    stats->noResp200Layer = (apr_uint64_t *) ((char *) stats + sizeof(stats_data) + sizeof(apr_uint64_t) * layerCount);
1333
1334    /* the last_check and planet_timestamp arrays have a variable size as well,
1335     * however they are not in shared memory. */
1336    last_check = (apr_time_t *) apr_pcalloc(pconf, sizeof(apr_time_t) * layerCount);
1337    planet_timestamp = (apr_time_t *) apr_pcalloc(pconf, sizeof(apr_time_t) * layerCount);
1338
1339    /* zero out all the non-fixed-length stuff */
1340    for (i=0; i<layerCount; i++) {
1341        stats->noResp404Layer[i] = 0;
1342        stats->noResp200Layer[i] = 0;
1343        last_check[i] = 0;
1344        planet_timestamp[i] = 0;
1345    }
1346
1347    delayp = (delaypool *)apr_shm_baseaddr_get(delaypool_shm);
1348   
1349    delayp->last_tile_fillup = apr_time_now();
1350    delayp->last_render_fillup = apr_time_now();
1351
1352    for (i = 0; i < DELAY_HASHTABLE_SIZE; i++) {
1353        memset(&(delayp->users[i].ip_addr),0, sizeof(struct in6_addr));
1354        delayp->users[i].available_tiles = 0;
1355        delayp->users[i].available_render_req = 0;
1356    }
1357    for (i = 0; i < DELAY_HASHTABLE_WHITELIST_SIZE; i++) {
1358        delayp->whitelist[i] = (in_addr_t)0;
1359    }
1360    /* TODO: need a way to initialise the delaypool whitelist */
1361
1362
1363    /* Create global mutex */
1364
1365    /*
1366     * Create another unique filename to lock upon. Note that
1367     * depending on OS and locking mechanism of choice, the file
1368     * may or may not be actually created.
1369     */
1370    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex.%ld",
1371                                 (long int) getpid());
1372
1373    rs = apr_global_mutex_create(&stats_mutex, (const char *) mutexfilename,
1374                                 APR_LOCK_DEFAULT, pconf);
1375    if (rs != APR_SUCCESS) {
1376        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1377                     "Failed to create mutex on file %s",
1378                     mutexfilename);
1379        return HTTP_INTERNAL_SERVER_ERROR;
1380    }
1381
1382#ifdef MOD_TILE_SET_MUTEX_PERMS
1383    rs = unixd_set_global_mutex_perms(stats_mutex);
1384    if (rs != APR_SUCCESS) {
1385        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1386                     "Parent could not set permissions on mod_tile "
1387                     "mutex: check User and Group directives");
1388        return HTTP_INTERNAL_SERVER_ERROR;
1389    }
1390#endif /* MOD_TILE_SET_MUTEX_PERMS */
1391
1392    /*
1393     * Create another unique filename to lock upon. Note that
1394     * depending on OS and locking mechanism of choice, the file
1395     * may or may not be actually created.
1396     */
1397    mutexfilename = apr_psprintf(pconf, "/tmp/httpd_mutex_delay.%ld",
1398                                 (long int) getpid());
1399
1400    rs = apr_global_mutex_create(&delay_mutex, (const char *) mutexfilename,
1401                                 APR_LOCK_DEFAULT, pconf);
1402    if (rs != APR_SUCCESS) {
1403        ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
1404                     "Failed to create mutex on file %s",
1405                     mutexfilename);
1406        return HTTP_INTERNAL_SERVER_ERROR;
1407    }
1408
1409#ifdef MOD_TILE_SET_MUTEX_PERMS
1410    rs = unixd_set_global_mutex_perms(delay_mutex);
1411    if (rs != APR_SUCCESS) {
1412        ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1413                     "Parent could not set permissions on mod_tile "
1414                     "mutex: check User and Group directives");
1415        return HTTP_INTERNAL_SERVER_ERROR;
1416    }
1417#endif /* MOD_TILE_SET_MUTEX_PERMS */
1418
1419    return OK;
1420}
1421
1422
1423/*
1424 * This routine gets called when a child inits. We use it to attach
1425 * to the shared memory segment, and reinitialize the mutex.
1426 */
1427
1428static void mod_tile_child_init(apr_pool_t *p, server_rec *s)
1429{
1430    apr_status_t rs;
1431
1432     /*
1433      * Re-open the mutex for the child. Note we're reusing
1434      * the mutex pointer global here.
1435      */
1436     rs = apr_global_mutex_child_init(&stats_mutex,
1437                                      (const char *) mutexfilename,
1438                                      p);
1439     if (rs != APR_SUCCESS) {
1440         ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
1441                     "Failed to reopen mutex on file %s",
1442                     shmfilename);
1443         /* There's really nothing else we can do here, since
1444          * This routine doesn't return a status. */
1445         exit(1); /* Ugly, but what else? */
1446     }
1447}
1448
1449static void register_hooks(__attribute__((unused)) apr_pool_t *p)
1450{
1451    ap_hook_post_config(mod_tile_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1452    ap_hook_child_init(mod_tile_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1453    ap_hook_handler(tile_handler_serve, NULL, NULL, APR_HOOK_MIDDLE);
1454    ap_hook_handler(tile_handler_dirty, NULL, NULL, APR_HOOK_MIDDLE);
1455    ap_hook_handler(tile_handler_status, NULL, NULL, APR_HOOK_MIDDLE);
1456    ap_hook_handler(tile_handler_json, NULL, NULL, APR_HOOK_MIDDLE);
1457    ap_hook_handler(tile_handler_mod_stats, NULL, NULL, APR_HOOK_MIDDLE);
1458    ap_hook_translate_name(tile_translate, NULL, NULL, APR_HOOK_MIDDLE);
1459    ap_hook_map_to_storage(tile_storage_hook, NULL, NULL, APR_HOOK_FIRST);
1460}
1461
1462static const char *_add_tile_config(cmd_parms *cmd, void *mconfig,
1463                                    const char *baseuri, const char *name, int minzoom, int maxzoom,
1464                                    const char * fileExtension, const char *mimeType, const char *description, const char * attribution,
1465                                    int noHostnames, char ** hostnames, char * cors)
1466{
1467    if (strlen(name) == 0) {
1468        return "ConfigName value must not be null";
1469    }
1470
1471    if (hostnames == NULL) {
1472        hostnames = malloc(sizeof(char *));
1473        /* FIXME: wouldn't be allocationg 7+len+1 bytes be enough? */
1474        hostnames[0] = malloc(PATH_MAX);
1475        strncpy(hostnames[0],"http://", PATH_MAX);
1476        if (cmd->server->server_hostname == NULL) {
1477            ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, cmd->server,
1478                         "Could not determine host name of server to configure tile-json request. Using localhost instead");
1479
1480            strncat(hostnames[0],"localhost",PATH_MAX-10);
1481        } else 
1482            strncat(hostnames[0],cmd->server->server_hostname,PATH_MAX-strlen(hostnames[0])-1);
1483        noHostnames = 1;
1484    }
1485
1486    if ((minzoom < 0) || (maxzoom > MAX_ZOOM_SERVER)) {
1487        return "The configured zoom level lies outside of the range supported by this server";
1488    }
1489    if (maxzoom > global_max_zoom) global_max_zoom = maxzoom;
1490
1491
1492    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1493    tile_config_rec *tilecfg = apr_array_push(scfg->configs);
1494
1495    // Ensure URI string ends with a trailing slash
1496    int urilen = strlen(baseuri);
1497
1498    if (urilen==0)
1499      snprintf(tilecfg->baseuri, PATH_MAX, "/");
1500    else if (baseuri[urilen-1] != '/')
1501      snprintf(tilecfg->baseuri, PATH_MAX, "%s/", baseuri);
1502    else
1503      snprintf(tilecfg->baseuri, PATH_MAX, "%s", baseuri);
1504
1505    strncpy(tilecfg->xmlname, name, XMLCONFIG_MAX-1);
1506    strncpy(tilecfg->fileExtension, fileExtension, XMLCONFIG_MAX-1);
1507    strncpy(tilecfg->mimeType, mimeType, XMLCONFIG_MAX-1);
1508    tilecfg->xmlname[XMLCONFIG_MAX-1] = 0;
1509    tilecfg->minzoom = minzoom;
1510    tilecfg->maxzoom = maxzoom;
1511    tilecfg->description = description;
1512    tilecfg->attribution = attribution;
1513    tilecfg->noHostnames = noHostnames;
1514    tilecfg->hostnames = hostnames;
1515    tilecfg->cors = cors;
1516
1517    ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, cmd->server,
1518                    "Loading tile config %s at %s for zooms %i - %i from tile directory %s with extension .%s and mime type %s",
1519                 name, baseuri, minzoom, maxzoom, scfg->tile_dir, fileExtension, mimeType);
1520
1521    layerCount++;
1522    return NULL;
1523}
1524
1525static const char *add_tile_mime_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name, const char * fileExtension)
1526{
1527    if (strcmp(fileExtension,"png") == 0) {
1528        return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "image/png",NULL,DEFAULT_ATTRIBUTION,0,NULL,NULL);
1529    }
1530    if (strcmp(fileExtension,"js") == 0) {
1531        return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "text/javascript",NULL,DEFAULT_ATTRIBUTION,0,NULL,"*");
1532    }
1533    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, fileExtension, "image/png",NULL,DEFAULT_ATTRIBUTION,0,NULL,NULL);
1534}
1535
1536static const char *add_tile_config(cmd_parms *cmd, void *mconfig, const char *baseuri, const char *name)
1537{
1538    return _add_tile_config(cmd, mconfig, baseuri, name, 0, MAX_ZOOM, "png", "image/png",NULL,DEFAULT_ATTRIBUTION,0,NULL,NULL);
1539}
1540
1541static const char *load_tile_config(cmd_parms *cmd, void *mconfig, const char *conffile)
1542{
1543    FILE * hini ;
1544    char filename[PATH_MAX];
1545    char url[PATH_MAX];
1546    char xmlname[XMLCONFIG_MAX];
1547    char line[INILINE_MAX];
1548    char key[INILINE_MAX];
1549    char value[INILINE_MAX];
1550    const char * result;
1551    char fileExtension[INILINE_MAX];
1552    char mimeType[INILINE_MAX];
1553    char * description = NULL;
1554    char * attribution = NULL;
1555    char * cors = NULL;
1556    char **hostnames = NULL;
1557    char **hostnames_tmp;
1558    int noHostnames = 0;
1559    int tilelayer = 0;
1560    int minzoom = 0;
1561    int maxzoom = MAX_ZOOM;
1562
1563    if (strlen(conffile) == 0) {
1564        strcpy(filename, RENDERD_CONFIG);
1565    } else {
1566        strcpy(filename, conffile);
1567    }
1568
1569    // Load the config
1570    if ((hini=fopen(filename, "r"))==NULL) {
1571        return "Unable to open config file";
1572    }
1573
1574    while (fgets(line, INILINE_MAX, hini)!=NULL) {
1575        if (line[0] == '#') continue;
1576        if (line[0] == ';') continue;
1577        if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
1578        if (line[0] == '[') {
1579            /*Add the previous section to the configuration */
1580            if (tilelayer == 1) {
1581                result = _add_tile_config(cmd, mconfig, url, xmlname, minzoom, maxzoom, fileExtension, mimeType,
1582                                          description,attribution,noHostnames,hostnames, cors);
1583                if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1584                if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1585                if (result != NULL) {
1586                    fclose(hini);
1587                    return result;
1588                }
1589            }
1590            if (strlen(line) >= XMLCONFIG_MAX) {
1591                if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1592                if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1593                fclose(hini);
1594                return "XML name too long";
1595            }
1596            sscanf(line, "[%[^]]", xmlname);
1597            if ((strcmp(xmlname,"mapnik") == 0) || (strcmp(xmlname,"renderd") == 0)) {
1598                /* These aren't tile layers but configuration sections for renderd */
1599                tilelayer = 0;
1600            } else {
1601                tilelayer = 1;
1602            }
1603            /* Initialise default values for tile layer */
1604            strcpy(url,"");
1605            strcpy(fileExtension,"png");
1606            strcpy(mimeType,"image/png");
1607            description = NULL;
1608            cors = NULL;
1609            if (attribution) free(attribution);
1610            attribution = malloc(sizeof(char)*(strlen(DEFAULT_ATTRIBUTION) + 1));
1611            strcpy(attribution,DEFAULT_ATTRIBUTION);
1612            hostnames = NULL;
1613            noHostnames = 0;
1614            minzoom = 0;
1615            maxzoom = MAX_ZOOM;
1616        } else if (sscanf(line, "%[^=]=%[^;#]", key, value) == 2
1617               ||  sscanf(line, "%[^=]=\"%[^\"]\"", key, value) == 2) {
1618
1619            if (!strcmp(key, "URI")){
1620                if (strlen(value) >= PATH_MAX){
1621                    if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1622                    if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1623                    fclose(hini);
1624                    return "URI too long";
1625                }
1626                strcpy(url, value);
1627            }
1628            if (!strcmp(key, "TYPE")){
1629                if (strlen(value) >= PATH_MAX){
1630                    if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1631                    if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1632                    fclose(hini);
1633                    return "TYPE too long";
1634                }
1635                if (sscanf(value, "%[^ ] %[^;#]", fileExtension, mimeType) != 2) {
1636                    if (description) {free(description); description = NULL;} if (attribution) {free(attribution); attribution = NULL;}
1637                    if (hostnames) {free(hostnames); hostnames = NULL;} if (cors) {free(cors); cors = NULL;}
1638                    fclose(hini);
1639                    return "TYPE is not correctly parsable";
1640                }
1641            }
1642            if (!strcmp(key, "DESCRIPTION")){
1643                if (description) free(description);
1644                description = malloc(sizeof(char) * (strlen(value) + 1));
1645                strcpy(description, value);
1646            }
1647            if (!strcmp(key, "ATTRIBUTION")){
1648                if (attribution) free(attribution);
1649                attribution = malloc(sizeof(char) * (strlen(value) + 1));
1650                strcpy(attribution, value);
1651            }
1652            if (!strcmp(key, "CORS")){
1653                if (cors) free(cors);
1654                cors = malloc(sizeof(char) * (strlen(value) + 1));
1655                strcpy(cors, value);
1656            }
1657            if (!strcmp(key, "SERVER_ALIAS")){
1658                if (hostnames == NULL) {
1659                    noHostnames = 1;
1660                    hostnames = malloc((noHostnames + 1) * sizeof(char *));
1661                } else {
1662                    hostnames_tmp = hostnames;
1663                    hostnames = malloc((noHostnames + 1) * sizeof(char *));
1664                    memcpy(hostnames, hostnames_tmp,sizeof(char *) * noHostnames);
1665                    free(hostnames_tmp);
1666                    noHostnames++;
1667                }
1668                hostnames[noHostnames - 1] = malloc(sizeof(char)*(strlen(value) + 1));
1669                strcpy(hostnames[noHostnames - 1], value);
1670            }
1671            if (!strcmp(key, "MINZOOM")){
1672                minzoom = atoi(value);
1673            }
1674            if (!strcmp(key, "MAXZOOM")){
1675                maxzoom = atoi(value);
1676            }
1677        }
1678    }
1679    if (tilelayer == 1) {
1680        ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, cmd->server,
1681                "Committing tile config %s", xmlname);
1682        result = _add_tile_config(cmd, mconfig, url, xmlname, minzoom, maxzoom, fileExtension, mimeType,
1683                                  description,attribution,noHostnames,hostnames, cors);
1684        if (description) free(description);
1685        if (attribution) free(attribution);
1686        if (hostnames) free(hostnames);
1687        if (cors) free(cors);
1688        if (result != NULL) {
1689            fclose(hini);
1690            return result;
1691        }
1692    }
1693    fclose(hini);
1694    return NULL;
1695}
1696
1697static const char *mod_tile_request_timeout_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1698{
1699    int request_timeout;
1700
1701    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1702        return "ModTileRequestTimeout needs integer argument";
1703    }
1704
1705    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1706    scfg->request_timeout = request_timeout;
1707    return NULL;
1708}
1709
1710static const char *mod_tile_request_timeout_missing_config(cmd_parms *cmd, void *mconfig, const char *request_timeout_string)
1711{
1712    int request_timeout;
1713
1714    if (sscanf(request_timeout_string, "%d", &request_timeout) != 1) {
1715        return "ModTileMissingRequestTimeout needs integer argument";
1716    }
1717
1718    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1719    scfg->request_timeout_priority = request_timeout;
1720    return NULL;
1721}
1722
1723static const char *mod_tile_max_load_old_config(cmd_parms *cmd, void *mconfig, const char *max_load_old_string)
1724{
1725    int max_load_old;
1726
1727    if (sscanf(max_load_old_string, "%d", &max_load_old) != 1) {
1728        return "ModTileMaxLoadOld needs integer argument";
1729    }
1730
1731    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1732    scfg->max_load_old = max_load_old;
1733    return NULL;
1734}
1735
1736static const char *mod_tile_max_load_missing_config(cmd_parms *cmd, void *mconfig, const char *max_load_missing_string)
1737{
1738    int max_load_missing;
1739
1740    if (sscanf(max_load_missing_string, "%d", &max_load_missing) != 1) {
1741        return "ModTileMaxLoadMissing needs integer argument";
1742    }
1743
1744    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1745    scfg->max_load_missing = max_load_missing;
1746    return NULL;
1747}
1748
1749static const char *mod_tile_renderd_socket_name_config(cmd_parms *cmd, void *mconfig, const char *renderd_socket_name_string)
1750{
1751    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1752    strncpy(scfg->renderd_socket_name, renderd_socket_name_string, PATH_MAX-1);
1753    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1754    return NULL;
1755}
1756
1757static const char *mod_tile_tile_dir_config(cmd_parms *cmd, void *mconfig, const char *tile_dir_string)
1758{
1759    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1760    strncpy(scfg->tile_dir, tile_dir_string, PATH_MAX-1);
1761    scfg->tile_dir[PATH_MAX-1] = 0;
1762    return NULL;
1763}
1764
1765static const char *mod_tile_cache_extended_host_name_config(cmd_parms *cmd, void *mconfig, const char *cache_extended_hostname)
1766{
1767    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1768    strncpy(scfg->cache_extended_hostname, cache_extended_hostname, PATH_MAX-1);
1769    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1770
1771    return NULL;
1772}
1773
1774static const char *mod_tile_cache_extended_duration_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1775{
1776    int cache_duration;
1777    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1778    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1779            return "ModTileCacheExtendedDuration needs integer argument";
1780    }
1781    scfg->cache_extended_duration = cache_duration;
1782
1783    return NULL;
1784}
1785
1786static const char *mod_tile_cache_lastmod_factor_config(cmd_parms *cmd, void *mconfig, const char *modified_factor_string)
1787{
1788    float modified_factor;
1789    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1790            &tile_module);
1791    if (sscanf(modified_factor_string, "%f", &modified_factor) != 1) {
1792        return "ModTileCacheLastModifiedFactor needs float argument";
1793    }
1794    scfg->cache_duration_last_modified_factor = modified_factor;
1795    return NULL;
1796}
1797
1798static const char *mod_tile_cache_duration_max_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1799{
1800    int cache_duration;
1801    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1802            &tile_module);
1803    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1804        return "ModTileCacheDurationMax needs integer argument";
1805    }
1806    scfg->cache_duration_max = cache_duration;
1807    return NULL;
1808}
1809
1810static const char *mod_tile_cache_duration_dirty_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1811{
1812    int cache_duration;
1813    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1814            &tile_module);
1815    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1816        return "ModTileCacheDurationDirty needs integer argument";
1817    }
1818    scfg->cache_duration_dirty = cache_duration;
1819    return NULL;
1820}
1821
1822static const char *mod_tile_cache_duration_minimum_config(cmd_parms *cmd, void *mconfig, const char *cache_duration_string)
1823{
1824    int cache_duration;
1825    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config,
1826            &tile_module);
1827    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1828        return "ModTileCacheDurationMinimum needs integer argument";
1829    }
1830    scfg->cache_duration_minimum = cache_duration;
1831    return NULL;
1832}
1833
1834static const char *mod_tile_cache_duration_low_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1835{
1836    int zoom_level;
1837    int cache_duration;
1838    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1839    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1840            return "ModTileCacheDurationLowZoom needs integer argument";
1841    }
1842    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1843            return "ModTileCacheDurationLowZoom needs integer argument";
1844    }
1845    scfg->cache_level_low_zoom = zoom_level;
1846    scfg->cache_duration_low_zoom = cache_duration;
1847
1848    return NULL;
1849}
1850static const char *mod_tile_cache_duration_medium_config(cmd_parms *cmd, void *mconfig, const char *zoom_level_string, const char *cache_duration_string)
1851{
1852    int zoom_level;
1853    int cache_duration;
1854    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1855    if (sscanf(zoom_level_string, "%d", &zoom_level) != 1) {
1856            return "ModTileCacheDurationMediumZoom needs integer argument";
1857    }
1858    if (sscanf(cache_duration_string, "%d", &cache_duration) != 1) {
1859            return "ModTileCacheDurationMediumZoom needs integer argument";
1860    }
1861    scfg->cache_level_medium_zoom = zoom_level;
1862    scfg->cache_duration_medium_zoom = cache_duration;
1863
1864    return NULL;
1865}
1866
1867static const char *mod_tile_enable_stats(cmd_parms *cmd, void *mconfig, int enableStats)
1868{
1869    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1870    scfg->enableGlobalStats = enableStats;
1871    return NULL;
1872}
1873
1874static const char *mod_tile_enable_throttling(cmd_parms *cmd, void *mconfig, int enableThrottling)
1875{
1876    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1877    scfg->enableTileThrottling = enableThrottling;
1878    return NULL;
1879}
1880
1881static const char *mod_tile_enable_throttling_xforward(cmd_parms *cmd, void *mconfig, const char * enableThrottlingXForward)
1882{
1883    int throttle_xforward;
1884    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1885    if (sscanf(enableThrottlingXForward, "%d", &throttle_xforward) != 1) {
1886        return "ModTileEnableTileThrottlingXForward needs integer argument between 0 and 2";
1887    }
1888    if ((throttle_xforward < 0) || (throttle_xforward > 2)) {
1889        return "ModTileEnableTileThrottlingXForward needs integer argument between 0 and 2 (0 => off; 1 => use client; 2 => use last entry in chain";
1890    }
1891    scfg->enableTileThrottlingXForward = throttle_xforward;
1892    return NULL;
1893}
1894
1895static const char *mod_tile_bulk_mode(cmd_parms *cmd, void *mconfig, int bulkMode)
1896{
1897    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1898    scfg->bulkMode = bulkMode;
1899    return NULL;
1900}
1901
1902static const char *mod_tile_delaypool_tiles_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1903{
1904    int bucketsize;
1905    float topuprate;
1906
1907    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1908    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1909            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1910    }
1911    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1912            return "ModTileThrottlingTiles needs two numerical arguments, the first one must be integer";
1913    }
1914    scfg->delaypoolTileSize = bucketsize;
1915
1916    /*Convert topup rate into microseconds per tile */
1917    scfg->delaypoolTileRate = (long)(1000000.0/topuprate);
1918
1919    return NULL;
1920}
1921
1922static const char *mod_tile_delaypool_render_config(cmd_parms *cmd, void *mconfig, const char *bucketsize_string, const char *topuprate_string)
1923{
1924    int bucketsize;
1925    float topuprate;
1926
1927    tile_server_conf *scfg = ap_get_module_config(cmd->server->module_config, &tile_module);
1928    if (sscanf(bucketsize_string, "%d", &bucketsize) != 1) {
1929            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1930    }
1931    if (sscanf(topuprate_string, "%f", &topuprate) != 1) {
1932            return "ModTileThrottlingRenders needs two numerical arguments, the first one must be integer";
1933    }
1934    scfg->delaypoolRenderSize = bucketsize;
1935
1936    /*Convert topup rate into microseconds per tile */
1937    scfg->delaypoolRenderRate = (long)(1000000.0/topuprate);
1938
1939    return NULL;
1940}
1941
1942static void *create_tile_config(apr_pool_t *p, server_rec *s)
1943{
1944    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1945
1946    scfg->configs = apr_array_make(p, 4, sizeof(tile_config_rec));
1947    scfg->request_timeout = REQUEST_TIMEOUT;
1948    scfg->request_timeout_priority = REQUEST_TIMEOUT;
1949    scfg->max_load_old = MAX_LOAD_OLD;
1950    scfg->max_load_missing = MAX_LOAD_MISSING;
1951    strncpy(scfg->renderd_socket_name, RENDER_SOCKET, PATH_MAX-1);
1952    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1953    strncpy(scfg->tile_dir, HASH_PATH, PATH_MAX-1);
1954    scfg->tile_dir[PATH_MAX-1] = 0;
1955    memset(&(scfg->cache_extended_hostname),0,PATH_MAX);
1956    scfg->cache_extended_duration = 0;
1957    scfg->cache_duration_dirty = 15*60;
1958    scfg->cache_duration_last_modified_factor = 0.0;
1959    scfg->cache_duration_max = 7*24*60*60;
1960    scfg->cache_duration_minimum = 3*60*60;
1961    scfg->cache_duration_low_zoom = 6*24*60*60;
1962    scfg->cache_duration_medium_zoom = 1*24*60*60;
1963    scfg->cache_level_low_zoom = 0;
1964    scfg->cache_level_medium_zoom = 0;
1965    scfg->enableGlobalStats = 1;
1966    scfg->enableTileThrottling = 0;
1967    scfg->enableTileThrottlingXForward = 0;
1968    scfg->delaypoolTileSize = AVAILABLE_TILE_BUCKET_SIZE;
1969    scfg->delaypoolTileRate = RENDER_TOPUP_RATE;
1970    scfg->delaypoolRenderSize = AVAILABLE_RENDER_BUCKET_SIZE;
1971    scfg->delaypoolRenderRate = RENDER_TOPUP_RATE;
1972    scfg->bulkMode = 0;
1973
1974
1975    return scfg;
1976}
1977
1978static void *merge_tile_config(apr_pool_t *p, void *basev, void *overridesv)
1979{
1980    int i;
1981    tile_server_conf * scfg = (tile_server_conf *) apr_pcalloc(p, sizeof(tile_server_conf));
1982    tile_server_conf * scfg_base = (tile_server_conf *) basev;
1983    tile_server_conf * scfg_over = (tile_server_conf *) overridesv;
1984
1985    scfg->configs = apr_array_append(p, scfg_base->configs, scfg_over->configs);
1986    scfg->request_timeout = scfg_over->request_timeout;
1987    scfg->request_timeout_priority = scfg_over->request_timeout_priority;
1988    scfg->max_load_old = scfg_over->max_load_old;
1989    scfg->max_load_missing = scfg_over->max_load_missing;
1990    strncpy(scfg->renderd_socket_name, scfg_over->renderd_socket_name, PATH_MAX-1);
1991    scfg->renderd_socket_name[PATH_MAX-1] = 0;
1992    strncpy(scfg->tile_dir, scfg_over->tile_dir, PATH_MAX-1);
1993    scfg->tile_dir[PATH_MAX-1] = 0;
1994    strncpy(scfg->cache_extended_hostname, scfg_over->cache_extended_hostname, PATH_MAX-1);
1995    scfg->cache_extended_hostname[PATH_MAX-1] = 0;
1996    scfg->cache_extended_duration = scfg_over->cache_extended_duration;
1997    scfg->cache_duration_dirty = scfg_over->cache_duration_dirty;
1998    scfg->cache_duration_last_modified_factor = scfg_over->cache_duration_last_modified_factor;
1999    scfg->cache_duration_max = scfg_over->cache_duration_max;
2000    scfg->cache_duration_minimum = scfg_over->cache_duration_minimum;
2001    scfg->cache_duration_low_zoom = scfg_over->cache_duration_low_zoom;
2002    scfg->cache_duration_medium_zoom = scfg_over->cache_duration_medium_zoom;
2003    scfg->cache_level_low_zoom = scfg_over->cache_level_low_zoom;
2004    scfg->cache_level_medium_zoom = scfg_over->cache_level_medium_zoom;
2005    scfg->enableGlobalStats = scfg_over->enableGlobalStats;
2006    scfg->enableTileThrottling = scfg_over->enableTileThrottling;
2007    scfg->enableTileThrottlingXForward = scfg_over->enableTileThrottlingXForward;
2008    scfg->delaypoolTileSize = scfg_over->delaypoolTileSize;
2009    scfg->delaypoolTileRate = scfg_over->delaypoolTileRate;
2010    scfg->delaypoolRenderSize = scfg_over->delaypoolRenderSize;
2011    scfg->delaypoolRenderRate = scfg_over->delaypoolRenderRate;
2012    scfg->bulkMode = scfg_over->bulkMode;
2013
2014    //Construct a table of minimum cache times per zoom level
2015    for (i = 0; i <= MAX_ZOOM_SERVER; i++) {
2016        if (i <= scfg->cache_level_low_zoom) {
2017            scfg->mincachetime[i] = scfg->cache_duration_low_zoom;
2018        } else if (i <= scfg->cache_level_medium_zoom) {
2019            scfg->mincachetime[i] = scfg->cache_duration_medium_zoom;
2020        } else {
2021            scfg->mincachetime[i] = scfg->cache_duration_minimum;
2022        }
2023    }
2024
2025    return scfg;
2026}
2027
2028static const command_rec tile_cmds[] =
2029{
2030    AP_INIT_TAKE1(
2031        "LoadTileConfigFile",            /* directive name */
2032        load_tile_config,                /* config action routine */
2033        NULL,                            /* argument to include in call */
2034        OR_OPTIONS,                      /* where available */
2035        "load an entire renderd config file"  /* directive description */
2036    ),
2037    AP_INIT_TAKE2(
2038        "AddTileConfig",                 /* directive name */
2039        add_tile_config,                 /* config action routine */
2040        NULL,                            /* argument to include in call */
2041        OR_OPTIONS,                      /* where available */
2042        "path and name of renderd config to use"  /* directive description */
2043    ),
2044    AP_INIT_TAKE3(
2045            "AddTileMimeConfig",         /* directive name */
2046            add_tile_mime_config,        /* config action routine */
2047            NULL,                        /* argument to include in call */
2048            OR_OPTIONS,                  /* where available */
2049            "path, name and file extension of renderd config to use"  /* directive description */
2050        ),
2051    AP_INIT_TAKE1(
2052        "ModTileRequestTimeout",         /* directive name */
2053        mod_tile_request_timeout_config, /* config action routine */
2054        NULL,                            /* argument to include in call */
2055        OR_OPTIONS,                      /* where available */
2056        "Set timeout in seconds on mod_tile requests"  /* directive description */
2057    ),
2058    AP_INIT_TAKE1(
2059        "ModTileMissingRequestTimeout",         /* directive name */
2060        mod_tile_request_timeout_missing_config, /* config action routine */
2061        NULL,                            /* argument to include in call */
2062        OR_OPTIONS,                      /* where available */
2063        "Set timeout in seconds on missing mod_tile requests"  /* directive description */
2064    ),
2065    AP_INIT_TAKE1(
2066        "ModTileMaxLoadOld",             /* directive name */
2067        mod_tile_max_load_old_config,    /* config action routine */
2068        NULL,                            /* argument to include in call */
2069        OR_OPTIONS,                      /* where available */
2070        "Set max load for rendering old tiles"  /* directive description */
2071    ),
2072    AP_INIT_TAKE1(
2073        "ModTileMaxLoadMissing",         /* directive name */
2074        mod_tile_max_load_missing_config, /* config action routine */
2075        NULL,                            /* argument to include in call */
2076        OR_OPTIONS,                      /* where available */
2077        "Set max load for rendering missing tiles"  /* directive description */
2078    ),
2079    AP_INIT_TAKE1(
2080        "ModTileRenderdSocketName",      /* directive name */
2081        mod_tile_renderd_socket_name_config, /* config action routine */
2082        NULL,                            /* argument to include in call */
2083        OR_OPTIONS,                      /* where available */
2084        "Set name of unix domain socket for connecting to rendering daemon"  /* directive description */
2085    ),
2086    AP_INIT_TAKE1(
2087        "ModTileTileDir",                /* directive name */
2088        mod_tile_tile_dir_config,        /* config action routine */
2089        NULL,                            /* argument to include in call */
2090        OR_OPTIONS,                      /* where available */
2091        "Set name of tile cache directory"  /* directive description */
2092    ),
2093    AP_INIT_TAKE1(
2094        "ModTileCacheExtendedHostName",                /* directive name */
2095        mod_tile_cache_extended_host_name_config,        /* config action routine */
2096        NULL,                            /* argument to include in call */
2097        OR_OPTIONS,                      /* where available */
2098        "set hostname for extended period caching"  /* directive description */
2099    ),
2100    AP_INIT_TAKE1(
2101        "ModTileCacheExtendedDuration",                /* directive name */
2102        mod_tile_cache_extended_duration_config,        /* config action routine */
2103        NULL,                            /* argument to include in call */
2104        OR_OPTIONS,                      /* where available */
2105        "set length of extended period caching"  /* directive description */
2106    ),
2107    AP_INIT_TAKE1(
2108        "ModTileCacheDurationMax",                /* directive name */
2109        mod_tile_cache_duration_max_config,        /* config action routine */
2110        NULL,                            /* argument to include in call */
2111        OR_OPTIONS,                      /* where available */
2112        "Set the maximum cache expiry in seconds"  /* directive description */
2113    ),
2114    AP_INIT_TAKE1(
2115        "ModTileCacheDurationDirty",                    /* directive name */
2116        mod_tile_cache_duration_dirty_config,           /* config action routine */
2117        NULL,                                           /* argument to include in call */
2118        OR_OPTIONS,                                     /* where available */
2119        "Set the cache expiry for serving dirty tiles"  /* directive description */
2120    ),
2121    AP_INIT_TAKE1(
2122        "ModTileCacheDurationMinimum",          /* directive name */
2123        mod_tile_cache_duration_minimum_config, /* config action routine */
2124        NULL,                                   /* argument to include in call */
2125        OR_OPTIONS,                             /* where available */
2126        "Set the minimum cache expiry"          /* directive description */
2127    ),
2128    AP_INIT_TAKE1(
2129        "ModTileCacheLastModifiedFactor",       /* directive name */
2130        mod_tile_cache_lastmod_factor_config,   /* config action routine */
2131        NULL,                                   /* argument to include in call */
2132        OR_OPTIONS,                             /* where available */
2133        "Set the factor by which the last modified determins cache expiry" /* directive description */
2134    ),
2135    AP_INIT_TAKE2(
2136        "ModTileCacheDurationLowZoom",       /* directive name */
2137        mod_tile_cache_duration_low_config,                 /* config action routine */
2138        NULL,                            /* argument to include in call */
2139        OR_OPTIONS,                      /* where available */
2140        "Set the minimum cache duration and zoom level for low zoom tiles"  /* directive description */
2141    ),
2142    AP_INIT_TAKE2(
2143        "ModTileCacheDurationMediumZoom", /* directive name */
2144        mod_tile_cache_duration_medium_config,                 /* config action routine */
2145        NULL,                            /* argument to include in call */
2146        OR_OPTIONS,                      /* where available */
2147        "Set the minimum cache duration and zoom level for medium zoom tiles"  /* directive description */
2148    ),
2149    AP_INIT_FLAG(
2150        "ModTileEnableStats",            /* directive name */
2151        mod_tile_enable_stats,           /* config action routine */
2152        NULL,                            /* argument to include in call */
2153        OR_OPTIONS,                      /* where available */
2154        "On Off - enable of keeping stats about what mod_tile is serving"  /* directive description */
2155    ),
2156    AP_INIT_FLAG(
2157        "ModTileEnableTileThrottling",   /* directive name */
2158        mod_tile_enable_throttling,      /* config action routine */
2159        NULL,                            /* argument to include in call */
2160        OR_OPTIONS,                      /* where available */
2161        "On Off - enable of throttling of IPs that excessively download tiles such as scrapers"  /* directive description */
2162    ),
2163    AP_INIT_TAKE1(
2164        "ModTileEnableTileThrottlingXForward",   /* directive name */
2165        mod_tile_enable_throttling_xforward,      /* config action routine */
2166        NULL,                            /* argument to include in call */
2167        OR_OPTIONS,                      /* where available */
2168        "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 */
2169    ),
2170    AP_INIT_TAKE2(
2171        "ModTileThrottlingTiles",        /* directive name */
2172        mod_tile_delaypool_tiles_config, /* config action routine */
2173        NULL,                            /* argument to include in call */
2174        OR_OPTIONS,                      /* where available */
2175        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
2176    ),
2177    AP_INIT_TAKE2(
2178        "ModTileThrottlingRenders",      /* directive name */
2179        mod_tile_delaypool_render_config,/* config action routine */
2180        NULL,                            /* argument to include in call */
2181        OR_OPTIONS,                      /* where available */
2182        "Set the initial bucket size (number of tiles) and top up rate (tiles per second) for throttling tile request per IP"  /* directive description */
2183    ),
2184    AP_INIT_FLAG(
2185        "ModTileBulkMode",               /* directive name */
2186        mod_tile_bulk_mode,              /* config action routine */
2187        NULL,                            /* argument to include in call */
2188        OR_OPTIONS,                      /* where available */
2189        "On Off - make all requests to renderd with bulk render priority, never mark tiles dirty"  /* directive description */
2190    ),
2191    {NULL}
2192};
2193
2194module AP_MODULE_DECLARE_DATA tile_module =
2195{
2196    STANDARD20_MODULE_STUFF,
2197    NULL,                                /* dir config creater */
2198    NULL,                                /* dir merger --- default is to override */
2199    create_tile_config,                  /* server config */
2200    merge_tile_config,                   /* merge server config */
2201    tile_cmds,                           /* command apr_table_t */
2202    register_hooks                       /* register hooks */
2203};
2204
Note: See TracBrowser for help on using the repository browser.