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

Last change on this file since 27369 was 27369, checked in by apmon, 8 years ago

[mod_tile] Add per tile-layer stats.

If mod_tile serves many different tile styles, it might be interesting how havily various
layers are used and which layers particularly have many 404 errors due to not rendering fast enough.

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