1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <stdio.h>
30 #include <sys/file.h>
31 #include <sys/stat.h>
32
33 #include "service.h"
34 #include "../util/io.h"
35 #include "../util/pblock.h"
36 #include "../util/util.h"
37 #include "../daemon/protocol.h"
38 #include "../daemon/vfs.h"
39
40 #include "../util/strbuf.h"
41 #include <ucx/string.h>
42 #include <ucx/utils.h>
43
44 #include <errno.h>
45
46
47
48
49
50
51
52
53 SYS_FILE prepare_service_file(Session *sn, Request *rq, VFSContext *vfs,
struct stat *s) {
54 char *path = pblock_findkeyval(pb_key_path, rq->vars);
55
56
57 SYS_FILE fd = vfs_open(vfs, path,
O_RDONLY);
58 if(!fd) {
59
60 return NULL;
61 }
62
63
64 if(vfs_fstat(vfs, fd, s) !=
0) {
65
66 protocol_status(sn, rq,
500,
NULL);
67 return NULL;
68 }
69
70
71 if(
S_ISDIR(s->st_mode)) {
72 pblock_nvinsert(
"content-length",
"0", rq->srvhdrs);
73 pblock_removekey(pb_key_content_type, rq->srvhdrs);
74 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
75 size_t urilen = strlen(uri);
76 char *location = pool_malloc(sn->pool, urilen +
2);
77 memcpy(location, uri, urilen);
78 location[urilen] =
'/';
79 location[urilen+
1] =
'\0';
80 pblock_kvinsert(pb_key_location, location, urilen +
1, rq->srvhdrs);
81 protocol_status(sn, rq,
302,
NULL);
82 http_start_response(sn, rq);
83 vfs_close(fd);
84 return NULL;
85 }
86
87
88 if(http_set_finfo(sn, rq, s) !=
REQ_PROCEED) {
89 vfs_close(fd);
90 return NULL;
91 }
92
93
94 pblock_kvinsert(pb_key_accept_ranges,
"bytes",
5, rq->srvhdrs);
95
96
97 protocol_status(sn, rq,
200,
NULL);
98
99 return fd;
100 }
101
102 static void free_range(Session *sn, HttpRange *range) {
103 HttpRange *elm = range;
104 while(elm) {
105 HttpRange *next = elm->next;
106 pool_free(sn->pool, elm);
107 elm = next;
108 }
109 }
110
111 static HttpRange* parse_range(Session *sn,
char *header,
int *status) {
112 *status =
PROTOCOL_OK;
113
114 sstr_t range = sstrtrim(sstr(header));
115 if(!sstrprefix(range,
S(
"bytes="))) {
116
117 return NULL;
118 }
119
120
121 range = sstrsubs(range,
6);
122 if(range.length <
1) {
123 return NULL;
124 }
125
126 HttpRange *range_list =
NULL;
127 HttpRange *last =
NULL;
128 off_t begin = -
1;
129 int start =
0;
130 int hasbegin =
0;
131 for(
int i=
0;i<=range.length;i++) {
132 char c = range.ptr[i];
133 if(c ==
'-') {
134 sstr_t num = sstrsubsl(range, start, i-start);
135 if(num.length ==
0) {
136
137 hasbegin =
1;
138 begin = -
1;
139 start = i+
1;
140 continue;
141 }
142 char *end;
143 long long n = strtoll(num.ptr, &end,
10);
144 if(errno ==
0 && end == range.ptr + i && n >=
0) {
145 begin = n;
146 hasbegin =
1;
147 start = i+
1;
148 }
else {
149
150 free_range(sn, range_list);
151 return NULL;
152 }
153 }
else if(c ==
',' || c ==
'\0') {
154 sstr_t num = sstrsubsl(range, start, i-start);
155 if(hasbegin) {
156 long long n;
157 if(num.length ==
0) {
158
159 n = -
1;
160 }
else {
161 char *end;
162 n = strtoll(num.ptr, &end,
10);
163 if(errno !=
0 || end != range.ptr + i || n <
0) {
164
165 free_range(sn, range_list);
166 return NULL;
167 }
168 }
169
170 if(!(begin <
0 && n <
0)) {
171
172 HttpRange *rangeelm = pool_malloc(sn->pool,
sizeof(HttpRange));
173 if(!rangeelm) {
174 free_range(sn, range_list);
175 *status =
PROTOCOL_SERVER_ERROR;
176 return NULL;
177 }
178 rangeelm->begin = begin;
179 rangeelm->end = n;
180 rangeelm->next =
NULL;
181 if(!last) {
182 range_list = rangeelm;
183 last = rangeelm;
184 }
else {
185 last->next = rangeelm;
186 last = rangeelm;
187 }
188
189 hasbegin =
0;
190 start = i+
1;
191 continue;
192 }
193 }
194
195
196 free_range(sn, range_list);
197 return NULL;
198 }
199 }
200
201 return range_list;
202 }
203
204 static int validate_range(HttpRange *range,
struct stat *finfo,
int *status) {
205 off_t max_len = finfo->st_size;
206 while(range) {
207 if(range->begin >
0 && range->end >
0) {
208 if(range->end < range->begin) {
209 *status =
PROTOCOL_REQUESTED_RANGE_NOT_SATISFIABLE;
210 return 0;
211 }
212 }
213 if(range->begin >= max_len) {
214 *status =
PROTOCOL_REQUESTED_RANGE_NOT_SATISFIABLE;
215 return 0;
216 }
217 if(range->end >= max_len) {
218 *status =
PROTOCOL_REQUESTED_RANGE_NOT_SATISFIABLE;
219 return 0;
220 }
221
222 range = range->next;
223 }
224
225
226
227 return 1;
228 }
229
230
231
232
233
234 static void range2off(HttpRange *range,
off_t filelen,
off_t *begin,
off_t *length) {
235 if(range->begin <
0) {
236
237 *begin = filelen - range->end;
238 *length = range->end;
239 }
else if(range->end <
0) {
240
241 *begin = range->begin;
242 *length = filelen - range->begin;
243 }
else {
244
245 *begin = range->begin;
246 *length = range->end +
1 - range->begin;
247 }
248 }
249
250 #define SF_MAX_LEN 0x8000000
251
252 static int send_range(Session *sn,
SYS_FILE fd,
off_t offset,
off_t length,
char *header,
int headerlen) {
253 off_t remaining = length;
254
255 sendfiledata sfd;
256 sfd.fd = fd;
257 sfd.header = header;
258 sfd.hlen = headerlen;
259 sfd.trailer =
NULL;
260
261 while(remaining >
0) {
262 size_t sflen = remaining <
SF_MAX_LEN ? remaining :
SF_MAX_LEN;
263 sfd.offset = offset;
264 sfd.len = sflen;
265
266 ssize_t r = net_sendfile(sn->csd, &sfd);
267 if(r <
0) {
268 return -
1;
269 }
270
271 sfd.header =
NULL;
272 offset += r;
273 remaining -= r;
274 }
275
276 return 0;
277 }
278
279
280 static void send_range_cleanup(AsyncSendRange *asr) {
281 WSBool error = asr->error;
282 Session *sn = asr->sn;
283 Request *rq = asr->rq;
284
285 pool_handle_t *pool = asr->sn->pool;
286 vfs_close(asr->in);
287 pool_free(pool, asr->aio->buf);
288 pool_free(pool, asr->aio);
289 pool_free(pool, asr->readev);
290 pool_free(pool, asr->writeev);
291 pool_free(pool, asr);
292
293 int ret =
REQ_PROCEED;
294 if(error) {
295 rq->rq_attr.keep_alive =
0;
296 ret =
REQ_ABORTED;
297 }
298
299 nsapi_function_return(sn, rq, ret);
300 }
301
302 static int send_buf(
303 SYS_NETFD out,
304 char *restrict buf,
305 size_t len,
306 size_t *restrict pos)
307 {
308 while(*pos < len) {
309 ssize_t w = net_write(out, buf + *pos, len - *pos);
310 if(w <=
0) {
311 return -
1;
312 }
313 *pos += w;
314 }
315 return 0;
316 }
317
318 static int send_bytes(AsyncSendRange *asr, WSBool *completed) {
319 *completed =
FALSE;
320 if(asr->header) {
321 if(send_buf(asr->out, asr->header, asr->headerlen, &asr->headerpos)) {
322 if(net_errno(asr->out) ==
EAGAIN) {
323 return 0;
324 }
else {
325 asr->error =
TRUE;
326 return 1;
327 }
328 }
329 if(asr->headerpos >= asr->headerlen) {
330 asr->header =
NULL;
331 }
332 }
333
334 if(send_buf(asr->out, asr->aio->buf, asr->aio->result, &asr->wpos)) {
335 if(net_errno(asr->out) ==
EAGAIN) {
336 return 0;
337 }
else {
338 asr->error =
TRUE;
339 return 1;
340 }
341 }
342
343 if(!asr->read_complete) {
344
345 asr->aio->offset += asr->aio->result;
346 size_t length = asr->end - asr->offset;
347 asr->aio->nbytes =
AIO_BUF_SIZE < length ?
AIO_BUF_SIZE : length;
348 asr->read_inprogress =
TRUE;
349 if(system_aio_read(asr->aio)) {
350 asr->error =
TRUE;
351 return 1;
352 }
353 }
354 *completed =
TRUE;
355 return 0;
356 }
357
358 static int send_range_readevent(EventHandler *ev, Event *event) {
359 AsyncSendRange *asr = event->cookie;
360 asr->read_inprogress =
FALSE;
361 asr->wpos =
0;
362 asr->offset += asr->aio->result;
363 if(asr->error || asr->aio->result <
0) {
364 return 0;
365 }
366
367 int ret =
1;
368 if(asr->aio->result ==
0 || asr->offset >= asr->end) {
369 asr->read_complete =
TRUE;
370 ret =
0;
371 }
372
373 WSBool completed;
374 if(send_bytes(asr, &completed)) {
375 return 0;
376 }
377 if(!completed && !asr->write_inprogress) {
378 asr->write_inprogress =
TRUE;
379 if(event_pollout(ev, asr->out, asr->writeev)) {
380 asr->error =
TRUE;
381 return 0;
382 }
383 }
384
385 return ret;
386 }
387
388 static int send_range_writeevent(EventHandler *ev, Event *event) {
389 AsyncSendRange *asr = event->cookie;
390 if(asr->error) {
391 return 1;
392 }
393
394 WSBool completed;
395 if(send_bytes(asr, &completed)) {
396 return 1;
397 }
398
399 if(completed) {
400 return 0;
401 }
402
403 return 1;
404 }
405
406 static int send_range_aio_finish(EventHandler *ev, Event *event) {
407 AsyncSendRange *asr = event->cookie;
408 if(!asr->write_inprogress) {
409 send_range_cleanup(asr);
410 }
411 asr->read_inprogress =
FALSE;
412 return 0;
413 }
414
415 static int send_range_poll_finish(EventHandler *ev, Event *event) {
416 AsyncSendRange *asr = event->cookie;
417 if(!asr->read_inprogress) {
418 send_range_cleanup(asr);
419 }
420 asr->write_inprogress =
FALSE;
421 return 0;
422 }
423
424 static int send_range_aio(Session *sn, Request *rq,
SYS_FILE fd,
off_t offset,
off_t length,
char *header,
int headerlen) {
425 net_setnonblock(sn->csd,
TRUE);
426
427
428 ssize_t hw = net_write(sn->csd, header, headerlen);
429 if(hw <
0) {
430 if(net_errno(sn->csd) ==
EAGAIN) {
431 hw =
0;
432 }
else {
433 return REQ_ABORTED;
434 }
435 }
436
437 AsyncSendRange *asr = pool_malloc(sn->pool,
sizeof(AsyncSendRange));
438 asr->sn = sn;
439 asr->rq = rq;
440 asr->in = fd;
441 asr->out = sn->csd;
442 asr->offset = offset;
443 asr->end = offset + length;
444
445 asr->pos = offset;
446 asr->read_complete =
FALSE;
447 asr->read_inprogress =
FALSE;
448 asr->write_inprogress =
FALSE;
449 asr->error =
FALSE;
450 if(hw == headerlen) {
451 asr->header =
NULL;
452 asr->headerlen =
0;
453 asr->headerpos =
0;
454 }
else {
455 asr->header = header;
456 asr->headerlen = headerlen;
457 asr->headerpos = hw;
458 }
459
460 Event *readev = pool_malloc(sn->pool,
sizeof(Event));
461 ZERO(readev,
sizeof(Event));
462 readev->cookie = asr;
463 readev->fn = send_range_readevent;
464 readev->finish = send_range_aio_finish;
465
466 Event *writeev = pool_malloc(sn->pool,
sizeof(Event));
467 ZERO(writeev,
sizeof(Event));
468 writeev->cookie = asr;
469 writeev->fn = send_range_writeevent;
470 writeev->finish = send_range_poll_finish;
471
472 asr->readev = readev;
473 asr->writeev = writeev;
474
475 aiocb_s *aio = pool_malloc(sn->pool,
sizeof(aiocb_s));
476 aio->buf = pool_malloc(sn->pool,
AIO_BUF_SIZE);
477 aio->nbytes =
AIO_BUF_SIZE < length ?
AIO_BUF_SIZE : length;
478 aio->filedes = fd;
479 aio->offset = offset;
480 aio->evhandler = sn->ev;
481 aio->event = readev;
482
483 asr->aio = aio;
484 asr->wpos =
0;
485
486 asr->read_inprogress =
TRUE;
487 if(system_aio_read(aio)) {
488 send_range_cleanup(asr);
489 return REQ_ABORTED;
490 }
491 asr->read_inprogress =
TRUE;
492
493 return REQ_PROCESSING;
494 }
495
496 struct multi_range_elm {
497 sstr_t header;
498 off_t offset;
499 off_t length;
500 };
501
502 static int send_multi_range(Session *sn, Request *rq,
SYS_FILE fd,
off_t filelen, HttpRange *range) {
503 pb_param *content_type = pblock_remove(
"content-type", rq->srvhdrs);
504
505 char sep[
64];
506 int seplen = util_mime_separator(sep);
507
508 sstr_t newct = ucx_sprintf(
"multipart/byteranges; boundary=%s", sep+
4);
509 pblock_kvinsert(
510 pb_key_content_type,
511 newct.ptr,
512 newct.length,
513 rq->srvhdrs);
514 free(newct.ptr);
515
516
517 off_t response_len =
0;
518
519 int nrange =
0;
520 HttpRange *rangeelm = range;
521 while(rangeelm) {
522 nrange++;
523 rangeelm = rangeelm->next;
524 }
525
526 struct multi_range_elm *r = calloc(nrange,
sizeof(
struct multi_range_elm));
527 rangeelm = range;
528 int i=
0;
529 while(rangeelm) {
530 range2off(rangeelm, filelen, &(r[i].offset), &(r[i].length));
531 r[i].header = ucx_sprintf(
532 "%s\r\nContent-Type: %s\r\nContent-Range: bytes %lld-%lld/%lld\r\n\r\n",
533 sep,
534 content_type->value,
535 (
long long)r[i].offset,
536 (
long long)r[i].offset+r[i].length -
1,
537 filelen);
538
539 response_len += r[i].header.length + r[i].length;
540
541 rangeelm = rangeelm->next;
542 i++;
543 }
544
545 response_len += seplen +
4;
546
547
548 pblock_kllinsert(
549 pb_key_content_length,
550 (
long long)response_len,
551 rq->srvhdrs);
552
553
554 http_start_response(sn, rq);
555
556 rangeelm = range;
557 i =
0;
558 while(rangeelm) {
559 if(send_range(sn, fd, r[i].offset, r[i].length, r[i].header.ptr, r[i].header.length)) {
560
561 }
562 rangeelm = rangeelm->next;
563 i++;
564 }
565 net_printf(sn->csd,
"%s--\r\n", sep);
566
567 free(r);
568 return 0;
569 }
570
571 int send_file(pblock *pb, Session *sn, Request *rq) {
572 struct stat s;
573 VFSContext *vfs = vfs_request_context(sn, rq);
574 SYS_FILE fd = prepare_service_file(sn, rq, vfs, &s);
575 if(!fd) {
576
577
578 return REQ_ABORTED;
579 }
580
581
582 char *range_header = pblock_findkeyval(pb_key_range, rq->headers);
583 HttpRange *range =
NULL;
584 if(range_header) {
585 int status;
586 range = parse_range(sn, range_header, &status);
587 if(status !=
PROTOCOL_OK) {
588 protocol_status(sn, rq, status,
NULL);
589 vfs_close(fd);
590 return REQ_ABORTED;
591 }
592
593 if(!validate_range(range, &s, &status)) {
594 protocol_status(sn, rq, status,
NULL);
595 free_range(sn, range);
596 vfs_close(fd);
597 return REQ_ABORTED;
598 }
599 }
600
601 int single_range =
1;
602 off_t offset;
603 off_t length;
604 if(range) {
605 protocol_status(sn, rq,
206,
NULL);
606 pblock_removekey(pb_key_content_length, rq->srvhdrs);
607
608 if(range->next) {
609 single_range =
0;
610 }
else {
611 range2off(range, s.st_size, &offset, &length);
612
613 pblock_kllinsert(
614 pb_key_content_length,
615 (
long long)length,
616 rq->srvhdrs);
617
618 sstr_t content_range = ucx_sprintf(
619 "%lld-%lld/%lld",
620 (
long long)offset,
621 (
long long)offset+length -
1,
622 s.st_size);
623 pblock_kvinsert(
624 pb_key_content_range,
625 content_range.ptr,
626 content_range.length,
627 rq->srvhdrs);
628 free(content_range.ptr);
629 }
630 }
else {
631 offset =
0;
632 length = s.st_size;
633 }
634
635 int ret =
REQ_NOACTION;
636 if(single_range) {
637
638 http_start_response(sn, rq);
639
640
641
642
643
644
645
646 if(send_range(sn, fd, offset, length,
NULL,
0)) {
647
648 }
649 }
else {
650 ret = send_multi_range(sn, rq, fd, s.st_size, range);
651
652 }
653
654
655 vfs_close(fd);
656 free_range(sn, range);
657
658 return ret;
659 }
660
661
662
663 int service_hello(pblock *pb, Session *sn, Request *rq) {
664 pblock_removekey(pb_key_content_type, rq->srvhdrs);
665 pblock_nvinsert(
"content-type",
"text/plain", rq->srvhdrs);
666 pblock_nninsert(
"content-length",
13, rq->srvhdrs);
667 protocol_status(sn, rq,
200,
NULL);
668 http_start_response(sn, rq);
669 net_write(sn->csd,
"Hello World!\n",
13);
670 return REQ_PROCEED;
671 }
672
673 static int ws_msghandler(WebSocket *ws, WSMessage *msg) {
674 if(msg->type ==
1) {
675 printf(
"Message(text): %.*s\n", (
int)msg->length, msg->data);
676 websocket_send_text(ws->userdata,
"hello",
5);
677 }
else {
678 printf(
"Message: opcode: %d | length: %d\n", msg->type, (
int)msg->length);
679 }
680 return 0;
681 }
682
683 int service_ws_hello(pblock *pb, Session *sn, Request *rq) {
684 WebSocket ws;
685 ZERO(&ws,
sizeof(WebSocket));
686 ws.userdata = sn->csd;
687
688 ws.on_message = ws_msghandler;
689 return http_handle_websocket(sn, rq, &ws);
690 }
691
692 int service_index(pblock *pb, Session *sn, Request *rq) {
693
694
695 char *path = pblock_findkeyval(pb_key_path, rq->vars);
696 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
697
698 sstr_t r_uri = sstr(uri);
699
700
701 VFSContext *vfs = vfs_request_context(sn, rq);
702 VFS_DIR dir = vfs_opendir(vfs, path);
703 if(!dir) {
704 return REQ_ABORTED;
705 }
706
707 sbuf_t *out = sbuf_new(
1024);
708
709
710 sbuf_puts(out,
"<html>\n<head>\n<title>Index of ");
711 sbuf_puts(out, uri);
712 sbuf_puts(out,
"</title>\n</head><body>\n<h1>Index of ");
713 sbuf_puts(out, uri);
714 sbuf_puts(out,
"</h1><hr>\n\n");
715
716
717 VFS_ENTRY f;
718 while(vfs_readdir(dir, &f)) {
719 sstr_t filename = sstr(f.name);
720
721 sbuf_puts(out,
"<a href=\"");
722 sbuf_append(out, r_uri);
723 sbuf_append(out, filename);
724 sbuf_puts(out,
"\">");
725 sbuf_append(out, filename);
726 sbuf_puts(out,
"</a><br>\n");
727 }
728
729 sbuf_puts(out,
"\n</body>\n</html>\n");
730
731
732 pblock_removekey(pb_key_content_type, rq->srvhdrs);
733 pblock_kvinsert(pb_key_content_type,
"text/html",
9, rq->srvhdrs);
734 pblock_nninsert(
"content-length", out->length, rq->srvhdrs);
735 protocol_status(sn, rq,
200,
NULL);
736 http_start_response(sn, rq);
737
738 net_write(sn->csd, out->ptr, out->length);
739
740
741 vfs_closedir(dir);
742 sbuf_free(out);
743
744 return REQ_PROCEED;
745 }
746
747 int send_options(pblock *pb, Session *sn, Request *rq) {
748 char *allow =
"HEAD, GET, PUT, DELETE, TRACE, OPTIONS, MOVE, COPY, "
749 "PROPFIND, PROPPATCH, MKCOL, LOCK, UNLOCK, ACL, REPORT";
750 char *dav =
"1,2,access-control";
751
752 pblock_removekey(pb_key_content_type, rq->srvhdrs);
753 pblock_nvinsert(
"allow", allow, rq->srvhdrs);
754 pblock_nvinsert(
"dav", dav, rq->srvhdrs);
755 pblock_nninsert(
"content-length",
0, rq->srvhdrs);
756 protocol_status(sn, rq,
204,
NULL);
757 http_start_response(sn, rq);
758
759 return REQ_PROCEED;
760 }
761