pebblisp/src/web.c

192 lines
5.0 KiB
C

#ifdef STANDALONE
#include <string.h>
#include <microhttpd.h>
#include <stdio.h>
#include "web.h"
#include "tokens.h"
#ifdef _MHD_FLAGS_ENUM
typedef enum MHD_Result HttpResult;
#else
typedef int HttpResult;
#endif
enum RouteType {
GET,
POST,
};
struct Route {
const char* path;
Object routeAction;
struct Environment* env;
enum RouteType type;
};
int requestDefinition = -1;
int routeCount = 0;
struct Route routes[10];
int addRoute(struct Route route)
{
routes[routeCount] = route;
routeCount += 1;
return 0;
}
int methodMatches(const char* method, struct Route* route)
{
switch (route->type) {
case GET:
return method[0] == 'G' || method[0] == 'g';
case POST:
return (method[0] == 'P' || method[0] == 'p') && (method[1] == 'O' || method[1] == 'o');
default:
return 0;
}
}
static HttpResult
add_query_param(void* queryParamsV, enum MHD_ValueKind kind, const char* key,
const char* value)
{
(void) kind; /* Unused. Silent compiler warning. */
Object* queryParams = queryParamsV;
Object pair = startList(nullTerminated(key));
Object parsed;
if (isDigit(value[0])) {
parsed = parseEval(value, NULL);
} else {
parsed = stringFromSlice(value, strlen(value));
}
nf_addToList(&pair, parsed);
nf_addToList(queryParams, pair);
return MHD_YES;
}
static HttpResult
answer_to_connection(void* cls, struct MHD_Connection* connection,
const char* url, const char* method,
const char* version, const char* upload_data,
size_t* upload_data_size, void** con_cls)
{
(void) cls; /* Unused. Silence compiler warning. */
(void) upload_data_size; /* Unused. Silence compiler warning. */
(void) con_cls; /* Unused. Silence compiler warning. */
char* page = NULL;
printf("%s :: %s URL: '%s'\n", method, version, url);
if (upload_data) {
printf("upload_data: %s\n", upload_data);
}
for (int i = 0; i < routeCount; i++) {
if (methodMatches(method, &routes[i]) && strcmp(url, routes[i].path) == 0) {
Object queryParams = listObject();
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, add_query_param, &queryParams);
char* password = NULL;
char* username = MHD_basic_auth_get_username_password(connection, &password);
Object usernameO = username ? nullTerminated(username) : stringFromSlice("", 0);
Object passwordO = password ? nullTerminated(password) : stringFromSlice("", 0);
MHD_free(username);
MHD_free(password);
Object res = structObject(requestDefinition);
res.structObject->fields[0] = queryParams;
res.structObject->fields[1] = usernameO;
res.structObject->fields[2] = passwordO;
Object route = cloneObject(routes[i].routeAction);
Object result = funcyEval(&route, &res, 1, routes[i].env);
cleanObject(&res);
page = result.string;
break;
}
}
if (!page) {
printf("no route found!\n");
page = "<html><body><h1>404, Dumbass.</h1></body></html>";
}
struct MHD_Response* response = MHD_create_response_from_buffer(
strlen(page), (void*) page, MHD_RESPMEM_PERSISTENT);
HttpResult ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
MHD_destroy_response(response);
return ret;
}
void initialize()
{
printf("Initializing...\n");
Object o = parseEval("(struct Request (queryParams username password))", global());
cleanObject(&o);
requestDefinition = getStructIndex("Request");
}
int start(int port)
{
static int initialized = 0;
if (!initialized) {
initialize();
initialized = 1;
}
struct MHD_Daemon* daemon = MHD_start_daemon(
MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
port, NULL, NULL, &answer_to_connection, NULL, MHD_OPTION_END);
if (NULL == daemon) {
return 1;
}
return 0;
}
void addRouteO(Object path, Object textFunc, struct Environment* env, enum RouteType type)
{
char* p = strdup(path.string);
struct Route r;
r.path = p;
r.routeAction = cloneObject(textFunc);
r.env = env;
r.type = type;
env->refs += 1;
addRoute(r);
}
Object addGetRoute(Object* params, int length, struct Environment* env)
{
Object path = params[0];
Object textFunc = params[1];
addRouteO(path, textFunc, env, GET);
return numberObject(1);
}
Object addPostRoute(Object* params, int length, struct Environment* env)
{
Object path = params[0];
Object textFunc = params[1];
addRouteO(path, textFunc, env, POST);
return numberObject(1);
}
Object startServer(Object* params, int length, struct Environment* env)
{
Object portObject = params[0];
int port = 8888;
if (length && portObject.type == TYPE_NUMBER) {
port = portObject.number;
}
return numberObject(start(port));
}
#endif // STANDALONE