commit 2161d48
uint
·
2026-04-28 20:50:53 +0000 UTC
parent 3093334
add struct response, remove reply_... unify all responses to reply() and reply using struct response
1 files changed,
+111,
-120
+111,
-120
1@@ -21,6 +21,17 @@
2 #include "users.h"
3 #include "util.h"
4
5+struct response {
6+ const char* status;
7+ const char* ctype;
8+ const char* extra;
9+ const void* body;
10+
11+ size_t len;
12+ int send_body;
13+ int preflight;
14+};
15+
16 static void auth_delay_sleep(void);
17 static int cors_build(char* out, size_t outsz, const char* hdr, int preflight);
18 static const struct item* find_item(uint64_t id);
19@@ -30,11 +41,7 @@ static int parse_hex64(const char* s, uint64_t* out);
20 static int path_item(struct item* it, const char* path, const char* prefix, const struct user* u, int c, const char* hdr);
21 static int parse_range(const char* hdr, size_t total, size_t* start, size_t* end);
22 static int read_request(int c, char* method, size_t msz, char* path, size_t psz, char* hdr, size_t hsz);
23-static void reply(int c, const char* hdr, const char* status, const char* ctype, const char* extra, const void* body, size_t len, int send_body, int preflight);
24-static void reply_json(int c, const char* hdr, const char* status, const char* body, size_t len, int send_body);
25-static void reply_preflight(int c, const char* hdr);
26-static void reply_text(int c, const char* hdr, const char* status, const char* body);
27-static void reply_unauth(int c, const char* hdr, int send_body);
28+static void reply(int c, const char* hdr, const struct response* res);
29 static int stat_item(const struct item* it, struct stat* st);
30 static int stream_file(int c, const struct item* it, const char* hdr, int head_only);
31
32@@ -336,17 +343,41 @@ static int path_item(struct item* it, const char* path, const char* prefix, cons
33 int fr;
34
35 if (parse_hex64(path + strlen(prefix), &id) < 0) {
36- reply_text(c, hdr, HTTP_400, "Bad Request\n");
37+ reply(c, hdr, &(struct response){
38+ .status = HTTP_400,
39+ .ctype = HTTP_TEXT,
40+ .extra = NULL,
41+ .body = "Bad Request\n",
42+ .len = sizeof("Bad Request\n") - 1,
43+ .send_body = 1,
44+ .preflight = 0,
45+ });
46 return 1;
47 }
48
49 fr = item_path_for_id(it->path, id, u);
50 if (fr == 1) {
51- reply_text(c, hdr, HTTP_404, "Not Found\n");
52+ reply(c, hdr, &(struct response){
53+ .status = HTTP_404,
54+ .ctype = HTTP_TEXT,
55+ .extra = NULL,
56+ .body = "Not Found\n",
57+ .len = sizeof("Not Found\n") - 1,
58+ .send_body = 1,
59+ .preflight = 0,
60+ });
61 return 1;
62 }
63 if (fr < 0) {
64- reply_text(c, hdr, HTTP_500, "Server Error\n");
65+ reply(c, hdr, &(struct response){
66+ .status = HTTP_500,
67+ .ctype = HTTP_TEXT,
68+ .extra = NULL,
69+ .body = "Server Error\n",
70+ .len = sizeof("Server Error\n") - 1,
71+ .send_body = 1,
72+ .preflight = 0,
73+ });
74 return -1;
75 }
76
77@@ -507,20 +538,14 @@ static int read_request(int c, char* method, size_t msz, char* path, size_t psz,
78 *
79 * @param c Connected client socket file descriptor
80 * @param hdr Request header block
81- * @param status HTTP status line
82- * @param ctype Content-Type header string
83- * @param extra Extra response headers
84- * @param body Response body
85- * @param len Response body length
86- * @param send_body Whether to send body bytes
87- * @param preflight Whether this is a preflight response
88+ * @param res Response descriptor
89 */
90-static void reply(int c, const char* hdr, const char* status, const char* ctype, const char* extra, const void* body, size_t len, int send_body, int preflight)
91+static void reply(int c, const char* hdr, const struct response* res)
92 {
93 char resp[HTTP_RESP_MAX];
94 char cors[512];
95
96- (void)cors_build(cors, sizeof(cors), hdr, preflight);
97+ (void)cors_build(cors, sizeof(cors), hdr, res->preflight);
98
99 int n = snprintf(
100 resp, sizeof(resp),
101@@ -533,92 +558,34 @@ static void reply(int c, const char* hdr, const char* status, const char* ctype,
102 HTTP_CLOSE
103 "\r\n",
104
105- status,
106+ res->status,
107 cors,
108- (ctype && ctype[0]) ? ctype : "",
109- (extra && extra[0]) ? extra : "",
110- len
111+ (res->ctype && res->ctype[0]) ? res->ctype : "",
112+ (res->extra && res->extra[0]) ? res->extra : "",
113+ res->len
114 );
115
116 if (n < 0)
117 return;
118
119 if ((size_t)n >= sizeof(resp)) {
120- /* fail if header too large */
121- reply_text(c, hdr, HTTP_500, "Server Error\n");
122+ struct response err = {
123+ .status = HTTP_500,
124+ .ctype = HTTP_TEXT,
125+ .extra = NULL,
126+ .body = "Server Error\n",
127+ .len = sizeof("Server Error\n") - 1,
128+ .send_body = 1,
129+ .preflight = 0,
130+ };
131+ reply(c, hdr, &err);
132 return;
133 }
134
135 (void)write_all(c, resp, (size_t)n);
136
137- if (send_body && body && len > 0)
138- (void)write_all(c, body, len);
139-}
140-
141-/**
142- * @brief Send JSON HTTP response
143- *
144- * @param c Connected client socket file descriptor
145- * @param hdr Request header block
146- * @param status HTTP status line
147- * @param body Response body
148- * @param len Response body length
149- * @param send_body Whether to send body bytes
150- */
151-static void reply_json(int c, const char* hdr, const char* status, const char* body, size_t len, int send_body)
152-{
153- reply(c, hdr, status, HTTP_JSON, NULL, body, len, send_body, 0);
154-}
155-
156-/**
157- * @brief Send CORS preflight response
158- *
159- * @param c Connected client socket file descriptor
160- * @param hdr Request header block
161- */
162-static void reply_preflight(int c, const char* hdr)
163-{
164- reply(c, hdr, HTTP_204, NULL, NULL, NULL, (size_t)0, 0, 1);
165-}
166-
167-/**
168- * @brief Send text/plain HTTP response
169- *
170- * @param c Connected client socket file descriptor
171- * @param hdr Request header block
172- * @param status HTTP status line
173- * @param body Response body
174- */
175-static void reply_text(int c, const char* hdr, const char* status, const char* body)
176-{
177- size_t len = strlen(body);
178- reply(c, hdr, status, HTTP_TEXT, NULL, body, len, 1, 0);
179-}
180-
181-/**
182- * @brief Send 401 Unauthorized response
183- *
184- * @param c Connected client socket file descriptor
185- * @param hdr Request header block
186- * @param send_body Whether to send body bytes
187- */
188-static void reply_unauth(int c, const char* hdr, int send_body)
189-{
190- const char* body = "unauthorized\n";
191- size_t blen = strlen(body);
192-
193- auth_delay_sleep();
194-
195- reply(
196- c, hdr,
197- "HTTP/1.1 401 Unauthorized\r\n",
198- HTTP_TEXT,
199- "WWW-Authenticate: Basic realm=\"parados\"\r\n",
200- body,
201- blen,
202- send_body,
203- 0
204- );
205+ if (res->send_body && res->body && res->len > 0)
206+ (void)write_all(c, res->body, res->len);
207 }
208
209 /**
210@@ -662,7 +629,7 @@ static int stream_file(int c, const struct item* it, const char* hdr, int head_o
211 /* build absolute path */
212 if (join_path(full, sizeof(full), media_dir, it->path) < 0) {
213 LOG(verbose_log, "HTTP", "Stream FAILED Path too long");
214- reply_text(c, hdr, HTTP_500, "Server Error\n");
215+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "Server Error\n", sizeof("Server Error\n") - 1, 1, 0 });
216 return -1;
217 }
218
219@@ -670,7 +637,7 @@ static int stream_file(int c, const struct item* it, const char* hdr, int head_o
220 int fd = open(full, O_RDONLY);
221 if (fd < 0) {
222 LOG(verbose_log, "HTTP", "Open FAILED %s", it->path);
223- reply_text(c, hdr, HTTP_404, "Not Found\n");
224+ reply(c, hdr, &(struct response){ HTTP_404, HTTP_TEXT, NULL, "Not Found\n", sizeof("Not Found\n") - 1, 1, 0 });
225 return 0;
226 }
227
228@@ -678,14 +645,14 @@ static int stream_file(int c, const struct item* it, const char* hdr, int head_o
229 struct stat st;
230 if (fstat(fd, &st) < 0) {
231 close(fd);
232- reply_text(c, hdr, HTTP_500, "Server Error\n");
233+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "Server Error\n", sizeof("Server Error\n") - 1, 1, 0 });
234 return -1;
235 }
236
237 /* reject non-regular files */
238 if (!S_ISREG(st.st_mode)) {
239 close(fd);
240- reply_text(c, hdr, HTTP_404, "Not Found\n");
241+ reply(c, hdr, &(struct response){ HTTP_404, HTTP_TEXT, NULL, "Not Found\n", sizeof("Not Found\n") - 1, 1, 0 });
242 return 0;
243 }
244
245@@ -697,20 +664,20 @@ static int stream_file(int c, const struct item* it, const char* hdr, int head_o
246 int partial = parse_range(hdr, total, &start, &end);
247 if (partial == RANGE_BAD) {
248 close(fd);
249- reply_text(c, hdr, HTTP_400, "Bad Range\n");
250+ reply(c, hdr, &(struct response){ HTTP_400, HTTP_TEXT, NULL, "Bad Range\n", sizeof("Bad Range\n") - 1, 1, 0 });
251 return 0;
252 }
253
254 if (partial == RANGE_UNSAT) {
255 close(fd);
256- reply_text(c, hdr, HTTP_416, "Range Not Satisfiable\n");
257+ reply(c, hdr, &(struct response){ HTTP_416, HTTP_TEXT, NULL, "Range Not Satisfiable\n", sizeof("Range Not Satisfiable\n") - 1, 1, 0 });
258 return 0;
259 }
260
261 if (partial == RANGE_OK) {
262 if (lseek(fd, (off_t)start, SEEK_SET) < 0) {
263 close(fd);
264- reply_text(c, hdr, HTTP_500, "Server Error\n");
265+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "Server Error\n", sizeof("Server Error\n") - 1, 1, 0 });
266 return -1;
267 }
268 }
269@@ -723,7 +690,7 @@ static int stream_file(int c, const struct item* it, const char* hdr, int head_o
270
271 if (snprintf(ctype, sizeof(ctype), HTTP_CTYPE, type) >= (int)sizeof(ctype)) {
272 close(fd);
273- reply_text(c, hdr, HTTP_500, "Server Error\n");
274+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "Server Error\n", sizeof("Server Error\n") - 1, 1, 0 });
275 return -1;
276 }
277
278@@ -736,21 +703,37 @@ static int stream_file(int c, const struct item* it, const char* hdr, int head_o
279 start, end, total
280 ) >= (int)sizeof(extra)) {
281 close(fd);
282- reply_text(c, hdr, HTTP_500, "Server Error\n");
283+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "Server Error\n", sizeof("Server Error\n") - 1, 1, 0 });
284 return -1;
285 }
286
287- reply(c, hdr, HTTP_206, ctype, extra, NULL, len, 0, 0);
288+ reply(c, hdr, &(struct response){
289+ .status = HTTP_206,
290+ .ctype = ctype,
291+ .extra = extra,
292+ .body = NULL,
293+ .len = len,
294+ .send_body = 0,
295+ .preflight = 0,
296+ });
297 }
298 else {
299 /* non-partial */
300 if (snprintf(extra, sizeof(extra), HTTP_RANGE) >= (int)sizeof(extra)) {
301 close(fd);
302- reply_text(c, hdr, HTTP_500, "Server Error\n");
303+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "Server Error\n", sizeof("Server Error\n") - 1, 1, 0 });
304 return -1;
305 }
306
307- reply(c, hdr, HTTP_200, ctype, extra, NULL, len, 0, 0);
308+ reply(c, hdr, &(struct response){
309+ .status = HTTP_200,
310+ .ctype = ctype,
311+ .extra = extra,
312+ .body = NULL,
313+ .len = len,
314+ .send_body = 0,
315+ .preflight = 0,
316+ });
317 }
318
319 /* HEAD: no body */
320@@ -805,14 +788,13 @@ int http_handle(int c)
321 char method[16];
322 char path[1024];
323 char hdr[HTTP_REQ_MAX];
324-
325 const struct user* u = NULL;
326 method[0] = '\0';
327 path[0] = '\0';
328 hdr[0] = '\0';
329
330 if (read_request(c, method, sizeof(method), path, sizeof(path), hdr, sizeof(hdr)) < 0) {
331- reply_text(c, hdr, HTTP_400, "Bad Request\n");
332+ reply(c, hdr, &(struct response){ HTTP_400, HTTP_TEXT, NULL, "Bad Request\n", sizeof("Bad Request\n") - 1, 1, 0 });
333 return -1;
334 }
335 LOG(verbose_log, "HTTP", "Request %s %s", method, path);
336@@ -825,25 +807,34 @@ int http_handle(int c)
337 head_only = 1;
338 }
339 else if (strcmp(method, "OPTIONS") == 0) {
340- reply_preflight(c, hdr);
341+ reply(c, hdr, &(struct response){ HTTP_204, NULL, NULL, NULL, 0, 0, 1 });
342 return 0;
343 }
344 else {
345 LOG(verbose_log, "HTTP", "Method forbidden %s", method);
346- reply_text(c, hdr, HTTP_405, "Method Not Allowed\n");
347+ reply(c, hdr, &(struct response){ HTTP_405, HTTP_TEXT, NULL, "Method Not Allowed\n", sizeof("Method Not Allowed\n") - 1, 1, 0 });
348 return 0;
349 }
350
351 if (strcmp(path, "/ping") == 0) {
352 LOG(verbose_log, "HTTP", "Route /ping");
353- reply_text(c, hdr, HTTP_200, "OK\n");
354+ reply(c, hdr, &(struct response){ HTTP_200, HTTP_TEXT, NULL, "OK\n", sizeof("OK\n") - 1, 1, 0 });
355 return 0;
356 }
357
358 if (users.len > 0) {
359 u = users_auth_from_hdr(hdr);
360 if (!u) {
361- reply_unauth(c, hdr, !head_only);
362+ auth_delay_sleep();
363+ reply(c, hdr, &(struct response){
364+ .status = "HTTP/1.1 401 Unauthorized\r\n",
365+ .ctype = HTTP_TEXT,
366+ .extra = "WWW-Authenticate: Basic realm=\"parados\"\r\n",
367+ .body = "unauthorized\n",
368+ .len = sizeof("unauthorized\n") - 1,
369+ .send_body = !head_only,
370+ .preflight = 0,
371+ });
372 return 0;
373 }
374 }
375@@ -865,7 +856,7 @@ int http_handle(int c)
376 view.items = calloc(lib.len, sizeof(*view.items));
377 if (!view.items) {
378 mtx_unlock(&lib_lock);
379- reply_text(c, hdr, HTTP_500, "Server Error\n");
380+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "Server Error\n", sizeof("Server Error\n") - 1, 1, 0 });
381 return -1;
382 }
383 }
384@@ -885,15 +876,14 @@ int http_handle(int c)
385 mtx_unlock(&lib_lock);
386
387 LOG(verbose_log, "JSON", "Encode FAILED");
388- reply_text(c, hdr, HTTP_500, "JSON Encode Failed\n");
389+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "JSON Encode Failed\n", sizeof("JSON Encode Failed\n") - 1, 1, 0 });
390 return -1;
391 }
392
393 mtx_unlock(&lib_lock);
394
395 LOG(verbose_log, "JSON", "Encoded bytes %zu bytes", j.len);
396-
397- reply_json(c, hdr, HTTP_200, j.buf, j.len, !head_only);
398+ reply(c, hdr, &(struct response){ HTTP_200, HTTP_JSON, NULL, j.buf, j.len, !head_only, 0 });
399 json_free(&j);
400
401 if (u)
402@@ -907,7 +897,7 @@ int http_handle(int c)
403
404 if (users.len == 0) {
405 LOG(true, "HTTP", "Rescan forbidden Auth disabled");
406- reply_text(c, hdr, HTTP_403, "Forbidden\n");
407+ reply(c, hdr, &(struct response){ HTTP_403, HTTP_TEXT, NULL, "Forbidden\n", sizeof("Forbidden\n") - 1, 1, 0 });
408 return 0;
409 }
410
411@@ -938,13 +928,13 @@ int http_handle(int c)
412
413 if (ok < 0) {
414 LOG(true, "SCAN", "Rescan FAILED %s (%ld ms)", media_dir, ms);
415- reply_text(c, hdr, HTTP_500, "Rescan Failed\n");
416+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "Rescan Failed\n", sizeof("Rescan Failed\n") - 1, 1, 0 });
417 return -1;
418 }
419
420 LOG(true, "SCAN", "Rescan OK %zu -> %zu (%ld ms)", before, after, ms);
421
422- reply_text(c, hdr, HTTP_200, "OK\n");
423+ reply(c, hdr, &(struct response){ HTTP_200, HTTP_TEXT, NULL, "OK\n", sizeof("OK\n") - 1, 1, 0 });
424 return 0;
425 }
426
427@@ -975,7 +965,7 @@ int http_handle(int c)
428
429 struct stat st;
430 if (stat_item(&tmp, &st) < 0) {
431- reply_text(c, hdr, HTTP_404, "Not Found\n");
432+ reply(c, hdr, &(struct response){ HTTP_404, HTTP_TEXT, NULL, "Not Found\n", sizeof("Not Found\n") - 1, 1, 0 });
433 return 0;
434 }
435
436@@ -983,18 +973,19 @@ int http_handle(int c)
437
438 struct json j;
439 if (json_meta(&j, &tmp, (size_t)st.st_size, (long)st.st_mtime, type) < 0) {
440- reply_text(c, hdr, HTTP_500, "JSON Failed\n");
441+ reply(c, hdr, &(struct response){ HTTP_500, HTTP_TEXT, NULL, "JSON Failed\n", sizeof("JSON Failed\n") - 1, 1, 0 });
442 return -1;
443 }
444
445- reply_json(c, hdr, HTTP_200, j.buf, j.len, !head_only);
446+ reply(c, hdr, &(struct response){ HTTP_200, HTTP_JSON, NULL, j.buf, j.len, !head_only, 0 });
447 json_free(&j);
448
449 return 0;
450 }
451
452 LOG(verbose_log, "HTTP", "Route not found %s", path);
453- reply_text(c, hdr, HTTP_404, "Not Found\n");
454+ reply(c, hdr, &(struct response){ HTTP_404, HTTP_TEXT, NULL, "Not Found\n", sizeof("Not Found\n") - 1, 1, 0 });
455
456 return 0;
457 }
458+