4 * This module handles the task of setting up a listening socket, accepting
5 * connections, and dispatching active connections onto a pool of worker
8 * Copyright (c) 1996-2016 by the citadel.org team
10 * This program is open source software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
19 #include "webcit.h" // All other headers are included from this header.
21 int num_threads_executing = 1; // Number of worker threads currently bound to a connected client
22 int num_threads_existing = 1; // Total number of worker threads that exist right now
23 int is_https = 0; // Set to nonzero if we are running as an HTTPS server today.
24 static void *original_brk = NULL; // Remember the original program break so we can test for leaks
28 * Spawn an additional worker thread into the pool.
30 void spawn_another_worker_thread(int *pointer_to_master_socket)
32 pthread_t th; // Thread descriptor
33 pthread_attr_t attr; // Thread attributes
35 /* set attributes for the new thread */
36 pthread_attr_init(&attr);
37 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
38 pthread_attr_setstacksize(&attr, 1048576); // Large stacks to prevent MIME parser crash on FreeBSD
40 /* now create the thread */
41 if (pthread_create(&th, &attr, (void *(*)(void *)) worker_entry, (void *)pointer_to_master_socket) != 0) {
42 syslog(LOG_WARNING, "Can't create thread: %s", strerror(errno));
45 ++num_threads_existing;
46 ++num_threads_executing;
49 /* free up the attributes */
50 pthread_attr_destroy(&attr);
55 * Entry point for worker threads
57 void worker_entry(int *pointer_to_master_socket)
59 int master_socket = *pointer_to_master_socket;
61 int fail_this_transaction = 0;
62 struct client_handle ch;
65 /* Each worker thread blocks on accept() while waiting for something to do. */
66 memset(&ch, 0, sizeof ch);
70 --num_threads_executing;
71 syslog(LOG_DEBUG, "Additional memory allocated since startup: %d bytes", (int)(sbrk(0)-original_brk));
72 syslog(LOG_DEBUG, "Thread 0x%x calling accept() on master socket %d", (unsigned int)pthread_self(), master_socket);
73 ch.sock = accept(master_socket, NULL, 0);
75 syslog(LOG_DEBUG, "accept() : %s", strerror(errno));
77 ++num_threads_executing;
78 syslog(LOG_DEBUG, "socket %d is awake , threads executing: %d , threads total: %d", ch.sock, num_threads_executing, num_threads_existing);
79 } while ((master_socket > 0) && (ch.sock < 0));
81 /* If all threads are executing, spawn more, up to the maximum */
82 if ( (num_threads_executing >= num_threads_existing) && (num_threads_existing <= MAX_WORKER_THREADS) ) {
83 spawn_another_worker_thread(pointer_to_master_socket);
86 /* We have a client. Do some work. */
88 /* Set the SO_REUSEADDR socket option */
90 setsockopt(ch.sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
92 /* If we are an HTTPS server, go crypto now. */
95 if (ch.ssl_handle == NULL) {
96 fail_this_transaction = 1;
102 fdflags = fcntl(ch.sock, F_GETFL);
104 syslog(LOG_WARNING, "unable to get server socket flags! %s", strerror(errno));
108 /* Perform an HTTP transaction... */
109 if (fail_this_transaction == 0) {
110 perform_one_http_transaction(&ch);
113 /* Shut down SSL/TLS if required... */
117 /* ...and close the socket. */
118 //syslog(LOG_DEBUG, "Closing socket %d...", ch.sock);
119 //lingering_close(ch.sock);
121 syslog(LOG_DEBUG, "Closed socket %d.", ch.sock);
127 * Start up a TCP HTTP[S] server on the requested port
129 int webserver(char *webserver_interface, int webserver_port, int webserver_protocol)
131 int master_socket = (-1) ;
132 original_brk = sbrk(0);
134 switch(webserver_protocol) {
136 syslog(LOG_DEBUG, "Starting HTTP server on %s:%d", webserver_interface, webserver_port);
137 master_socket = webcit_tcp_server(webserver_interface, webserver_port, 10);
139 case WEBSERVER_HTTPS:
140 syslog(LOG_DEBUG, "Starting HTTPS server on %s:%d", webserver_interface, webserver_port);
141 master_socket = webcit_tcp_server(webserver_interface, webserver_port, 10);
146 syslog(LOG_ERR, "unknown protocol");
150 if (master_socket < 1) {
151 syslog(LOG_ERR, "Unable to bind the web server listening socket");
155 syslog(LOG_INFO, "Listening on socket %d", master_socket);
156 signal(SIGPIPE, SIG_IGN);
158 worker_entry(&master_socket); // this thread becomes a worker