Pharos
0.7.23
Modern Web Framework & Web App Hosting Service.
Toggle main menu visibility
Loading...
Searching...
No Matches
ucal_static_server.c
Go to the documentation of this file.
1
/**
2
* @file
3
* @brief Provides a minimal loopback static server for serving UCAL build artifacts and route-mapped files directly from a build root.
4
*/
5
6
#include <arpa/inet.h>
7
#include <errno.h>
8
#include <netinet/in.h>
9
#include <signal.h>
10
#include <stddef.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <sys/socket.h>
15
#include <sys/stat.h>
16
#include <sys/types.h>
17
#include <unistd.h>
18
19
static
int
send_all
(
int
fd,
const
char
*buf,
size_t
len
) {
20
while
(
len
> 0) {
21
ssize_t n = send(fd, buf,
len
, 0);
22
if
(n <= 0)
return
-1;
23
buf += (size_t)n;
24
len
-= (size_t)n;
25
}
26
return
0;
27
}
28
29
static
char
*
read_file
(
const
char
*path,
size_t
*len_out) {
30
FILE *f = fopen(path,
"rb"
);
31
long
n;
32
char
*buf;
33
if
(!f)
return
NULL;
34
if
(fseek(f, 0, SEEK_END) != 0) { fclose(f);
return
NULL; }
35
n = ftell(f);
36
if
(n < 0) { fclose(f);
return
NULL; }
37
if
(fseek(f, 0, SEEK_SET) != 0) { fclose(f);
return
NULL; }
38
buf = (
char
*)malloc((
size_t
)n + 1);
39
if
(!buf) { fclose(f);
return
NULL; }
40
if
(fread(buf, 1, (
size_t
)n, f) != (
size_t
)n) { free(buf); fclose(f);
return
NULL; }
41
fclose(f);
42
buf[n] =
'\0'
;
43
*len_out = (size_t)n;
44
return
buf;
45
}
46
47
static
int
path_has_parent_reference
(
const
char
*path) {
48
return
strstr(path,
".."
) != NULL;
49
}
50
51
static
const
char
*
content_type_for_path
(
const
char
*path) {
52
const
char
*ext = strrchr(path,
'.'
);
53
if
(!ext)
return
"application/octet-stream"
;
54
if
(strcmp(ext,
".html"
) == 0)
return
"text/html; charset=utf-8"
;
55
if
(strcmp(ext,
".json"
) == 0)
return
"application/json"
;
56
if
(strcmp(ext,
".css"
) == 0)
return
"text/css; charset=utf-8"
;
57
if
(strcmp(ext,
".js"
) == 0)
return
"application/javascript; charset=utf-8"
;
58
if
(strcmp(ext,
".svg"
) == 0)
return
"image/svg+xml"
;
59
if
(strcmp(ext,
".png"
) == 0)
return
"image/png"
;
60
if
(strcmp(ext,
".jpg"
) == 0 || strcmp(ext,
".jpeg"
) == 0)
return
"image/jpeg"
;
61
if
(strcmp(ext,
".gif"
) == 0)
return
"image/gif"
;
62
if
(strcmp(ext,
".webp"
) == 0)
return
"image/webp"
;
63
if
(strcmp(ext,
".ico"
) == 0)
return
"image/x-icon"
;
64
return
"application/octet-stream"
;
65
}
66
67
static
int
resolve_direct_asset
(
const
char
*root,
const
char
*path,
68
char
*file_path,
size_t
file_path_len,
69
char
*content_type,
size_t
content_type_len) {
70
const
char
*relative = path[0] ==
'/'
? path + 1 : path;
71
if
(!(strncmp(path,
"/static/"
, 8) == 0 || strncmp(path,
"/media/"
, 7) == 0))
return
0;
72
if
(
path_has_parent_reference
(relative))
return
-1;
73
if
(snprintf(file_path, file_path_len,
"%s/%s"
, root, relative) >= (
int
)file_path_len)
return
-1;
74
if
(snprintf(content_type, content_type_len,
"%s"
,
content_type_for_path
(file_path)) >= (
int
)content_type_len)
return
-1;
75
return
1;
76
}
77
78
static
int
lookup_route_map
(
const
char
*root,
const
char
*path,
79
char
*artifact,
size_t
artifact_len,
80
char
*content_type,
size_t
content_type_len) {
81
char
map_path[2048];
82
char
line[4096];
83
FILE *f;
84
if
(snprintf(map_path,
sizeof
(map_path),
"%s/runtime-route-map.tsv"
, root) >= (
int
)
sizeof
(map_path))
return
0;
85
f = fopen(map_path,
"rb"
);
86
if
(!f)
return
0;
87
while
(fgets(line,
sizeof
(line), f)) {
88
char
*method = line;
89
char
*route_path = strchr(method,
'\t'
);
90
char
*route_artifact;
91
char
*route_type;
92
char
*status;
93
if
(!route_path)
continue
;
94
*route_path++ =
'\0'
;
95
route_artifact = strchr(route_path,
'\t'
);
96
if
(!route_artifact)
continue
;
97
*route_artifact++ =
'\0'
;
98
route_type = strchr(route_artifact,
'\t'
);
99
if
(!route_type)
continue
;
100
*route_type++ =
'\0'
;
101
status = strchr(route_type,
'\t'
);
102
if
(!status)
continue
;
103
*status++ =
'\0'
;
104
status[strcspn(status,
"\r\n"
)] =
'\0'
;
105
if
(strcmp(method,
"GET"
) == 0 && strcmp(route_path, path) == 0) {
106
if
(snprintf(artifact, artifact_len,
"%s"
, route_artifact) >= (
int
)artifact_len ||
107
snprintf(content_type, content_type_len,
"%s"
, route_type) >= (
int
)content_type_len) {
108
fclose(f);
109
return
0;
110
}
111
fclose(f);
112
return
1;
113
}
114
}
115
fclose(f);
116
return
0;
117
}
118
119
/**
120
* @brief Serves one loopback client request from the UCAL build root.
121
*/
122
static
void
handle_client
(
int
client,
const
char
*root) {
123
char
req[4096];
124
char
method[16];
125
char
path[1024];
126
char
content_type[128];
127
char
rel[1024];
128
char
file_path[2048];
129
char
header[512];
130
char
*body;
131
int
direct_asset_status;
132
size_t
body_len = 0;
133
ssize_t got = recv(client, req,
sizeof
(req) - 1, 0);
134
if
(got <= 0)
return
;
135
req[got] =
'\0'
;
136
method[0] =
'\0'
;
137
path[0] =
'\0'
;
138
if
(sscanf(req,
"%15s %1023s"
, method, path) != 2 || strcmp(method,
"GET"
) != 0) {
139
const
char
*bad =
"HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
;
140
(void)
send_all
(client, bad, strlen(bad));
141
return
;
142
}
143
direct_asset_status =
resolve_direct_asset
(root, path, file_path,
sizeof
(file_path), content_type,
sizeof
(content_type));
144
if
(direct_asset_status < 0) {
145
const
char
*bad =
"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
;
146
(void)
send_all
(client, bad, strlen(bad));
147
return
;
148
}
149
if
(direct_asset_status == 0) {
150
if
(!
lookup_route_map
(root, path, rel,
sizeof
(rel), content_type,
sizeof
(content_type))) {
151
const
char
*not_found =
"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
;
152
(void)
send_all
(client, not_found, strlen(not_found));
153
return
;
154
}
155
if
(snprintf(file_path,
sizeof
(file_path),
"%s/%s"
, root, rel) >= (
int
)
sizeof
(file_path)) {
156
const
char
*too_long =
"HTTP/1.1 414 URI Too Long\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
;
157
(void)
send_all
(client, too_long, strlen(too_long));
158
return
;
159
}
160
}
161
body =
read_file
(file_path, &body_len);
162
if
(!body) {
163
const
char
*missing =
"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
;
164
(void)
send_all
(client, missing, strlen(missing));
165
return
;
166
}
167
(void)snprintf(header,
sizeof
(header),
168
"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n"
,
169
content_type, body_len);
170
(void)
send_all
(client, header, strlen(header));
171
(void)
send_all
(client, body, body_len);
172
free(body);
173
}
174
175
/**
176
* @brief Starts the minimal UCAL loopback static server.
177
*/
178
int
main
(
int
argc,
char
**argv) {
179
const
char
*root = argc > 1 ? argv[1] :
"."
;
180
int
port = argc > 2 ? atoi(argv[2]) : 19090;
181
int
max_requests = argc > 3 ? atoi(argv[3]) : 3;
182
int
server_fd;
183
int
opt = 1;
184
struct
sockaddr_in addr;
185
if
(port <= 0 || port > 65535)
return
64;
186
if
(max_requests <= 0) max_requests = 3;
187
signal(SIGPIPE, SIG_IGN);
188
server_fd = socket(AF_INET, SOCK_STREAM, 0);
189
if
(server_fd < 0)
return
70;
190
(void)setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof
(opt));
191
memset(&addr, 0,
sizeof
(addr));
192
addr.sin_family = AF_INET;
193
addr.sin_port = htons((uint16_t)port);
194
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
195
if
(bind(server_fd, (
struct
sockaddr *)&addr,
sizeof
(addr)) != 0)
return
71;
196
if
(listen(server_fd, 16) != 0)
return
72;
197
for
(
int
i = 0; i < max_requests; i++) {
198
int
client = accept(server_fd, NULL, NULL);
199
if
(client < 0) {
200
if
(errno == EINTR) { i--;
continue
; }
201
break
;
202
}
203
handle_client
(client, root);
204
close(client);
205
}
206
close(server_fd);
207
return
0;
208
}
lookup_route_map
static int lookup_route_map(const char *root, const char *path, char *artifact, size_t artifact_len, char *content_type, size_t content_type_len)
Definition
ucal_static_server.c:78
main
int main(int argc, char **argv)
Starts the minimal UCAL loopback static server.
Definition
ucal_static_server.c:178
handle_client
static void handle_client(int client, const char *root)
Serves one loopback client request from the UCAL build root.
Definition
ucal_static_server.c:122
resolve_direct_asset
static int resolve_direct_asset(const char *root, const char *path, char *file_path, size_t file_path_len, char *content_type, size_t content_type_len)
Definition
ucal_static_server.c:67
send_all
static int send_all(int fd, const char *buf, size_t len)
Definition
ucal_static_server.c:19
path_has_parent_reference
static int path_has_parent_reference(const char *path)
Definition
ucal_static_server.c:47
content_type_for_path
static const char * content_type_for_path(const char *path)
Definition
ucal_static_server.c:51
read_file
static char * read_file(const char *path, size_t *len_out)
Definition
ucal_static_server.c:29
len
const char size_t len
Definition
yyjson.h:8364
src
native_runtime
c
ucal_static_server.c
Generated by
1.17.0