mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-05-10 13:24:06 +02:00
update build, silent folders, use threads
This commit is contained in:
parent
f8438c591b
commit
2b1b536172
@ -9,10 +9,13 @@
|
||||
# NOTICE that `RELEASE=1` is __after__ `make`
|
||||
#
|
||||
|
||||
MAKEFLAGS += -j $(shell nproc)
|
||||
|
||||
C_FLAGS = \
|
||||
-std=c17 \
|
||||
-Wall -Wextra -Wpedantic -Wconversion \
|
||||
-pedantic -pedantic-errors \
|
||||
-Wno-unused-parameter \
|
||||
|
||||
L_FLAGS = -lm -pthread $(shell pkg-config sqlite3 openssl --libs)
|
||||
C_FLAGS = $(shell pkg-config sqlite3 openssl --cflags)
|
||||
@ -38,13 +41,15 @@ O_FILES = $(patsubst src/%.c,build/%.o,$(C_FILES))
|
||||
|
||||
CC = gcc
|
||||
|
||||
all: server
|
||||
TARGET=server
|
||||
|
||||
server: $(O_FILES)
|
||||
$(CC) -o build/$@ $^ $(F_FLAGS) $(OPTIMIZATION) $(L_FLAGS)
|
||||
all: build/$(TARGET)
|
||||
|
||||
build/$(TARGET): $(O_FILES)
|
||||
$(CC) -o $@ $^ $(F_FLAGS) $(OPTIMIZATION) $(L_FLAGS)
|
||||
|
||||
build/%.o: src/%.c $(HEADERS)
|
||||
mkdir -p $(@D)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $< -c -o $@ $(C_FLAGS) $(OPTIMIZATION) $(F_FLAGS)
|
||||
|
||||
clean:
|
||||
|
@ -6,4 +6,5 @@ xc
|
||||
-Wconversion
|
||||
-pedantic
|
||||
-pedantic-errors
|
||||
-Wno-unused-parameter
|
||||
|
||||
|
@ -10,6 +10,14 @@ typedef struct {
|
||||
Db* db;
|
||||
} Cx;
|
||||
|
||||
void route_get_index(HttpCtx* ctx);
|
||||
void route_post_set_number(HttpCtx* ctx);
|
||||
void route_get_not_found(HttpCtx* ctx);
|
||||
|
||||
void route_get_products_all(HttpCtx* ctx);
|
||||
|
||||
void route_post_user_register(HttpCtx* ctx);
|
||||
|
||||
#define RESPOND(HTTP_CTX, STATUS, MIME_TYPE, ...) \
|
||||
{ \
|
||||
HttpCtx* _ctx = (HTTP_CTX); \
|
||||
@ -30,4 +38,5 @@ typedef struct {
|
||||
#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \
|
||||
RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__)
|
||||
|
||||
void route_get_products_all(HttpCtx* ctx);
|
||||
#define RESPOND_BAD_REQUEST(HTTP_CTX) \
|
||||
RESPOND_JSON(HTTP_CTX, 400, "{\"ok\":false}")
|
||||
|
@ -10,7 +10,7 @@ void route_get_products_all(HttpCtx* ctx)
|
||||
ProductVec products;
|
||||
product_vec_construct(&products);
|
||||
|
||||
DbRes db_res = db_product_all_fill(cx->db, &products);
|
||||
DbRes db_res = db_product_all(cx->db, &products);
|
||||
if (db_res != DbRes_Ok) {
|
||||
RESPOND_JSON(ctx, 500, "{\"ok\":false,\"msg\":\"db error\"}");
|
||||
return;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "collection.h"
|
||||
#include "models.h"
|
||||
|
||||
DEFINE_VEC(int64_t, Ids, ids, 8)
|
||||
DEFINE_VEC(Product, ProductVec, product_vec, 32)
|
||||
|
||||
typedef enum {
|
||||
@ -16,5 +17,8 @@ typedef struct Db Db;
|
||||
/// `user.id` field is ignored.
|
||||
DbRes db_user_insert(Db* db, User* user);
|
||||
DbRes db_user_from_id(Db* db, User* user, int64_t id);
|
||||
/// Expects `ids` to be constructed.
|
||||
DbRes db_users_with_email(Db* db, Ids* ids, const char* email);
|
||||
|
||||
DbRes db_product_all_fill(Db* db, ProductVec* vec);
|
||||
/// Expects `vec` to be constructed.
|
||||
DbRes db_product_all(Db* db, ProductVec* vec);
|
||||
|
@ -139,7 +139,39 @@ l0_return:
|
||||
return res;
|
||||
}
|
||||
|
||||
DbRes db_product_all_fill(Db* db, ProductVec* vec)
|
||||
DbRes db_users_with_email(Db* db, Ids* ids, const char* email)
|
||||
{
|
||||
static_assert(sizeof(User) == 40, "model has changed");
|
||||
|
||||
sqlite3* connection;
|
||||
CONNECT;
|
||||
DbRes res;
|
||||
int sqlite_res;
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
sqlite_res = sqlite3_prepare_v2(
|
||||
connection, "SELECT id FROM users WHERE email = ?", -1, &stmt, NULL);
|
||||
sqlite3_bind_text(stmt, 1, email, -1, NULL);
|
||||
|
||||
while ((sqlite_res = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
int64_t id = GET_INT(0);
|
||||
ids_push(ids, id);
|
||||
}
|
||||
if (sqlite_res != SQLITE_DONE) {
|
||||
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
|
||||
res = DbRes_Error;
|
||||
goto l0_return;
|
||||
}
|
||||
|
||||
res = DbRes_Ok;
|
||||
l0_return:
|
||||
if (stmt)
|
||||
sqlite3_finalize(stmt);
|
||||
DISCONNECT;
|
||||
return res;
|
||||
}
|
||||
|
||||
DbRes db_product_all(Db* db, ProductVec* vec)
|
||||
{
|
||||
sqlite3* connection;
|
||||
CONNECT;
|
||||
|
@ -130,6 +130,11 @@ void* http_ctx_user_ctx(HttpCtx* ctx)
|
||||
return ctx->user_ctx;
|
||||
}
|
||||
|
||||
const char* http_ctx_req_path(HttpCtx* ctx)
|
||||
{
|
||||
return ctx->req->path;
|
||||
}
|
||||
|
||||
bool http_ctx_req_headers_has(HttpCtx* ctx, const char* key)
|
||||
{
|
||||
return req_has_header(ctx->req, key);
|
||||
@ -274,7 +279,7 @@ static inline void worker_handle_request(Worker* worker, Client* client)
|
||||
MAX_HEADER_BUFFER_SIZE);
|
||||
goto l0_return;
|
||||
}
|
||||
puts((char*)buffer);
|
||||
// puts((char*)buffer);
|
||||
|
||||
Req req;
|
||||
size_t body_idx;
|
||||
@ -309,6 +314,8 @@ static inline void worker_handle_request(Worker* worker, Client* client)
|
||||
|
||||
header_vec_construct(&handler_ctx.res_headers);
|
||||
|
||||
bool been_handled = false;
|
||||
|
||||
for (size_t i = 0; i < worker->ctx->server->handlers.size; ++i) {
|
||||
Handler* handler = &worker->ctx->server->handlers.data[i];
|
||||
if (handler->method != req.method)
|
||||
@ -316,9 +323,14 @@ static inline void worker_handle_request(Worker* worker, Client* client)
|
||||
if (strcmp(handler->path, req.path) != 0)
|
||||
continue;
|
||||
handler->handler(&handler_ctx);
|
||||
been_handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!been_handled && worker->ctx->server->not_found_handler != NULL) {
|
||||
worker->ctx->server->not_found_handler(&handler_ctx);
|
||||
}
|
||||
|
||||
l1_return:
|
||||
header_vec_destroy(&handler_ctx.res_headers);
|
||||
req_destroy(&req);
|
||||
@ -333,11 +345,11 @@ static inline int parse_header(
|
||||
{
|
||||
StrSplitter splitter = str_split(buf, buf_size, "\r\n");
|
||||
|
||||
StrSlice first = strsplit_next(&splitter);
|
||||
StrSplitter first_splitter = str_split(first.ptr, first.len, " ");
|
||||
StrSlice method_str = strsplit_next(&first_splitter);
|
||||
StrSlice path_str = strsplit_next(&first_splitter);
|
||||
StrSlice version_str = strsplit_next(&first_splitter);
|
||||
StrSlice req_line = strsplit_next(&splitter);
|
||||
StrSplitter req_line_splitter = str_split(req_line.ptr, req_line.len, " ");
|
||||
StrSlice method_str = strsplit_next(&req_line_splitter);
|
||||
StrSlice uri_str = strsplit_next(&req_line_splitter);
|
||||
StrSlice version_str = strsplit_next(&req_line_splitter);
|
||||
|
||||
if (strncmp(version_str.ptr, "HTTP/1.1", 8) != 0) {
|
||||
fprintf(stderr, "error: unrecognized http version '%.*s'\n",
|
||||
@ -356,14 +368,32 @@ static inline int parse_header(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (path_str.len >= MAX_PATH_LEN + 1) {
|
||||
if (uri_str.len >= MAX_PATH_LEN + 1) {
|
||||
fprintf(stderr, "error: path too long\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* path = calloc(MAX_PATH_LEN + 1, sizeof(char));
|
||||
strncpy(path, path_str.ptr, path_str.len);
|
||||
path[path_str.len] = '\0';
|
||||
size_t path_len = 0;
|
||||
while (path_len < uri_str.len && uri_str.ptr[path_len] != '?'
|
||||
&& uri_str.ptr[path_len] != '#') {
|
||||
path_len += 1;
|
||||
}
|
||||
|
||||
char* path = calloc(path_len + 1, sizeof(char));
|
||||
strncpy(path, uri_str.ptr, path_len);
|
||||
path[path_len] = '\0';
|
||||
|
||||
char* query = NULL;
|
||||
if (path_len < uri_str.len) {
|
||||
size_t query_len = 0;
|
||||
while (path_len + query_len < uri_str.len
|
||||
&& uri_str.ptr[path_len + query_len] != '#') {
|
||||
query_len += 1;
|
||||
}
|
||||
query = calloc(query_len + 1, sizeof(char));
|
||||
strncpy(query, &uri_str.ptr[path_len], query_len);
|
||||
query[query_len] = '\0';
|
||||
}
|
||||
|
||||
HeaderVec headers;
|
||||
header_vec_construct(&headers);
|
||||
@ -402,7 +432,7 @@ static inline int parse_header(
|
||||
header_vec_push(&headers, (Header) { key, value });
|
||||
}
|
||||
|
||||
*req = (Req) { method, path, headers };
|
||||
*req = (Req) { method, path, query, headers };
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ void http_server_post(
|
||||
void http_server_set_not_found(HttpServer* server, HttpHandlerFn handler);
|
||||
|
||||
void* http_ctx_user_ctx(HttpCtx* ctx);
|
||||
const char* http_ctx_req_path(HttpCtx* ctx);
|
||||
bool http_ctx_req_headers_has(HttpCtx* ctx, const char* key);
|
||||
const char* http_ctx_req_headers_get(HttpCtx* ctx, const char* key);
|
||||
const char* http_ctx_req_body(HttpCtx* ctx);
|
||||
|
@ -47,6 +47,7 @@ static inline void worker_handle_request(Worker* worker, Client* req);
|
||||
#define MAX_HEADER_BUFFER_SIZE 8192
|
||||
|
||||
#define MAX_PATH_LEN 128 - 1
|
||||
#define MAX_QUERY_LEN 128 - 1
|
||||
#define MAX_HEADERS_LEN 32
|
||||
#define MAX_HEADER_KEY_LEN 32 - 1
|
||||
#define MAX_HEADER_VALUE_LEN 512 - 1
|
||||
@ -66,6 +67,7 @@ DEFINE_VEC(Header, HeaderVec, header_vec, 8)
|
||||
typedef struct {
|
||||
Method method;
|
||||
char* path;
|
||||
char* query;
|
||||
HeaderVec headers;
|
||||
} Req;
|
||||
|
||||
|
@ -8,42 +8,6 @@
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void route_get_index(HttpCtx* ctx)
|
||||
{
|
||||
Cx* cx = http_ctx_user_ctx(ctx);
|
||||
|
||||
RESPOND_HTML(ctx, 200,
|
||||
"<!DOCTYPE html><html><head><meta "
|
||||
"charset=\"utf-8\"></head><body><h1>Number = %d</h1></body></html>",
|
||||
cx->number);
|
||||
}
|
||||
|
||||
void route_post_set_number(HttpCtx* ctx)
|
||||
{
|
||||
Cx* cx = http_ctx_user_ctx(ctx);
|
||||
|
||||
const char* body_text = http_ctx_req_body(ctx);
|
||||
JsonParser parser;
|
||||
json_parser_construct(&parser, body_text, strlen(body_text));
|
||||
JsonValue* body = json_parser_parse(&parser);
|
||||
json_parser_destroy(&parser);
|
||||
|
||||
if (!json_object_has(body, "value")) {
|
||||
RESPOND_JSON(
|
||||
ctx, 200, "{\"ok\": false, \"msg\": \"no 'value' key\"}\r\n");
|
||||
goto l0_return;
|
||||
}
|
||||
|
||||
int64_t value = json_int(json_object_get(body, "value"));
|
||||
cx->number = (int)value;
|
||||
|
||||
RESPOND_JSON(ctx, 200, "{\"ok\": true}\r\n");
|
||||
|
||||
l0_return:
|
||||
json_free(body);
|
||||
}
|
||||
|
||||
HttpServer* server;
|
||||
|
||||
int main(void)
|
||||
@ -65,11 +29,15 @@ int main(void)
|
||||
|
||||
http_server_set_user_ctx(server, &cx);
|
||||
|
||||
http_server_get(server, "/products/all", route_get_products_all);
|
||||
http_server_get(server, "/api/products/all", route_get_products_all);
|
||||
|
||||
http_server_post(server, "/api/users/register", route_post_user_register);
|
||||
|
||||
http_server_get(server, "/", route_get_index);
|
||||
http_server_post(server, "/set_number", route_post_set_number);
|
||||
|
||||
http_server_set_not_found(server, route_get_not_found);
|
||||
|
||||
printf("listening at http://127.0.0.1:8080/\n");
|
||||
http_server_listen(server);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "models.h"
|
||||
#include "json.h"
|
||||
#include "models_json.h"
|
||||
#include "str_util.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
@ -7,6 +8,8 @@
|
||||
|
||||
void user_free(User* m)
|
||||
{
|
||||
static_assert(sizeof(User) == 40, "model has changed");
|
||||
|
||||
free(m->name);
|
||||
free(m->email);
|
||||
free(m->password_hash);
|
||||
@ -14,30 +17,57 @@ void user_free(User* m)
|
||||
|
||||
void coord_free(Coord* m)
|
||||
{
|
||||
static_assert(sizeof(Coord) == 24, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void product_free(Product* m)
|
||||
{
|
||||
static_assert(sizeof(Product) == 40, "model has changed");
|
||||
|
||||
free(m->name);
|
||||
free(m->barcode);
|
||||
}
|
||||
|
||||
void product_price_free(ProductPrice* m)
|
||||
{
|
||||
static_assert(sizeof(ProductPrice) == 24, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void cart_free(Cart* m)
|
||||
{
|
||||
static_assert(sizeof(Cart) == 16, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void cart_item_free(CartItem* m)
|
||||
{
|
||||
static_assert(sizeof(CartItem) == 24, "model has changed");
|
||||
|
||||
(void)m;
|
||||
}
|
||||
|
||||
void users_register_req_free(UsersRegisterReq* model)
|
||||
{
|
||||
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
|
||||
|
||||
free(model->name);
|
||||
free(model->email);
|
||||
free(model->password);
|
||||
}
|
||||
|
||||
void auth_login_req_free(AuthLoginReq* model)
|
||||
{
|
||||
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
|
||||
|
||||
free(model->email);
|
||||
free(model->password);
|
||||
}
|
||||
|
||||
char* user_to_json_string(const User* m)
|
||||
{
|
||||
static_assert(sizeof(User) == 40, "model has changed");
|
||||
@ -155,6 +185,43 @@ char* cart_item_to_json_string(const CartItem* m)
|
||||
return result;
|
||||
}
|
||||
|
||||
char* users_register_req_to_json(const UsersRegisterReq* m)
|
||||
{
|
||||
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
|
||||
|
||||
String string;
|
||||
string_construct(&string);
|
||||
string_pushf(&string,
|
||||
"{"
|
||||
"\"name\":\"%s\","
|
||||
"\"email\":\"%s\","
|
||||
"\"password\":\"%s\""
|
||||
"}",
|
||||
m->name, m->email, m->password);
|
||||
|
||||
char* result = string_copy(&string);
|
||||
string_destroy(&string);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* auth_login_req_to_json(const AuthLoginReq* m)
|
||||
{
|
||||
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
|
||||
|
||||
String string;
|
||||
string_construct(&string);
|
||||
string_pushf(&string,
|
||||
"{"
|
||||
"\"email\":\"%s\","
|
||||
"\"password\":\"%s\""
|
||||
"}",
|
||||
m->email, m->password);
|
||||
|
||||
char* result = string_copy(&string);
|
||||
string_destroy(&string);
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char* key;
|
||||
JsonType type;
|
||||
@ -299,3 +366,39 @@ int cart_item_from_json(CartItem* m, const JsonValue* json)
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int users_register_req_from_json(UsersRegisterReq* m, const JsonValue* json)
|
||||
{
|
||||
static_assert(sizeof(UsersRegisterReq) == 24, "model has changed");
|
||||
|
||||
ObjField fields[] = {
|
||||
{ "name", JsonType_String },
|
||||
{ "email", JsonType_String },
|
||||
{ "password", JsonType_String },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
return -1;
|
||||
*m = (UsersRegisterReq) {
|
||||
.name = GET_STR("name"),
|
||||
.email = GET_STR("email"),
|
||||
.password = GET_STR("password"),
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int auth_login_req_from_json(AuthLoginReq* m, const JsonValue* json)
|
||||
{
|
||||
static_assert(sizeof(AuthLoginReq) == 16, "model has changed");
|
||||
|
||||
ObjField fields[] = {
|
||||
{ "email", JsonType_String },
|
||||
{ "password", JsonType_String },
|
||||
};
|
||||
if (!obj_conforms(json, fields, sizeof(fields) / sizeof(fields[0])))
|
||||
return -1;
|
||||
*m = (AuthLoginReq) {
|
||||
.email = GET_STR("email"),
|
||||
.password = GET_STR("password"),
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
@ -47,3 +47,19 @@ void product_free(Product* model);
|
||||
void product_price_free(ProductPrice* model);
|
||||
void cart_free(Cart* model);
|
||||
void cart_item_free(CartItem* model);
|
||||
|
||||
//
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
char* email;
|
||||
char* password;
|
||||
} UsersRegisterReq;
|
||||
|
||||
typedef struct {
|
||||
char* email;
|
||||
char* password;
|
||||
} AuthLoginReq;
|
||||
|
||||
void users_register_req_free(UsersRegisterReq* model);
|
||||
void auth_login_req_free(AuthLoginReq* model);
|
||||
|
@ -11,3 +11,6 @@ DEFINE_MODEL_JSON(Product, product)
|
||||
DEFINE_MODEL_JSON(ProductPrice, product_price)
|
||||
DEFINE_MODEL_JSON(Cart, cart)
|
||||
DEFINE_MODEL_JSON(CartItem, cart_item)
|
||||
|
||||
DEFINE_MODEL_JSON(UsersRegisterReq, users_register_req)
|
||||
DEFINE_MODEL_JSON(AuthLoginReq, auth_login_req)
|
||||
|
Loading…
x
Reference in New Issue
Block a user