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

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

[mod_tile] support storage of gzip compressed content in metatiles

In some cases (e.g. geojson vector tiles) storing the content of metatiles in compressed form can be benefitial.
In this case, make sure that the Content-Encoding header is set to gzip, so that clients can correctly
decode the data.

Wether data in a metatile is stored in compressed form or not is defined by the first 4 magic bytes, which are either
META for uncompressed data or METZ for compressed date.

Currently mod_tile does not yet support decompressing the data it self. So if a client doesn't send the
Accept-Encoding: gzip header, they will get jumbled (still compressed) content back.

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