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