int http_server_init(struct http_server *server) { /* * one time global initializers */ if (0 > mime_types_init()) return LOGGER_ERROR("failed to initialize mime types"), -1; if (0 > http_headers_init()) return LOGGER_ERROR("failed to initialize http headers"), -1; /* * idle connection handler */ server->idle_ctx = ribs_context_create(SMALL_STACK_SIZE, http_server_idle_handler); server->idle_ctx->data.ptr = server; /* * context pool */ if (0 == server->stack_size || 0 == server->num_stacks) { struct rlimit rlim; if (0 > getrlimit(RLIMIT_STACK, &rlim)) return LOGGER_PERROR("getrlimit(RLIMIT_STACK)"), -1; long total_mem = sysconf(_SC_PHYS_PAGES); if (total_mem < 0) return LOGGER_PERROR("sysconf"), -1; total_mem *= getpagesize(); size_t num_ctx_in_one_map = total_mem / rlim.rlim_cur; /* half of total mem to start with so we don't need to enable overcommit */ num_ctx_in_one_map >>= 1; LOGGER_INFO("http server pool: initial=%zu, grow=%zu", num_ctx_in_one_map, num_ctx_in_one_map); ctx_pool_init(&server->ctx_pool, num_ctx_in_one_map, num_ctx_in_one_map, rlim.rlim_cur, sizeof(struct http_server_context) + server->context_size); } else {
int http_client_pool_init(struct http_client_pool *http_client_pool, size_t initial, size_t grow) { LOGGER_INFO("http client pool: initial=%zu, grow=%zu", initial, grow); if (0 > ctx_pool_init(&http_client_pool->ctx_pool, initial, grow, CLIENT_STACK_SIZE, sizeof(struct http_client_context))) return -1; /* Global to all clients */ if (!client_chains) { struct rlimit rlim; if (0 > getrlimit(RLIMIT_NOFILE, &rlim)) return LOGGER_PERROR("getrlimit(RLIMIT_NOFILE)"), -1; client_chains = calloc(rlim.rlim_cur, sizeof(struct list)); if (!client_chains) return LOGGER_PERROR("calloc client_chains"), -1; /* storage for multiple client chains */ client_heads = calloc(rlim.rlim_cur, sizeof(struct list)); struct list *tmp = client_heads, *tmp_end = tmp + rlim.rlim_cur; if (!client_heads) return LOGGER_PERROR("calloc client_heads"), -1; for (; tmp != tmp_end; ++tmp) list_insert_tail(&free_list, tmp); idle_ctx = ribs_context_create(SMALL_STACK_SIZE, http_client_idle_handler); hashtable_init(&ht_persistent_clients, rlim.rlim_cur); } return timeout_handler_init(&http_client_pool->timeout_handler); }
int ribs_timer_create(void (*handler)(int)) { int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); if (0 > tfd) return LOGGER_PERROR("timerfd_create"), -1; struct ribs_context *ctx = ribs_context_create(1024 * 1024, sizeof(void (*)(void)), _ribs_timer_wrapper); void (**ref)(int) = (void (**)(int))ctx->reserved; *ref = handler; if (0 > ribs_epoll_add(tfd, EPOLLIN, ctx)) return LOGGER_PERROR("epoll_add"), close(tfd), -1; return tfd; }
int ribs_epoll_add(int fd, uint32_t events, struct ribs_context* ctx) { struct epoll_event ev = { .events = events, .data.fd = fd }; if (0 > epoll_ctl(ribs_epoll_fd, EPOLL_CTL_ADD, fd, &ev)) return LOGGER_PERROR("epoll_ctl"), -1; epoll_worker_set_fd_ctx(fd, ctx); return 0; } struct ribs_context* small_ctx_for_fd(int fd, void (*func)(void)) { void *ctx=ribs_context_create(SMALL_STACK_SIZE, func); if (NULL == ctx) return LOGGER_PERROR("ribs_context_create"), NULL; if (0 > ribs_epoll_add(fd, EPOLLIN, ctx)) return NULL; return ctx; }
int epoll_worker_init(void) { struct rlimit rlim; if (0 > getrlimit(RLIMIT_NOFILE, &rlim)) return LOGGER_PERROR("getrlimit(RLIMIT_NOFILE)"), -1; epoll_worker_fd_map = calloc(rlim.rlim_cur, sizeof(struct epoll_worker_fd_data)); ribs_epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (ribs_epoll_fd < 0) return LOGGER_PERROR("epoll_create1"), -1; /* block some signals */ sigset_t set; sigemptyset(&set); sigaddset(&set, SIGPIPE); if (-1 == sigprocmask(SIG_BLOCK, &set, NULL)) return LOGGER_PERROR("sigprocmask"), -1; #ifdef UGLY_GETADDRINFO_WORKAROUND sigemptyset(&set); sigaddset(&set, SIGRTMIN); if (-1 == sigprocmask(SIG_BLOCK, &set, NULL)) return LOGGER_PERROR("sigprocmask"), -1; /* sigrtmin to context */ int sfd = signalfd(-1, &set, SFD_NONBLOCK); if (0 > sfd) return LOGGER_PERROR("signalfd"), -1; if (NULL == small_ctx_for_fd(sfd, sigrtmin_to_context)) return -1; #endif event_loop_ctx = ribs_context_create(SMALL_STACK_SIZE, event_loop); /* pipe to conetxt */ int pipefd[2]; if (0 > pipe2(pipefd, O_NONBLOCK)) return LOGGER_PERROR("pipe"), -1; if (NULL == small_ctx_for_fd(pipefd[0], pipe_to_context)) return -1; queue_ctx_fd = pipefd[1]; return ribs_epoll_add(queue_ctx_fd, EPOLLOUT | EPOLLET, event_loop_ctx); }
static int _set_signals(void) { struct sigaction sa = { .sa_handler = signal_handler, .sa_flags = 0 }; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); if (0 > sigprocmask(SIG_BLOCK, &sigset, NULL)) return LOGGER_PERROR("sigprocmask"), -1; sigfd = signalfd(-1, &sigset, SFD_NONBLOCK | SFD_CLOEXEC); if (0 > sigfd) return LOGGER_PERROR("signalfd"), -1; if (NULL == (sigfd_ctx = ribs_context_create(SIG_CHLD_STACK_SIZE, 0, _handle_sig_child))) return -1; return 0; } static int _init_subprocesses(const char *pidfilename, int num_forks) { if (0 >= num_forks) { num_forks = sysconf(_SC_NPROCESSORS_CONF); if (0 > num_forks) exit(EXIT_FAILURE); } LOGGER_INFO("num forks = %d", num_forks); num_instances = num_forks; daemon_instance = 0; children_pids = calloc(num_forks - 1, sizeof(pid_t)); int i; for (i = 1; i < num_forks; ++i) { LOGGER_INFO("starting sub-process %d", i); pid_t pid = fork(); if (0 > pid) return LOGGER_PERROR("fork"), -1; if (0 == pid) { daemon_instance = i; if (0 > prctl(PR_SET_PDEATHSIG, SIGTERM)) return LOGGER_PERROR("prctl"), -1; if (0 > _set_signals()) return LOGGER_ERROR("failed to set signals"), -1; LOGGER_INFO("sub-process %d started", i); return 0; } else children_pids[i-1] = pid; } if (pidfilename && 0 > _set_pidfile(pidfilename)) return LOGGER_ERROR("failed to set pidfile"), -1; if (0 > _set_signals()) return LOGGER_ERROR("failed to set signals"), -1; return 0; } static int ribs_server_init_daemon(const char *pidfilename, const char *logfilename, int num_forks) { if (0 > pipe2(child_is_up_pipe, O_CLOEXEC)) return LOGGER_ERROR("failed to create pipe"), -1; pid_t pid = fork(); if (pid < 0) return LOGGER_PERROR("ribs_server_init_daemon, fork"), -1; if (pid > 0) { close(child_is_up_pipe[1]); /* close the write side */ /* wait for child to come up */ uint8_t t; int res; LOGGER_INFO("waiting for the child process [%d] to start...", pid); if (0 >= (res = read(child_is_up_pipe[0], &t, sizeof(t)))) { if (0 > res) LOGGER_PERROR("pipe"); LOGGER_ERROR("child process failed to start"); return -1; } LOGGER_INFO("child process started successfully"); exit(EXIT_SUCCESS); } close(child_is_up_pipe[0]); /* close the read side */ umask(0); if (0 > setsid()) return LOGGER_PERROR("daemonize, setsid"), -1; int fdnull = open("/dev/null", O_RDWR); if (0 > fdnull) return LOGGER_PERROR("/dev/null"), -1; dup2(fdnull, STDIN_FILENO); if (logfilename) { if (0 > _logger_init(logfilename)) return -1; } else { dup2(fdnull, STDOUT_FILENO); dup2(fdnull, STDERR_FILENO); } close(fdnull); LOGGER_INFO("child process started"); return _init_subprocesses(pidfilename, num_forks); } int ribs_server_init(int daemonize, const char *pidfilename, const char *logfilename, int num_forks) { if (0 > hashtable_init(&ht_pid_to_ctx, 0)) return -1; if (daemonize) return ribs_server_init_daemon(pidfilename, logfilename, num_forks); /* if (logfilename && 0 > _logger_init(logfilename)) */ /* exit(EXIT_FAILURE); */ return _init_subprocesses(pidfilename, num_forks); } int ribs_server_signal_children(int sig) { if (0 >= num_instances || 0 != daemon_instance) return 0; int i, res = 0; for (i = 0; i < num_instances-1; ++i) { if (0 < children_pids[i] && 0 > kill(children_pids[i], sig)) { LOGGER_PERROR("kill [%d] %d", sig, children_pids[i]); res = -1; } } return res; } void ribs_server_start(void) { daemon_finalize(); if (0 <= sigfd && 0 > ribs_epoll_add(sigfd, EPOLLIN, sigfd_ctx)) return LOGGER_ERROR("ribs_epoll_add: sigfd"); epoll_worker_loop(); if (0 >= num_instances || 0 != daemon_instance) return; LOGGER_INFO("sending SIGTERM to sub-processes"); ribs_server_signal_children(SIGTERM); LOGGER_INFO("waiting for sub-processes to exit"); int i, status; for (i = 0; i < num_instances-1; ++i) { if (0 < children_pids[i] && 0 > waitpid(children_pids[i], &status, 0)) LOGGER_PERROR("waitpid %d", children_pids[i]); } LOGGER_INFO("sub-processes terminated"); }
int http_server_init(struct http_server *server) { /* * one time global initializers */ if (0 > mime_types_init()) return LOGGER_ERROR("failed to initialize mime types"), -1; if (0 > http_headers_init()) return LOGGER_ERROR("failed to initialize http headers"), -1; /* * idle connection handler */ server->idle_ctx = ribs_context_create(SMALL_STACK_SIZE, sizeof(struct http_server *), http_server_idle_handler); struct http_server **server_ref = (struct http_server **)server->idle_ctx->reserved; *server_ref = server; /* * context pool */ if (0 == server->num_stacks) server->num_stacks = DEFAULT_NUM_STACKS; if (0 == server->stack_size) { struct rlimit rlim; if (0 > getrlimit(RLIMIT_STACK, &rlim)) return LOGGER_PERROR("getrlimit(RLIMIT_STACK)"), -1; server->stack_size = rlim.rlim_cur; } LOGGER_INFO("http server pool: initial=%zu, grow=%zu, stack_size=%zu", server->num_stacks, server->num_stacks, server->stack_size); ctx_pool_init(&server->ctx_pool, server->num_stacks, server->num_stacks, server->stack_size, sizeof(struct http_server_context) + server->context_size); /* * listen socket */ const int LISTEN_BACKLOG = 32768; LOGGER_INFO("listening on port: %d, backlog: %d", server->port, LISTEN_BACKLOG); int lfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); if (0 > lfd) return -1; int rc; const int option = 1; rc = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); if (0 > rc) return LOGGER_PERROR("setsockopt, SO_REUSEADDR"), rc; rc = setsockopt(lfd, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option)); if (0 > rc) return LOGGER_PERROR("setsockopt, TCP_NODELAY"), rc; struct linger ls; ls.l_onoff = 0; ls.l_linger = 0; rc = setsockopt(lfd, SOL_SOCKET, SO_LINGER, (void *)&ls, sizeof(ls)); if (0 > rc) return LOGGER_PERROR("setsockopt, SO_LINGER"), rc; struct sockaddr_in addr; memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(server->port); addr.sin_addr.s_addr = INADDR_ANY; if (0 > bind(lfd, (struct sockaddr *)&addr, sizeof(addr))) return LOGGER_PERROR("bind"), -1; if (0 > listen(lfd, LISTEN_BACKLOG)) return LOGGER_PERROR("listen"), -1; server->accept_ctx = ribs_context_create(ACCEPTOR_STACK_SIZE, sizeof(struct http_server *), http_server_accept_connections); server->fd = lfd; server_ref = (struct http_server **)server->accept_ctx->reserved; *server_ref = server; if (server->max_req_size == 0) server->max_req_size = DEFAULT_MAX_REQ_SIZE; return 0; }