mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-28 08:44:06 +02:00
runtime: parse req, send res
This commit is contained in:
parent
aeeed02c35
commit
355c907135
@ -3,29 +3,33 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define DEFINE_VEC(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
#define MAYBE_UNUSED __attribute__((unused))
|
||||||
|
|
||||||
|
#define DEFINE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
||||||
|
typedef TYPE VEC_TYPE##T; \
|
||||||
typedef struct { \
|
typedef struct { \
|
||||||
TYPE* data; \
|
VEC_TYPE##T* data; \
|
||||||
size_t capacity; \
|
size_t capacity; \
|
||||||
size_t size; \
|
size_t size; \
|
||||||
} VEC_TYPE; \
|
} VEC_TYPE;
|
||||||
\
|
|
||||||
static inline int FN_PREFIX##_construct(VEC_TYPE* vec) \
|
#define DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
||||||
|
MAYBE_UNUSED static inline int FN_PREFIX##_construct(VEC_TYPE* vec) \
|
||||||
{ \
|
{ \
|
||||||
const size_t capacity = INITIAL_CAPACITY; \
|
const size_t capacity = INITIAL_CAPACITY; \
|
||||||
TYPE* data = malloc(sizeof(TYPE) * capacity); \
|
VEC_TYPE##T* data = malloc(sizeof(VEC_TYPE##T) * capacity); \
|
||||||
if (!data) \
|
if (!data) \
|
||||||
return -1; \
|
return -1; \
|
||||||
*vec = (VEC_TYPE) { data, capacity, .size = 0 }; \
|
*vec = (VEC_TYPE) { data, capacity, .size = 0 }; \
|
||||||
return 0; \
|
return 0; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline void FN_PREFIX##_destroy(VEC_TYPE* vec) \
|
MAYBE_UNUSED static inline void FN_PREFIX##_destroy(VEC_TYPE* vec) \
|
||||||
{ \
|
{ \
|
||||||
free(vec->data); \
|
free(vec->data); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline VEC_TYPE* FN_PREFIX##_new(void) \
|
MAYBE_UNUSED static inline VEC_TYPE* FN_PREFIX##_new(void) \
|
||||||
{ \
|
{ \
|
||||||
VEC_TYPE* vec = malloc(sizeof(VEC_TYPE)); \
|
VEC_TYPE* vec = malloc(sizeof(VEC_TYPE)); \
|
||||||
if (!vec) \
|
if (!vec) \
|
||||||
@ -36,13 +40,14 @@
|
|||||||
return vec; \
|
return vec; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline void FN_PREFIX##_free(VEC_TYPE* vec) \
|
MAYBE_UNUSED static inline void FN_PREFIX##_free(VEC_TYPE* vec) \
|
||||||
{ \
|
{ \
|
||||||
FN_PREFIX##_destroy(vec); \
|
FN_PREFIX##_destroy(vec); \
|
||||||
free(vec); \
|
free(vec); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline int FN_PREFIX##_push(VEC_TYPE* vec, TYPE value) \
|
MAYBE_UNUSED static inline int FN_PREFIX##_push( \
|
||||||
|
VEC_TYPE* vec, VEC_TYPE##T value) \
|
||||||
{ \
|
{ \
|
||||||
if (vec->size + 1 > vec->capacity) { \
|
if (vec->size + 1 > vec->capacity) { \
|
||||||
size_t new_capacity = vec->capacity * 2; \
|
size_t new_capacity = vec->capacity * 2; \
|
||||||
@ -57,22 +62,30 @@
|
|||||||
return 0; \
|
return 0; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline TYPE* FN_PREFIX##_at(VEC_TYPE* vec, size_t idx) \
|
MAYBE_UNUSED static inline VEC_TYPE##T* FN_PREFIX##_at( \
|
||||||
|
VEC_TYPE* vec, size_t idx) \
|
||||||
{ \
|
{ \
|
||||||
return &vec->data[idx]; \
|
return &vec->data[idx]; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline const TYPE* FN_PREFIX##_at_const( \
|
MAYBE_UNUSED static inline const VEC_TYPE##T* FN_PREFIX##_at_const( \
|
||||||
const VEC_TYPE* vec, size_t idx) \
|
const VEC_TYPE* vec, size_t idx) \
|
||||||
{ \
|
{ \
|
||||||
return &vec->data[idx]; \
|
return &vec->data[idx]; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline TYPE FN_PREFIX##_get(const VEC_TYPE* vec, size_t idx) \
|
MAYBE_UNUSED static inline VEC_TYPE##T FN_PREFIX##_get( \
|
||||||
|
const VEC_TYPE* vec, size_t idx) \
|
||||||
{ \
|
{ \
|
||||||
return vec->data[idx]; \
|
return vec->data[idx]; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DEFINE_VEC(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
||||||
|
DEFINE_VEC_TYPE(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY) \
|
||||||
|
DEFINE_VEC_IMPL(TYPE, VEC_TYPE, FN_PREFIX, INITIAL_CAPACITY)
|
||||||
|
|
||||||
|
#define DEFINE_MAYBE_UNUSED
|
||||||
|
|
||||||
#define DEFINE_STATIC_QUEUE(TYPE, QUEUE_TYPE, FN_PREFIX) \
|
#define DEFINE_STATIC_QUEUE(TYPE, QUEUE_TYPE, FN_PREFIX) \
|
||||||
typedef struct { \
|
typedef struct { \
|
||||||
TYPE* data; \
|
TYPE* data; \
|
||||||
@ -81,7 +94,7 @@
|
|||||||
size_t front; \
|
size_t front; \
|
||||||
} QUEUE_TYPE; \
|
} QUEUE_TYPE; \
|
||||||
\
|
\
|
||||||
static inline int FN_PREFIX##_construct( \
|
MAYBE_UNUSED static inline int FN_PREFIX##_construct( \
|
||||||
QUEUE_TYPE* queue, size_t capacity) \
|
QUEUE_TYPE* queue, size_t capacity) \
|
||||||
{ \
|
{ \
|
||||||
*queue = (QUEUE_TYPE) { \
|
*queue = (QUEUE_TYPE) { \
|
||||||
@ -93,12 +106,12 @@
|
|||||||
return 0; \
|
return 0; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline void FN_PREFIX##_destroy(QUEUE_TYPE* queue) \
|
MAYBE_UNUSED static inline void FN_PREFIX##_destroy(QUEUE_TYPE* queue) \
|
||||||
{ \
|
{ \
|
||||||
free(queue->data); \
|
free(queue->data); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline QUEUE_TYPE* FN_PREFIX##_new(size_t capacity) \
|
MAYBE_UNUSED static inline QUEUE_TYPE* FN_PREFIX##_new(size_t capacity) \
|
||||||
{ \
|
{ \
|
||||||
QUEUE_TYPE* queue = malloc(sizeof(QUEUE_TYPE)); \
|
QUEUE_TYPE* queue = malloc(sizeof(QUEUE_TYPE)); \
|
||||||
if (!queue) \
|
if (!queue) \
|
||||||
@ -109,13 +122,14 @@
|
|||||||
return queue; \
|
return queue; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline void FN_PREFIX##_free(QUEUE_TYPE* queue) \
|
MAYBE_UNUSED static inline void FN_PREFIX##_free(QUEUE_TYPE* queue) \
|
||||||
{ \
|
{ \
|
||||||
FN_PREFIX##_destroy(queue); \
|
FN_PREFIX##_destroy(queue); \
|
||||||
free(queue); \
|
free(queue); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline int FN_PREFIX##_push(QUEUE_TYPE* queue, TYPE req) \
|
MAYBE_UNUSED static inline int FN_PREFIX##_push( \
|
||||||
|
QUEUE_TYPE* queue, TYPE req) \
|
||||||
{ \
|
{ \
|
||||||
size_t front = queue->front + 1; \
|
size_t front = queue->front + 1; \
|
||||||
if (front >= queue->capacity) { \
|
if (front >= queue->capacity) { \
|
||||||
@ -129,14 +143,16 @@
|
|||||||
return 0; \
|
return 0; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline size_t FN_PREFIX##_size(const QUEUE_TYPE* queue) \
|
MAYBE_UNUSED static inline size_t FN_PREFIX##_size( \
|
||||||
|
const QUEUE_TYPE* queue) \
|
||||||
{ \
|
{ \
|
||||||
return queue->front >= queue->back \
|
return queue->front >= queue->back \
|
||||||
? queue->front - queue->back \
|
? queue->front - queue->back \
|
||||||
: (queue->capacity - queue->back) + queue->front; \
|
: (queue->capacity - queue->back) + queue->front; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline int FN_PREFIX##_pop(QUEUE_TYPE* queue, TYPE* req) \
|
MAYBE_UNUSED static inline int FN_PREFIX##_pop( \
|
||||||
|
QUEUE_TYPE* queue, TYPE* req) \
|
||||||
{ \
|
{ \
|
||||||
if (queue->back == queue->front) { \
|
if (queue->back == queue->front) { \
|
||||||
return -1; \
|
return -1; \
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "http_server.h"
|
#include "http_server.h"
|
||||||
#include "http_server_internal.h"
|
#include "http_server_internal.h"
|
||||||
|
#include "str_util.h"
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@ -294,6 +295,8 @@ static inline void worker_handle_request(Worker* worker, Client* client)
|
|||||||
const char* length_val = req_get_header(&req, "Content-Length");
|
const char* length_val = req_get_header(&req, "Content-Length");
|
||||||
size_t length = strtoul(length_val, NULL, 10);
|
size_t length = strtoul(length_val, NULL, 10);
|
||||||
body = calloc(length + 1, sizeof(char));
|
body = calloc(length + 1, sizeof(char));
|
||||||
|
strncpy(body, (char*)&buffer[body_idx], length);
|
||||||
|
body[length] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpCtx handler_ctx = {
|
HttpCtx handler_ctx = {
|
||||||
@ -327,13 +330,13 @@ l0_return:
|
|||||||
static inline int parse_header(
|
static inline int parse_header(
|
||||||
Req* req, size_t* body_idx, const char* const buf, size_t buf_size)
|
Req* req, size_t* body_idx, const char* const buf, size_t buf_size)
|
||||||
{
|
{
|
||||||
Strlitter splitter = string_split(buf, buf_size, "\r\n");
|
StrSplitter splitter = str_split(buf, buf_size, "\r\n");
|
||||||
|
|
||||||
StrSlice first = split_next(&splitter);
|
StrSlice first = strsplit_next(&splitter);
|
||||||
Strlitter first_splitter = string_split(first.ptr, first.len, " ");
|
StrSplitter first_splitter = str_split(first.ptr, first.len, " ");
|
||||||
StrSlice method_str = split_next(&first_splitter);
|
StrSlice method_str = strsplit_next(&first_splitter);
|
||||||
StrSlice path_str = split_next(&first_splitter);
|
StrSlice path_str = strsplit_next(&first_splitter);
|
||||||
StrSlice version_str = split_next(&first_splitter);
|
StrSlice version_str = strsplit_next(&first_splitter);
|
||||||
|
|
||||||
if (strncmp(version_str.ptr, "HTTP/1.1", 8) != 0) {
|
if (strncmp(version_str.ptr, "HTTP/1.1", 8) != 0) {
|
||||||
fprintf(stderr, "error: unrecognized http version '%.*s'\n",
|
fprintf(stderr, "error: unrecognized http version '%.*s'\n",
|
||||||
@ -363,7 +366,7 @@ static inline int parse_header(
|
|||||||
header_vec_construct(&headers);
|
header_vec_construct(&headers);
|
||||||
|
|
||||||
while (headers.size < MAX_HEADERS_LEN) {
|
while (headers.size < MAX_HEADERS_LEN) {
|
||||||
StrSlice line = split_next(&splitter);
|
StrSlice line = strsplit_next(&splitter);
|
||||||
if (line.len == 0) {
|
if (line.len == 0) {
|
||||||
*body_idx = (size_t)&line.ptr[2] - (size_t)buf;
|
*body_idx = (size_t)&line.ptr[2] - (size_t)buf;
|
||||||
break;
|
break;
|
||||||
@ -427,39 +430,3 @@ static inline const char* req_get_header(const Req* req, const char* key)
|
|||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Strlitter string_split(
|
|
||||||
const char* text, size_t text_len, const char* split)
|
|
||||||
{
|
|
||||||
return (Strlitter) {
|
|
||||||
.text = text,
|
|
||||||
.text_len = text_len,
|
|
||||||
.i = 0,
|
|
||||||
.split = split,
|
|
||||||
.split_len = strlen(split),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline StrSlice split_next(Strlitter* splitter)
|
|
||||||
{
|
|
||||||
for (size_t i = splitter->i; i < splitter->text_len; ++i) {
|
|
||||||
if (strncmp(&splitter->text[i], splitter->split, splitter->split_len)
|
|
||||||
== 0) {
|
|
||||||
const char* ptr = &splitter->text[splitter->i];
|
|
||||||
size_t len = i - splitter->i;
|
|
||||||
splitter->i = i + splitter->split_len;
|
|
||||||
return (StrSlice) { ptr, len };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (StrSlice) {
|
|
||||||
.ptr = &splitter->text[splitter->i],
|
|
||||||
.len = splitter->text_len - splitter->i,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void string_push_str(String* string, const char* str)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < strlen(str); ++i) {
|
|
||||||
string_push(string, str[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -103,25 +103,4 @@ struct HttpCtx {
|
|||||||
void* user_ctx;
|
void* user_ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char* ptr;
|
|
||||||
size_t len;
|
|
||||||
} StrSlice;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char* text;
|
|
||||||
size_t text_len;
|
|
||||||
size_t i;
|
|
||||||
const char* split;
|
|
||||||
size_t split_len;
|
|
||||||
} Strlitter;
|
|
||||||
|
|
||||||
static inline Strlitter string_split(
|
|
||||||
const char* text, size_t text_len, const char* split);
|
|
||||||
static inline StrSlice split_next(Strlitter* splitter);
|
|
||||||
|
|
||||||
DEFINE_VEC(char, String, string, 8)
|
|
||||||
|
|
||||||
static inline void string_push_str(String* string, const char* str);
|
|
||||||
|
|
||||||
const char* http_response_code_string(int code);
|
const char* http_response_code_string(int code);
|
||||||
|
376
slige/runtime/src/json.c
Normal file
376
slige/runtime/src/json.c
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
#include "json.h"
|
||||||
|
#include "collection.h"
|
||||||
|
#include "str_util.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* key;
|
||||||
|
JsonValue* val;
|
||||||
|
} KV;
|
||||||
|
|
||||||
|
DEFINE_VEC(JsonValue*, Arr, arr, 4)
|
||||||
|
DEFINE_VEC(KV, Obj, obj, 4)
|
||||||
|
|
||||||
|
struct JsonValue {
|
||||||
|
JsonType type;
|
||||||
|
union {
|
||||||
|
bool bool_val;
|
||||||
|
char* str_val;
|
||||||
|
Arr arr_val;
|
||||||
|
Obj obj_val;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool json_is(const JsonValue* value, JsonType type)
|
||||||
|
{
|
||||||
|
return value != NULL && value->type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool json_bool(const JsonValue* value)
|
||||||
|
{
|
||||||
|
return value->bool_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t json_int(const JsonValue* value)
|
||||||
|
{
|
||||||
|
return strtoll(value->str_val, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
double json_float(const JsonValue* value)
|
||||||
|
{
|
||||||
|
return strtof(value->str_val, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* json_string(const JsonValue* value)
|
||||||
|
{
|
||||||
|
return value->str_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t json_array_size(const JsonValue* value)
|
||||||
|
{
|
||||||
|
return value->arr_val.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const JsonValue* json_array_get(const JsonValue* value, size_t idx)
|
||||||
|
{
|
||||||
|
return value->arr_val.data[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool json_object_has(const JsonValue* value, const char* key)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < value->obj_val.size; ++i) {
|
||||||
|
KV* kv = &value->obj_val.data[i];
|
||||||
|
if (strcmp(key, kv->key) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const JsonValue* json_object_get(const JsonValue* value, const char* key)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < value->obj_val.size; ++i) {
|
||||||
|
KV* kv = &value->obj_val.data[i];
|
||||||
|
if (strcmp(key, kv->key) == 0) {
|
||||||
|
return kv->val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_value_free(JsonValue* value)
|
||||||
|
{
|
||||||
|
switch (value->type) {
|
||||||
|
case JsonType_Error:
|
||||||
|
case JsonType_Null:
|
||||||
|
case JsonType_Bool:
|
||||||
|
break;
|
||||||
|
case JsonType_Number:
|
||||||
|
case JsonType_String:
|
||||||
|
free(value->str_val);
|
||||||
|
break;
|
||||||
|
case JsonType_Array:
|
||||||
|
for (size_t i = 0; i < value->arr_val.size; ++i) {
|
||||||
|
json_value_free(value->arr_val.data[i]);
|
||||||
|
}
|
||||||
|
arr_destroy(&value->arr_val);
|
||||||
|
break;
|
||||||
|
case JsonType_Object:
|
||||||
|
for (size_t i = 0; i < value->obj_val.size; ++i) {
|
||||||
|
free(value->obj_val.data[i].key);
|
||||||
|
json_value_free(value->obj_val.data[i].val);
|
||||||
|
}
|
||||||
|
obj_destroy(&value->obj_val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TOK_EOF '\0'
|
||||||
|
#define TOK_ERROR 'e'
|
||||||
|
#define TOK_NULL 'n'
|
||||||
|
#define TOK_FALSE 'f'
|
||||||
|
#define TOK_TRUE 't'
|
||||||
|
#define TOK_NUMBER '0'
|
||||||
|
#define TOK_STRING '"'
|
||||||
|
|
||||||
|
static inline JsonValue* alloc(JsonValue init);
|
||||||
|
static inline void lex(JsonParser* p);
|
||||||
|
static inline void lstep(JsonParser* p);
|
||||||
|
|
||||||
|
void json_parser_construct(JsonParser* p, const char* text, size_t text_len)
|
||||||
|
{
|
||||||
|
*p = (JsonParser) {
|
||||||
|
text,
|
||||||
|
text_len,
|
||||||
|
.i = 0,
|
||||||
|
.ch = text[0],
|
||||||
|
.curr_tok = TOK_EOF,
|
||||||
|
.curr_val = NULL,
|
||||||
|
};
|
||||||
|
lex(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_parser_destroy(JsonParser* p)
|
||||||
|
{
|
||||||
|
(void)p;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonValue* json_parser_parse(JsonParser* p)
|
||||||
|
{
|
||||||
|
switch (p->curr_tok) {
|
||||||
|
case TOK_EOF:
|
||||||
|
fprintf(stderr, "error: json: unexpected eof\n");
|
||||||
|
return NULL;
|
||||||
|
case TOK_ERROR:
|
||||||
|
lex(p);
|
||||||
|
return NULL;
|
||||||
|
case TOK_NULL:
|
||||||
|
lex(p);
|
||||||
|
return alloc((JsonValue) { .type = JsonType_Null });
|
||||||
|
case TOK_FALSE:
|
||||||
|
lex(p);
|
||||||
|
return alloc(
|
||||||
|
(JsonValue) { .type = JsonType_Bool, .bool_val = false });
|
||||||
|
case TOK_TRUE:
|
||||||
|
lex(p);
|
||||||
|
return alloc(
|
||||||
|
(JsonValue) { .type = JsonType_Null, .bool_val = true });
|
||||||
|
case TOK_NUMBER: {
|
||||||
|
char* val = p->curr_val;
|
||||||
|
lex(p);
|
||||||
|
return alloc(
|
||||||
|
(JsonValue) { .type = JsonType_Number, .str_val = val });
|
||||||
|
}
|
||||||
|
case TOK_STRING: {
|
||||||
|
char* val = p->curr_val;
|
||||||
|
lex(p);
|
||||||
|
return alloc(
|
||||||
|
(JsonValue) { .type = JsonType_String, .str_val = val });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->curr_tok == '[') {
|
||||||
|
lex(p);
|
||||||
|
Arr arr;
|
||||||
|
arr_construct(&arr);
|
||||||
|
{
|
||||||
|
JsonValue* value = json_parser_parse(p);
|
||||||
|
if (!value)
|
||||||
|
return NULL;
|
||||||
|
arr_push(&arr, value);
|
||||||
|
}
|
||||||
|
while (p->curr_tok != TOK_EOF && p->curr_tok != ']') {
|
||||||
|
if (p->curr_tok != ',') {
|
||||||
|
fprintf(stderr, "error: json: expected ',' in array\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lex(p);
|
||||||
|
JsonValue* value = json_parser_parse(p);
|
||||||
|
if (!value)
|
||||||
|
return NULL;
|
||||||
|
arr_push(&arr, value);
|
||||||
|
}
|
||||||
|
if (p->curr_tok != ']') {
|
||||||
|
fprintf(stderr, "error: json: expected ']' after array\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lex(p);
|
||||||
|
return alloc((JsonValue) { .type = JsonType_Array, .arr_val = arr });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->curr_tok == '{') {
|
||||||
|
lex(p);
|
||||||
|
Obj obj;
|
||||||
|
obj_construct(&obj);
|
||||||
|
{
|
||||||
|
if (p->curr_tok != '"') {
|
||||||
|
fprintf(stderr, "error: json: expected '\"' in kv\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char* key = p->curr_val;
|
||||||
|
lex(p);
|
||||||
|
if (p->curr_tok != ':') {
|
||||||
|
fprintf(stderr, "error: json: expected ':' in kv\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lex(p);
|
||||||
|
JsonValue* value = json_parser_parse(p);
|
||||||
|
if (!value)
|
||||||
|
return NULL;
|
||||||
|
obj_push(&obj, (KV) { key, value });
|
||||||
|
}
|
||||||
|
while (p->curr_tok != TOK_EOF && p->curr_tok != '}') {
|
||||||
|
if (p->curr_tok != ',') {
|
||||||
|
fprintf(stderr, "error: json: expected ',' in object\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lex(p);
|
||||||
|
|
||||||
|
if (p->curr_tok != '"') {
|
||||||
|
fprintf(stderr, "error: json: expected '\"' in kv\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char* key = p->curr_val;
|
||||||
|
lex(p);
|
||||||
|
if (p->curr_tok != ':') {
|
||||||
|
fprintf(stderr, "error: json: expected ':' in kv\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lex(p);
|
||||||
|
JsonValue* value = json_parser_parse(p);
|
||||||
|
if (!value)
|
||||||
|
return NULL;
|
||||||
|
obj_push(&obj, (KV) { key, value });
|
||||||
|
}
|
||||||
|
if (p->curr_tok != '}') {
|
||||||
|
fprintf(stderr, "error: json: expected '}' after object\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lex(p);
|
||||||
|
return alloc((JsonValue) { .type = JsonType_Object, .obj_val = obj });
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "error: json: unexpeted tok\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline JsonValue* alloc(JsonValue init)
|
||||||
|
{
|
||||||
|
JsonValue* value = malloc(sizeof(JsonValue));
|
||||||
|
*value = init;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void lex(JsonParser* p)
|
||||||
|
{
|
||||||
|
if (p->i >= p->text_len) {
|
||||||
|
p->curr_tok = TOK_EOF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (p->ch) {
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
lstep(p);
|
||||||
|
lex(p);
|
||||||
|
return;
|
||||||
|
case '[':
|
||||||
|
case ']':
|
||||||
|
case '{':
|
||||||
|
case '}':
|
||||||
|
case ',':
|
||||||
|
case ':':
|
||||||
|
p->curr_tok = p->ch;
|
||||||
|
lstep(p);
|
||||||
|
return;
|
||||||
|
case '0':
|
||||||
|
lstep(p);
|
||||||
|
p->curr_tok = TOK_NUMBER;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((p->ch >= '1' && p->ch <= '9') || p->ch == '.') {
|
||||||
|
String value;
|
||||||
|
string_construct(&value);
|
||||||
|
int dec_seps = 0;
|
||||||
|
while (p->i < p->text_len
|
||||||
|
&& ((p->ch >= '0' && p->ch <= '9')
|
||||||
|
|| (p->ch == '.' && dec_seps <= 1))) {
|
||||||
|
if (p->ch == '.') {
|
||||||
|
dec_seps += 1;
|
||||||
|
}
|
||||||
|
string_push(&value, p->ch);
|
||||||
|
lstep(p);
|
||||||
|
}
|
||||||
|
// should be interned
|
||||||
|
char* copy = string_copy(&value);
|
||||||
|
|
||||||
|
string_destroy(&value);
|
||||||
|
p->curr_tok = TOK_NUMBER;
|
||||||
|
p->curr_val = copy;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (p->ch == '"') {
|
||||||
|
lstep(p);
|
||||||
|
|
||||||
|
String value;
|
||||||
|
string_construct(&value);
|
||||||
|
|
||||||
|
while (p->i < p->text_len && p->text[p->i] != '"') {
|
||||||
|
if (p->text[p->i] == '\\') {
|
||||||
|
lstep(p);
|
||||||
|
if (p->i >= p->text_len)
|
||||||
|
break;
|
||||||
|
switch (p->ch) {
|
||||||
|
case '0':
|
||||||
|
string_push(&value, '\0');
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
string_push(&value, '\n');
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
string_push(&value, '\r');
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
string_push(&value, '\t');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
string_push(&value, p->ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
string_push(&value, p->ch);
|
||||||
|
}
|
||||||
|
lstep(p);
|
||||||
|
}
|
||||||
|
if (p->i >= p->text_len || p->text[p->i] != '"') {
|
||||||
|
string_destroy(&value);
|
||||||
|
fprintf(stderr, "error: json: bad string\n");
|
||||||
|
p->curr_tok = TOK_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lstep(p);
|
||||||
|
|
||||||
|
// should be interned
|
||||||
|
char* copy = string_copy(&value);
|
||||||
|
|
||||||
|
string_destroy(&value);
|
||||||
|
p->curr_tok = '"';
|
||||||
|
p->curr_val = copy;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "error: json: illegal char '%c'\n", p->ch);
|
||||||
|
lstep(p);
|
||||||
|
p->curr_tok = TOK_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void lstep(JsonParser* p)
|
||||||
|
{
|
||||||
|
p->i += 1;
|
||||||
|
p->ch = p->text[p->i];
|
||||||
|
}
|
44
slige/runtime/src/json.h
Normal file
44
slige/runtime/src/json.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
JsonType_Error,
|
||||||
|
JsonType_Null,
|
||||||
|
JsonType_Bool,
|
||||||
|
JsonType_Number,
|
||||||
|
JsonType_String,
|
||||||
|
JsonType_Array,
|
||||||
|
JsonType_Object,
|
||||||
|
} JsonType;
|
||||||
|
|
||||||
|
typedef struct JsonValue JsonValue;
|
||||||
|
|
||||||
|
bool json_is(const JsonValue* value, JsonType type);
|
||||||
|
bool json_bool(const JsonValue* value);
|
||||||
|
int64_t json_int(const JsonValue* value);
|
||||||
|
double json_float(const JsonValue* value);
|
||||||
|
const char* json_string(const JsonValue* value);
|
||||||
|
size_t json_array_size(const JsonValue* value);
|
||||||
|
const JsonValue* json_array_get(const JsonValue* value, size_t idx);
|
||||||
|
bool json_object_has(const JsonValue* value, const char* key);
|
||||||
|
const JsonValue* json_object_get(const JsonValue* value, const char* key);
|
||||||
|
|
||||||
|
void json_value_free(JsonValue* value);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* text;
|
||||||
|
size_t text_len;
|
||||||
|
size_t i;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
char curr_tok;
|
||||||
|
char* curr_val;
|
||||||
|
} JsonParser;
|
||||||
|
|
||||||
|
void json_parser_construct(
|
||||||
|
JsonParser* parser, const char* text, size_t text_len);
|
||||||
|
void json_parser_destroy(JsonParser* parser);
|
||||||
|
JsonValue* json_parser_parse(JsonParser* parser);
|
@ -1,4 +1,5 @@
|
|||||||
#include "http_server.h"
|
#include "http_server.h"
|
||||||
|
#include "json.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -6,28 +7,52 @@ typedef struct {
|
|||||||
int number;
|
int number;
|
||||||
} Cx;
|
} Cx;
|
||||||
|
|
||||||
|
#define RESPOND(HTTP_CTX, STATUS, MIME_TYPE, ...) \
|
||||||
|
{ \
|
||||||
|
HttpCtx* _ctx = (HTTP_CTX); \
|
||||||
|
char _body[512]; \
|
||||||
|
snprintf(_body, 512 - 1, __VA_ARGS__); \
|
||||||
|
\
|
||||||
|
char content_length[24] = { 0 }; \
|
||||||
|
snprintf(content_length, 24 - 1, "%ld", strlen(_body)); \
|
||||||
|
\
|
||||||
|
http_ctx_res_headers_set(_ctx, "Content-Type", MIME_TYPE); \
|
||||||
|
http_ctx_res_headers_set(_ctx, "Content-Length", content_length); \
|
||||||
|
\
|
||||||
|
http_ctx_respond(_ctx, (STATUS), _body); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RESPOND_HTML(HTTP_CTX, STATUS, ...) \
|
||||||
|
RESPOND(HTTP_CTX, STATUS, "text/html", __VA_ARGS__)
|
||||||
|
#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \
|
||||||
|
RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__)
|
||||||
|
|
||||||
void route_get_index(HttpCtx* ctx)
|
void route_get_index(HttpCtx* ctx)
|
||||||
{
|
{
|
||||||
Cx* cx = http_ctx_user_ctx(ctx);
|
Cx* cx = http_ctx_user_ctx(ctx);
|
||||||
|
|
||||||
char body[512];
|
RESPOND_HTML(ctx, 200,
|
||||||
snprintf(body, 512 - 1,
|
|
||||||
"<!DOCTYPE html><html><head><meta "
|
"<!DOCTYPE html><html><head><meta "
|
||||||
"charset=\"utf-8\"></head><body><h1>Number = %d</h1></body></html>",
|
"charset=\"utf-8\"></head><body><h1>Number = %d</h1></body></html>",
|
||||||
cx->number);
|
cx->number);
|
||||||
|
|
||||||
char content_length[24] = { 0 };
|
|
||||||
snprintf(content_length, 24 - 1, "%ld", strlen(body));
|
|
||||||
|
|
||||||
http_ctx_res_headers_set(ctx, "Content-Type", "text/html");
|
|
||||||
http_ctx_res_headers_set(ctx, "Content-Length", content_length);
|
|
||||||
|
|
||||||
http_ctx_respond(ctx, 200, body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void route_post_set_number(HttpCtx* ctx)
|
void route_post_set_number(HttpCtx* ctx)
|
||||||
{
|
{
|
||||||
printf("set number\n");
|
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);
|
||||||
|
|
||||||
|
int64_t value = json_int(json_object_get(body, "value"));
|
||||||
|
cx->number = (int)value;
|
||||||
|
|
||||||
|
json_value_free(body);
|
||||||
|
|
||||||
|
RESPOND_JSON(ctx, 200, "{\"ok\": true}\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpServer* server;
|
HttpServer* server;
|
||||||
|
46
slige/runtime/src/str_util.c
Normal file
46
slige/runtime/src/str_util.c
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include "str_util.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
StrSplitter str_split(const char* text, size_t text_len, const char* split)
|
||||||
|
{
|
||||||
|
return (StrSplitter) {
|
||||||
|
.text = text,
|
||||||
|
.text_len = text_len,
|
||||||
|
.i = 0,
|
||||||
|
.split = split,
|
||||||
|
.split_len = strlen(split),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
StrSlice strsplit_next(StrSplitter* splitter)
|
||||||
|
{
|
||||||
|
for (size_t i = splitter->i; i < splitter->text_len; ++i) {
|
||||||
|
if (strncmp(&splitter->text[i], splitter->split, splitter->split_len)
|
||||||
|
== 0) {
|
||||||
|
const char* ptr = &splitter->text[splitter->i];
|
||||||
|
size_t len = i - splitter->i;
|
||||||
|
splitter->i = i + splitter->split_len;
|
||||||
|
return (StrSlice) { ptr, len };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (StrSlice) {
|
||||||
|
.ptr = &splitter->text[splitter->i],
|
||||||
|
.len = splitter->text_len - splitter->i,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void string_push_str(String* string, const char* str)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < strlen(str); ++i) {
|
||||||
|
string_push(string, str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* string_copy(const String* string)
|
||||||
|
{
|
||||||
|
char* copy = malloc(string->size + 1);
|
||||||
|
strncpy(copy, string->data, string->size);
|
||||||
|
copy[string->size] = '\0';
|
||||||
|
return copy;
|
||||||
|
}
|
27
slige/runtime/src/str_util.h
Normal file
27
slige/runtime/src/str_util.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "collection.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* ptr;
|
||||||
|
size_t len;
|
||||||
|
} StrSlice;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* text;
|
||||||
|
size_t text_len;
|
||||||
|
size_t i;
|
||||||
|
const char* split;
|
||||||
|
size_t split_len;
|
||||||
|
} StrSplitter;
|
||||||
|
|
||||||
|
StrSplitter str_split(const char* text, size_t text_len, const char* split);
|
||||||
|
StrSlice strsplit_next(StrSplitter* splitter);
|
||||||
|
|
||||||
|
DEFINE_VEC(char, String, string, 8)
|
||||||
|
|
||||||
|
void string_push_str(String* string, const char* str);
|
||||||
|
char* string_copy(const String* string);
|
||||||
|
|
||||||
|
DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)
|
Loading…
x
Reference in New Issue
Block a user