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

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

[mod_tile] Support tile throttling with IPv6 as well

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