Skip to content

Commit

Permalink
add URL param support
Browse files Browse the repository at this point in the history
  • Loading branch information
ngn13 committed Jan 13, 2025
1 parent 9775981 commit ed6b16f
Show file tree
Hide file tree
Showing 35 changed files with 593 additions and 458 deletions.
18 changes: 6 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,13 @@ jobs:
run: make example

- name: 'run example #1'
run: |
./dist/example_hello &
./scripts/hello_test.sh
killall -9 example_hello
run: ./dist/test.sh 1

- name: 'run example #2'
run: |
./dist/example_echo &
./scripts/echo_test.sh
killall -9 example_echo
run: ./dist/test.sh 2

- name: 'run example #3'
run: |
./dist/example_middleware &
./scripts/middleware_test.sh
killall -9 example_middleware
run: ./dist/test.sh 3

- name: 'run example #4'
run: ./dist/test.sh 4
14 changes: 11 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ DISTDIR = dist
CC = gcc

# sources
SRCS = $(shell find src/ -type f -name '*.c')
OBJS = $(patsubst src/%.c,$(DISTDIR)/%.o,$(SRCS))
CSRCS = $(shell find src/ -type f -name '*.c')
SSRCS = $(shell find src/ -type f -name '*.S')
OBJS = $(patsubst src/%.c,$(DISTDIR)/%.c.o,$(CSRCS))
OBJS += $(patsubst src/%.S,$(DISTDIR)/%.S.o,$(SSRCS))
HDRS = $(wildcard inc/*.h)

# dirs
Expand All @@ -30,7 +32,12 @@ all: $(DISTDIR)/libctorm.so
dist/libctorm.so: $(OBJS)
$(CC) -shared -o $@ $^ $(LIBS) $(CFLAGS)

$(DISTDIR)/%.o: src/%.c $(OBJDIRS)
$(DISTDIR)/%.c.o: src/%.c $(OBJDIRS)
$(CC) $(CFLAGS) $(INCLUDE) -c -Wall -fPIC -o $@ $< $(LIBS) \
-DCTORM_JSON_SUPPORT=$(CTORM_JSON_SUPPORT) \
-DCTORM_DEBUG=$(CTORM_DEBUG)

$(DISTDIR)/%.S.o: src/%.S $(OBJDIRS)
$(CC) $(CFLAGS) $(INCLUDE) -c -Wall -fPIC -o $@ $< $(LIBS) \
-DCTORM_JSON_SUPPORT=$(CTORM_JSON_SUPPORT) \
-DCTORM_DEBUG=$(CTORM_DEBUG)
Expand All @@ -48,6 +55,7 @@ uninstall:

format:
clang-format -i -style=file $(SRCS) $(HDRS) example/*/*.c
black scripts/*.py

clean:
rm -rf dist
Expand Down
31 changes: 14 additions & 17 deletions example/Makefile
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
CC = gcc
# WARN: you should run the binaries with LD_LIBRARY_PATH=../dist

# you should run the binaries with LD_LIBRARY_PATH=../dist
CC = gcc
INCD = ../inc
DIST = ../dist

all: ../dist/example_hello ../dist/example_echo ../dist/example_middleware
DIRS = $(shell find * -maxdepth 0 -type d)
BINS = $(patsubst %,$(DIST)/example_%,$(DIRS))

../dist/libctorm.so:
ifeq (,$(wildcard ../dist/libctorm.so))
@echo "!!!! you should first compile libctorm !!!!"
all: $(BINS)

$(DIST)/libctorm.so:
ifeq (,$(wildcard $(DIST)/libctorm.so))
@echo "you should first compile libctorm"
exit 1
endif

../dist/example_hello: hello/*.c ../dist/libctorm.so
mkdir -pv ../dist
$(CC) -I../inc -L../dist hello/*.c -lctorm -o $@

../dist/example_echo: echo/*.c ../dist/libctorm.so
mkdir -pv ../dist
$(CC) -I../inc -L../dist echo/*.c -lctorm -o $@

../dist/example_middleware: middleware/*.c ../dist/libctorm.so
mkdir -pv ../dist
$(CC) -I../inc -L../dist middleware/*.c -lctorm -lcjson -o $@
$(DIST)/example_%: %/main.c $(DIST)/libctorm.so
@mkdir -pv $(DIST)
$(CC) -I$(INCD) -L$(DIST) $< -lctorm -lcjson -o $@
7 changes: 3 additions & 4 deletions example/echo/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
FROM ghcr.io/ngn13/ctorm:latest

WORKDIR /example/echo
COPY static ./static
COPY html ./html
COPY main.c ./
COPY static ./static
COPY html ./html
COPY main.c ./

# examples uses the local headers, replace it with the global ones
RUN sed -i 's/#include "..\/..\/include\/all.h"/#include <ctorm\/all.h>/' main.c
RUN gcc -O3 -o /app main.c -lctorm

WORKDIR /
Expand Down
10 changes: 5 additions & 5 deletions example/echo/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ void GET_notfound(ctorm_req_t *req, ctorm_res_t *res) {
}

void POST_form(ctorm_req_t *req, ctorm_res_t *res) {
enc_url_t *form = NULL;
char *msg = NULL;
ctorm_url_t *form = NULL;
char *msg = NULL;

if ((form = REQ_FORM()) == NULL) {
RES_CODE(400);
ctorm_fail("failed to parse the form data: %s", ctorm_geterror());
return RES_SEND("bad body");
}

if (NULL == (msg = enc_url_get(form, "msg"))) {
if (NULL == (msg = ctorm_url_get(form, "msg"))) {
RES_CODE(400);
enc_url_free(form);
ctorm_url_free(form);
ctorm_fail("form data does not contain the message");
return RES_SEND("bad body");
}
Expand All @@ -28,7 +28,7 @@ void POST_form(ctorm_req_t *req, ctorm_res_t *res) {

RES_SET("cool", "yes");

enc_url_free(form);
ctorm_url_free(form);
}

void GET_index(ctorm_req_t *req, ctorm_res_t *res) {
Expand Down
3 changes: 1 addition & 2 deletions example/hello/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
FROM ghcr.io/ngn13/ctorm:latest

WORKDIR /example/hello
COPY main.c ./
COPY main.c ./

# examples uses the local headers, replace it with the global ones
RUN sed -i 's/#include "..\/..\/include\/all.h"/#include <ctorm\/all.h>/' main.c
RUN gcc -O3 -o /app main.c -lctorm

WORKDIR /
Expand Down
3 changes: 1 addition & 2 deletions example/middleware/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
FROM ghcr.io/ngn13/ctorm:latest

WORKDIR /example/middleware
COPY main.c ./
COPY main.c ./

# examples uses the local headers, replace it with the global ones
RUN sed -i 's/#include "..\/..\/include\/all.h"/#include <ctorm\/all.h>/' main.c
RUN gcc -O3 -o /app main.c -lcjson -lctorm

WORKDIR /
Expand Down
10 changes: 10 additions & 0 deletions example/params/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM ghcr.io/ngn13/ctorm:latest

WORKDIR /example/params
COPY main.c ./

# examples uses the local headers, replace it with the global ones
RUN gcc -O3 -o /app main.c -lctorm

WORKDIR /
CMD ["/app"]
25 changes: 25 additions & 0 deletions example/params/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <ctorm.h>

void GET_index(ctorm_req_t *req, ctorm_res_t *res) {
RES_REDIRECT("/echo/changeme/nothing");
}

void GET_param(ctorm_req_t *req, ctorm_res_t *res) {
RES_FMT("param: %s", REQ_PARAM("param"));
}

int main() {
// create the app
ctorm_app_t *app = ctorm_app_new(NULL);

// setup the routes
GET(app, "/", GET_index);
GET(app, "/echo/:param/*", GET_param);

// run the app
if (!ctorm_app_run(app, "0.0.0.0:8080"))
ctorm_fail("failed to start the app: %s", ctorm_geterror());

// clean up
ctorm_app_free(app);
}
9 changes: 5 additions & 4 deletions inc/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ typedef void (*ctorm_route_t)(ctorm_req_t *, ctorm_res_t *);
typedef struct ctorm_routemap {
struct ctorm_routemap *next;
bool is_middleware;
bool is_all;
char *path;
method_t method;
ctorm_route_t handler;
} ctorm_routemap_t;

#define ctorm_routemap_is_all(route) (route->method == -1)

/*
* ctorm web app structure
Expand All @@ -43,9 +44,9 @@ typedef struct ctorm_routemap {
typedef struct ctorm_app {
ctorm_routemap_t *middleware_maps; // middleware map
ctorm_routemap_t *route_maps; // route map
char *staticpath; // static directory serving path
char *staticdir; // static directory
ctorm_route_t allroute; // all handler route (see app_all())
char *static_path; // static directory serving path
char *static_dir; // static directory
ctorm_route_t all_route; // all handler route (see app_all())
bool running; // is the app running?
pool_t *pool; // thread pool for the app
pthread_mutex_t request_mutex; // mutex used to lock request threads
Expand Down
1 change: 1 addition & 0 deletions inc/ctorm.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#define REQ_BODY(buffer, size) ctorm_req_body(req, buffer, size)
#define REQ_GET(header) ctorm_req_get(req, header)
#define REQ_QUERY(query) ctorm_req_query(req, query)
#define REQ_PARAM(param) ctorm_req_param(req, param)
#define REQ_FORM() ctorm_req_form(req)
#define REQ_JSON() ctorm_req_json(req)
#define REQ_CANCEL() (req->cancel = true)
Expand Down
16 changes: 8 additions & 8 deletions inc/encoding.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#pragma once
#include <stdint.h>
#include "util.h"
#include "pair.h"

// URL encoding (application/x-www-form-urlencoded)
typedef pair_t enc_url_t;
typedef ctorm_pair_t ctorm_url_t;

enc_url_t *enc_url_parse(char *, uint64_t); // parse URL encoded data from the byte array
char *enc_url_get(enc_url_t *, char *name); // get a value by it's name from the URL encoded data
void enc_url_free(enc_url_t *); // free the URL encoded data
ctorm_url_t *ctorm_url_parse(char *data, uint64_t); // parse URL encoded data from the byte array
char *ctorm_url_get(ctorm_url_t *data, char *name); // get a value by it's name from the URL encoded data
void ctorm_url_free(ctorm_url_t *data); // free the URL encoded data

// JSON encoding (application/json)
#if __has_include(<cjson/cJSON.h>)
Expand All @@ -16,6 +16,6 @@ void enc_url_free(enc_url_t *); // free the URL encoded data
typedef void cJSON;
#endif

cJSON *enc_json_parse(char *); // parse JSON encoded data from the byte array
char *enc_json_dump(cJSON *, uint64_t *); // dump JSON encoded data to a byte array
void enc_json_free(cJSON *); // free the JSON encoded data
cJSON *ctorm_json_parse(char *data); // parse JSON encoded data from the byte array
char *ctorm_json_dump(cJSON *json, uint64_t *size); // dump JSON encoded data to a byte array
void ctorm_json_free(cJSON *json); // free the JSON encoded data
17 changes: 9 additions & 8 deletions inc/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ typedef int8_t method_t;

// HTTP method map
typedef struct {
method_t code;
char *name;
bool body;
method_t code;
const char *name;
bool body;
} method_map_t;

extern method_map_t http_method_map[];
Expand Down Expand Up @@ -58,12 +58,13 @@ typedef struct {
extern http_static_t http_static;
void http_static_load();

method_t http_method_id(char *);
char *http_method_name(int);
bool http_method_has_body(int);
method_t http_method_id(char *);
const char *http_method_name(int);
bool http_method_has_body(int);

#define http_is_valid_header_char(c) (is_digit(c) || is_letter(c) || contains("_ :;.,\\/\"'?!(){}[]@<>=-+*#$&`|~^%", c))
#define http_is_valid_path_char(c) (is_digit(c) || is_letter(c) || contains("-._~:/?#[]@!$&'()*+,;%=", c))
#define http_is_valid_header_char(c) \
(cu_is_digit(c) || cu_is_letter(c) || cu_contains("_ :;.,\\/\"'?!(){}[]@<>=-+*#$&`|~^%", c))
#define http_is_valid_path_char(c) (cu_is_digit(c) || cu_is_letter(c) || cu_contains("-._~:/?#[]@!$&'()*+,;%=", c))

const char *http_version_get(char *);

Expand Down
15 changes: 15 additions & 0 deletions inc/pair.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#ifndef CTROM_EXPORT

typedef struct ctorm_pair {
char *key, *value;
struct ctorm_pair *next;
} ctorm_pair_t;

ctorm_pair_t *ctorm_pair_add(ctorm_pair_t **head, char *key, char *value);
#define ctorm_pair_next(head, cur) for (ctorm_pair_t *cur = head; cur != NULL; cur = cur->next)
ctorm_pair_t *ctorm_pair_find(ctorm_pair_t *head, char *key);
void ctorm_pair_free(ctorm_pair_t *head);

#endif
26 changes: 14 additions & 12 deletions inc/req.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "encoding.h"
#include "headers.h"
#include "http.h"
#include "pair.h"

typedef struct ctorm_req {
connection_t *con; // socket connection
Expand All @@ -14,11 +15,11 @@ typedef struct ctorm_req {
char *path; // url decoded path (does not include queries)
const char *version; // HTTP version number (for example "HTTP/1.1")

headers_t headers; // HTTP headers
bool received_headers; // did we receive all the HTTP headers
enc_url_t *queries; // HTTP queries (for example "?key=1")
pair_t *params; // HTTP path params (for example "/blog/:slug")
int64_t bodysize; // size of the HTTP body
headers_t headers; // HTTP headers
bool received_headers; // did we receive all the HTTP headers
ctorm_url_t *queries; // HTTP queries (for example "?key=1")
ctorm_pair_t *params; // HTTP path params (for example "/blog/:slug")
int64_t bodysize; // size of the HTTP body
} ctorm_req_t;

#ifndef CTORM_EXPORT
Expand All @@ -30,15 +31,16 @@ void ctorm_req_end(ctorm_req_t *); // completely receive the HT

#endif

char *ctorm_req_method(ctorm_req_t *); // get the request method (GET, POST, PUT etc.)
char *ctorm_req_query(ctorm_req_t *, char *); // get a request URL query
char *ctorm_req_get(ctorm_req_t *, char *); // get a request header
const char *ctorm_req_method(ctorm_req_t *); // get the request method (GET, POST, PUT etc.)
char *ctorm_req_query(ctorm_req_t *, char *name); // get a request URL query
char *ctorm_req_param(ctorm_req_t *, char *name); // get a request URL param
char *ctorm_req_get(ctorm_req_t *, char *header); // get a request header

uint64_t ctorm_req_body(ctorm_req_t *, char *, uint64_t); // copy given length of body to the buffer
uint64_t ctorm_req_body_size(ctorm_req_t *); // get the body size
uint64_t ctorm_req_body(ctorm_req_t *, char *buf, uint64_t size); // copy given amount of bytes from body to the buffer
uint64_t ctorm_req_body_size(ctorm_req_t *); // get the body size

char *ctorm_req_ip(ctorm_req_t *, char *); // get the requester IPv4/IPv6 address as string
#define ctorm_req_addr(r) ((r)->con->addr) // get the requester address as sockaddr

enc_url_t *ctorm_req_form(ctorm_req_t *); // parse URL encoded form body
cJSON *ctorm_req_json(ctorm_req_t *); // parse JSON encoded form body
ctorm_url_t *ctorm_req_form(ctorm_req_t *); // parse URL encoded form body
cJSON *ctorm_req_json(ctorm_req_t *); // parse JSON encoded form body
6 changes: 3 additions & 3 deletions inc/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
#include <stdint.h>
#include <netdb.h>

bool socket_parse_host(const char *host, struct addrinfo *info);
bool socket_set_opts(ctorm_app_t *app, int sockfd);
bool socket_start(ctorm_app_t *app, const char *addr);
bool ctorm_socket_parse_host(const char *host, struct addrinfo *info);
bool ctorm_socket_set_opts(ctorm_app_t *app, int sockfd);
bool ctorm_socket_start(ctorm_app_t *app, const char *addr);
Loading

0 comments on commit ed6b16f

Please sign in to comment.