add minimal working implementation for resourcepool_lookup() webdav

Thu, 27 Jan 2022 15:47:18 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 27 Jan 2022 15:47:18 +0100
branch
webdav
changeset 272
f210681d9dd0
parent 271
fd5765c5068c
child 273
f4eb5d125e58

add minimal working implementation for resourcepool_lookup()

src/server/daemon/httprequest.c file | annotate | diff | comparison | revisions
src/server/daemon/httprequest.h file | annotate | diff | comparison | revisions
src/server/daemon/resourcepool.c file | annotate | diff | comparison | revisions
src/server/daemon/resourcepool.h file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/Makefile file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/resource.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/service.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/service.h file | annotate | diff | comparison | revisions
src/server/public/nsapi.h file | annotate | diff | comparison | revisions
--- 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);

mercurial