Meta: Add files to repository
This commit is contained in:
parent
6d27d43268
commit
52cb92f587
120 changed files with 71820 additions and 0 deletions
394
Slon/Http/AdminServer.HC
Normal file
394
Slon/Http/AdminServer.HC
Normal file
|
@ -0,0 +1,394 @@
|
|||
U0 @slon_admin_html_form_from_json_object(SlonHttpSession* session, U8* buf, JsonObject* o)
|
||||
{
|
||||
if (!session || !buf || !o)
|
||||
return;
|
||||
|
||||
JsonKey* key = o->keys;
|
||||
String.Append(buf, "<table>");
|
||||
while (key) {
|
||||
switch (key->type) {
|
||||
case JSON_BOOLEAN:
|
||||
case JSON_STRING:
|
||||
case JSON_NUMBER:
|
||||
String.Append(buf, "<tr><td><label>%s</label></td><td>", key->name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (key->type) {
|
||||
case JSON_BOOLEAN:
|
||||
String.Append(buf, "<input name=%s type=checkbox %s>", key->name, @t(key->value, "checked", ""));
|
||||
break;
|
||||
case JSON_STRING:
|
||||
String.Append(buf, "<input name=%s type=text value=\"%s\" required>", key->name, key->value);
|
||||
break;
|
||||
case JSON_NUMBER:
|
||||
String.Append(buf, "<input name=%s type=text value=\"%d\" required>", key->name, ToI64(key->value));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
String.Append(buf, "</td></tr>");
|
||||
key = key->next;
|
||||
}
|
||||
String.Append(buf, "</table>");
|
||||
}
|
||||
|
||||
U0 @slon_admin_create_ap_actor(SlonHttpSession* session, JsonObject* acct)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
|
||||
JsonObject* actors = db->o("actors");
|
||||
U8* domain = db->o("instance")->@("uri");
|
||||
U8* username = acct->@("username");
|
||||
|
||||
JsonObject* actor = Json.Clone(SLON_DEFAULT_ACTOR_OBJECT);
|
||||
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s", domain, username);
|
||||
actor->set("id", scratch_buffer, JSON_STRING);
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s/following", domain, username);
|
||||
actor->set("following", scratch_buffer, JSON_STRING);
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s/followers", domain, username);
|
||||
actor->set("followers", scratch_buffer, JSON_STRING);
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s/inbox", domain, username);
|
||||
actor->set("inbox", scratch_buffer, JSON_STRING);
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s/outbox", domain, username);
|
||||
actor->set("outbox", scratch_buffer, JSON_STRING);
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s/collections/featured", domain, username);
|
||||
actor->set("featured", scratch_buffer, JSON_STRING);
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s/collections/tags", domain, username);
|
||||
actor->set("featuredTags", scratch_buffer, JSON_STRING);
|
||||
actor->set("preferredUsername", username, JSON_STRING);
|
||||
actor->set("name", acct->@("display_name"), JSON_STRING);
|
||||
actor->set("summary", acct->@("note"), JSON_STRING);
|
||||
|
||||
JsonObject* icon = Json.Parse("{\"type\":\"Image\"}");
|
||||
icon->set("url", acct->@("avatar"), JSON_STRING);
|
||||
actor->set("icon", icon, JSON_OBJECT);
|
||||
|
||||
StrPrint(scratch_buffer, "https://%s/@%s", domain, username);
|
||||
actor->set("url", scratch_buffer, JSON_STRING);
|
||||
actor->set("published", acct->@("created_at"), JSON_STRING);
|
||||
actor->set("attachment", acct->@("fields"), JSON_ARRAY);
|
||||
actor->set("accountId", acct->@("id"), JSON_STRING);
|
||||
|
||||
db->o("private_keys")->set(username, request_json->@("privatekey"), JSON_STRING);
|
||||
|
||||
JsonObject* publickey = Json.CreateObject();
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s#main-key", domain, username);
|
||||
publickey->set("id", scratch_buffer, JSON_STRING);
|
||||
StrPrint(scratch_buffer, "https://%s/users/%s", domain, username);
|
||||
publickey->set("owner", scratch_buffer, JSON_STRING);
|
||||
I64 x;
|
||||
publickey->set("publicKeyPem", @base64_decode(request_json->@("publickey"), &x), JSON_STRING);
|
||||
actor->set("publicKey", publickey, JSON_OBJECT);
|
||||
|
||||
actors->set(acct->@("username"), actor, JSON_OBJECT);
|
||||
@slon_db_save_to_disk;
|
||||
}
|
||||
|
||||
U0 @slon_admin_create_account(SlonHttpSession* session)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
|
||||
U8* id = @slon_api_generate_unique_id(session);
|
||||
U8* created_at = @slon_api_timestamp_from_cdate(session, Now);
|
||||
|
||||
JsonObject* acct = Json.CreateObject();
|
||||
JsonObject* source = Json.CreateObject();
|
||||
|
||||
acct->set("id", id, JSON_STRING);
|
||||
acct->set("created_at", created_at, JSON_STRING);
|
||||
acct->set("username", request_json->@("username"), JSON_STRING);
|
||||
acct->set("acct", request_json->@("username"), JSON_STRING);
|
||||
acct->set("display_name", request_json->@("display_name"), JSON_STRING);
|
||||
acct->set("email", request_json->@("email"), JSON_STRING);
|
||||
acct->set("note", request_json->@("bio"), JSON_STRING);
|
||||
acct->set("avatar", request_json->@("avatar"), JSON_STRING);
|
||||
acct->set("header", request_json->@("header"), JSON_STRING);
|
||||
acct->set("avatar_static", acct->@("avatar"), JSON_STRING);
|
||||
acct->set("header_static", acct->@("header"), JSON_STRING);
|
||||
acct->set("last_status_at", "0", JSON_STRING);
|
||||
|
||||
acct->set("followers_count", 0, JSON_NUMBER);
|
||||
acct->set("following_count", 0, JSON_NUMBER);
|
||||
acct->set("statuses_count", 0, JSON_NUMBER);
|
||||
|
||||
acct->set("locked", FALSE, JSON_BOOLEAN);
|
||||
acct->set("bot", FALSE, JSON_BOOLEAN);
|
||||
acct->set("discoverable", FALSE, JSON_BOOLEAN);
|
||||
acct->set("indexable", FALSE, JSON_BOOLEAN);
|
||||
acct->set("hide_collections", FALSE, JSON_BOOLEAN);
|
||||
|
||||
acct->set("emojis", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
|
||||
acct->set("fields", SLON_EMPTY_JSON_ARRAY, JSON_ARRAY);
|
||||
|
||||
source->set("privacy", "public", JSON_STRING);
|
||||
source->set("sensitive", FALSE, JSON_BOOLEAN);
|
||||
source->set("language", "", JSON_STRING);
|
||||
source->set("note", acct->@("note"), JSON_STRING);
|
||||
source->set("fields", acct->@("fields"), JSON_ARRAY);
|
||||
source->set("follow_requests_count", 0, JSON_NUMBER);
|
||||
|
||||
acct->set("source", source, JSON_OBJECT);
|
||||
|
||||
StrPrint(scratch_buffer, "https://%s/@%s", db->o("instance")->@("uri"), acct->@("username"));
|
||||
acct->set("url", scratch_buffer, JSON_STRING);
|
||||
|
||||
db->a("accounts")->append(Json.CreateItem(acct, JSON_OBJECT));
|
||||
db->o("statuses")->set(acct->@("id"), Json.CreateArray(), JSON_ARRAY);
|
||||
@slon_admin_create_ap_actor(session, acct);
|
||||
|
||||
@slon_db_instance_update_user_count;
|
||||
@slon_db_save_to_disk;
|
||||
|
||||
@slon_free(session, created_at);
|
||||
@slon_free(session, id);
|
||||
}
|
||||
|
||||
U0 @slon_admin_settings_accounts_new_get(SlonHttpSession* session, U8* buf)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn scratch_buffer, request_json;
|
||||
String.Append(buf, "settings/accounts/new");
|
||||
String.Append(buf, "<br><br><form action=/settings/accounts/new/save>");
|
||||
@slon_admin_html_form_from_json_object(session, buf, SLON_DEFAULT_ACCT_OBJECT);
|
||||
String.Append(buf, "<br><br><input id=save type=submit value=Save></form>");
|
||||
}
|
||||
|
||||
U0 @slon_admin_settings_accounts_get(SlonHttpSession* session, U8* buf)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn scratch_buffer, request_json;
|
||||
String.Append(buf, "settings/accounts");
|
||||
String.Append(buf, "<br><br><table><tr><th>id</th><th>username</th></tr>");
|
||||
JsonArray* arr = db->a("accounts");
|
||||
JsonObject* acct;
|
||||
I64 i;
|
||||
for (i = 0; i < arr->length; i++) {
|
||||
acct = arr->@(i);
|
||||
if (acct) {
|
||||
String.Append(buf, "<tr><td>%s</td><td>%s</td></tr>", acct->@("id"), acct->@("username"));
|
||||
}
|
||||
}
|
||||
String.Append(buf, "</table><br><br><button onclick=\"window.location='/settings/accounts/new'\">New</button>");
|
||||
}
|
||||
|
||||
U0 @slon_admin_settings_apps_get(SlonHttpSession* session, U8* buf)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn scratch_buffer, request_json;
|
||||
String.Append(buf, "settings/apps");
|
||||
String.Append(buf, "<br><br>");
|
||||
U8* tmp = Json.Stringify(db->o("apps"));
|
||||
String.Append(buf, tmp);
|
||||
Free(tmp);
|
||||
}
|
||||
|
||||
U0 @slon_admin_settings_oauth_get(SlonHttpSession* session, U8* buf)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn scratch_buffer, request_json;
|
||||
String.Append(buf, "settings/oauth");
|
||||
String.Append(buf, "<br><br>");
|
||||
U8* tmp = Json.Stringify(db->o("oauth"));
|
||||
String.Append(buf, tmp);
|
||||
Free(tmp);
|
||||
}
|
||||
|
||||
U0 @slon_admin_settings_instance_save_get(SlonHttpSession* session, U8* buf)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn scratch_buffer;
|
||||
|
||||
JsonObject* instance = db->o("instance");
|
||||
instance->set("uri", request_json->@("uri"));
|
||||
instance->set("title", request_json->@("title"));
|
||||
instance->set("short_description", request_json->@("short_description"));
|
||||
instance->set("description", request_json->@("description"));
|
||||
instance->set("email", request_json->@("email"));
|
||||
instance->set("version", request_json->@("version"));
|
||||
if (!request_json->@("registrations")) {
|
||||
instance->set("registrations", FALSE);
|
||||
} else {
|
||||
instance->set("registrations", !StrICmp("on", request_json->@("registrations")));
|
||||
}
|
||||
String.Append(buf, "<script>window.location='/settings/instance';</script>");
|
||||
}
|
||||
|
||||
U0 @slon_admin_settings_instance_get(SlonHttpSession* session, U8* buf)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn scratch_buffer, request_json;
|
||||
|
||||
String.Append(buf, "settings/instance");
|
||||
String.Append(buf, "<br><br><form action=/settings/instance/save>");
|
||||
@slon_admin_html_form_from_json_object(session, buf, db->o("instance"));
|
||||
String.Append(buf, "<br><br><input type=submit value=Save></form>");
|
||||
}
|
||||
|
||||
U0 @slon_admin_new_account(SlonHttpSession* session)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
|
||||
if (db->o("actors")->@(request_json->@("username"))) {
|
||||
StrPrint(scratch_buffer, "{\"error\":\"account already exists\"}");
|
||||
@slon_http_set_content_type(session, "application/json");
|
||||
@slon_http_send(session, scratch_buffer, StrLen(scratch_buffer));
|
||||
} else {
|
||||
@slon_admin_create_account(session);
|
||||
@slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
|
||||
}
|
||||
}
|
||||
|
||||
U0 @slon_admin_manage_accounts(SlonHttpSession* session)
|
||||
{
|
||||
@slon_http_send_json(session, db->a("accounts"));
|
||||
}
|
||||
|
||||
U0 @slon_admin_info_stats(SlonHttpSession* session)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn request_json;
|
||||
|
||||
StrPrint(scratch_buffer, "{");
|
||||
String.Append(scratch_buffer, "\"uptime\":\"%d\"", cnts.jiffies);
|
||||
String.Append(scratch_buffer, "}");
|
||||
|
||||
@slon_http_set_content_type(session, "application/json");
|
||||
@slon_http_send(session, scratch_buffer, StrLen(scratch_buffer));
|
||||
}
|
||||
|
||||
U0 @slon_admin_server_get(SlonHttpSession* session)
|
||||
{
|
||||
if (!db->@("setup")) {
|
||||
if (StrICmp("/", @slon_http_request_path(session))) {
|
||||
@slon_http_set_status_code(session, 302);
|
||||
@slon_http_set_header(session, "Location", "/");
|
||||
} else {
|
||||
@slon_http_send_html_file(session, "M:/Slon/Static/html/admin/setup_instance.html");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StrICmp("/info/stats", @slon_http_request_path(session))) {
|
||||
@slon_admin_info_stats(session);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StrICmp("/manage/accounts", @slon_http_request_path(session))) {
|
||||
@slon_admin_manage_accounts(session);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StrICmp("/", @slon_http_request_path(session))) {
|
||||
@slon_http_send_html_file(session, "M:/Slon/Static/html/admin/main.html");
|
||||
return;
|
||||
}
|
||||
|
||||
@slon_http_set_status_code(session, 404);
|
||||
}
|
||||
|
||||
U0 @slon_admin_setup_instance(SlonHttpSession* session)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn scratch_buffer;
|
||||
|
||||
JsonObject* instance = db->o("instance");
|
||||
instance->set("uri", request_json->@("uri"));
|
||||
instance->set("title", request_json->@("title"));
|
||||
instance->set("short_description", request_json->@("description"));
|
||||
instance->set("description", request_json->@("description"));
|
||||
instance->set("email", request_json->@("email"));
|
||||
instance->set("registrations", request_json->@("registrations"));
|
||||
@slon_db_save_to_disk;
|
||||
db->set("setup", TRUE);
|
||||
|
||||
@slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
|
||||
}
|
||||
|
||||
U0 @slon_admin_server_post(SlonHttpSession* session)
|
||||
{
|
||||
if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) {
|
||||
@slon_http_parse_request_as_json(session);
|
||||
}
|
||||
|
||||
if (!StrICmp("/setup/instance", @slon_http_request_path(session))) {
|
||||
@slon_admin_setup_instance(session);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StrICmp("/new/account", @slon_http_request_path(session))) {
|
||||
@slon_admin_new_account(session);
|
||||
return;
|
||||
}
|
||||
|
||||
@slon_http_set_status_code(session, 404);
|
||||
}
|
||||
|
||||
U0 @slon_admin_http_handle_get_request(SlonHttpSession* session)
|
||||
{
|
||||
if (@slon_http_request_has_query_string(session)) {
|
||||
@slon_http_parse_query_string(session);
|
||||
}
|
||||
@slon_admin_server_get(session);
|
||||
}
|
||||
|
||||
U0 @slon_admin_http_handle_post_request(SlonHttpSession* session)
|
||||
{
|
||||
@slon_admin_server_post(session);
|
||||
}
|
||||
|
||||
U0 @slon_admin_http_handle_request(SlonHttpSession* session)
|
||||
{
|
||||
switch (@slon_http_request_verb(session)) {
|
||||
case SLON_HTTP_VERB_GET:
|
||||
@slon_admin_http_handle_get_request(session);
|
||||
break;
|
||||
case SLON_HTTP_VERB_POST:
|
||||
@slon_admin_http_handle_post_request(session);
|
||||
break;
|
||||
default:
|
||||
@slon_http_set_status_code(session, 405);
|
||||
}
|
||||
}
|
||||
|
||||
U0 @slon_admin_http_task(TcpSocket* s)
|
||||
{
|
||||
// Bail if we can't acquire socket for some reason
|
||||
if (!@tcp_socket_accept(s))
|
||||
return;
|
||||
|
||||
// Init session
|
||||
SlonHttpSession* session = @slon_http_init_session(s);
|
||||
|
||||
// Parse headers if they are available
|
||||
while (!@slon_http_request_headers_have_been_parsed(session)) {
|
||||
@slon_http_receive(session);
|
||||
|
||||
// Handle malformed requests (anything less than "GET / HTTP/1.0\r\n\r\n" is probably a bad request)
|
||||
if (session->request->buffer->size < 18) {
|
||||
@slon_http_set_status_code(session, 400);
|
||||
goto slon_admin_http_task_send_response;
|
||||
}
|
||||
|
||||
@slon_http_try_parse_request_headers(session);
|
||||
}
|
||||
|
||||
// If we have a content-length header, consume until we receive all the data, then set request->data pointer and size
|
||||
if (StrLen(@slon_http_request_header(session, "content-length"))) {
|
||||
I64 content_length = Str2I64(@slon_http_request_header(session, "content-length"));
|
||||
while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length)
|
||||
@slon_http_receive(session);
|
||||
}
|
||||
|
||||
@slon_admin_http_handle_request(session);
|
||||
|
||||
slon_admin_http_task_send_response:
|
||||
@slon_http_send_response(session);
|
||||
@slon_http_free_session(session);
|
||||
s->close();
|
||||
}
|
||||
|
||||
Adam("U0 @spawn_slon_admin_http_task(TcpSocket *s){Spawn(%d, s, \"SlonAdminHttpTask\");};\n", &@slon_admin_http_task);
|
||||
@tcp_socket_bind(9000, "@spawn_slon_admin_http_task");
|
232
Slon/Http/LocalServer.HC
Normal file
232
Slon/Http/LocalServer.HC
Normal file
|
@ -0,0 +1,232 @@
|
|||
U0 @slon_local_server_set_mime_type(SlonHttpSession* session, U8* filepath)
|
||||
{
|
||||
// FIXME: Do this programmatically like the Jakt version, this is awful
|
||||
if (String.EndsWith(".html", filepath)) {
|
||||
@slon_http_set_content_type(session, "text/html");
|
||||
return;
|
||||
}
|
||||
if (String.EndsWith(".txt", filepath)) {
|
||||
@slon_http_set_content_type(session, "text/plain");
|
||||
return;
|
||||
}
|
||||
if (String.EndsWith(".css", filepath)) {
|
||||
@slon_http_set_content_type(session, "text/css");
|
||||
return;
|
||||
}
|
||||
if (String.EndsWith(".js", filepath)) {
|
||||
@slon_http_set_content_type(session, "text/javascript");
|
||||
return;
|
||||
}
|
||||
if (String.EndsWith(".json", filepath)) {
|
||||
@slon_http_set_content_type(session, "application/json");
|
||||
return;
|
||||
}
|
||||
if (String.EndsWith(".gif", filepath)) {
|
||||
@slon_http_set_content_type(session, "image/gif");
|
||||
return;
|
||||
}
|
||||
if (String.EndsWith(".png", filepath)) {
|
||||
@slon_http_set_content_type(session, "image/png");
|
||||
return;
|
||||
}
|
||||
if (String.EndsWith(".jpeg", filepath) || String.EndsWith(".jpg", filepath)) {
|
||||
@slon_http_set_content_type(session, "image/jpeg");
|
||||
return;
|
||||
}
|
||||
@slon_http_set_content_type(session, "application/octet-stream");
|
||||
}
|
||||
|
||||
U0 @slon_local_server_send_file(SlonHttpSession* session, U8* filepath)
|
||||
{
|
||||
@slon_local_server_set_mime_type(session, filepath);
|
||||
@slon_http_send_file(session, filepath);
|
||||
}
|
||||
|
||||
U0 @slon_local_server_directory_listing(SlonHttpSession* session, U8* path)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn request_json;
|
||||
|
||||
U8* html = @slon_calloc(session, 1048576);
|
||||
|
||||
String.Append(html, "<html><head><title>Index of ");
|
||||
String.Append(html, path);
|
||||
String.Append(html, "</title><style type=text/css>.img-back{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAMAAAD3n0w0AAAAElBMVEX////M//+ZmZlmZmYzMzMAAACei5rnAAAAAnRSTlP/AOW3MEoAAABVSURBVHjabdFBCsBACENR45j7X7kQtC0T//KRjRhYevGgyjBL+VLZUtlS2VItS1AI1QQONgNZHCSUZJAc+ZB3sViFGzPcDmxZqdsvgRB/aJRu73D0HuO2BJfZn2SOAAAAAElFTkSuQmCC)} .img-folder{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAMAAAD3n0w0AAAAElBMVEX/////zJnM//+ZZjMzMzMAAADCEvqoAAAAA3RSTlP//wDXyg1BAAAASElEQVR42s3KAQbAQAxE0W4m//5XboesdihQ6A/ES4566TsyPZE1caNtwmFE22bBuDTtG8ZMaoyZ8Z+fijEWytpYdEZfWGRdJzEsA9OaTRTxAAAAAElFTkSuQmCC)} .img-file{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAMAAAD3n0w0AAAAD1BMVEX////M//+ZmZkzMzMAAABVsTOVAAAAAnRSTlP/AOW3MEoAAAA6SURBVHja3cjHAcAwEIRATqj/lp3jWhUYfoPPag+5EkeII8QRYmB3O3ArENrSB0k8+ivaXrliVY+qZm7SAaxLXnOsAAAAAElFTkSuQmCC)}</style></head><body><h1>Index of ");
|
||||
String.Append(html, path);
|
||||
String.Append(html, "</h1><table>");
|
||||
String.Append(html, "<tr><th></th><th align=left style=padding-right:16px>Name</th><th align=left>Last modified</th><th align=right>Size</th></tr>");
|
||||
String.Append(html, "<tr><th colspan=4><hr></th></tr>");
|
||||
StrPrint(scratch_buffer, "A:%s*", path);
|
||||
CDirEntry* files = FilesFind(scratch_buffer);
|
||||
CDirEntry* de = files->next;
|
||||
CDateStruct ds;
|
||||
while (de) {
|
||||
String.Append(html, "<tr><td><a href=\"");
|
||||
String.Append(html, de->name);
|
||||
String.Append(html, "\">");
|
||||
if (!StrICmp("..", de->name)) {
|
||||
String.Append(html, "<img class=img-back alt=Back>");
|
||||
} else {
|
||||
if (de->attr & RS_ATTR_DIR) {
|
||||
String.Append(html, "<img class=img-folder alt=Folder>");
|
||||
} else {
|
||||
String.Append(html, "<img class=img-file alt=File>");
|
||||
}
|
||||
}
|
||||
String.Append(html, "</a></td>");
|
||||
String.Append(html, "<td><a href=\"");
|
||||
String.Append(html, de->name);
|
||||
if (de->attr & RS_ATTR_DIR) {
|
||||
String.Append(html, "/");
|
||||
}
|
||||
String.Append(html, "\">");
|
||||
if (!StrICmp("..", de->name)) {
|
||||
String.Append(html, "Parent Directory");
|
||||
} else {
|
||||
String.Append(html, de->name);
|
||||
}
|
||||
String.Append(html, "</a></td><td align=right>");
|
||||
Date2Struct(&ds, de->datetime);
|
||||
String.Append(html, "%02d-%03tZ-%04d %02d:%02d", ds.day_of_mon, ds.mon - 1, "ST_MONTHS", ds.year, ds.hour, ds.min);
|
||||
String.Append(html, " ");
|
||||
String.Append(html, "</td><td align=right>");
|
||||
if (de->attr & RS_ATTR_DIR) {
|
||||
String.Append(html, " - ");
|
||||
} else {
|
||||
String.Append(html, "%d", de->size);
|
||||
}
|
||||
|
||||
String.Append(html, "</td></tr>");
|
||||
de = de->next;
|
||||
}
|
||||
DirTreeDel(files);
|
||||
|
||||
String.Append(html, "<tr><th colspan=4><hr></th></tr>");
|
||||
String.Append(html, "</table>");
|
||||
String.Append(html, "<address>Slon static file webserver for (TempleOS) Server</address>");
|
||||
String.Append(html, "</body></html>");
|
||||
|
||||
@slon_http_set_content_type(session, "text/html");
|
||||
@slon_http_send(session, html, StrLen(html));
|
||||
@slon_free(session, html);
|
||||
}
|
||||
|
||||
U0 @slon_local_server_not_found(SlonHttpSession* session)
|
||||
{
|
||||
@slon_http_set_status_code(session, 404);
|
||||
@slon_http_set_content_type(session, "text/html");
|
||||
@slon_http_send(session, "<h2>404 Not Found</h2>", 22);
|
||||
}
|
||||
|
||||
U0 @slon_local_server_get(SlonHttpSession* session)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn request_json;
|
||||
|
||||
U8* path = @slon_http_request_path(session);
|
||||
|
||||
if (!path || !StrLen(path) || StrFind(":", path) > 0) {
|
||||
@slon_local_server_not_found(session);
|
||||
return;
|
||||
}
|
||||
|
||||
if (path[0] == '/' && StrLen(path) == 1) {
|
||||
// Handle root path
|
||||
if (FileFind("A:/index.html")) {
|
||||
@slon_local_server_send_file(session, "A:/index.html");
|
||||
} else {
|
||||
@slon_local_server_directory_listing(session, "/");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (String.EndsWith("/", path)) {
|
||||
StrPrint(scratch_buffer, "A:%sindex.html", path);
|
||||
if (FileFind(scratch_buffer)) {
|
||||
@slon_local_server_send_file(session, scratch_buffer);
|
||||
} else {
|
||||
StrPrint(scratch_buffer, "A:%s", path);
|
||||
scratch_buffer[StrLen(scratch_buffer) - 1] = NULL;
|
||||
if (IsDir(scratch_buffer)) {
|
||||
@slon_local_server_directory_listing(session, path);
|
||||
} else {
|
||||
@slon_local_server_not_found(session);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
StrPrint(scratch_buffer, "A:%s", path);
|
||||
if (!FileFind(scratch_buffer)) {
|
||||
@slon_local_server_not_found(session);
|
||||
return;
|
||||
} else {
|
||||
if (IsDir(scratch_buffer)) {
|
||||
@slon_http_set_status_code(session, 301);
|
||||
StrPrint(scratch_buffer, "%s/", path);
|
||||
@slon_http_set_header(session, "Location", scratch_buffer);
|
||||
} else {
|
||||
@slon_local_server_send_file(session, scratch_buffer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// shouldn't get here :^)
|
||||
@slon_http_set_status_code(session, 400);
|
||||
}
|
||||
|
||||
U0 @slon_local_http_handle_get_request(SlonHttpSession* session)
|
||||
{
|
||||
@slon_local_server_get(session);
|
||||
}
|
||||
|
||||
U0 @slon_local_http_handle_request(SlonHttpSession* session)
|
||||
{
|
||||
switch (@slon_http_request_verb(session)) {
|
||||
case SLON_HTTP_VERB_GET:
|
||||
@slon_local_http_handle_get_request(session);
|
||||
break;
|
||||
default:
|
||||
@slon_http_set_status_code(session, 405);
|
||||
}
|
||||
}
|
||||
|
||||
U0 @slon_local_http_task(TcpSocket* s)
|
||||
{
|
||||
// Bail if we can't acquire socket for some reason
|
||||
if (!@tcp_socket_accept(s))
|
||||
return;
|
||||
|
||||
// Init session
|
||||
SlonHttpSession* session = @slon_http_init_session(s);
|
||||
|
||||
// Parse headers if they are available
|
||||
while (!@slon_http_request_headers_have_been_parsed(session)) {
|
||||
@slon_http_receive(session);
|
||||
|
||||
// Handle malformed requests (anything less than "GET / HTTP/1.0\r\n\r\n" is probably a bad request)
|
||||
if (session->request->buffer->size < 18) {
|
||||
@slon_http_set_status_code(session, 400);
|
||||
goto slon_local_http_task_send_response;
|
||||
}
|
||||
|
||||
@slon_http_try_parse_request_headers(session);
|
||||
}
|
||||
|
||||
// If we have a content-length header, consume until we receive all the data, then set request->data pointer and size
|
||||
if (StrLen(@slon_http_request_header(session, "content-length"))) {
|
||||
I64 content_length = Str2I64(@slon_http_request_header(session, "content-length"));
|
||||
while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length)
|
||||
@slon_http_receive(session);
|
||||
}
|
||||
|
||||
@slon_local_http_handle_request(session);
|
||||
|
||||
slon_local_http_task_send_response:
|
||||
@slon_http_send_response(session);
|
||||
@slon_http_free_session(session);
|
||||
s->close();
|
||||
}
|
||||
|
||||
Adam("U0 @spawn_slon_local_http_task(TcpSocket *s){Spawn(%d, s, \"SlonLocalHttpTask\");};\n", &@slon_local_http_task);
|
||||
@tcp_socket_bind(8000, "@spawn_slon_local_http_task");
|
542
Slon/Http/Server.HC
Normal file
542
Slon/Http/Server.HC
Normal file
|
@ -0,0 +1,542 @@
|
|||
SlonHttpBuffer* @slon_http_init_buffer(SlonHttpSession* session)
|
||||
{
|
||||
SlonHttpBuffer* buffer = @slon_calloc(session, sizeof(SlonHttpBuffer));
|
||||
buffer->data = @slon_calloc(session, SLON_HTTP_BUFFER_SIZE);
|
||||
buffer->capacity = SLON_HTTP_BUFFER_SIZE;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
U0 @slon_http_free_response(SlonHttpSession* session, SlonHttpResponse* response)
|
||||
{
|
||||
// FIXME: Free headers JsonObject
|
||||
if (response) {
|
||||
if (response->buffer && response->buffer->data) {
|
||||
@slon_free(session, response->buffer->data);
|
||||
@slon_free(session, response->buffer);
|
||||
}
|
||||
@slon_free(session, response);
|
||||
}
|
||||
}
|
||||
|
||||
U0 @slon_http_free_request(SlonHttpSession* session, SlonHttpRequest* request)
|
||||
{
|
||||
// FIXME: Free headers JsonObject
|
||||
if (request) {
|
||||
if (request->buffer && request->buffer->data) {
|
||||
@slon_free(session, request->buffer->data);
|
||||
@slon_free(session, request->buffer);
|
||||
}
|
||||
if (request->verb)
|
||||
@slon_free(session, session->request->verb);
|
||||
if (request->raw_path)
|
||||
@slon_free(session, session->request->raw_path);
|
||||
if (request->path)
|
||||
@slon_free(session, session->request->path);
|
||||
@slon_free(session, request);
|
||||
}
|
||||
}
|
||||
|
||||
U0 @slon_http_free_session(SlonHttpSession* session)
|
||||
{
|
||||
if (!session)
|
||||
return;
|
||||
@slon_http_free_response(session, session->response);
|
||||
@slon_http_free_request(session, session->request);
|
||||
I64 bytes_used = session->bytes_used - MSize2(session);
|
||||
Free(session);
|
||||
if (bytes_used) {
|
||||
AdamLog("*** Session leaked %d bytes of memory ***\n", bytes_used);
|
||||
}
|
||||
}
|
||||
|
||||
SlonHttpRequest* @slon_http_init_request(SlonHttpSession* session)
|
||||
{
|
||||
SlonHttpRequest* request = @slon_calloc(session, sizeof(SlonHttpRequest));
|
||||
request->buffer = @slon_http_init_buffer(session);
|
||||
request->headers = Json.CreateObject();
|
||||
return request;
|
||||
}
|
||||
|
||||
SlonHttpResponse* @slon_http_init_response(SlonHttpSession* session)
|
||||
{
|
||||
SlonHttpResponse* response = @slon_calloc(session, sizeof(SlonHttpResponse));
|
||||
response->buffer = @slon_http_init_buffer(session);
|
||||
response->headers = Json.CreateObject();
|
||||
return response;
|
||||
}
|
||||
|
||||
SlonHttpSession* @slon_http_init_session(TcpSocket* s)
|
||||
{
|
||||
SlonHttpSession* session = CAlloc(sizeof(SlonHttpSession), adam_task);
|
||||
session->bytes_used = MSize2(session);
|
||||
session->s = s;
|
||||
session->request = @slon_http_init_request(session);
|
||||
session->response = @slon_http_init_response(session);
|
||||
return session;
|
||||
}
|
||||
|
||||
U0 @slon_http_receive(SlonHttpSession* session)
|
||||
{
|
||||
// FIXME: grow the buffer
|
||||
SlonHttpBuffer* buffer = session->request->buffer;
|
||||
I64 chunk_size = @tcp_socket_receive(session->s, buffer->data + buffer->size, 65536);
|
||||
buffer->size += chunk_size;
|
||||
}
|
||||
|
||||
Bool @slon_http_request_headers_have_been_parsed(SlonHttpSession* session)
|
||||
{
|
||||
return session->request->headers_have_been_parsed;
|
||||
}
|
||||
|
||||
U0 @slon_http_buffer_append(SlonHttpBuffer* buffer, U8* src, I64 size)
|
||||
{
|
||||
if (!buffer || !src || !size)
|
||||
return;
|
||||
MemCpy(buffer->data + buffer->size, src, size);
|
||||
buffer->size += size;
|
||||
}
|
||||
|
||||
U0 @slon_http_buffer_append_string(SlonHttpBuffer* buffer, U8* str)
|
||||
{
|
||||
@slon_http_buffer_append(buffer, str, StrLen(str));
|
||||
}
|
||||
|
||||
U0 @slon_http_send_response(SlonHttpSession* session)
|
||||
{
|
||||
SlonHttpBuffer* buffer = session->response->buffer;
|
||||
U8 scratch_buffer[256][4];
|
||||
|
||||
StrPrint(scratch_buffer[0], "%d", session->response->status_code);
|
||||
StrPrint(scratch_buffer[1], "HTTP/1.0 %d %s\r\n", session->response->status_code, Json.Get(SLON_HTTP_STATUS_CODES, scratch_buffer[0]));
|
||||
@slon_http_buffer_append_string(buffer, scratch_buffer[1]);
|
||||
|
||||
JsonKey* key = session->response->headers->keys;
|
||||
while (key) {
|
||||
StrPrint(scratch_buffer[0], "%s: %s\r\n", key->name, key->value);
|
||||
@slon_http_buffer_append_string(buffer, scratch_buffer[0]);
|
||||
key = key->next;
|
||||
}
|
||||
|
||||
StrPrint(scratch_buffer[0], "content-length: %d\r\n", session->response->size);
|
||||
@slon_http_buffer_append_string(buffer, scratch_buffer[0]);
|
||||
|
||||
StrCpy(scratch_buffer[0], "pragma: no-cache\r\n\r\n");
|
||||
@slon_http_buffer_append_string(buffer, scratch_buffer[0]);
|
||||
|
||||
if (session->response->data && session->response->size) {
|
||||
@slon_http_buffer_append(buffer, session->response->data, session->response->size);
|
||||
@slon_free(session, session->response->data);
|
||||
}
|
||||
|
||||
@tcp_socket_send(session->s, buffer->data, buffer->size);
|
||||
}
|
||||
|
||||
U0 @slon_http_rstrip_char_from_string(U8* str, I64 ch)
|
||||
{
|
||||
while (str[StrLen(str) - 1] == ch)
|
||||
str[StrLen(str) - 1] = NULL;
|
||||
}
|
||||
|
||||
U0 @slon_http_try_parse_request_headers(SlonHttpSession* session)
|
||||
{
|
||||
SlonHttpBuffer* buffer = session->request->buffer;
|
||||
I64 i = 0;
|
||||
// Do we have headers yet? let's find out
|
||||
while (i < buffer->size) {
|
||||
if (!MemCmp(buffer->data + i, "\r\n\r\n", 4)) {
|
||||
i += 4;
|
||||
goto slon_http_parse_request_headers;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return;
|
||||
|
||||
slon_http_parse_request_headers:
|
||||
// Set pointer for request content
|
||||
session->request->data = buffer->data + i;
|
||||
|
||||
// We have headers, let's parse them
|
||||
U8* raw_headers = @slon_calloc(session, i);
|
||||
MemCpy(raw_headers, buffer->data, i - 4);
|
||||
|
||||
I64 raw_header_lines_count = 0;
|
||||
U8** raw_header_lines = String.Split(raw_headers, '\n', &raw_header_lines_count);
|
||||
|
||||
if (!raw_header_lines_count) {
|
||||
// FIXME: Handle this
|
||||
}
|
||||
|
||||
I64 request_first_line_segments_count = 0;
|
||||
U8** request_first_line_segments = String.Split(raw_header_lines[0], ' ', &request_first_line_segments_count);
|
||||
|
||||
if (request_first_line_segments_count < 2) {
|
||||
// FIXME: Handle this
|
||||
}
|
||||
|
||||
session->request->verb = @slon_strnew(session, request_first_line_segments[0]);
|
||||
session->request->raw_path = @slon_strnew(session, request_first_line_segments[1]);
|
||||
if (StrFind("?", session->request->raw_path)) {
|
||||
session->request->path = @slon_strnew(session, session->request->raw_path);
|
||||
*(StrFind("?", session->request->path)) = NULL;
|
||||
} else {
|
||||
session->request->path = @slon_strnew(session, session->request->raw_path);
|
||||
}
|
||||
|
||||
U8* key;
|
||||
U8* value;
|
||||
|
||||
for (i = 1; i < raw_header_lines_count; i++) {
|
||||
key = NULL;
|
||||
value = NULL;
|
||||
if (StrFind(": ", raw_header_lines[i])) {
|
||||
value = StrFind(": ", raw_header_lines[i]) + 2;
|
||||
@slon_http_rstrip_char_from_string(value, '\r');
|
||||
*(StrFind(": ", raw_header_lines[i])) = NULL;
|
||||
key = raw_header_lines[i];
|
||||
Json.Set(session->request->headers, key, value, JSON_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
@slon_free(session, raw_headers);
|
||||
session->request->headers_have_been_parsed = TRUE;
|
||||
}
|
||||
|
||||
U0 @slon_http_authorize(SlonHttpSession* session)
|
||||
{
|
||||
if (StrLen(@slon_http_request_header(session, "authorization"))) {
|
||||
U8* access_token = StrFind(" ", @slon_http_request_header(session, "authorization")) + 1;
|
||||
session->auth = db->o("oauth")->o("tokens")->@(access_token);
|
||||
}
|
||||
}
|
||||
|
||||
U0 @slon_http_debug_print_request(SlonHttpSession* session, Bool show_headers = FALSE)
|
||||
{
|
||||
AdamLog("[httpd] %d => request: %s %s\n", session->s, session->request->verb, session->request->raw_path);
|
||||
if (show_headers) {
|
||||
U8* headers_stringified = Json.Stringify(session->request->headers);
|
||||
AdamLog("[httpd] %d => headers: %s\n", session->s, headers_stringified);
|
||||
Free(headers_stringified);
|
||||
//@slon_free(session, headers_stringified);
|
||||
}
|
||||
}
|
||||
|
||||
U0 @slon_http_debug_print_response(SlonHttpSession* session, Bool show_headers = FALSE)
|
||||
{
|
||||
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
|
||||
no_warn request_json;
|
||||
|
||||
StrPrint(scratch_buffer, "%d", session->response->status_code);
|
||||
AdamLog("[httpd] %d <= response: %d %s\n", session->s, session->response->status_code, Json.Get(SLON_HTTP_STATUS_CODES, scratch_buffer));
|
||||
if (show_headers) {
|
||||
U8* headers_stringified = Json.Stringify(session->response->headers);
|
||||
AdamLog("[httpd] %d <= headers: %s\n", session->s, headers_stringified);
|
||||
Free(headers_stringified);
|
||||
//@slon_free(session, headers_stringified);
|
||||
}
|
||||
if (session->response->data) {
|
||||
AdamLog("data: %s\n", session->response->data);
|
||||
}
|
||||
}
|
||||
|
||||
U8* @slon_http_json_string_from_form_urlencoded_string(SlonHttpSession* session, U8* form_urlencoded_string)
|
||||
{
|
||||
// FIXME: Implement arrays, objects per https://jsonapi.org/format/#fetching
|
||||
U8* json_string = @slon_calloc(session, StrLen(form_urlencoded_string) * 2);
|
||||
String.Append(json_string, "{");
|
||||
U8* form_urlencoded_string_copy = @slon_strnew(session, form_urlencoded_string);
|
||||
I64 raw_values_count = 0;
|
||||
U8** raw_values = String.Split(form_urlencoded_string_copy, '&', &raw_values_count);
|
||||
I64 i = 0;
|
||||
U8* key;
|
||||
U8* value;
|
||||
for (i = 0; i < raw_values_count; i++) {
|
||||
value = StrFind("=", raw_values[i]) + 1;
|
||||
*(StrFind("=", raw_values[i])) = NULL;
|
||||
key = raw_values[i];
|
||||
U8* decoded_value = @slon_http_decode_urlencoded_string(session, value);
|
||||
String.Append(json_string, "\"%s\":\"%s\"", key, decoded_value);
|
||||
@slon_free(session, decoded_value);
|
||||
if (i < raw_values_count - 1) {
|
||||
String.Append(json_string, ",");
|
||||
}
|
||||
}
|
||||
String.Append(json_string, "}");
|
||||
@slon_free(session, form_urlencoded_string_copy);
|
||||
return json_string;
|
||||
}
|
||||
|
||||
U8* @slon_http_json_string_from_multipart_form_data(SlonHttpSession* session, U8* multipart_form_data)
|
||||
{
|
||||
U8* json_string = @slon_calloc(session, StrLen(multipart_form_data) * 2);
|
||||
String.Append(json_string, "{");
|
||||
U8* multipart_form_data_copy = @slon_strnew(session, multipart_form_data);
|
||||
|
||||
U8* boundary = StrFind("boundary=", @slon_http_request_header(session, "content-type")) + 9;
|
||||
// Strip begin double-quotes and ending CRLF, double-quotes
|
||||
while (boundary[0] == '"')
|
||||
boundary++;
|
||||
|
||||
while (boundary[StrLen(boundary) - 1] == '\"' || boundary[StrLen(boundary) - 1] == ' ' || boundary[StrLen(boundary) - 1] == '\r' || boundary[StrLen(boundary) - 1] == '\n')
|
||||
boundary[StrLen(boundary) - 1] = NULL;
|
||||
|
||||
I64 state = SLON_MULTIPART_PARSER_CONSUME_BOUNDARY;
|
||||
I64 lines_count = 0;
|
||||
U8** lines = String.Split(multipart_form_data_copy, '\n', &lines_count);
|
||||
|
||||
U8* line;
|
||||
U8* name;
|
||||
U8* replace_line;
|
||||
|
||||
I64 i = 0;
|
||||
while (i < lines_count) {
|
||||
line = lines[i];
|
||||
// Strip any ending CRLF
|
||||
while (line[StrLen(line) - 1] == '\r' || line[StrLen(line) - 1] == '\n') {
|
||||
line[StrLen(line) - 1] = NULL;
|
||||
}
|
||||
switch (state) {
|
||||
case SLON_MULTIPART_PARSER_CONSUME_BOUNDARY:
|
||||
if (StrFind(boundary, line)) {
|
||||
state = SLON_MULTIPART_PARSER_CONSUME_CONTENT_DISPOSITION;
|
||||
}
|
||||
break;
|
||||
case SLON_MULTIPART_PARSER_CONSUME_CONTENT_DISPOSITION:
|
||||
if (StrFind("ontent-", line) && StrFind("isposition:", line) && StrFind("name=", line)) {
|
||||
name = StrFind("name=", line) + 5;
|
||||
// Strip begin/end double-quotes
|
||||
while (name[0] == '"')
|
||||
name++;
|
||||
while (name[StrLen(name) - 1] == '\"')
|
||||
name[StrLen(name) - 1] = NULL;
|
||||
String.Append(json_string, "\"%s\":\"", name);
|
||||
state = SLON_MULTIPART_PARSER_CONSUME_CONTENT;
|
||||
}
|
||||
break;
|
||||
case SLON_MULTIPART_PARSER_CONSUME_CONTENT:
|
||||
if (StrFind(boundary, line)) {
|
||||
String.Append(json_string, "\"");
|
||||
if (!String.EndsWith("--", line)) {
|
||||
String.Append(json_string, ",");
|
||||
state = SLON_MULTIPART_PARSER_CONSUME_CONTENT_DISPOSITION;
|
||||
} else {
|
||||
state = SLON_MULTIPART_PARSER_DONE;
|
||||
}
|
||||
} else {
|
||||
replace_line = String.Replace(line, "\"", "\\\"");
|
||||
String.Append(json_string, replace_line);
|
||||
Free(replace_line);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
String.Append(json_string, "}");
|
||||
@slon_free(session, multipart_form_data_copy);
|
||||
return json_string;
|
||||
}
|
||||
|
||||
U0 @slon_http_parse_query_string(SlonHttpSession* session)
|
||||
{
|
||||
U8* raw_path_copy = @slon_strnew(session, session->request->raw_path);
|
||||
I64 raw_path_split_count = 0;
|
||||
U8** raw_path_split = String.Split(raw_path_copy, '?', &raw_path_split_count);
|
||||
if (raw_path_split_count > 1) {
|
||||
U8* json_string = @slon_http_json_string_from_form_urlencoded_string(session, raw_path_split[1]);
|
||||
session->request->json = Json.Parse(json_string);
|
||||
@slon_free(session, json_string);
|
||||
}
|
||||
@slon_free(session, raw_path_copy);
|
||||
}
|
||||
|
||||
U0 @slon_http_parse_request_as_form_urlencoded(SlonHttpSession* session)
|
||||
{
|
||||
U8* json_string = @slon_http_json_string_from_form_urlencoded_string(session, session->request->data);
|
||||
session->request->json = Json.Parse(json_string);
|
||||
@slon_free(session, json_string);
|
||||
}
|
||||
|
||||
U0 @slon_http_parse_request_as_multipart_form_data(SlonHttpSession* session)
|
||||
{
|
||||
U8* json_string = @slon_http_json_string_from_multipart_form_data(session, session->request->data);
|
||||
session->request->json = Json.Parse(json_string);
|
||||
@slon_free(session, json_string);
|
||||
}
|
||||
|
||||
U0 @slon_http_parse_request_as_json(SlonHttpSession* session)
|
||||
{
|
||||
session->request->json = Json.Parse(session->request->data);
|
||||
}
|
||||
|
||||
U0 @slon_http_handle_delete_request(SlonHttpSession* session)
|
||||
{
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#include "Endpoints/Delete/Statuses";
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
// FIXME: Implement this
|
||||
@slon_http_send_json(session, SLON_EMPTY_JSON_OBJECT);
|
||||
}
|
||||
|
||||
U0 @slon_http_handle_get_request(SlonHttpSession* session)
|
||||
{
|
||||
if (@slon_http_request_has_query_string(session)) {
|
||||
@slon_http_parse_query_string(session);
|
||||
}
|
||||
|
||||
SLON_DEBUG_PRINT_REQUEST_JSON
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#include "Endpoints/Get/Accounts";
|
||||
#include "Endpoints/Get/ActivityPub";
|
||||
#include "Endpoints/Get/Blocks";
|
||||
#include "Endpoints/Get/Bookmarks";
|
||||
#include "Endpoints/Get/Conversations";
|
||||
#include "Endpoints/Get/CustomEmojis";
|
||||
#include "Endpoints/Get/Favourites";
|
||||
#include "Endpoints/Get/Filters";
|
||||
#include "Endpoints/Get/FollowRequests";
|
||||
#include "Endpoints/Get/FollowedTags";
|
||||
#include "Endpoints/Get/Instance";
|
||||
#include "Endpoints/Get/Notifications";
|
||||
#include "Endpoints/Get/OAuth";
|
||||
#include "Endpoints/Get/Suggestions";
|
||||
#include "Endpoints/Get/Timelines";
|
||||
#include "Endpoints/Get/Web";
|
||||
#include "Endpoints/Get/WellKnown";
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
@slon_http_set_status_code(session, 404);
|
||||
}
|
||||
|
||||
U0 @slon_http_handle_patch_request(SlonHttpSession* session)
|
||||
{
|
||||
if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) {
|
||||
@slon_http_parse_request_as_json(session);
|
||||
}
|
||||
if (String.BeginsWith("application/x-www-form-urlencoded", @slon_http_request_header(session, "content-type"))) {
|
||||
@slon_http_parse_request_as_form_urlencoded(session);
|
||||
}
|
||||
if (String.BeginsWith("multipart/form-data", @slon_http_request_header(session, "content-type"))) {
|
||||
@slon_http_parse_request_as_multipart_form_data(session);
|
||||
}
|
||||
|
||||
SLON_DEBUG_PRINT_REQUEST_JSON
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#include "Endpoints/Patch/Accounts";
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
@slon_http_set_status_code(session, 404);
|
||||
}
|
||||
|
||||
U0 @slon_http_handle_post_request(SlonHttpSession* session)
|
||||
{
|
||||
if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) {
|
||||
@slon_http_parse_request_as_json(session);
|
||||
}
|
||||
if (String.BeginsWith("application/x-www-form-urlencoded", @slon_http_request_header(session, "content-type"))) {
|
||||
@slon_http_parse_request_as_form_urlencoded(session);
|
||||
}
|
||||
if (String.BeginsWith("multipart/form-data", @slon_http_request_header(session, "content-type"))) {
|
||||
@slon_http_parse_request_as_multipart_form_data(session);
|
||||
}
|
||||
// Workaround for IceCubesApp: https://github.com/Dimillian/IceCubesApp/issues/2235
|
||||
if (!StrLen(@slon_http_request_header(session, "content-type")) && @slon_http_request_has_query_string(session)) {
|
||||
@slon_http_parse_query_string(session);
|
||||
}
|
||||
|
||||
SLON_DEBUG_PRINT_REQUEST_JSON
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#include "Endpoints/Post/ActivityPub";
|
||||
#include "Endpoints/Post/Apps";
|
||||
#include "Endpoints/Post/OAuth";
|
||||
#include "Endpoints/Post/Statuses";
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
@slon_http_set_status_code(session, 404);
|
||||
}
|
||||
|
||||
U0 @slon_http_handle_request(SlonHttpSession* session)
|
||||
{
|
||||
|
||||
// .purge_expired_idempotency_keys()
|
||||
@slon_http_authorize(session);
|
||||
switch (@slon_http_request_verb(session)) {
|
||||
case SLON_HTTP_VERB_DELETE:
|
||||
@slon_http_handle_delete_request(session);
|
||||
break;
|
||||
case SLON_HTTP_VERB_GET:
|
||||
@slon_http_handle_get_request(session);
|
||||
break;
|
||||
case SLON_HTTP_VERB_OPTIONS:
|
||||
@slon_http_set_status_code(session, 200);
|
||||
break;
|
||||
case SLON_HTTP_VERB_PATCH:
|
||||
@slon_http_handle_patch_request(session);
|
||||
break;
|
||||
case SLON_HTTP_VERB_POST:
|
||||
@slon_http_handle_post_request(session);
|
||||
break;
|
||||
default:
|
||||
@slon_http_set_status_code(session, 405);
|
||||
}
|
||||
}
|
||||
|
||||
U0 @slon_http_task(TcpSocket* s)
|
||||
{
|
||||
// Bail if we can't acquire socket for some reason
|
||||
if (!@tcp_socket_accept(s))
|
||||
return;
|
||||
|
||||
// Init session
|
||||
SlonHttpSession* session = @slon_http_init_session(s);
|
||||
|
||||
// Parse headers if they are available
|
||||
while (!@slon_http_request_headers_have_been_parsed(session)) {
|
||||
@slon_http_receive(session);
|
||||
|
||||
// Handle malformed requests (anything less than "GET / HTTP/1.0\r\n\r\n" is probably a bad request)
|
||||
if (session->request->buffer->size < 18) {
|
||||
@slon_http_set_status_code(session, 400);
|
||||
goto slon_http_task_send_response;
|
||||
}
|
||||
|
||||
@slon_http_try_parse_request_headers(session);
|
||||
}
|
||||
|
||||
//@slon_http_debug_print_request(session, FALSE);
|
||||
|
||||
// If we have a content-length header, consume until we receive all the data, then set request->data pointer and size
|
||||
if (StrLen(@slon_http_request_header(session, "content-length"))) {
|
||||
I64 content_length = Str2I64(@slon_http_request_header(session, "content-length"));
|
||||
while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length)
|
||||
@slon_http_receive(session);
|
||||
}
|
||||
|
||||
@slon_http_handle_request(session);
|
||||
|
||||
slon_http_task_send_response:
|
||||
//@slon_http_debug_print_response(session, FALSE);
|
||||
|
||||
@slon_http_send_response(session);
|
||||
|
||||
@slon_http_free_session(session);
|
||||
|
||||
AdamLog("\n");
|
||||
s->close();
|
||||
}
|
||||
|
||||
Adam("U0 @spawn_slon_http_task(TcpSocket *s){Spawn(%d, s, \"SlonHttpTask\");};\n", &@slon_http_task);
|
||||
@tcp_socket_bind(80, "@spawn_slon_http_task");
|
Loading…
Add table
Add a link
Reference in a new issue