summaryrefslogtreecommitdiff
path: root/src/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server.c')
-rw-r--r--src/server.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/server.c b/src/server.c
new file mode 100644
index 0000000..66c1ac5
--- /dev/null
+++ b/src/server.c
@@ -0,0 +1,160 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "server.h"
+#include "buffer.h"
+#include "http.h"
+
+static bool enqueue(struct fd_queue *queue, int fd) {
+ if (queue->count == MAX_FD_QUEUE) {
+ return false;
+ }
+
+ queue->queue[queue->back] = fd;
+ queue->count++;
+ queue->back = (queue->back + 1) % MAX_FD_QUEUE;
+
+ return true;
+}
+
+static int dequeue(struct fd_queue *queue) {
+ if (queue->count == 0) {
+ return -1;
+ }
+
+ int fd = queue->queue[queue->front];
+ queue->count--;
+ queue->front = (queue->front + 1) % MAX_FD_QUEUE;
+
+ return fd;
+}
+
+static void *handle_connection(void *data) {
+ assert(data);
+ struct thread_data *tdata = (struct thread_data*)data;
+ int fd = -1;
+ char buf[BUFSIZ + 1];
+ ssize_t rlen;
+ struct buffer *request_buf, *reply_buf;
+ struct reply *reply;
+
+ while(true) {
+ pthread_mutex_lock(&tdata->lock);
+ fd = dequeue(&tdata->queue);
+ if (fd == -1) {
+ pthread_cond_wait(&tdata->cond, &tdata->lock);
+ fd = dequeue(&tdata->queue);
+ }
+ pthread_mutex_unlock(&tdata->lock);
+
+ printf("got fd %d\n", fd);
+
+ if ((rlen = read(fd, buf, BUFSIZ)) < 0) {
+ perror("read failed");
+ close(fd);
+ continue;
+ }
+
+ buf[rlen] = '\0';
+ request_buf = buf_new(buf);
+
+ while (rlen >= BUFSIZ) {
+ if ((rlen = read(fd, buf, BUFSIZ)) < 0) {
+ perror("read failed");
+ close(fd);
+ continue;
+ }
+ buf[rlen] = '\0';
+ buf_append(request_buf, buf);
+ }
+
+ reply = http_handle_request(tdata->http, request_buf);
+ reply_buf = http_build_reply(reply);
+ free(reply);
+
+ write(fd, reply_buf->data, reply_buf->size);
+
+ buf_del(&reply_buf);
+ buf_del(&request_buf);
+
+ close(fd);
+ }
+}
+
+struct server *server_init(struct http *http) {
+ struct server *server = malloc(sizeof(struct server));
+
+ server->server_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (server->server_fd < 0) {
+ perror("socket failure");
+ return NULL;
+ }
+
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = INADDR_ANY,
+ .sin_port = htons(45748),
+ };
+
+ int addrlen = sizeof(addr);
+ int opt = 1;
+
+ if (setsockopt(server->server_fd, SOL_SOCKET, SO_REUSEADDR,
+ &opt, sizeof(opt)) < 0) {
+ perror("setsockopt failed");
+ return NULL;
+ }
+
+ if (bind(server->server_fd, (struct sockaddr*)&addr, addrlen) < 0) {
+ perror("bind failed");
+ return NULL;
+ }
+
+ if (listen(server->server_fd, 16) < 0) {
+ perror("listen failed");
+ return NULL;
+ }
+
+ server->addr = addr;
+ server->addrlen = addrlen;
+ server->thread_pool_size = THREAD_POOL_NUM;
+ server->data.http = http;
+ server->pool = calloc(server->thread_pool_size, sizeof(pthread_t));
+ pthread_mutex_init(&server->data.lock, NULL);
+ pthread_cond_init(&server->data.cond, NULL);
+ server->data.queue.back = -1;
+ server->data.queue.front = -1;
+ server->data.queue.count = 0;
+
+ for (size_t i = 0; i < server->thread_pool_size; i++) {
+ pthread_create(&server->pool[i], NULL, handle_connection, &server->data);
+ }
+
+ return server;
+}
+
+void server_poll(struct server *server) {
+ int client_fd;
+
+ while (true) {
+ if ((client_fd = accept(server->server_fd, (struct sockaddr*)&server->addr,
+ (socklen_t*)&server->addrlen)) < 0) {
+ perror("accept failed");
+ return;
+ }
+
+ printf("connection accepted on fd %d\n", client_fd);
+
+ pthread_mutex_lock(&server->data.lock);
+ enqueue(&server->data.queue, client_fd);
+ pthread_cond_signal(&server->data.cond);
+ pthread_mutex_unlock(&server->data.lock);
+
+ }
+}