static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache; struct sqlite_conn *conn; sqlite3_stmt *stmt; int ret; mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,1); if (GC_HAS_ERROR(ctx)) { if(tile->tileset->read_only || !tile->tileset->source) { mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_FAILURE; } else { /* not an error in this case, as the db file may not have been created yet */ ctx->clear_errors(ctx); mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_CACHE_MISS; } } conn = SQLITE_CONN(pc); stmt = conn->prepared_statements[GET_TILE_STMT_IDX]; if(!stmt) { sqlite3_prepare(conn->handle, cache->get_stmt.sql, -1, &conn->prepared_statements[GET_TILE_STMT_IDX], NULL); stmt = conn->prepared_statements[GET_TILE_STMT_IDX]; } cache->bind_stmt(ctx, stmt, cache, tile); do { ret = sqlite3_step(stmt); if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) { ctx->set_error(ctx, 500, "sqlite backend failed on get: %s", sqlite3_errmsg(conn->handle)); sqlite3_reset(stmt); mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_FAILURE; } } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED); if (ret == SQLITE_DONE) { sqlite3_reset(stmt); mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_CACHE_MISS; } else { const void *blob = sqlite3_column_blob(stmt, 0); int size = sqlite3_column_bytes(stmt, 0); if(size>0 && ((char*)blob)[0] == '#') { tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy ,blob,&tile->nodata); } else { tile->encoded_data = mapcache_buffer_create(size, ctx->pool); memcpy(tile->encoded_data->buf, blob, size); tile->encoded_data->size = size; } if (sqlite3_column_count(stmt) > 1) { time_t mtime = sqlite3_column_int64(stmt, 1); apr_time_ansi_put(&(tile->mtime), mtime); } sqlite3_reset(stmt); mapcache_sqlite_release_conn(ctx, pc); return MAPCACHE_SUCCESS; } }
/** * \brief get content of given tile * * fills the mapcache_tile::data of the given tile with content stored on the memcache server * \private \memberof mapcache_cache_memcache * \sa mapcache_cache::tile_get() */ static int _mapcache_cache_memcache_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { char *key; int rv; mapcache_cache_memcache *cache = (mapcache_cache_memcache*)pcache; mapcache_pooled_connection *pc; mapcache_buffer *encoded_data; struct mapcache_memcache_pooled_connection *mpc; pc = _mapcache_memcache_get_conn(ctx,cache,tile); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } mpc = pc->connection; key = mapcache_util_get_tile_key(ctx, tile,NULL," \r\n\t\f\e\a\b","#"); if(GC_HAS_ERROR(ctx)) { rv = MAPCACHE_FAILURE; goto cleanup; } encoded_data = mapcache_buffer_create(0,ctx->pool); rv = apr_memcache_getp(mpc->memcache,ctx->pool,key,(char**)&encoded_data->buf,&encoded_data->size,NULL); if(rv != APR_SUCCESS) { rv = MAPCACHE_CACHE_MISS; goto cleanup; } if(encoded_data->size == 0) { ctx->set_error(ctx,500,"memcache cache returned 0-length data for tile %d %d %d\n",tile->x,tile->y,tile->z); rv = MAPCACHE_FAILURE; goto cleanup; } /* extract the tile modification time from the end of the data returned */ memcpy( &tile->mtime, &(((char*)encoded_data->buf)[encoded_data->size-sizeof(apr_time_t)]), sizeof(apr_time_t)); ((char*)encoded_data->buf)[encoded_data->size+sizeof(apr_time_t)]='\0'; encoded_data->avail = encoded_data->size; encoded_data->size -= sizeof(apr_time_t); if(((char*)encoded_data->buf)[0] == '#' && encoded_data->size > 1) { tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy ,encoded_data->buf,&tile->nodata); } else { tile->encoded_data = encoded_data; } rv = MAPCACHE_SUCCESS; cleanup: _mapcache_memcache_release_conn(ctx,pc); return rv; }
mapcache_http_response *mapcache_core_get_tile(mapcache_context *ctx, mapcache_request_get_tile *req_tile) { int expires = 0; mapcache_http_response *response; int i,is_empty=1 /* response image is initially empty */; char *timestr; mapcache_image *base=NULL; mapcache_image_format *format = NULL; #ifdef DEBUG if(req_tile->ntiles ==0) { ctx->set_error(ctx,500,"BUG: get_tile called with 0 tiles"); return NULL; } #endif response = mapcache_http_response_create(ctx->pool); mapcache_prefetch_tiles(ctx,req_tile->tiles,req_tile->ntiles); if(GC_HAS_ERROR(ctx)) return NULL; /* loop through tiles, and eventually merge them vertically together */ for(i=0; i<req_tile->ntiles; i++) { mapcache_tile *tile = req_tile->tiles[i]; /* shortcut */ if(tile->mtime && (tile->mtime < response->mtime || response->mtime == 0)) response->mtime = tile->mtime; if(tile->expires && (tile->expires < expires || expires == 0)) { expires = tile->expires; } if(tile->nodata) { /* treat the special case where the cache explicitely stated that the tile was empty, and we don't have any vertical merging to do */ if(tile->encoded_data && req_tile->ntiles == 1) { response->data = tile->encoded_data; /* we don't touch is_empty, as we have access to the encoded empty image, but the resulting tile is empty */ } continue; } /* treat the most common case: - we have a single tile request (i.e. isempty is true) - the cache returned the encoded image */ if(is_empty && tile->encoded_data) { response->data = tile->encoded_data; /* just in case we also have the raw image data available, keep a ref to it if we need to merge another tile ontop of it*/ if(tile->raw_image) { base = tile->raw_image; } is_empty = 0; /* we now know we might need to do some vertical merging */ continue; } /* if we're here, either * - we need to merge the current tile onto the previous one(s), or * - we only have the tile's raw data available */ if(!is_empty) { /* we have an existing tile, so we know we need to merge the current one into it */ if(!base) { /* the existing tile has not been decoded yet, but we need the access to the raw pixels*/ base = mapcache_imageio_decode(ctx, response->data); if(!base) return NULL; } response->data = NULL; /* the encoded data is now obsolete, as we will be merging the current tile */ /* we need to access the current tile's pixel data */ if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx,tile->encoded_data); if(!tile->raw_image) return NULL; } mapcache_image_merge(ctx, base, tile->raw_image); } else { /* we don't need to merge onto an existing tile and don't have access to the tile's encoded data. * * we don't encode the tile's raw image data just yet because we might need to merge another one on top * of it later. */ base = tile->raw_image; is_empty = 0; } } if(!response->data) { /* we need to encode the raw image data*/ if(base) { if(req_tile->format) { format = req_tile->format; } else { format = req_tile->tiles[0]->tileset->format; if(!format) { format = ctx->config->default_image_format; /* this one is always defined */ } } response->data = format->write(ctx, base, format); if(GC_HAS_ERROR(ctx)) { return NULL; } } else { #ifdef DEBUG if(!is_empty) { ctx->set_error(ctx,500,"BUG: no image data to encode, but tile not marked as empty"); return NULL; } #endif unsigned char empty[5] = {'#',0,0,0,0}; response->data = mapcache_empty_png_decode(ctx,empty,&is_empty); /* is_empty is unchanged and left to 1 */ format = mapcache_configuration_get_image_format(ctx->config,"PNG8"); } } /* compute the content-type */ mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,response->data); if(t == GC_PNG) apr_table_set(response->headers,"Content-Type","image/png"); else if(t == GC_JPEG) apr_table_set(response->headers,"Content-Type","image/jpeg"); /* compute expiry headers */ if(expires) { apr_time_t now = apr_time_now(); apr_time_t additional = apr_time_from_sec(expires); apr_time_t texpires = now + additional; apr_table_set(response->headers, "Cache-Control",apr_psprintf(ctx->pool, "max-age=%d", expires)); timestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN); apr_rfc822_date(timestr, texpires); apr_table_setn(response->headers, "Expires", timestr); } return response; }