Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed init.db
Empty file.
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ See `/examples` for more.
- SQLite integration
- Built-in JSON library
- Static file serving
- Request validator library
- Request validator library
- 'Hot Restart' mechanism

## In Progress

Expand Down
2 changes: 1 addition & 1 deletion src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ int runProject() {
}

int buildProject() {
return system("make");
return system("make -s");
}

// migrations are being deferred because that sounds like a PAIN IN THE ASS
Expand Down
2 changes: 1 addition & 1 deletion src/lavandula.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ AppBuilder createBuilder() {

App createApp() {
App app = {
.port = 3000
.port = 3000,
};

initAppMiddleware(&app);
Expand Down
105 changes: 99 additions & 6 deletions src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <termios.h>
#include <fcntl.h>

#include "include/server.h"
#include "include/http.h"
Expand All @@ -13,8 +16,26 @@
#include "include/sql.h"
#include "include/app.h"

typedef enum {
STATE_RUNNING,
STATE_RESTARTING,
STATE_SHUTDOWN
} ServerState;

volatile ServerState serverState = STATE_RUNNING;

#define BUFFER_SIZE 4096

void set_nonblocking_input() {
struct termios ttystate;

tcgetattr(STDIN_FILENO, &ttystate);
ttystate.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);

fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
}

HttpResponse defaultNotFoundController(RequestContext context) {
(void)context;
return (HttpResponse) {
Expand Down Expand Up @@ -52,9 +73,39 @@ void freeServer(Server *server) {
freeRouter(&server->router);
}

void* key_listener(void* arg) {
(void)arg;

while (serverState == STATE_RUNNING) {
int ch = getchar();
if (ch != EOF) {
if (ch == 'r') {
printf("restarting server...\n");
serverState = STATE_RESTARTING;
return NULL;
} else if (ch == 'q') {
printf("shutting down server...\n");
serverState = STATE_SHUTDOWN;
return NULL;
}
}
usleep(10000);
}
return NULL;
}


void runServer(App *app) {
if (!app) return;

pthread_t thread_id;
set_nonblocking_input();

if (pthread_create(&thread_id, NULL, key_listener, NULL)) {
perror("Failed to create thread");
return;
}

app->server.fileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (app->server.fileDescriptor < 0) {
perror("socket failed");
Expand Down Expand Up @@ -87,16 +138,41 @@ void runServer(App *app) {
exit(EXIT_FAILURE);
}

printf("Lavandula Server is running! -> http://127.0.0.1:%d\n", app->server.port);
printf("\n");
printf("┌───────────────────────────────────────────────┐\n");
printf("│ 🌿 Lavandula Server is RUNNING │\n");
printf("├───────────────────────────────────────────────┤\n");
printf("│ Listening on: http://127.0.0.1:%-12d │\n", app->server.port);
printf("│ │\n");
printf("│ Controls: │\n");
printf("│ • Press 'r' to reload the server │\n");
printf("│ • Press 'q' to shut down │\n");
printf("└───────────────────────────────────────────────┘\n\n");


int flags = fcntl(app->server.fileDescriptor, F_GETFL, 0);
if (flags == -1) {
perror("fcntl get flags failed");
exit(EXIT_FAILURE);
}
if (fcntl(app->server.fileDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl set non-blocking failed");
exit(EXIT_FAILURE);
}

while (1) {
while (serverState == STATE_RUNNING) {
struct sockaddr_in clientAddr;
socklen_t clientLen = sizeof(clientAddr);

int clientSocket = accept(app->server.fileDescriptor, (struct sockaddr *)&clientAddr, &clientLen);
if (clientSocket < 0) {
perror("accept failed");
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) {
usleep(10000);
continue;
} else {
perror("accept failed");
continue;
}
}

char buffer[BUFFER_SIZE] = {0}; // oops
Expand Down Expand Up @@ -129,7 +205,6 @@ void runServer(App *app) {

free(pathOnly);


RequestContext context = requestContext(app, request);

context.hasBody = parser.isValid && request.bodyLength > 0;
Expand Down Expand Up @@ -180,4 +255,22 @@ void runServer(App *app) {

close(clientSocket);
}
}

freeServer(&app->server);
pthread_join(thread_id, NULL);

if (serverState == STATE_RESTARTING) {
int result = system("make -s");
if (result != 0) {
fprintf(stderr, "Build failed.\n");
exit(1);
}

char *args[] = {"./build/lavu", NULL};
execvp(args[0], args);
perror("execvp failed");
exit(1);
} else if (serverState == STATE_SHUTDOWN) {
exit(0);
}
}