Skip to content

Commit

Permalink
Merge pull request #3 from ngn13/1.7-dev
Browse files Browse the repository at this point in the history
1.7
  • Loading branch information
ngn13 authored Jan 15, 2025
2 parents 082d383 + 7d56efa commit fdd394d
Show file tree
Hide file tree
Showing 31 changed files with 279 additions and 114 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ jobs:

- name: 'run example #4'
run: ./scripts/test.sh 4

- name: 'run example #5'
run: ./scripts/test.sh 5
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
_____/ /_____ _________ ___
/ ___/ __/ __ \/ ___/ __ `__ \
/ /__/ /_/ /_/ / / / / / / / /
\___/\__/\____/_/ /_/ /_/ /_/ 1.6
\___/\__/\____/_/ /_/ /_/ /_/ 1.7
```

Expand Down
9 changes: 9 additions & 0 deletions docs/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,12 @@ to a 404 page, you set a custom route for this:
```c
ctorm_app_all(app, all_route);
```

### Global locals
If you want to pass a variable to all the routes and middlewares, you can use global
locals:
```c
ctorm_app_local(app, "config", &config);
```
To access the local from the route or the middleware handler, you can use `REQ_LOCAL` or
`ctorm_req_local`. See the [request documentation](req.md) for more information.
29 changes: 29 additions & 0 deletions docs/req.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,32 @@ if(NULL == agent){

ctorm_info("user-agent is %s", agent);
```
### Request locals
You may want to pass variables around different routes/middlewares that handle the same
request. To do this, you can use request locals, inspired by [fiber's ctx locals](https://docs.gofiber.io/api/ctx/#locals).
Here is a middleware that creates a new local named "username":
```c
char *username = REQ_QUERY("username");
if(NULL == username){
RES_SEND("please specify a username");
REQ_CANCEL();
return;
}
for(char *c = username; *c != 0; c++)
*c = tolower(*c);
REQ_LOCAL("username", username);
// ctorm_req_local(req, "username", username, NULL);
```
In an another route/middleware that handle the same request after the previous middleware,
you can access the "username" local:
```c
char *username = REQ_LOCAL("username");
// char *username = ctorm_req_local(req, "username", NULL);
```
If you want to pass a variable to all routes/middlewares, you should use global locals. See
the [app documentation](app.md).
13 changes: 13 additions & 0 deletions example/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM ghcr.io/ngn13/ctorm:latest

WORKDIR /example
COPY . ./

RUN mkdir /dist
RUN make \
DONT_CHECK_LIBCTORM=1 \
INCD=/usr/include/ctorm \
DIST=/dist

WORKDIR /
CMD ["/example/init.sh"]
6 changes: 4 additions & 2 deletions example/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ BINS = $(patsubst %,$(DIST)/example_%,$(DIRS))
all: $(BINS)

$(DIST)/libctorm.so:
ifeq (,$(wildcard $(DIST)/libctorm.so))
$(error you should first compile libctorm)
ifneq ($(DONT_CHECK_LIBCTORM), 1)
ifeq (,$(wildcard $(DIST)/libctorm.so))
$(error you should first compile libctorm)
endif
endif

$(DIST)/example_%: %/main.c $(DIST)/libctorm.so
Expand Down
12 changes: 0 additions & 12 deletions example/echo/Dockerfile

This file was deleted.

2 changes: 1 addition & 1 deletion example/echo/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ int main() {
ctorm_app_all(app, GET_notfound);

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

// clean up
Expand Down
10 changes: 0 additions & 10 deletions example/hello/Dockerfile

This file was deleted.

24 changes: 24 additions & 0 deletions example/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

# docker init script

examples=()
pids=()

for example in dist/*; do
examples+=("${example}")
done

for i in "${!examples[@]}"; do
"./${examples[$i]}" &
pids[$i]=$!
done

for i in "${!pids[@]}"; do
if ! wait "${pids[$i]}"; then
echo "${examples[$i]} exited with a non zero exit-code (${?})"
exit 1
fi
done

exit 0
38 changes: 38 additions & 0 deletions example/locals/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <ctorm.h>

void GET_index(ctorm_req_t *req, ctorm_res_t *res) {
char *username = REQ_LOCAL("username");
char *format = REQ_LOCAL("format");
RES_FMT(format, username);
}

void username_middleware(ctorm_req_t *req, ctorm_res_t *res) {
char *username = REQ_QUERY("username");

if (NULL == username) {
RES_SEND("no username provided");
REQ_CANCEL();
return;
}

REQ_LOCAL("username", username);
}

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

// add a global local
ctorm_app_local(app, "format", "username: %s");

// setup the routes
MIDDLEWARE_GET(app, "/*", username_middleware);
GET(app, "/", GET_index);

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

// clean up
ctorm_app_free(app);
}
10 changes: 0 additions & 10 deletions example/middleware/Dockerfile

This file was deleted.

2 changes: 1 addition & 1 deletion example/middleware/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ int main() {
GET(app, "/", GET_index);
GET(app, "/users", GET_user_list);

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

ctorm_app_free(app);
Expand Down
10 changes: 0 additions & 10 deletions example/params/Dockerfile

This file was deleted.

2 changes: 1 addition & 1 deletion example/params/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ int main() {
GET(app, "/echo/:param/*", GET_param);

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

// clean up
Expand Down
15 changes: 15 additions & 0 deletions inc/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "config.h"
#include "http.h"
#include "pair.h"
#include "pool.h"
#include "util.h"

Expand Down Expand Up @@ -67,6 +68,7 @@ typedef struct {
cu_str_t static_dir; /// static directory path
ctorm_route_t all_route; /// all handler route (see app_all())
bool running; /// is the app running?
ctorm_pair_t *locals; /// global locals
ctorm_pool_t *pool; /// thread pool for the app
pthread_mutex_t request_mutex; /// mutex used to lock request threads

Expand Down Expand Up @@ -125,6 +127,19 @@ bool ctorm_app_run(ctorm_app_t *app, const char *host);
*/
bool ctorm_app_stop(ctorm_app_t *app);

/*!
* Get or set a local, these locals will be copied to every single request so they
* are global
* param[in] app ctorm server application
* param[in] name Local name
* param[in] value Local value
* @return Returns false if an error occurs, you can obtain the error from the errno
*/
bool ctorm_app_local(ctorm_app_t *app, char *name, void *value);

/*!
* Add a route handler to the web server, which will handle all the requests
Expand Down
12 changes: 11 additions & 1 deletion inc/ctorm.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,16 @@
*/
#define REQ_PARAM(param) ctorm_req_param(req, param)

/*!
* Get or set a local
* @param[in] name Local name
* @param[in] ... Local value
* @return Local value
*/
#define REQ_LOCAL(local, ...) ctorm_req_local(req, local, ##__VA_ARGS__, NULL)

/*!
* Parse URL form encoded request body
Expand Down Expand Up @@ -348,7 +358,7 @@
* Set response body to a formatted string
* @param[in] fmt Format string
* @return Format string arguments
* @param[in] ... Arguments for the format string
*/
#define RES_FMT(fmt, ...) ctorm_res_fmt(res, fmt, __VA_ARGS__)
Expand Down
3 changes: 3 additions & 0 deletions inc/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ typedef enum {
PortTooLarge = 9937,
NameTooLarge = 9938,
BadName = 9939,
BadLocalPointer = 9940,
BadQueryPointer = 9941,
BadParamPointer = 9942,
} ctorm_error_t;

/*!
Expand Down
12 changes: 6 additions & 6 deletions inc/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ void ctorm_debug(const char *fmt, ...);
* Print messages marked as "info", you can use this
* to print informative messages
* @param[in] fmt Message format string
* @param[in] ... Arguments for the formatted string
* @param[in] fmt Format string
* @param[in] ... Arguments for the format string
*/
void ctorm_info(const char *fmt, ...);
Expand All @@ -48,8 +48,8 @@ void ctorm_info(const char *fmt, ...);
* Print messages marked as "warn", you can use this
* to print warnings
* @param[in] fmt Message format string
* @param[in] ... Arguments for the formatted string
* @param[in] fmt Format string
* @param[in] ... Arguments for the format string
*/
void ctorm_warn(const char *fmt, ...);
Expand All @@ -59,8 +59,8 @@ void ctorm_warn(const char *fmt, ...);
* Print messages marked as "fail", you can use this
* to print failures
* @param[in] fmt Message format string
* @param[in] ... Arguments for the formatted string
* @param[in] fmt Format string
* @param[in] ... Arguments for the format string
*/
void ctorm_fail(const char *fmt, ...);
31 changes: 17 additions & 14 deletions inc/req.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,33 @@ typedef struct {
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")
ctorm_pair_t *locals; /// Local variables to pass along with the request
int64_t bodysize; /// size of the HTTP body
} ctorm_req_t;

#ifndef CTORM_EXPORT

#define ctorm_req_is_valid(req) \
(NULL != (req)->version && NULL != (req)->encpath && NULL != (req)->path) // check if the request is valid
void ctorm_req_init(ctorm_req_t *, connection_t *); // setup a request
void ctorm_req_free(ctorm_req_t *); // cleanup a request
bool ctorm_req_start(ctorm_req_t *); // receive the (at least the first part) of the HTTP request
void ctorm_req_end(ctorm_req_t *); // completely receive the HTTP request
void ctorm_req_init(ctorm_req_t *req, connection_t *con); // setup a request
void ctorm_req_free(ctorm_req_t *req); // cleanup a request
bool ctorm_req_start(ctorm_req_t *req); // receive the (at least the first part) of the HTTP request
void ctorm_req_end(ctorm_req_t *req); // completely receive the HTTP request

#endif

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
const char *ctorm_req_method(ctorm_req_t *req); // get the request method (GET, POST, PUT etc.)
char *ctorm_req_query(ctorm_req_t *req, char *name); // get a request URL query
char *ctorm_req_param(ctorm_req_t *req, char *name); // get a request URL param
void *ctorm_req_local(ctorm_req_t *req, char *name, ...); // get or set a local by name
char *ctorm_req_get(ctorm_req_t *req, char *header); // get a request header

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
uint64_t ctorm_req_body(
ctorm_req_t *req, char *buf, uint64_t size); // copy given amount of bytes from body to the buffer
uint64_t ctorm_req_body_size(ctorm_req_t *req); // 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
char *ctorm_req_ip(ctorm_req_t *req, char *); // get the requester IPv4/IPv6 address as string
#define ctorm_req_addr(req) ((req)->con->addr) // get the requester address as sockaddr

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
ctorm_url_t *ctorm_req_form(ctorm_req_t *req); // parse URL encoded form body
cJSON *ctorm_req_json(ctorm_req_t *req); // parse JSON encoded form body
Loading

0 comments on commit fdd394d

Please sign in to comment.