mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
runtime: parse req, send res
This commit is contained in:
parent
aeeed02c35
commit
355c907135
@ -3,29 +3,33 @@
|
||||
#include <stddef.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 { \
|
||||
TYPE* data; \
|
||||
VEC_TYPE##T* data; \
|
||||
size_t capacity; \
|
||||
size_t size; \
|
||||
} VEC_TYPE; \
|
||||
\
|
||||
static inline int FN_PREFIX##_construct(VEC_TYPE* vec) \
|
||||
} VEC_TYPE;
|
||||
|
||||
#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; \
|
||||
TYPE* data = malloc(sizeof(TYPE) * capacity); \
|
||||
VEC_TYPE##T* data = malloc(sizeof(VEC_TYPE##T) * capacity); \
|
||||
if (!data) \
|
||||
return -1; \
|
||||
*vec = (VEC_TYPE) { data, capacity, .size = 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); \
|
||||
} \
|
||||
\
|
||||
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)); \
|
||||
if (!vec) \
|
||||
@ -36,13 +40,14 @@
|
||||
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); \
|
||||
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) { \
|
||||
size_t new_capacity = vec->capacity * 2; \
|
||||
@ -57,22 +62,30 @@
|
||||
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]; \
|
||||
} \
|
||||
\
|
||||
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) \
|
||||
{ \
|
||||
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]; \
|
||||
}
|
||||
|
||||
#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) \
|
||||
typedef struct { \
|
||||
TYPE* data; \
|
||||
@ -81,7 +94,7 @@
|
||||
size_t front; \
|
||||
} QUEUE_TYPE; \
|
||||
\
|
||||
static inline int FN_PREFIX##_construct( \
|
||||
MAYBE_UNUSED static inline int FN_PREFIX##_construct( \
|
||||
QUEUE_TYPE* queue, size_t capacity) \
|
||||
{ \
|
||||
*queue = (QUEUE_TYPE) { \
|
||||
@ -93,12 +106,12 @@
|
||||
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); \
|
||||
} \
|
||||
\
|
||||
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)); \
|
||||
if (!queue) \
|
||||
@ -109,13 +122,14 @@
|
||||
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); \
|
||||
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; \
|
||||
if (front >= queue->capacity) { \
|
||||
@ -129,14 +143,16 @@
|
||||
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 \
|
||||
? queue->front - queue->back \
|
||||
: (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) { \
|
||||
return -1; \
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "http_server.h"
|
||||
#include "http_server_internal.h"
|
||||
#include "str_util.h"
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.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");
|
||||
size_t length = strtoul(length_val, NULL, 10);
|
||||
body = calloc(length + 1, sizeof(char));
|
||||
strncpy(body, (char*)&buffer[body_idx], length);
|
||||
body[length] = '\0';
|
||||
}
|
||||
|
||||
HttpCtx handler_ctx = {
|
||||
@ -327,13 +330,13 @@ l0_return:
|
||||
static inline int parse_header(
|
||||
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);
|
||||
Strlitter first_splitter = string_split(first.ptr, first.len, " ");
|
||||
StrSlice method_str = split_next(&first_splitter);
|
||||
StrSlice path_str = split_next(&first_splitter);
|
||||
StrSlice version_str = split_next(&first_splitter);
|
||||
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);
|
||||
|
||||
if (strncmp(version_str.ptr, "HTTP/1.1", 8) != 0) {
|
||||
fprintf(stderr, "error: unrecognized http version '%.*s'\n",
|
||||
@ -363,7 +366,7 @@ static inline int parse_header(
|
||||
header_vec_construct(&headers);
|
||||
|
||||
while (headers.size < MAX_HEADERS_LEN) {
|
||||
StrSlice line = split_next(&splitter);
|
||||
StrSlice line = strsplit_next(&splitter);
|
||||
if (line.len == 0) {
|
||||
*body_idx = (size_t)&line.ptr[2] - (size_t)buf;
|
||||
break;
|
||||
@ -427,39 +430,3 @@ static inline const char* req_get_header(const Req* req, const char* key)
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
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);
|
||||
|
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 "json.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -6,28 +7,52 @@ typedef struct {
|
||||
int number;
|
||||
} 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)
|
||||
{
|
||||
Cx* cx = http_ctx_user_ctx(ctx);
|
||||
|
||||
char body[512];
|
||||
snprintf(body, 512 - 1,
|
||||
RESPOND_HTML(ctx, 200,
|
||||
"<!DOCTYPE html><html><head><meta "
|
||||
"charset=\"utf-8\"></head><body><h1>Number = %d</h1></body></html>",
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
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