summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2025-01-18 12:04:22 +0100
committerMitya Selivanov <automainint@guattari.tech>2025-01-18 12:04:22 +0100
commite3603be3dce9faa55c3d0785563b22b19adaf651 (patch)
tree0c2d1b1dc3c8e876fba1d621acebe9f09a282311 /main.c
parentbd16206096354a53dbe096fa41483bd325b78703 (diff)
downloadcgi-e3603be3dce9faa55c3d0785563b22b19adaf651.zip
Impl CGI redirectsHEADdev
Diffstat (limited to 'main.c')
-rwxr-xr-xmain.c232
1 files changed, 214 insertions, 18 deletions
diff --git a/main.c b/main.c
index 7ade799..9949195 100755
--- a/main.c
+++ b/main.c
@@ -70,6 +70,10 @@ exit $? # */
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/wait.h>
// ================================================================
//
@@ -156,8 +160,17 @@ typedef struct {
} Location;
typedef struct {
- c8 const *alias;
- c8 const *url;
+ b8 END;
+ c8 const *name;
+ c8 const *value;
+} Replace_Header;
+
+typedef struct {
+ c8 const * alias;
+ c8 const * url;
+ c8 * cgi;
+ b8 is_binary;
+ Replace_Header *replace_headers;
} Redirect;
// ================================================================
@@ -492,7 +505,36 @@ static Redirect redirects[] = {
}, {
.alias = "/deezer/2021",
.url = "https://deezer.page.link/PXS8q1jEqECa79rEA",
- },
+ }, {
+ .alias = "/juliaset",
+ .url = "/static/plain/juliaset/index.htm",
+ .cgi = "/usr/lib/cgit/cgit.cgi",
+ .replace_headers = (Replace_Header[]) {
+ { .name = "Content-Type", .value = "text/html; charset=UTF-8", },
+ { .name = "X-Content-Type-Options", .value = NULL, },
+ { .name = "Content-Security-Policy", .value = NULL, },
+ { .name = "Content-Disposition", .value = NULL, },
+ { .name = "Last-Modified", .value = NULL, },
+ { .name = "Expires", .value = NULL, },
+ { .name = "ETag", .value = NULL, },
+ { .END=1, }
+ },
+ }, {
+ .alias = "/juliaset/julia_set.wasm",
+ .url = "/static/plain/juliaset/julia_set.wasm",
+ .cgi = "/usr/lib/cgit/cgit.cgi",
+ .is_binary = 1,
+ .replace_headers = (Replace_Header[]) {
+ { .name = "Content-Type", .value = "application/wasm", },
+ { .name = "X-Content-Type-Options", .value = NULL, },
+ { .name = "Content-Security-Policy", .value = NULL, },
+ { .name = "Content-Disposition", .value = NULL, },
+ { .name = "Last-Modified", .value = NULL, },
+ { .name = "Expires", .value = NULL, },
+ { .name = "ETag", .value = NULL, },
+ { .END=1, }
+ },
+ }
};
static Location locs[] = {
@@ -2391,12 +2433,67 @@ c8 const *headlines[] = {
"A renaissance of the Pythagorean mindset.",
};
+c8 cgi_output[1024 * 1024 * 10] = {0};
+i32 cgi_output_size = 0;
+
// ================================================================
//
// Procedures
//
// ================================================================
+i32 run_cgi(c8 const *cgi, c8 *const *argv, c8 *const *envp) {
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGALRM, SIG_IGN);
+
+ i32 pipe_out[2];
+
+ if (pipe(pipe_out) == -1)
+ return 1;
+
+ if (fcntl(pipe_out[1], F_SETFL, O_NONBLOCK) == -1)
+ return 2;
+
+ pid_t id = fork();
+
+ switch (id) {
+ case -1:
+ return 3;
+
+ case 0:
+ if (dup2(pipe_out[1], STDOUT_FILENO) == -1)
+ return 4;
+
+ close(pipe_out[0]);
+ close(pipe_out[1]);
+
+ execve(cgi, argv, envp);
+ return 5;
+
+ default:;
+ }
+
+ for (;;) {
+ int status;
+
+ if (waitpid(id, &status, 0) == -1)
+ break;
+
+ if (WIFEXITED(status))
+ break;
+ }
+
+ i64 n = read(pipe_out[0], cgi_output, sizeof cgi_output - 1);
+
+ if (n <= 0)
+ return 6;
+
+ cgi_output[n] = '\0';
+ cgi_output_size = n;
+
+ return 0;
+}
+
c8 const *get_color(i32 index) {
static i64 rainbow_index = -1;
if (rainbow_index == -1)
@@ -3091,7 +3188,7 @@ void setup_theme_cookie(c8 const *cookie, c8 const *query) {
COLORS[i][5]);
printf("; Path=/; SameSite=Lax; Expires=");
print_time_gmt(6 * SECONDS_IN_MONTH);
- printf("; HttpOnly\n");
+ printf("; HttpOnly\r\n");
}
}
@@ -3099,6 +3196,11 @@ i32 main(i32 argc, c8 **argv) {
(void) argc;
(void) argv;
+ FILE *_in = freopen(NULL, "rb", stdin);
+ FILE *_out = freopen(NULL, "wb", stdout);
+ (void) _in;
+ (void) _out;
+
struct timespec time_begin;
timespec_get(&time_begin, TIME_UTC);
@@ -3119,19 +3221,19 @@ i32 main(i32 argc, c8 **argv) {
strlen(content_type) > MAX_CONTENT_TYPE_SIZE ||
(!str_eq(request_method, NULL, "GET", NULL) &&
!str_eq(request_method, NULL, "POST", NULL))) {
- printf("Status: 500 Internal Server Error\n");
+ printf("Status: 500 Internal Server Error 1\r\n");
return 0;
}
// TODO Check blacklist and throttling
if (0) {
- printf("Status: 400 Forbidden\n");
+ printf("Status: 400 Forbidden\r\n");
return 0;
}
if (0) {
- printf("Status: 429 Too Many Requests\n");
+ printf("Status: 429 Too Many Requests\r\n");
return 0;
}
@@ -3139,12 +3241,104 @@ i32 main(i32 argc, c8 **argv) {
for (i64 i = 0; i < (i64)(sizeof redirects / sizeof *redirects); ++i)
if (str_eq_with_slash(document_uri, redirects[i].alias)) {
- printf("Status: 301 Redirect\n");
- printf("Location: %s\n", redirects[i].url);
- printf("Content-Type: text/html\n");
- printf("\n");
- printf("Redirect: <a href='%s'>%s</a>\n", redirects[i].url, redirects[i].url);
- return 0;
+ if (redirects[i].url == NULL) {
+ printf("Status: 500 Internal Server Error 2\r\n");
+ return 0;
+ }
+
+ if (redirects[i].cgi != NULL) {
+ static c8 buf[4096] = {0};
+ snprintf(buf, sizeof buf - 1, "PATH_INFO=%s", redirects[i].url);
+
+ c8 *argv[] = { redirects[i].cgi, NULL };
+ c8 *envp[] = { buf, NULL };
+
+ i32 s = run_cgi(redirects[i].cgi, argv, envp);
+ if (s != 0) {
+ printf("Status: 500 Internal Server Error %d\r\n", s);
+ return 0;
+ }
+
+ printf("Status: 200 OK\r\n");
+ printf("Cross-Origin-Opener-Policy: same-origin\r\n");
+ printf("Cross-Origin-Embedder-Policy: require-corp\r\n");
+
+ c8 *p = cgi_output;
+
+ while (p < cgi_output + cgi_output_size) {
+ if (*p == '\r' || *p == '\n') {
+ printf("\r\n");
+ if (*p == '\r') ++p;
+ if (*p == '\n') ++p;
+ break;
+ }
+
+ c8 *q = p;
+ while (q < cgi_output + cgi_output_size && *q != '\r' && *q != '\n')
+ ++q;
+ if (q[0] == '\r' && q[1] == '\n') {
+ ++q;
+ *q = '\0';
+ }
+ *q = '\0';
+
+ b8 replaced = 0;
+
+ for (i32 j = 0;; ++j) {
+ if (redirects[i].replace_headers[j].END)
+ break;
+
+ c8 const *name = redirects[i].replace_headers[j].name;
+ c8 const *value = redirects[i].replace_headers[j].value;
+
+ if (name == NULL)
+ continue;
+
+ i32 len = strlen(name);
+
+ if (q - p > len && p[len] == ':' && str_eq(p, p + len, name, name + len)) {
+ if (value != NULL)
+ printf("%s: %s\r\n", name, value);
+ replaced = 1;
+ break;
+ }
+ }
+
+ if (!replaced)
+ printf("%s\r\n", p);
+
+ p = q + 1;
+ }
+
+ if (redirects[i].is_binary) {
+ fflush(stdout);
+ i32 written = write(STDOUT_FILENO, p, (cgi_output + cgi_output_size) - p);
+ (void) written;
+ } else
+ while (p < cgi_output + cgi_output_size) {
+ c8 *q = p;
+ while (q < cgi_output + cgi_output_size && *q != '\r' && *q != '\n')
+ ++q;
+ if (q[0] == '\r' && q[1] == '\n') {
+ ++q;
+ *q = '\0';
+ }
+ *q = '\0';
+
+ printf("%s\r\n", p);
+
+ p = q + 1;
+ }
+
+ return 0;
+ } else {
+ printf("Status: 301 Redirect\r\n");
+ printf("Location: %s\r\n", redirects[i].url);
+ printf("Content-Type: text/html; charset=UTF-8\r\n");
+ printf("\r\n");
+ printf("Redirect: <a href='%s'>%s</a>\r\n", redirects[i].url, redirects[i].url);
+ return 0;
+ }
}
for (i64 i = 0; i < (i64)(sizeof locs / sizeof *locs); ++i)
@@ -3155,14 +3349,16 @@ i32 main(i32 argc, c8 **argv) {
}
if (doc == DOC_404)
- printf("Status: 404 Page Not Found\n");
+ printf("Status: 404 Page Not Found\r\n");
else
- printf("Status: 200 OK\n");
+ printf("Status: 200 OK\r\n");
setup_theme_cookie(http_cookie, query_string);
- printf("Content-Type: text/html\n");
- printf("\n");
+ printf("Cross-Origin-Opener-Policy: same-origin\r\n");
+ printf("Cross-Origin-Embedder-Policy: require-corp\r\n");
+ printf("Content-Type: text/html\r\n");
+ printf("\r\n");
if (docs[doc].content[0].type == TAG_RAW) {
printf("%s", docs[doc].content[0].text);
@@ -3330,7 +3526,7 @@ i32 main(i32 argc, c8 **argv) {
"</script>",
delta_micros / 1000,
(delta_micros / 10) % 100);
- printf("\n");
+ printf("\r\n");
return 0;
}