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

Last change on this file since 22778 was 22778, checked in by apmon, 9 years ago

Add a tile traffic shaping / throtteling feature to mod_tile

Tile scrapers can often overburden the resources available to a tileserver by requesting large areas at high zooms.
Rather than banning these users from the tileserver, this patch adds the ability to mod_tile to automatically throttle
these clients to acceptable limits.

It uses a token bucket implementation with configurable bucket size and top up rate. Accounting is done in a hashtable
with a small possibility of ip hashtable clashes

The code also supports whitelisting a few ip addresses, such as proxy servers or CDN nodes, although at the moment
there is no way to configure these ip's other than through adding them to the code and recompiling.

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