mirror of
https://github.com/lammertb/libhttp.git
synced 2025-08-12 01:22:40 +03:00
298 lines
8.7 KiB
C
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 */
|