#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "server.h"
#include "ood/select_sets.h"
static void
connection_on_write(struct ood_inet_socket *connection);
static bool received_sigint = false;
static void
handle_sigint(int signum)
{
assert(SIGINT == signum);
received_sigint = true;
}
static void
connection_on_error(struct ood_inet_socket *connection)
{
assert(connection);
assert(connection->data);
struct server *server = connection->data;
fprintf(stderr, "dood: error on connection to %s\n", connection->address->formatted);
server_close_socket(server, connection);
}
static void
connection_on_read(struct ood_inet_socket *connection)
{
assert(connection);
assert(connection->data);
struct server *server = connection->data;
char buffer[4096];
ssize_t bytes_read = read(connection->fd, buffer, sizeof buffer);
if (bytes_read < 0) {
if (EINTR != errno && EWOULDBLOCK != errno) {
perror("dood");
server_close_socket(server, connection);
}
} else {
// TODO: parse request and handle zero byte read (i.e. EOF)
printf("dood: read %zi bytes\n", bytes_read);
buffer[bytes_read] = '\0';
printf("dood: \n\n%s\n", buffer);
connection->on_read = NULL;
connection->on_write = connection_on_write;
if (!ood_inet_socket_stop_reading(connection)) {
perror("dood");
server_close_socket(server, connection);
}
}
}
static void
connection_on_write(struct ood_inet_socket *connection)
{
assert(connection);
assert(connection->data);
struct server *server = connection->data;
char const response[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain; charset=UTF-8\r\n"
"Content-Length: 14\r\n"
"Connection: close\r\n"
"\r\n"
"Hello, world!\n";
ssize_t bytes_written = write(connection->fd, response, sizeof response);
if (bytes_written < 0) {
if (EINTR != errno && EWOULDBLOCK != errno) {
perror("dood");
server_close_socket(server, connection);
}
} else {
printf("dood: wrote %zi bytes\n", bytes_written);
if (bytes_written < sizeof response) {
fprintf(stderr, "dood: wrote less than full response\n");
// TODO: handle partial writes
}
printf("dood: closing client connection\n");
server_close_socket(server, connection);
}
}
static void
listener_on_error(struct ood_inet_socket *listener)
{
assert(listener);
assert(listener->data);
struct server *server = listener->data;
fprintf(stderr, "dood: error listening on %s\n", listener->address->formatted);
server_close_socket(server, listener);
}
static void
listener_on_read(struct ood_inet_socket *listener)
{
assert(listener);
assert(listener->data);
struct server *server = listener->data;
while (server_can_accept_connections(server)) {
struct ood_inet_socket *connection = NULL;
if (!ood_inet_socket_accept(listener, &connection)) {
ood_halt_on_error();
}
if (!connection) return;
connection->on_error = connection_on_error;
connection->on_read = connection_on_read;
connection->on_write = NULL;
connection->data = server;
server_add_socket(server, connection);
printf("dood: accepted connection from %s\n", connection->address->formatted);
}
}
int
main(int argc, char *argv[]) {
int const backlog = 4;
in_port_t const port = 8000;
unsigned const timeout_millis = 50;
printf("dood: starting\n");
signal(SIGINT, handle_sigint);
struct server *server = server_alloc(8);
if (!server) {
ood_halt_on_error();
}
if (!server_add_listener(server, INADDR_LOOPBACK, port, backlog, listener_on_read, listener_on_error)) {
ood_halt_on_error();
}
printf("dood: ready\n");
struct ood_select_sets select_sets;
while (server_is_running(server)) {
server_add_sockets_to_select_sets(server, &select_sets);
bool interrupted;
if (!ood_select_sets_select(&select_sets, timeout_millis, &interrupted)) {
ood_halt_on_error();
}
if (!interrupted) {
server_handle_events_in_select_sets(server, &select_sets);
}
if (received_sigint) {
received_sigint = false;
server_close_all_listeners(server);
printf("dood: draining\n");
}
}
printf("dood: shutting down\n");
server_free(server);
printf("dood: stopped\n");
return EXIT_SUCCESS;
}