vessel/old/main.c
Arija A. e7f57f8526
impl: Tempfiles and ByteStreams
Signed-off-by: Arija A. <ari@ari.lt>
2025-06-14 20:57:28 +03:00

793 lines
20 KiB
C

#include "include/conf.h"
/* This is just a testing program for now. Please don't take this for granted.
* As a final thing. This is nothing but a test while developing.
*
* Later on, this will become `vessel` CLI program.
*/
#define _POSIX_C_SOURCE 199309L
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <vessel-web/httpbody.h>
#include <vessel-web/srv.h>
#include <vessel-web/web.h>
#include <vessel/mem.h>
#include <vessel/def.h>
#include <vessel/log.h>
#include <vessel-web/http.h>
#include <vessel/server.h>
#include <vessel/main.h>
#include <vessel/hmap.h>
#include <vessel/stream.h>
#include <vessel/hashing.h>
#include <vessel-web/request.h>
#include <vessel-web/form.h>
#include <vessel-web/path-builtin.h>
#include <vessel/vessel.h>
#include <vessel/thread.h>
/* #include "include/response.h" */
volatile uint64_t n = 0;
static bool gbye(vs_SockServer *s) {
/* Sock_drop(*(int*)s->arg); */
(void)s;
return true;
}
static bool before(vs_SockWorker *w) {
vw_Proxy *p = (vw_Proxy *)w->arg;
/* HTTPResponse_begin(&p->req, HTTP_OLDEST_SUPPORTED, 666); */
vw_HTTPRequest_res_header(&p->req, "x-rizz", "this is a test");
vw_HTTPRequest_res_header(&p->req, "x-ip-addr", p->ip);
/* HTTPResponse_content_and_length(&p->req, */
/* "You have been blocked." HTTP_CRLF); */
return true;
}
static bool gactg(vw_Proxy *p) {
static const char *nucleotides = "ACTG";
vw_HTTPRequest_res_begin(&p->req, 200);
char sequence[1000];
srand((unsigned)time(NULL));
while (true) {
for (uint32_t idx = 0; idx < 1000; ++idx)
sequence[idx] = nucleotides[rand() % 4];
if (vs_Stream_ignore_err(vw_HTTPRequest_write_chunk(&p->req, sequence, 1000)) == 0)
break;
}
return true;
}
static bool gindex(vw_Proxy *p) {
if (vw_HTTPRequest_res_begin(&p->req, 200) == VS_STREAM_ERROR) {
puts("HERE 0");
fflush(stdout);
}
/* vs_HMap_print_as_str(&p->req.headers); */
if (vw_HTTPRequest_res_header(&p->req, "content-type", "text/html") == VS_STREAM_ERROR) {
puts("HERE 1");
fflush(stdout);
}
/* clang-format off */
if (vs_Stream_ignore_err(vw_HTTPRequest_writef(&p->req,
"<!DOCTYPE html>"
"<html lang=\"en\">"
" <head>"
" <meta charset=\"UTF-8\">"
" <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
" <title>Home Page</title>"
" </head>"
""
" <body>"
" <h1>Hello!</h1>"
" <p>This is a sample website. Your request number: %ju. Submit form here:</p>"
" <form enctype=\"multipart/form-data\" action=\"/save\" method=\"POST\"> <input name=test id=test type=\"text\" /> <input name=file id=file type=\"file\" /> <input name=file1 id=file1 type=\"file\" /> <input type=\"submit\" value=\"Submit\" /> </form>"
" </body>"
"</html>",
++n
)) == 0) {
puts("HERE 2"); fflush(stdout);
}
/* clang-format on */
return true;
}
static bool gmeow(vw_Proxy *p) {
vw_HTTPRequest_res_begin(&p->req, 200);
vw_HTTPRequest_res_header(&p->req, "content-type", "text/plain");
uintmax_t num = 0;
const vs_HMapEntry *bucket = vs_HMap_find(&p->params, "number");
if (bucket) {
const vw_PathMatch *match = bucket->value;
num = *(uintmax_t *)match->conv;
}
/* clang-format off */
vw_HTTPRequest_writef(&p->req, "meow, number : %ju", num);
/* clang-format on */
return true;
}
static bool ga(vw_Proxy *p) {
vw_HTTPRequest_res_begin(&p->req, 200);
vw_HTTPRequest_res_header(&p->req, "content-type", "text/plain");
/* clang-format off */
vw_HTTPRequest_write(&p->req, "a\n", 2);
/* clang-format on */
return true;
}
static bool gb(vw_Proxy *p) {
vw_HTTPRequest_res_begin(&p->req, 200);
vw_HTTPRequest_res_header(&p->req, "content-type", "text/plain");
/* clang-format off */
vw_HTTPRequest_write(&p->req, "b\n", 2);
/* clang-format on */
return true;
}
static bool notfound(vw_Proxy *p) {
vw_HTTPRequest_res_begin(&p->req, 404);
vw_HTTPRequest_res_header(&p->req, "content-type", "text/html");
/* clang-format off */
vw_HTTPRequest_writef(&p->req,
"<!DOCTYPE html>"
"<html lang=\"en\">"
" <head>"
" <meta charset=\"UTF-8\">"
" <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
" <title>404 Not Found</title>"
" </head>"
""
" <body>"
" <h1>HTTP/404: Not Found</h1>"
" <p>Requested resource was not found on the server. Your request number: %ju.</p>"
" </body>"
"</html>",
(uint64_t)0
);
/* clang-format on */
return true;
}
static bool gindex_html(vw_Proxy *p) {
vw_srv_redirect(&p->req, 307, "/");
return true;
}
static bool psave(vw_Proxy *p) {
vs_HMap h = { 0 };
if (!vs_HMap_init(&h))
return false;
if (!vw_Form_read(&p->req, &h)) {
puts("Oh no!");
vs_HMap_print_as_ptr(&h);
vw_Form_destroy(&h);
return false;
}
vw_Form_print(&h);
vs_HMapEntry *test = vs_HMap_find(&h, "test");
if (test) {
vw_FormField *tf = test->value;
const char *v = tf->value;
puts("---BEGIN FIELD TEST---");
while (*v) {
switch (*v) {
case '\r':
fputs("\\r", stdout);
break;
case '\n':
fputs("\\n\n", stdout);
break;
default:
putchar(*v);
break;
}
++v;
}
puts("\n---END FIELD TEST---");
}
/* vs_HMap_print_as_ptr(&h); */
vs_HMapEntry *b = vs_HMap_find(&h, "file");
if (b) {
vw_FormField *ff = b->value;
vw_FormFile *f = ff->value;
printf("%zu - %d %d // %ls => %s\n",
sizeof(*f->filename),
((char *)f->filename)[0],
((char *)f->filename)[1],
f->filename,
f->path);
fflush(stdout);
vs_HMap_print_as_str(&ff->headers);
fflush(stdout);
vw_FormFile_open(f);
vs_FileStat s = { 0 };
vs_File_stat(&f->file, &s);
printf("%zu\n", s.size);
vw_HTTPRequest_res_begin(&p->req, 200);
vw_HTTPRequest_res_headerf(&p->req, "content-length", "%zu", s.size);
if (vs_HMap_find(&ff->headers, "content-type"))
vw_HTTPRequest_res_header(
&p->req, "content-type", vs_HMap_find(&ff->headers, "content-type")->value);
vw_HTTPRequest_res_headers_end(&p->req);
char a[1024 * 256] = { 0 };
uint64_t total = 0;
while (total < s.size) {
const size_t now = vw_FormFile_read(f, a, 1024 * 256);
if (now <= 0)
break;
vw_HTTPBody_write(&p->req.body, a, now);
total += now;
}
} else {
vw_HTTPRequest_res_begin(&p->req, 200);
vw_HTTPRequest_res_header(&p->req, "content-type", "text/plain");
vw_HTTPRequest_writef(&p->req, "Your response has been registered without a file.");
}
vw_Form_destroy(&h);
return true;
}
#if 0
static bool hello(vw_Proxy *p) {
HTTPResponse_begin(&p->req, p->req.version, 200, "close");
HTTPResponse_content_and_length(&p->req, "hello");
return true;
}
static bool save(vw_Proxy *p) {
vs_HMap h = {0};
if (!vs_HMap_init(&h))
return false;
if (!Form_read(&p->req, &h)) {
puts("Oh no!");
vs_HMap_print_as_ptr(&h);
Form_destroy(&h);
return false;
}
vs_HMapEntry *test = vs_HMap_find(&h, "test");
if (test) {
FormField *tf = test->value;
const char *v = tf->value;
puts("---BEGIN FIELD TEST---");
while (*v) {
switch (*v) {
case '\r': fputs("\\r", stdout); break;
case '\n': fputs("\\n\n", stdout); break;
default: putchar(*v); break;
}
++v;
}
puts("\n---END FIELD TEST---");
}
/* vs_HMap_print_as_ptr(&h); */
vs_HMapEntry *b = vs_HMap_find(&h, "file");
if (b) {
FormField *ff = b->value;
FormFile *f = ff->value;
printf("%ju - %d %d // %ls => %s\n", sizeof(*f->filename),
((char *)f->filename)[0], ((char *)f->filename)[1], f->filename,
f->path);
fflush(stdout);
vs_HMap_print_as_str(&ff->headers);
fflush(stdout);
FormFile_open(f);
FileStat s = {0};
File_stat(&f->f, &s);
printf("%ju\n", s.size);
HTTPResponse_begin(&p->req, p->req.version, 200, "close");
HTTPRequest_res_headerf(&p->req, "content-length", "%ju", s.size);
if (vs_HMap_find(&ff->headers, "content-type"))
HTTPRequest_res_header(&p->req, "content-type",
vs_HMap_find(&ff->headers, "content-type")->value);
HTTPRequest_res_headers_end(&p->req);
char a[1024 * 256] = {0};
uint64_t total = 0;
while (total < s.size) {
const uint64_t now = FormFile_read(f, a, 1024 * 256);
if (now <= 0)
break;
Stream_write(p->req.fp, a, now);
total += now;
}
} else {
HTTPResponse_begin(&p->req, p->req.version, 200, "close");
HTTPResponse_content_and_length(
&p->req, "Your response has been registered without a file.");
}
Form_destroy(&h);
return true;
}
static bool gerror(vw_Proxy *p) {
(void)p;
return true;
/* return srv_redirect(&p->req, p->req.version, 301, "/"); */
}
#endif
int main(void) {
#if 0
const Logger plg = LOG_ALL;
vs_HMap path = {0};
vs_HMap_init(&path);
const char *pattern = "/hello/<int:hello?=123><path:meow?=>";
const char *matchee = "/hello/111/meow/test";
PathPattern *pat = PathPattern_compile(&plg, pattern);
if (!pat)
return 1;
PathPattern_print(pat, true);
uint32_t matched =
PathPattern_matchn(pat, &path, matchee, (uint32_t)strlen(matchee));
if (matched == 0)
return 1;
vs_HMap_print_as_ptr(&path);
printf("m=%u l=%zu\n", matched, strlen(matchee));
for (VS_HMAP_TYPE a = 0; a < path.size; ++a) {
vs_HMapEntry *b = &path.buckets[path.order_table[a]];
PathMatch *pm = (PathMatch *)b->value;
printf("X: %s %ld\n", pm->str, pm->conv ? (*(intmax_t *)pm->conv) : 69);
}
vs_HMap_destroy(&path);
PathPattern_destroy(pat);
return 0;
#endif
#if 0
/* clang-format off */
/* const char *i = "/<int(2..4/2):hello?=2>"; */
/* const char *i = */
/* "/<int!(..):hi?=11><str( 11 " */
/* ")>/j/hi<str(10)>\\\\h/<float(0..10):test?= 1 ><double:hi?=0.1>"; */
/* const char *i = "< str ! ( 1..69 ) : h ? =>"; */
/* const char *i = "<bool( hello world test/h 124 hi ):hello?= hELlo " */
/* ">/<uuid(4):hi?=ffc636fc-db2f-4a16-aa67-c9e6baec9afb>/<hex:h?=0>"; */
const char *i = "?/hello/wor?ld<Date:hi?=2024-09-01>";
/* clang-format on */
const Logger alg = LOG_ALL;
PathPattern *p = PathPattern_compile(&alg, i);
if (!p)
return 1;
if (!PathPattern_print(p, true)) {
PathPattern_destroy(p);
return 1;
}
if (!PathPattern_destroy(p))
return 1;
return 0;
#endif
#if 0
const char *passing_tests[] = {
"",
"0..10",
"-5..5",
"-10..-1",
"5..5",
"5",
"0..10/2",
"-10..10/5",
"1..20/3",
"-30..-10/10",
"..",
"5..",
"..5",
"../2",
"/3",
"5../2",
"..10/3",
"-10../4",
"..-42/2",
"1..10",
"1..10",
"1..10",
"1..10/2",
"1..10/2",
"0..0",
"-0..0",
"0..-0",
"0..0/1",
"42",
"-42",
"0",
"42../1",
"0",
"1000000..2000000",
"-2000000..-1000000",
"1..1000000/1000",
"2147483647..2147483647",
"-2147483648..-2147483648",
"-5..5/1",
"10..20/5",
"2..10/4",
};
const char *failing_tests[] = {
"a..b", "1..b", "a..2", "1..2/c", "abc", "$100",
"#1..10", "1..@10", "*1..10", "1&..&10", "1..2.3", "1,2",
"1-10", "1+10", "1:10", "1..10%2", "1+2", "1,..10",
"../", "//", "...", ".5", "5.", "5/..",
"5../ ..10", "1..2/", "1/2/3", "1....10", "//5", "1..2//",
"1../2..", "1..2/-1", "1..10/-5", "1.1..2", "1..2.2", "1..2/3.3",
".1..2", "1.1.2", "-", "+", "/", "!/2",
"1<..>10",
};
puts("Passing...");
for (uint64_t idx = 0;
idx < sizeof(passing_tests) / sizeof(passing_tests[0]); ++idx) {
IntRange r = {0};
printf("%s => ", passing_tests[idx]);
if (!IntRange_parse(&r, passing_tests[idx],
strlen(passing_tests[idx]))) {
fputs("(failed) ", stdout);
IntRange_print(&r);
return 1;
}
IntRange_print(&r);
}
puts("Failing...");
for (uint64_t idx = 0;
idx < sizeof(failing_tests) / sizeof(failing_tests[0]); ++idx) {
IntRange r = {0};
printf("%s => ", failing_tests[idx]);
if (IntRange_parse(&r, failing_tests[idx],
strlen(failing_tests[idx]))) {
fputs("(passed) ", stdout);
IntRange_print(&r);
return 1;
}
puts("(failed)");
}
return 0;
#endif
#if 0
Atom_u64 a = ATOM_INIT_VAR(0);
printf("%ju\n", ATOM_LOAD(&a));
ATOM_STORE(&a, 1);
printf("%ju\n", ATOM_LOAD(&a));
ATOM_STORE(&a, 2);
printf("%ju\n", ATOM_LOAD(&a));
return 0;
#endif
#if 0
vs_HMap h = {0};
vs_HMap_init(&h);
VS_HMAP_ID_TYPE ha = vs_HMap_insert(&h, "Hello", "World");
VS_HMAP_ID_TYPE hb = vs_HMap_insert(&h, "Test", "World");
VS_HMAP_ID_TYPE hc = vs_HMap_insert(&h, "Test", "World");
printf("%lu %lu %lu\n", ha, hb, hc);
vs_HMap_print_as_str(&h);
vs_HMapEntry *hab = vs_HMap_get(&h, ha);
vs_HMapEntry *hbb = vs_HMap_get(&h, hb);
vs_HMapEntry *hcb = vs_HMap_get(&h, hc);
VS_HMAP_ID_TYPE id = vs_HMap_find_id(&h, "Test");
printf("%d\n", hc == id);
if (hab)
puts(hab->key);
if (hbb)
puts(hbb->key);
if (hcb)
puts(hcb->key);
vs_HMap_rehash(&h, true);
hab = vs_HMap_get(&h, ha);
hbb = vs_HMap_get(&h, hb);
hcb = vs_HMap_get(&h, hc);
if (hab)
puts(hab->key);
if (hbb)
puts(hbb->key);
if (hcb)
puts(hcb->key);
vs_HMap_print_as_str(&h);
vs_HMap_insert(&h, "Testing...", "Meow!");
vs_HMap_rehash(&h, true);
puts("Deleting test");
vs_HMap_delete(&h, "Test");
hab = vs_HMap_get(&h, ha);
hbb = vs_HMap_get(&h, hb);
hcb = vs_HMap_get(&h, hc);
if (hab)
puts(hab->key);
if (hbb)
puts(hbb->key);
if (hcb)
puts(hcb->key);
vs_HMap_print_as_str(&h);
hab = vs_HMap_get(&h, ha);
hbb = vs_HMap_get(&h, hb);
hcb = vs_HMap_get(&h, hc);
if (hab)
puts(hab->key);
if (hbb)
puts(hbb->key);
if (hcb)
puts(hcb->key);
vs_HMap_destroy(&h);
return 0;
#endif
#if 0
Logger lo = LOG_ALL;
PathPattern *pat = PathPattern_compile("", &lo);
if (!pat) {
puts("compilation failed");
}
return 0;
#endif
#if 0
Stream hello_s = {0};
File bye_s = {0};
if (!File_init(&bye_s, NULL))
return 1;
if (!File_open(&bye_s, "chunked.test", FILEF_RD, FILEM_NONE))
return 1;
if (!Stream_init(&hello_s, STREAM_RD))
return 1;
if (!Stream_use(&hello_s, &bye_s))
return 1;
/* bool meow; */
char a[64] = {0};
uint64_t b = Stream_readb(&hello_s, NULL, 5, "END", 3, NULL, true);
printf("%ju\n", b);
b = Stream_readb(&hello_s, a, 64, "END", 3, NULL, true);
puts(a);
printf("%ju\n", b);
return 0;
#endif
#if 0
Stream hello_s = {0};
HTTPBody hsmeow = {0};
File bye_s = {0};
if (!File_init(&bye_s, NULL))
return 1;
if (!File_open(&bye_s, "chunked.test", FILEF_RD, FILEM_NONE))
return 1;
if (!Stream_init(&hello_s, STREAM_RD))
return 1;
if (!Stream_use(&hello_s, &bye_s))
return 1;
if (!HTTPBody_init(&hsmeow, &hello_s, 50, false))
return 1;
/* bool meow; */
char a[64] = {0};
printf("%ju\n", HTTPBody_read(&hsmeow, NULL, 5));
if (!HTTPBody_read1(&hsmeow, a))
return 1;
putchar(*a);
*a = '\0';
if (!HTTPBody_read1(&hsmeow, a))
return 1;
putchar(*a);
*a = '\0';
if (!HTTPBody_read1(&hsmeow, a))
return 1;
putchar(*a);
*a = '\0';
putchar('\n');
HTTPBody_readbf(&hsmeow, a, 64, NULL, true, "H%sE", "W");
printf("^%s$\n", a);
return 0;
#endif
#if 0
vessel_init(VESSEL_HEADER_VERSION);
Temple t;
if (!Temple_init(&t))
return 1;
if (!Temple_open(&t, "example.tmpl.test"))
return 1;
printf("version %x flags %x magic %c digest %d\n", t.version, t.flags, t.magic[0], t.digest[0]);
TempleState state;
if (!TempleState_init(&state, &t))
return 1;
if (!TempleState_destroy(&state, &t))
return 1;
if (!Temple_destroy(&t))
return 1;
return 0;
#endif
vs_Logger lg = VS_LOG_ALL;
vs_vessel_init(VS_VESSEL_HEADER_VERSION);
vs_SockServer s = { 0 };
vs_SockWorkerHook hooks[] = VS_HOOKS(VW_WEBSERVER_HOOKS);
s.host = "127.0.0.1";
s.port = 8080;
s.logger = &lg;
s.threads = 128;
if (!vs_SockServer_create_generic(&s)) {
vs_log_error(&lg, "Failed to create a generic TCP server.");
return 1;
}
if (!vs_SockServer_init(&s, VS_SEC2TO(30))) {
vs_log_error(&lg, "Failed to create a generic TCP server.");
vs_SockServer_destroy(&s);
return 1;
}
if (!vs_SockServer_setup_signals(&s)) {
vs_log_error(&lg, "Failed to handle signals for the server.");
vs_SockServer_destroy(&s);
return 1;
}
vw_Route routes[] = VW_ROUTES(VW_NEW_ROUTE(ANY, "/<int:u?>", normal, gindex),
VW_NEW_ROUTE(ANY, "/<int:number?>-?meow", normal, gmeow),
VW_NEW_ROUTE(ANY, "/a", normal, ga),
VW_NEW_ROUTE(ANY, "/b", normal, gb),
VW_NEW_ROUTE(ANY, "/index.html", normal, gindex_html),
VW_NEW_ROUTE(POST, "/save", normal, psave),
VW_NEW_ROUTE(GET, "/actg", normal, gactg),
VW_NEW_ROUTE(ANY, "<path>", not_found, notfound));
#if 0
{
NULL,
"%p",
RouteType_error,
gerror,
},
{
"GET",
"/logo.png",
RouteType_normal,
logo,
},
{
NULL,
"/hello",
RouteType_normal,
hello,
}
,{
"POST",
"/save",
RouteType_normal,
save,
}
#endif
const vw_RouteHook rhooks[] = VS_HOOKS({ vw_RouteHookType_pre_read, before });
vw_Router router = { 0 };
router.routes = routes;
router.hooks = rhooks;
const vs_SockServerHook oofhooks[] = VS_HOOKS({ vs_SockServerHookType_pre_worker, &gbye });
if (!vw_WebServer_init(&s, &router)) {
vs_log_error(s.logger, "Failed to init the web server.");
vs_SockServer_destroy(&s);
return 1;
}
if (!vs_SockServer_start(&s, hooks, oofhooks)) {
vs_log_error(s.logger, "Failed to start the server.");
vs_SockServer_destroy(&s);
return 1;
}
vw_WebServer_destroy(&router);
vs_SockServer_destroy(&s);
return 0;
}