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

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

Fix off by one error in string length guard

Fixes coverity scan issue #751697

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