src/server/daemon/httplistener.c

changeset 438
22eca559aded
parent 415
d938228c382e
child 440
d77b8f3e14e2
--- a/src/server/daemon/httplistener.c	Sun Nov 20 12:43:44 2022 +0100
+++ b/src/server/daemon/httplistener.c	Sat Nov 26 17:07:08 2022 +0100
@@ -67,7 +67,33 @@
 
 #define LISTENER_MAX_PROTOCOL_TOKENS 1024
 
-CxMap *listener_map = NULL;
+
+#define LISTENER_PROTO_IPV4 "ipv4"
+#define LISTENER_PROTO_IPV6 "ipv6"
+
+
+/*
+ * key: string  format: <protocol>:<port>
+ * value: WSSocket*
+ * 
+ * protocol: ipv4 | ipv6
+ * port: short
+ */
+static CxMap *listener_socket_map;
+
+static pthread_mutex_t listener_mutex;
+
+
+int http_listener_global_init(void) {
+    listener_socket_map = cxHashMapCreate(cxDefaultAllocator, 4);
+    if(!listener_socket_map) {
+        return 1;
+    }
+    
+    listener_mutex = PTHREAD_MUTEX_INITIALIZER;
+    
+    return 0;
+}
 
 int start_all_listener() {
     ServerConfiguration *conf = cfgmgr_get_server_config();
@@ -80,91 +106,244 @@
     return 0;
 }
 
-HttpListener* http_listener_create(ListenerConfig *conf) {
-    if(listener_map == NULL) {
-        listener_map = cxHashMapCreate(cxDefaultAllocator, 16);
+static HttpSSL* create_http_ssl(ListenerConfig *conf) {
+    SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
+    if(!ctx) {
+        return NULL;
     }
-
-    HttpListener *fl = cxMapGet(listener_map, cx_hash_key(conf->name.ptr, conf->name.length));
-    if(fl == NULL) {
-        return http_listener_new(conf);
-    }
+    SSL_CTX_set_options(
+            ctx,
+            SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv3);
     
-    HttpListener* newls = calloc(1, sizeof(HttpListener));
-    if(newls == NULL) {
-        // TODO: error
+    int error = 0;
+    if(conf->disable_proto.ptr) {
+        cxstring *plist = NULL;
+        ssize_t n = cx_strsplit_a(cxDefaultAllocator, conf->disable_proto, cx_str(","), LISTENER_MAX_PROTOCOL_TOKENS, &plist);
+        if(plist) {
+            for(int i=0;i<n;i++) {
+                cxstring proto = plist[i];
+                log_ereport(
+                        LOG_VERBOSE,
+                        "Listener %s: Disable protocol %s",
+                        conf->name.ptr,
+                        proto.ptr);
+                if(!cx_strcasecmp(cx_strtrim(proto), cx_str("SSLv2"))) {
+                    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+                } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("SSLv3"))) {
+                    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
+                } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1"))) {
+                    SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
+                } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.1"))) {
+#ifdef SSL_OP_NO_TLSv1_1
+                    SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
+#else
+                    log_ereport(
+                            LOG_WARN,
+                            "Listener: %s: TLSv1.1 not supported",
+                            conf->name.ptr);
+#endif
+                } else if(cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.2"))) {
+#ifdef SSL_OP_NO_TLSv1_2
+                    SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
+#else
+                    log_ereport(
+                            LOG_WARN,
+                            "Listener: %s: TLSv1.2 not supported",
+                            conf->name.ptr);
+#endif
+                } else if(cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.3"))) {
+#ifdef SSL_OP_NO_TLSv1_3
+                    SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_3);
+#else
+                    log_ereport(
+                            LOG_WARN,
+                            "Listener: %s: TLSv1.3 not supported",
+                            conf->name.ptr);
+#endif
+                } else {
+                    error = 1;
+                    log_ereport(
+                            LOG_MISCONFIG,
+                            "Listener: %s: Unknown protocol %s",
+                            conf->name.ptr,
+                            proto.ptr);
+                }
+            }
+            free(plist);
+        }
     }
     
-    newls->name = conf->name;
-    newls->cfg = conf->cfg;
-    newls->nacceptors = conf->nacceptors;
-    newls->default_vs.vs_name = conf->vs.ptr;  
-    newls->port = fl->port;
-    newls->server_socket = fl->server_socket;
-    newls->server_socket6 = fl->server_socket6;
-    newls->running = 1;
-    newls->threadpool = NULL;
-    newls->ref = 2; // 1 reference is fl->next
-    
-    newls->session_handler = fl->session_handler; // TODO
-    
-    // the listener threadpool might be changed
-    if(conf->threadpool.ptr != NULL) {
-        newls->threadpool = get_threadpool(cx_strcast(conf->threadpool));
-    }
-    if(newls->threadpool == NULL) {
-        newls->threadpool = get_default_threadpool();
+    if(error) {
+        SSL_CTX_free(ctx);
+        return NULL;
     }
     
-    // create acceptor threads
-    newls->acceptors = calloc(newls->nacceptors, sizeof(void*));
-    for (int i=0;i<newls->nacceptors;i++) {
-        newls->acceptors[i] = acceptor_new(newls);
+    int ret;
+    char errbuf[512];
+    
+    error = 0;
+    if(!conf->chainfile.ptr) {
+        ret = SSL_CTX_use_certificate_file(ctx, conf->certfile.ptr, SSL_FILETYPE_PEM);
+        if(!ret) {
+            ERR_error_string(ERR_get_error(), errbuf);
+            log_ereport(LOG_MISCONFIG, "Cannot load ssl chain file: %s", errbuf);
+            error = 1;
+        }
+    } else {
+        ret = SSL_CTX_use_certificate_chain_file(ctx, conf->chainfile.ptr);
+        if(!ret) { 
+            ERR_error_string(ERR_get_error(), errbuf);
+            log_ereport(LOG_MISCONFIG, "Cannot load ssl cert file: %s", errbuf);
+            error = 1;
+        }
+    }
+
+    ret = SSL_CTX_use_PrivateKey_file(ctx, conf->privkeyfile.ptr, SSL_FILETYPE_PEM);
+    if(!ret) { 
+        ERR_error_string(ERR_get_error(), errbuf);
+        log_ereport(LOG_MISCONFIG, "Cannot load ssl key file: %s", errbuf);
+        error = 1;
+    }
+    
+    if(error) {
+        SSL_CTX_free(ctx);
+        return NULL;
     }
     
-    newls->acceptors6 = calloc(newls->nacceptors, sizeof(void*));
-    for (int i=0;i<newls->nacceptors;i++) {
-        newls->acceptors6[i] = acceptor_new(newls);
-        newls->acceptors6[i]->ipv6 = TRUE;
-    }
+    HttpSSL *ssl = pool_malloc(conf->cfg->pool, sizeof(HttpSSL));
+    ZERO(ssl, sizeof(HttpSSL));
+    ssl->sslctx = ctx;
+    
+    return NULL;
+}
+
+static WSSocket* create_socket(ListenerConfig *conf, const char *protocol) {
+    WSBool ipv4 = !strcmp(protocol, "ipv4") ? TRUE : FALSE;
     
-    // fl hold one reference of newls
-    fl->next = newls;
-    
-    
-    cxMapPut(listener_map, cx_hash_key(newls->name.ptr, newls->name.length), newls);
-    
-    for (int i=0;i<newls->nacceptors;i++) {
-        //acceptor_start(newls->acceptors[i]);
-        acceptor_start(newls->acceptors6[i]);
+    int s = -1;
+    if(ipv4) {
+        // ipv4 socket
+        s = socket(AF_INET, SOCK_STREAM, 0);
+    } else {
+        // ipv6 socket
+        s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+    }
+    if(s < 0) {
+        log_ereport(
+                LOG_FAILURE,
+                "cannot create socket: protocol: %s port: %d error: %s",
+                protocol,
+                conf->port,
+                strerror(errno));
+        return NULL;
     }
     
-    // check if a restart is required to apply all changes
+    // socket options
+    int o = 1;
+    setsockopt(
+            s,
+            SOL_SOCKET, SO_REUSEADDR,
+            &o,
+            sizeof(int));
+    
+#ifdef LINUX
+    if(!ipv4) {
+        o = 1;
+        setsockopt(
+                listener->server_socket6,
+                IPPROTO_IPV6,
+                IPV6_V6ONLY,
+                &o,
+                sizeof(int));
+    }
+#endif
     
-    if(newls->port != conf->port) {
-        // TODO: log
+    // bind server socket to address
+    struct sockaddr_in servaddr4;
+    struct sockaddr_in6 servaddr6;
+    struct sockaddr *servaddr;
+    size_t servaddr_size;
+    if(ipv4) {
+        // ipv4
+        memset(&servaddr4, 0, sizeof(servaddr4));
+        servaddr4.sin_family = AF_INET;
+        servaddr4.sin_addr.s_addr = htonl(INADDR_ANY);
+        servaddr4.sin_port = htons(conf->port);
+        servaddr = (struct sockaddr *)&servaddr4;
+        servaddr_size = sizeof(servaddr4);
+    } else {
+        // ipv6
+        memset(&servaddr6, 0, sizeof(servaddr6));
+        servaddr6.sin6_family = AF_INET6;
+        servaddr6.sin6_addr = in6addr_any;
+        servaddr6.sin6_port = htons(conf->port);
+        servaddr = (struct sockaddr *)&servaddr6;
+        servaddr_size = sizeof(servaddr6);
     }
     
-    return newls;
+    if(bind(s, servaddr, servaddr_size)) {
+        log_ereport(
+                LOG_FAILURE,
+                "cannot bind socket: protocol: %s port: %d error: %s",
+                protocol,
+                conf->port,
+                strerror(errno));
+        close(s);
+        return NULL;
+    }
+    
+    // everything is ok, create WSSocket object
+    WSSocket *wssocket = malloc(sizeof(WSSocket));
+    if(!wssocket) {
+        close(s);
+        return NULL;
+    }
+    ZERO(wssocket, sizeof(WSSocket));
+    wssocket->socket = s;
+    
+    return wssocket;
 }
 
-HttpListener* http_listener_new(ListenerConfig *conf) {
-    // TODO: remove
-    if(listener_map == NULL) {
-        listener_map = cxHashMapCreate(cxDefaultAllocator, 16);
+static WSSocket* get_socket(ListenerConfig *conf, const char *protocol) {
+    char key_data[32];
+    size_t key_len = snprintf(key_data, 32, "%s:%d", protocol, conf->port);
+    CxHashKey key = cx_hash_key(key_data, key_len);
+    
+    WSSocket *sock = cxMapGet(listener_socket_map, key);
+    if(!sock) {
+        sock = create_socket(conf, protocol);
+        if(sock) {
+            cxMapPut(listener_socket_map, key, sock);
+        }
     }
+    return sock;
+}
 
-    HttpListener *fl = cxMapGet(listener_map, cx_hash_key(conf->name.ptr, conf->name.length));
-    if(fl != NULL) {
-        return fl;
+/*
+ * returns HttpSSL of socket1 or socket2 if ssl is available
+ */
+static HttpSSL* socket_get_ssl(WSSocket *socket1, WSSocket *socket2) {
+    if(socket1 && socket1->ssl) {
+        return socket1->ssl;
+    }
+    if(socket2 && socket2->ssl) {
+        return socket2->ssl;
     }
-    // end remove
+    return NULL;
+}
 
-    HttpListener *listener = malloc(sizeof(HttpListener));
+static HttpListener* listener_create(ListenerConfig *conf) {
+    pool_handle_t *pool = conf->cfg->pool;
+    HttpListener *listener = pool_malloc(pool, sizeof(HttpListener));
+    if(!listener) {
+        return NULL;
+    }
+    ZERO(listener, sizeof(HttpListener));
+    
     listener->running = 0;
     listener->cfg = conf->cfg;
-    listener->name = conf->name;
-    listener->default_vs.vs_name = conf->vs.ptr;
+    listener->name = cx_strdup_a(pool_allocator(pool), cx_strcast(conf->name));
+    listener->default_vs.vs_name = pool_strdup(pool, conf->vs.ptr);
     listener->threadpool = NULL;
     if(conf->threadpool.ptr != NULL) {
         listener->threadpool = get_threadpool(cx_strcast(conf->threadpool));
@@ -173,9 +352,9 @@
         listener->threadpool = get_default_threadpool();
     }
     if(conf->blockingio) {
-        listener->session_handler = create_basic_session_handler();
+        listener->session_handler = create_basic_session_handler(pool);
     } else {
-        listener->session_handler = create_event_session_handler();
+        listener->session_handler = create_event_session_handler(pool);
     }
     listener->nacceptors = conf->nacceptors;
     listener->port = conf->port;
@@ -183,170 +362,40 @@
     listener->next = NULL;
     listener->ssl = NULL;
     
-    int error = 0;
+    // create sockets
+    listener->server_socket = get_socket(conf, LISTENER_PROTO_IPV4);
+    listener->server_socket6 = get_socket(conf, LISTENER_PROTO_IPV6);
+    if(!listener->server_socket && !listener->server_socket6) {
+        log_ereport(LOG_FAILURE, "Listener %s: no server socket", conf->name.ptr);
+        return NULL;
+    }
     
+    if(listener->server_socket) {
+        wssocket_ref(listener->server_socket);
+    }
+    if(listener->server_socket6) {
+        wssocket_ref(listener->server_socket6);
+    }
+    
+    // create SSL context
     if(conf->ssl) {
-        listener->ssl = malloc(sizeof(HttpSSL));
-        
-        SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
-        SSL_CTX_set_options(
-                ctx,
-                SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv3);
-        if(conf->disable_proto.ptr) {
-            cxstring *plist = NULL;
-            ssize_t n = cx_strsplit_a(cxDefaultAllocator, conf->disable_proto, cx_str(","), LISTENER_MAX_PROTOCOL_TOKENS, &plist);
-            if(plist) {
-                for(int i=0;i<n;i++) {
-                    cxstring proto = plist[i];
-                    log_ereport(
-                            LOG_VERBOSE,
-                            "Listener %s: Disable protocol %s",
-                            listener->name.ptr,
-                            proto.ptr);
-                    if(!cx_strcasecmp(cx_strtrim(proto), cx_str("SSLv2"))) {
-                        SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
-                    } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("SSLv3"))) {
-                        SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
-                    } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1"))) {
-                        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
-                    } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.1"))) {
-#ifdef SSL_OP_NO_TLSv1_1
-                        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
-#else
-                        log_ereport(
-                                LOG_WARN,
-                                "Listener: %s: TLSv1.1 already not supported",
-                                listener->name.ptr);
-#endif
-                    } else if(cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.2"))) {
-#ifdef SSL_OP_NO_TLSv1_2
-                        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
-#else
-                        log_ereport(
-                                LOG_WARN,
-                                "Listener: %s: TLSv1.2 already not supported",
-                                listener->name.ptr);
-#endif
-                    } else if(cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.3"))) {
-#ifdef SSL_OP_NO_TLSv1_3
-                        SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_3);
-#else
-                        log_ereport(
-                                LOG_WARN,
-                                "Listener: %s: TLSv1.3 already not supported",
-                                listener->name.ptr);
-#endif
-                    } else {
-                        error = 1;
-                        log_ereport(
-                                LOG_MISCONFIG,
-                                "Listener: %s: Unknown protocol %s",
-                                listener->name.ptr,
-                                proto.ptr);
-                    }
-                }
-                free(plist);
-            }
-        }
-        
-        if(error) {
-            return NULL;
-        }
-        // TODO: cleanup on error
-        
-        int ret;
-        char errbuf[512];
-        
-        if(!conf->chainfile.ptr) {
-            ret = SSL_CTX_use_certificate_file(ctx, conf->certfile.ptr, SSL_FILETYPE_PEM);
-            if(!ret) {
-                ERR_error_string(ERR_get_error(), errbuf);
-                log_ereport(LOG_MISCONFIG, "Cannot load ssl chain file: %s", errbuf);
-                return NULL;
-            }
-        } else {
-            ret = SSL_CTX_use_certificate_chain_file(ctx, conf->chainfile.ptr);
-            if(!ret) { 
-                ERR_error_string(ERR_get_error(), errbuf);
-                log_ereport(LOG_MISCONFIG, "Cannot load ssl cert file: %s", errbuf);
+        HttpSSL *ssl = socket_get_ssl(listener->server_socket, listener->server_socket6);
+        if(!ssl) {
+            ssl = create_http_ssl(conf);
+            if(!ssl) {
+                log_ereport(LOG_FAILURE, "Listener %s: cannot create SSL context", conf->name.ptr);
                 return NULL;
             }
         }
-        
-        ret = SSL_CTX_use_PrivateKey_file(ctx, conf->privkeyfile.ptr, SSL_FILETYPE_PEM);
-        if(!ret) { 
-            ERR_error_string(ERR_get_error(), errbuf);
-            log_ereport(LOG_MISCONFIG, "Cannot load ssl key file: %s", errbuf);
-            return NULL;
+        if(listener->server_socket) {
+            listener->server_socket->ssl = ssl;
         }
-        
-        // TODO: chain
-        listener->ssl->sslctx = ctx;
-    }
-    
-    
-    cxMapPut(listener_map, cx_hash_key(listener->name.ptr, listener->name.length), listener);
-
-    struct sockaddr_in servaddr;   /* server address */
-    struct sockaddr_in6 servaddr6;
-
-    /* init address structure */
-    memset(&servaddr, 0, sizeof(servaddr));
-    servaddr.sin_family = AF_INET;
-    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
-    servaddr.sin_port = htons(conf->port);
-    
-    memset(&servaddr6, 0, sizeof(servaddr6));
-    servaddr6.sin6_family = AF_INET6;
-    servaddr6.sin6_addr = in6addr_any;
-    servaddr6.sin6_port = htons(conf->port);
-
-    /* create socket */
-    if((listener->server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
-        perror("Error: http_listener_new: socket");
-        return NULL;
-    }
-    
-    if((listener->server_socket6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-        perror("Error: http_listener_new: socket v6");
-        return NULL;
-    }
-    
-    int o = 1;
-    setsockopt(
-            listener->server_socket,
-            SOL_SOCKET, SO_REUSEADDR,
-            &o,
-            sizeof(int));
-    o = 1;
-    setsockopt(
-            listener->server_socket6,
-            SOL_SOCKET, SO_REUSEADDR,
-            &o,
-            sizeof(int));
-    
-#ifdef LINUX
-    o = 1;
-    setsockopt(
-            listener->server_socket6,
-            IPPROTO_IPV6,
-            IPV6_V6ONLY,
-            &o,
-            sizeof(int));
-#endif
-
-    /* bind server socket to address */
-    if(bind(listener->server_socket, (struct sockaddr*)&servaddr, sizeof(servaddr))){
-        log_ereport(LOG_FAILURE, "http_listener_new: bind failed. Port: %d", conf->port);
-        return NULL;
-    }
-    
-    if(bind(listener->server_socket6, (struct sockaddr*)&servaddr6, sizeof(servaddr6))){
-        log_ereport(LOG_FAILURE, "http_listener_new: bind v6 failed. Port: %d: %s", conf->port, strerror(errno));
-        return NULL;
+        if(listener->server_socket6) {
+            listener->server_socket6->ssl = ssl;
+        }
     }
 
-    /* create acceptors */
+    // create acceptors
     listener->acceptors = calloc(listener->nacceptors, sizeof(void*));
     listener->acceptors6 = calloc(listener->nacceptors, sizeof(void*));
     for (int i=0;i<listener->nacceptors;i++) {
@@ -358,26 +407,45 @@
     return listener;
 }
 
+HttpListener* http_listener_create(ListenerConfig *conf) {
+    pthread_mutex_lock(&listener_mutex);
+    HttpListener *listener = listener_create(conf);
+    pthread_mutex_unlock(&listener_mutex);
+    return listener;
+}
+
 int http_listener_start(HttpListener *listener) {
     if(listener->running) {
         return 0;
     }
     log_ereport(LOG_INFORM, "start listener on port %d", listener->port);
+    
+    WSBool ipv4 = listener->server_socket ? TRUE : FALSE;
+    WSBool ipv6 = listener->server_socket6 ? TRUE: FALSE;
 
-    if (listen(listener->server_socket, 256) == -1) {
+    if (ipv4 && !listener->server_socket->listening && listen(listener->server_socket->socket, 256) == -1) {
         log_ereport(LOG_FAILURE, "http_listener_start: listen failed: %s", strerror(errno));
         return -1;
+    } else {
+        listener->server_socket->listening = TRUE;
     }
-    if (listen(listener->server_socket6, 256) == -1) {
+    if (ipv6 && !listener->server_socket6->listening && listen(listener->server_socket6->socket, 256) == -1) {
         log_ereport(LOG_FAILURE, "http_listener_start: listen v6 failed: %s", strerror(errno));
         return -1;
+    } else {
+        listener->server_socket6->listening = TRUE;
     }
 
-    /* start acceptor threads */
+    // start acceptor threads
     for (int i=0;i<listener->nacceptors;i++) {
-        acceptor_start(listener->acceptors[i]);
-        acceptor_start(listener->acceptors6[i]);
+        if(ipv4) {
+            acceptor_start(listener->acceptors[i]);
+        }
+        if(ipv6) {
+            acceptor_start(listener->acceptors6[i]);
+        }
     }
+    listener->running = TRUE;
 
     return 0;
 }
@@ -389,6 +457,7 @@
 void http_listener_unref(HttpListener *listener) {
     uint32_t ref = ws_atomic_dec32(&listener->ref);
     if(ref == 0) {
+        log_ereport(LOG_VERBOSE, "HttpListener %s: destroy", listener->name.ptr);
         free(listener->acceptors);
         // TODO: unref cfg
         // TODO: unref session handler
@@ -412,7 +481,7 @@
             (void*(*)(void*))acceptor_thread,
             a) != 0)
     {
-        perror("Error: acceptor_start: pthread_create");
+        log_ereport(LOG_FAILURE, "Listener %s: acceptor_start: %s", a->listener->name.ptr, strerror(errno));
     }
 }
 
@@ -431,12 +500,12 @@
     socklen_t ca_length;
     ConnectionAddrType addr_type;
     if(acceptor->ipv6) {
-        server_socket = listener->server_socket6;
+        server_socket = listener->server_socket6->socket;
         ca_ptr = (struct sockaddr*)&ca.address_v6;
         ca_length = sizeof(ca.address_v6);
         addr_type = CONN_ADDR_IPV6;
     } else {
-        server_socket = listener->server_socket;
+        server_socket = listener->server_socket->socket;
         ca_ptr = (struct sockaddr*)&ca.address_v4;
         ca_length = sizeof(ca.address_v4);
         addr_type = CONN_ADDR_IPV4;
@@ -444,11 +513,11 @@
     
     
     for (;;) {
-        /* accept connections */
+        // accept connections
         int clientfd;
         socklen_t length = ca_length;
 
-        /* accept a connection */
+        // accept a connection
         clientfd = accept(
                 server_socket,
                 ca_ptr,
@@ -512,6 +581,7 @@
         
         if(acceptor_exit) {
             // this acceptor is outdated
+            log_ereport(LOG_VERBOSE, "acceptor thread %p: exit", (void*)acceptor->tid);
             break;
         }
     }
@@ -521,3 +591,13 @@
     
     return NULL;
 }
+
+void wssocket_ref(WSSocket *ws) {
+    ws_atomic_inc32(&ws->ref);
+}
+
+void wssocket_unref(WSSocket *ws) {
+    // does nothing yet, because maybe it is not a good idea to destroy
+    // a socket
+    ws_atomic_dec32(&ws->ref);
+}

mercurial