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

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

fix issues found by coverity static analysis in mod_tile.c

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