1
0
mirror of https://github.com/lammertb/libhttp.git synced 2025-08-12 01:22:40 +03:00
Files
libhttp/src/httplib_start.c
2016-11-16 02:06:45 +01:00

298 lines
8.7 KiB
C

/*
* Copyright (C) 2016 Lammert Bies
* Copyright (c) 2013-2016 the Civetweb developers
* Copyright (c) 2004-2013 Sergey Lyubka
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "libhttp-private.h"
/*
* struct mg_context *mg_start( const struct mg_callbacks *callbacks, void *user_data, const char **options );
*
* The function mg_start() functions as the main entry point for the LibHTTP
* server. The function starts all threads and when finished returns the
* context to the running server for future reference.
*/
struct mg_context *mg_start( const struct mg_callbacks *callbacks, void *user_data, const char **options ) {
struct mg_context *ctx;
const char *name;
const char *value;
const char *default_value;
int idx;
int ok;
int workerthreadcount;
unsigned int i;
void (*exit_callback)(const struct mg_context *ctx) = NULL;
struct mg_workerTLS tls;
#if defined(_WIN32)
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
#endif /* _WIN32 */
/* Allocate context and initialize reasonable general case defaults. */
if ((ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx))) == NULL) return NULL;
/* Random number generator will initialize at the first call */
ctx->auth_nonce_mask = (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
if (mg_atomic_inc(&sTlsInit) == 1) {
#if defined(_WIN32)
InitializeCriticalSection(&global_log_file_lock);
#endif /* _WIN32 */
#if !defined(_WIN32)
pthread_mutexattr_init(&pthread_mutex_attr);
pthread_mutexattr_settype(&pthread_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
#endif
if (0 != pthread_key_create(&sTlsKey, XX_httplib_tls_dtor)) {
/* Fatal error - abort start. However, this situation should
* never
* occur in practice. */
mg_atomic_dec(&sTlsInit);
mg_cry(fc(ctx), "Cannot initialize thread local storage");
mg_free(ctx);
return NULL;
}
} else {
/* TODO (low): istead of sleeping, check if sTlsKey is already
* initialized. */
mg_sleep(1);
}
tls.is_master = -1;
tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
#if defined(_WIN32)
tls.pthread_cond_helper_mutex = NULL;
#endif
pthread_setspecific(sTlsKey, &tls);
#if defined(USE_LUA)
lua_init_optional_libraries();
#endif
ok = 0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr);
#if !defined(ALTERNATIVE_QUEUE)
ok &= 0 == pthread_cond_init(&ctx->sq_empty, NULL);
ok &= 0 == pthread_cond_init(&ctx->sq_full, NULL);
#endif
ok &= 0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr);
if (!ok) {
/* Fatal error - abort start. However, this situation should never
* occur in practice. */
mg_cry(fc(ctx), "Cannot initialize thread synchronization objects");
mg_free(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
if (callbacks) {
ctx->callbacks = *callbacks;
exit_callback = callbacks->exit_context;
ctx->callbacks.exit_context = 0;
}
ctx->user_data = user_data;
ctx->handlers = NULL;
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
ctx->shared_lua_websockets = 0;
#endif
while (options && (name = *options++) != NULL) {
if ((idx = get_option_index(name)) == -1) {
mg_cry(fc(ctx), "Invalid option: %s", name);
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
} else if ((value = *options++) == NULL) {
mg_cry(fc(ctx), "%s: option value cannot be NULL", name);
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
if (ctx->config[idx] != NULL) {
mg_cry(fc(ctx), "warning: %s: duplicate option", name);
mg_free(ctx->config[idx]);
}
ctx->config[idx] = mg_strdup(value);
DEBUG_TRACE("[%s] -> [%s]", name, value);
}
/* Set default value if needed */
for (i = 0; config_options[i].name != NULL; i++) {
default_value = config_options[i].default_value;
if (ctx->config[i] == NULL && default_value != NULL) {
ctx->config[i] = mg_strdup(default_value);
}
}
#if defined(NO_FILES)
if (ctx->config[DOCUMENT_ROOT] != NULL) {
mg_cry(fc(ctx), "%s", "Document root must not be set");
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
#endif
XX_httplib_get_system_name(&ctx->systemName);
/* NOTE(lsm): order is important here. SSL certificates must
* be initialized before listening ports. UID must be set last. */
if (!XX_httplib_set_gpass_option(ctx) ||
#if !defined(NO_SSL)
!XX_httplib_set_ssl_option(ctx) ||
#endif
!XX_httplib_set_ports_option(ctx) ||
#if !defined(_WIN32)
!XX_httplib_set_uid_option(ctx) ||
#endif
!XX_httplib_set_acl_option(ctx)) {
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
#if !defined(_WIN32)
/* Ignore SIGPIPE signal, so if browser cancels the request, it
* won't kill the whole process. */
(void)signal(SIGPIPE, SIG_IGN);
#endif /* !_WIN32 */
workerthreadcount = atoi(ctx->config[NUM_THREADS]);
if (workerthreadcount > MAX_WORKER_THREADS) {
mg_cry(fc(ctx), "Too many worker threads");
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
if (workerthreadcount > 0) {
ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount));
ctx->workerthreadids =
(pthread_t *)mg_calloc(ctx->cfg_worker_threads, sizeof(pthread_t));
if (ctx->workerthreadids == NULL) {
mg_cry(fc(ctx), "Not enough memory for worker thread ID array");
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
#if defined(ALTERNATIVE_QUEUE)
ctx->client_wait_events = mg_calloc(sizeof(ctx->client_wait_events[0]),
ctx->cfg_worker_threads);
if (ctx->client_wait_events == NULL) {
mg_cry(fc(ctx), "Not enough memory for worker event array");
mg_free(ctx->workerthreadids);
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
ctx->client_socks =
mg_calloc(sizeof(ctx->client_socks[0]), ctx->cfg_worker_threads);
if (ctx->client_wait_events == NULL) {
mg_cry(fc(ctx), "Not enough memory for worker socket array");
mg_free(ctx->client_socks);
mg_free(ctx->workerthreadids);
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
ctx->client_wait_events[i] = event_create();
if (ctx->client_wait_events[i] == 0) {
mg_cry(fc(ctx), "Error creating worker event %i", i);
/* TODO: clean all and exit */
}
}
#endif
}
#if defined(USE_TIMERS)
if (timers_init(ctx) != 0) {
mg_cry(fc(ctx), "Error creating timers");
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
#endif
/* Context has been created - init user libraries */
if (ctx->callbacks.init_context) {
ctx->callbacks.init_context(ctx);
}
ctx->callbacks.exit_context = exit_callback;
ctx->context_type = 1; /* server context */
/* Start master (listening) thread */
mg_start_thread_with_id( XX_httplib_master_thread, ctx, &ctx->masterthreadid);
/* Start worker threads */
for (i = 0; i < ctx->cfg_worker_threads; i++) {
struct worker_thread_args *wta =
XX_httplib_malloc(sizeof(struct worker_thread_args));
if (wta) {
wta->ctx = ctx;
wta->index = (int)i;
}
if ((wta == NULL)
|| (XX_httplib_mg_start_thread_with_id(XX_httplib_worker_thread,
wta,
&ctx->workerthreadids[i]) != 0)) {
/* thread was not created */
if (wta != NULL) {
mg_free(wta);
}
if (i > 0) {
mg_cry(fc(ctx),
"Cannot start worker thread %i: error %ld",
i + 1,
(long)ERRNO);
} else {
mg_cry(fc(ctx),
"Cannot create threads: error %ld",
(long)ERRNO);
free_context(ctx);
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
break;
}
}
pthread_setspecific(sTlsKey, NULL);
return ctx;
} /* mg_start */