Thu, 27 Jan 2022 15:47:18 +0100
add minimal working implementation for resourcepool_lookup()
--- a/src/server/daemon/httprequest.c Tue Jan 25 17:45:56 2022 +0100 +++ b/src/server/daemon/httprequest.c Thu Jan 27 15:47:18 2022 +0100 @@ -566,6 +566,8 @@ } int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq) { + request_free_resources(sn, rq); + if(rq->rq.rq_attr.keep_alive) { SessionHandler *sh = sn->connection->session_handler; sh->keep_alive(sh, sn->connection); @@ -588,6 +590,16 @@ return 0; } +void request_free_resources(NSAPISession *sn, NSAPIRequest *rq) { + if(!rq->resources) return; + + UcxMapIterator i = ucx_map_iterator(rq->resources); + ResourceData *resource; + UCX_MAP_FOREACH(key, resource, i) { + resourcepool_free(&sn->sn, &rq->rq, resource); + } +} + int nsapi_authtrans(NSAPISession *sn, NSAPIRequest *rq) { HTTPObjectConfig *objconf = rq->vs->objects; httpd_object *obj = objconf->objects[0];
--- a/src/server/daemon/httprequest.h Tue Jan 25 17:45:56 2022 +0100 +++ b/src/server/daemon/httprequest.h Thu Jan 27 15:47:18 2022 +0100 @@ -93,6 +93,8 @@ int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq); int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq); +void request_free_resources(NSAPISession *sn, NSAPIRequest *rq); + int nsapi_authtrans(NSAPISession *sn, NSAPIRequest *rq); int nsapi_nametrans(NSAPISession *sn, NSAPIRequest *rq); int nsapi_pathcheck(NSAPISession *sn, NSAPIRequest *rq);
--- a/src/server/daemon/resourcepool.c Tue Jan 25 17:45:56 2022 +0100 +++ b/src/server/daemon/resourcepool.c Thu Jan 27 15:47:18 2022 +0100 @@ -30,6 +30,7 @@ #include "request.h" #include "session.h" #include "../public/nsapi.h" +#include "../util/atomic.h" #define RESOURCE_POOL_MAX_DEFAULT 32 @@ -83,7 +84,7 @@ respool->type = restype; respool->data = respool_data; - respool->min = 0; // TODO: get from node + respool->min = 4; // TODO: get from node respool->max = RESOURCE_POOL_MAX_DEFAULT; // TODO: get from node // don't allow too large resource pools @@ -94,7 +95,7 @@ } respool->resalloc = respool->max; - respool->resources = pool_malloc(cfg->pool, respool->resalloc * sizeof(void*)); + respool->resources = pool_malloc(cfg->pool, respool->resalloc * sizeof(ResourceDataPrivate*)); if(!respool->resources || ucx_map_sstr_put(cfg->resources, name, respool)) { log_ereport(LOG_FAILURE, "Cannot add resource pool: OOM"); @@ -109,53 +110,123 @@ return 0; } -int resourcepool_create_resources(ResourcePool *pool, int num_res) { - if(num_res > pool->resalloc) { - num_res = pool->resalloc; - } - - for(int i=pool->numresources;i<num_res;i++) { - void *resource = pool->type->createresource(pool->data); - if(resource) { - pool->resources[pool->numresources++] = resource; - } else { - return 1; // error - } - } - - return 0; -} - ResourceData* resourcepool_lookup(Session *sn, Request *rq, const char *name, int flags) { NSAPIRequest *request = (NSAPIRequest*)rq; NSAPISession *session = (NSAPISession*)sn; ServerConfiguration *cfg = session->config; - ResourceDataPrivate *res = NULL; + ResourceDataPrivate *resource = NULL; // was this resource already used by this request? if(request->resources) { - res = ucx_map_cstr_get(request->resources, name); - if(res) { - return (ResourceData*)res; + resource = ucx_map_cstr_get(request->resources, name); + if(resource) { + return &resource->data; } } - // TODO: get cached resource + ResourcePool *respool = ucx_map_cstr_get(cfg->resources, name); + if(!respool) return NULL; + + + pthread_mutex_lock(&respool->lock); + WSBool createResource = FALSE; + if(respool->numcreated < respool->min) { + createResource = TRUE; + } - /* - ResourceType *type = ucx_map_cstr_get(cfg->resources, name); - if(!type) { - return NULL; + if(createResource) { + // create a new resource and store it in the resourcepool + void *resourceData = respool->type->createresource(respool->data); + if(resourceData) { + respool->numcreated++; + + resource = pool_malloc(sn->pool, sizeof(ResourceDataPrivate)); + if(resource) { + resource->data.data = respool->type->getresourcedata(resourceData); + resource->data.resourcepool = respool; + } else { + respool->type->freeresource(respool->data, resourceData); + log_ereport(LOG_CATASTROPHE, "resourcepool_lookup: OOM"); + } + } + // else: respool->type->createresource does logging in case of errors + } else if(respool->numresources > 0) { + resource = respool->resources[--respool->numresources]; + } else { + // wait for free resource + pthread_cond_wait(&respool->available, &respool->lock); + if(respool->numresources > 0) { + resource = respool->resources[--respool->numresources]; + } } - */ + + // save the resource in the request object, for caching and also + // for cleanup later + int err = 0; + if(resource) { + if(!request->resources) { + request->resources = ucx_map_new_a(&session->allocator, 8); + } + + if(request->resources) { + if(ucx_map_cstr_put(request->resources, name, resource)) { + err = 1; + } + } else { + err = 1; + } + } + + if(err) { + // err == 1 caused by OOM + log_ereport(LOG_FAILURE, "resourcepool_lookup: OOM"); + // cleanup + resourcepool_destroy_resource(resource); + resource = NULL; + } + + pthread_mutex_unlock(&respool->lock); - return NULL; + return &resource->data; } -void resourcepool_free(ResourceData *data) { +void resourcepool_free(Session *sn, Request *rq, ResourceData *resource) { + ResourceDataPrivate *res = (ResourceDataPrivate*)resource; + ResourcePool *respool = resource->resourcepool; + + pthread_mutex_lock(&respool->lock); + if(respool->numresources >= respool->resalloc) { + // actually respool->resalloc == respool->max + // and numresources should always be smaller + // however just be extra safe here + respool->resalloc += 8; + ResourceDataPrivate **new_res_array = pool_realloc( + respool->pool, + respool->resources, + respool->resalloc * sizeof(ResourceDataPrivate*)); + if(new_res_array) { + respool->resources = new_res_array; + } else { + log_ereport(LOG_FAILURE, "resourcepool_free: OOM"); + resourcepool_destroy_resource(res); + pthread_mutex_unlock(&respool->lock); + return; + } + } + + respool->resources[respool->numresources++] = res; + + pthread_cond_signal(&respool->available); + pthread_mutex_unlock(&respool->lock); +} + +void resourcepool_destroy_resource(ResourceDataPrivate *res) { + res->data.resourcepool->numcreated--; + res->data.resourcepool->type->freeresource(res->data.resourcepool->data, res->resdata); + pool_free(res->data.resourcepool->pool, res); } void resourcepool_destroy(ResourcePool *respool) {
--- a/src/server/daemon/resourcepool.h Tue Jan 25 17:45:56 2022 +0100 +++ b/src/server/daemon/resourcepool.h Thu Jan 27 15:47:18 2022 +0100 @@ -39,13 +39,18 @@ extern "C" { #endif -typedef struct ResourcePool ResourcePool; typedef struct ResourceDataPrivate ResourceDataPrivate; struct ResourceDataPrivate { ResourceData data; - Request *rq; - ResourceType *type; + + /* + * the void* pointer returned by respool->type->createresource + * + * ResourceData.data contains the pointer returned by + * respool->type->getresourcedata(resdata)) + */ + void *resdata; }; struct ResourcePool { @@ -75,7 +80,7 @@ * Array of available resources * each entry is created with ResourceType.createresource */ - void **resources; + ResourceDataPrivate **resources; /* * Allocated size of the resources array @@ -87,6 +92,12 @@ */ size_t numresources; + + /* + * Number of created resources (in use + available) + */ + size_t numcreated; + /* * resource pool min parameter */ @@ -102,7 +113,7 @@ int resourcepool_new(ServerConfiguration *cfg, scstr_t type, scstr_t name, ConfigNode *node); -int resourcepool_create_resources(ResourcePool *pool, int num_res); +void resourcepool_destroy_resource(ResourceDataPrivate *res); void resourcepool_destroy(ResourcePool *respool);
--- a/src/server/plugins/postgresql/Makefile Tue Jan 25 17:45:56 2022 +0100 +++ b/src/server/plugins/postgresql/Makefile Thu Jan 27 15:47:18 2022 +0100 @@ -33,6 +33,7 @@ # list of source files SRC = init.c SRC += resource.c +SRC += service.c OBJ = $(SRC:%.c=$(BUILD_ROOT)/build/server/plugins/postgresql/%$(OBJ_EXT))
--- a/src/server/plugins/postgresql/resource.c Tue Jan 25 17:45:56 2022 +0100 +++ b/src/server/plugins/postgresql/resource.c Thu Jan 27 15:47:18 2022 +0100 @@ -90,7 +90,7 @@ } -void pg_resourcepool_destroy(PgResourcePool *pg) { +void pg_resourcepool_destroy(PgResourcePool *pg) { // unused } @@ -111,17 +111,17 @@ return res; } -void pg_resourcepool_freeresource(PgResourcePool *pg, PgResource *res) { +void pg_resourcepool_freeresource(PgResourcePool *pg, PgResource *res) { if(res->connection) { PQfinish(res->connection); } } -int pg_resourcepool_prepare(PgResourcePool *pg, PgResource *res) { +int pg_resourcepool_prepare(PgResourcePool *pg, PgResource *res) { return 0; } -int pg_resourcepool_finish(PgResourcePool *pg, PgResource *res) { +int pg_resourcepool_finish(PgResourcePool *pg, PgResource *res) { return 0; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/plugins/postgresql/service.c Thu Jan 27 15:47:18 2022 +0100 @@ -0,0 +1,101 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2022 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "service.h" + +#include <stdio.h> +#include <string.h> + +#include "resource.h" + +int pg_query(pblock *pb, Session *sn, Request *rq) { + char *respool = pblock_findval("resource", pb); + char *query = pblock_findval("query", pb); + + if(!respool) { + log_ereport(LOG_MISCONFIG, "pg-query: missing resource parameter"); + return REQ_ABORTED; + } + if(!query) { + log_ereport(LOG_MISCONFIG, "pg-query: missing query parameter"); + return REQ_ABORTED; + } + + // Get the named resource and check if the PG connection works + ResourceData *res = resourcepool_lookup(sn, rq, respool, 0); + if(!res) { + log_ereport(LOG_FAILURE, "pg-query: cannot get resource '%s'", respool); + return REQ_ABORTED; + } + + PGconn *connection = res->data; + if(pg_check_connection(LOG_FAILURE, respool, connection)) { + return REQ_ABORTED; + } + + // execute query + PGresult *result = PQexec(connection, query); + if(!result) return REQ_ABORTED; + + // start response + pblock_remove("content-type", rq->srvhdrs); + pblock_nvinsert("content-type", "text/html", rq->srvhdrs); + + protocol_status(sn, rq, 200, NULL); + http_start_response(sn, rq); + + // (html) header + net_printf(sn->csd, "%s", "<!DOCTYPE html>\n<html><head></head><body>\n"); + + int nfields = PQnfields(result); + if(nfields > 0) { + net_printf(sn->csd, "<table>\n<tr>\n"); + for(int i=0;i<nfields;i++) { + net_printf(sn->csd, "<th>%s</th>\n", PQfname(result, i)); + } + net_printf(sn->csd, "</tr>\n"); + + int nrows = PQntuples(result); + for(int r=0;r<nrows;r++) { + net_printf(sn->csd, "<tr>\n"); + for(int c=0;c<nfields;c++) { + net_printf(sn->csd, "<td>%s</td>\n", PQgetvalue(result, r, c)); + } + net_printf(sn->csd, "</tr>\n"); + } + + + net_printf(sn->csd, "</table>\n"); + } else { + net_printf(sn->csd, "<p>%s</p>\n", PQresStatus(PQresultStatus(result))); + } + + net_printf(sn->csd, "%s", "</body></html>\n"); + + return REQ_PROCEED; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/plugins/postgresql/service.h Thu Jan 27 15:47:18 2022 +0100 @@ -0,0 +1,52 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2022 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PG_SERVICE_H +#define PG_SERVICE_H + +#include "../../public/nsapi.h" + +#include <libpq-fe.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SAF parameter: + * resourcepool: name of the postgresql resource pool + * query: SQL query to execute + */ +int pg_query(pblock *pb, Session *sn, Request *rq); + +#ifdef __cplusplus +} +#endif + +#endif /* PG_SERVICE_H */ +
--- a/src/server/public/nsapi.h Tue Jan 25 17:45:56 2022 +0100 +++ b/src/server/public/nsapi.h Thu Jan 27 15:47:18 2022 +0100 @@ -764,6 +764,8 @@ typedef struct _http_listener HttpListener; +typedef struct ResourcePool ResourcePool; + typedef struct ResourceType ResourceType; typedef struct ResourceData ResourceData; @@ -786,7 +788,7 @@ }; struct ResourceData { - void *resourcepool; + ResourcePool *resourcepool; void *data; }; @@ -1608,7 +1610,7 @@ // resource pool ResourceData* resourcepool_lookup(Session *sn, Request *rq, const char *name, int flags); -void resourcepool_free(ResourceData *data); +void resourcepool_free(Session *sn, Request *rq, ResourceData *resource); // assert void ws_log_assert(const char *file, const char *func, int line);