slon/Slon/Http/LocalServer.HC

232 lines
8.3 KiB
HolyC

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)) {
session->content_type("text/html");
return;
}
if (String.EndsWith(".txt", filepath)) {
session->content_type("text/plain");
return;
}
if (String.EndsWith(".css", filepath)) {
session->content_type("text/css");
return;
}
if (String.EndsWith(".js", filepath)) {
session->content_type("text/javascript");
return;
}
if (String.EndsWith(".json", filepath)) {
session->content_type("application/json");
return;
}
if (String.EndsWith(".gif", filepath)) {
session->content_type("image/gif");
return;
}
if (String.EndsWith(".png", filepath)) {
session->content_type("image/png");
return;
}
if (String.EndsWith(".jpeg", filepath) || String.EndsWith(".jpg", filepath)) {
session->content_type("image/jpeg");
return;
}
session->content_type("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>");
session->content_type("text/html");
session->send(html, StrLen(html));
@slon_free(session, html);
}
U0 @slon_local_server_not_found(SlonHttpSession* session)
{
session->status(404);
session->content_type("text/html");
session->send("<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 = session->path();
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)) {
session->status(301);
StrPrint(scratch_buffer, "%s/", path);
session->header("Location", scratch_buffer);
} else {
@slon_local_server_send_file(session, scratch_buffer);
}
return;
}
// shouldn't get here :^)
session->status(400);
}
U0 @slon_local_http_handle_get_request(SlonHttpSession* session)
{
@slon_local_server_get(session);
}
U0 @slon_local_http_handle_request(SlonHttpSession* session)
{
switch (session->verb()) {
case SLON_HTTP_VERB_GET:
@slon_local_http_handle_get_request(session);
break;
default:
session->status(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) {
session->status(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(session->header("content-length"))) {
I64 content_length = Str2I64(session->header("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");