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

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

[mod_tile] Make minimum and maximum zoom levels per layer configurable

The minimum and maximum zoom levels can now be specified on a per layer
basis with the parameters "MINZOOM" and "MAXZOOM" in renderd.conf

Raise the maximum supported zoom level to 30

This only effects mod_tile for the moment. Renderd does not yet support configurable zoom levels, although
raising the compile time constant MAX_ZOOM should work.

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