Mercurial > hg > nginx-quic
comparison src/http/v3/ngx_http_v3_request.c @ 8237:1efee5e4194c quic
HTTP/3: introduced ngx_http_v3_filter.
The filter is responsible for creating HTTP/3 response header and body.
The change removes differences to the default branch for
ngx_http_chunked_filter_module and ngx_http_header_filter_module.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Fri, 27 Nov 2020 17:46:21 +0000 |
parents | 9dce2978e4fd |
children | 96eb6915d244 |
comparison
equal
deleted
inserted
replaced
8236:e9bd4305e68b | 8237:1efee5e4194c |
---|---|
8 #include <ngx_config.h> | 8 #include <ngx_config.h> |
9 #include <ngx_core.h> | 9 #include <ngx_core.h> |
10 #include <ngx_http.h> | 10 #include <ngx_http.h> |
11 | 11 |
12 | 12 |
13 /* static table indices */ | |
14 #define NGX_HTTP_V3_HEADER_AUTHORITY 0 | |
15 #define NGX_HTTP_V3_HEADER_PATH_ROOT 1 | |
16 #define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 | |
17 #define NGX_HTTP_V3_HEADER_DATE 6 | |
18 #define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 | |
19 #define NGX_HTTP_V3_HEADER_LOCATION 12 | |
20 #define NGX_HTTP_V3_HEADER_METHOD_GET 17 | |
21 #define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 | |
22 #define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 | |
23 #define NGX_HTTP_V3_HEADER_STATUS_200 25 | |
24 #define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 | |
25 #define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 | |
26 #define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 | |
27 #define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE 72 | |
28 #define NGX_HTTP_V3_HEADER_SERVER 92 | |
29 #define NGX_HTTP_V3_HEADER_USER_AGENT 95 | |
30 | |
31 | |
32 static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, | 13 static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, |
33 ngx_str_t *name, ngx_str_t *value); | 14 ngx_str_t *name, ngx_str_t *value); |
34 static ngx_int_t ngx_http_v3_push_resources(ngx_http_request_t *r, | |
35 ngx_chain_t ***out); | |
36 static ngx_int_t ngx_http_v3_push_resource(ngx_http_request_t *r, | |
37 ngx_str_t *path, ngx_chain_t ***out); | |
38 static ngx_int_t ngx_http_v3_create_push_request( | |
39 ngx_http_request_t *pr, ngx_str_t *path, uint64_t push_id); | |
40 static ngx_int_t ngx_http_v3_set_push_header(ngx_http_request_t *r, | |
41 const char *name, ngx_str_t *value); | |
42 static void ngx_http_v3_push_request_handler(ngx_event_t *ev); | |
43 static ngx_chain_t *ngx_http_v3_create_push_promise(ngx_http_request_t *r, | |
44 ngx_str_t *path, uint64_t push_id); | |
45 | 15 |
46 | 16 |
47 struct { | 17 struct { |
48 ngx_str_t name; | 18 ngx_str_t name; |
49 ngx_uint_t method; | 19 ngx_uint_t method; |
441 | 411 |
442 failed: | 412 failed: |
443 | 413 |
444 return NGX_ERROR; | 414 return NGX_ERROR; |
445 } | 415 } |
446 | |
447 | |
448 ngx_chain_t * | |
449 ngx_http_v3_create_header(ngx_http_request_t *r) | |
450 { | |
451 u_char *p; | |
452 size_t len, n; | |
453 ngx_buf_t *b; | |
454 ngx_str_t host; | |
455 ngx_uint_t i, port; | |
456 ngx_chain_t *out, *hl, *cl, **ll; | |
457 ngx_list_part_t *part; | |
458 ngx_table_elt_t *header; | |
459 ngx_connection_t *c; | |
460 ngx_http_core_loc_conf_t *clcf; | |
461 ngx_http_core_srv_conf_t *cscf; | |
462 u_char addr[NGX_SOCKADDR_STRLEN]; | |
463 | |
464 c = r->connection; | |
465 | |
466 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 create header"); | |
467 | |
468 out = NULL; | |
469 ll = &out; | |
470 | |
471 if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0 | |
472 && r->method != NGX_HTTP_HEAD) | |
473 { | |
474 if (ngx_http_v3_push_resources(r, &ll) != NGX_OK) { | |
475 return NULL; | |
476 } | |
477 } | |
478 | |
479 len = ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); | |
480 | |
481 if (r->headers_out.status == NGX_HTTP_OK) { | |
482 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
483 NGX_HTTP_V3_HEADER_STATUS_200); | |
484 | |
485 } else { | |
486 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
487 NGX_HTTP_V3_HEADER_STATUS_200, | |
488 NULL, 3); | |
489 } | |
490 | |
491 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
492 | |
493 if (r->headers_out.server == NULL) { | |
494 if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { | |
495 n = sizeof(NGINX_VER) - 1; | |
496 | |
497 } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { | |
498 n = sizeof(NGINX_VER_BUILD) - 1; | |
499 | |
500 } else { | |
501 n = sizeof("nginx") - 1; | |
502 } | |
503 | |
504 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
505 NGX_HTTP_V3_HEADER_SERVER, | |
506 NULL, n); | |
507 } | |
508 | |
509 if (r->headers_out.date == NULL) { | |
510 len += ngx_http_v3_encode_header_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE, | |
511 NULL, ngx_cached_http_time.len); | |
512 } | |
513 | |
514 if (r->headers_out.content_type.len) { | |
515 n = r->headers_out.content_type.len; | |
516 | |
517 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
518 && r->headers_out.charset.len) | |
519 { | |
520 n += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
521 } | |
522 | |
523 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
524 NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, | |
525 NULL, n); | |
526 } | |
527 | |
528 if (r->headers_out.content_length == NULL) { | |
529 if (r->headers_out.content_length_n > 0) { | |
530 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
531 NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, | |
532 NULL, NGX_OFF_T_LEN); | |
533 | |
534 } else if (r->headers_out.content_length_n == 0) { | |
535 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
536 NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); | |
537 } | |
538 } | |
539 | |
540 if (r->headers_out.last_modified == NULL | |
541 && r->headers_out.last_modified_time != -1) | |
542 { | |
543 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
544 NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, | |
545 sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); | |
546 } | |
547 | |
548 if (r->headers_out.location | |
549 && r->headers_out.location->value.len | |
550 && r->headers_out.location->value.data[0] == '/' | |
551 && clcf->absolute_redirect) | |
552 { | |
553 r->headers_out.location->hash = 0; | |
554 | |
555 if (clcf->server_name_in_redirect) { | |
556 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
557 host = cscf->server_name; | |
558 | |
559 } else if (r->headers_in.server.len) { | |
560 host = r->headers_in.server; | |
561 | |
562 } else { | |
563 host.len = NGX_SOCKADDR_STRLEN; | |
564 host.data = addr; | |
565 | |
566 if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { | |
567 return NULL; | |
568 } | |
569 } | |
570 | |
571 port = ngx_inet_get_port(c->local_sockaddr); | |
572 | |
573 n = sizeof("https://") - 1 + host.len | |
574 + r->headers_out.location->value.len; | |
575 | |
576 if (clcf->port_in_redirect) { | |
577 port = (port == 443) ? 0 : port; | |
578 | |
579 } else { | |
580 port = 0; | |
581 } | |
582 | |
583 if (port) { | |
584 n += sizeof(":65535") - 1; | |
585 } | |
586 | |
587 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
588 NGX_HTTP_V3_HEADER_LOCATION, NULL, n); | |
589 | |
590 } else { | |
591 ngx_str_null(&host); | |
592 port = 0; | |
593 } | |
594 | |
595 #if (NGX_HTTP_GZIP) | |
596 if (r->gzip_vary) { | |
597 if (clcf->gzip_vary) { | |
598 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
599 NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); | |
600 | |
601 } else { | |
602 r->gzip_vary = 0; | |
603 } | |
604 } | |
605 #endif | |
606 | |
607 part = &r->headers_out.headers.part; | |
608 header = part->elts; | |
609 | |
610 for (i = 0; /* void */; i++) { | |
611 | |
612 if (i >= part->nelts) { | |
613 if (part->next == NULL) { | |
614 break; | |
615 } | |
616 | |
617 part = part->next; | |
618 header = part->elts; | |
619 i = 0; | |
620 } | |
621 | |
622 if (header[i].hash == 0) { | |
623 continue; | |
624 } | |
625 | |
626 len += ngx_http_v3_encode_header_l(NULL, &header[i].key, | |
627 &header[i].value); | |
628 } | |
629 | |
630 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); | |
631 | |
632 b = ngx_create_temp_buf(r->pool, len); | |
633 if (b == NULL) { | |
634 return NULL; | |
635 } | |
636 | |
637 b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, | |
638 0, 0, 0); | |
639 | |
640 if (r->headers_out.status == NGX_HTTP_OK) { | |
641 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
642 NGX_HTTP_V3_HEADER_STATUS_200); | |
643 | |
644 } else { | |
645 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
646 NGX_HTTP_V3_HEADER_STATUS_200, | |
647 NULL, 3); | |
648 b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); | |
649 } | |
650 | |
651 if (r->headers_out.server == NULL) { | |
652 if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { | |
653 p = (u_char *) NGINX_VER; | |
654 n = sizeof(NGINX_VER) - 1; | |
655 | |
656 } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { | |
657 p = (u_char *) NGINX_VER_BUILD; | |
658 n = sizeof(NGINX_VER_BUILD) - 1; | |
659 | |
660 } else { | |
661 p = (u_char *) "nginx"; | |
662 n = sizeof("nginx") - 1; | |
663 } | |
664 | |
665 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
666 NGX_HTTP_V3_HEADER_SERVER, | |
667 p, n); | |
668 } | |
669 | |
670 if (r->headers_out.date == NULL) { | |
671 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
672 NGX_HTTP_V3_HEADER_DATE, | |
673 ngx_cached_http_time.data, | |
674 ngx_cached_http_time.len); | |
675 } | |
676 | |
677 if (r->headers_out.content_type.len) { | |
678 n = r->headers_out.content_type.len; | |
679 | |
680 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
681 && r->headers_out.charset.len) | |
682 { | |
683 n += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
684 } | |
685 | |
686 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
687 NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, | |
688 NULL, n); | |
689 | |
690 p = b->last; | |
691 b->last = ngx_cpymem(b->last, r->headers_out.content_type.data, | |
692 r->headers_out.content_type.len); | |
693 | |
694 if (r->headers_out.content_type_len == r->headers_out.content_type.len | |
695 && r->headers_out.charset.len) | |
696 { | |
697 b->last = ngx_cpymem(b->last, "; charset=", | |
698 sizeof("; charset=") - 1); | |
699 b->last = ngx_cpymem(b->last, r->headers_out.charset.data, | |
700 r->headers_out.charset.len); | |
701 | |
702 /* update r->headers_out.content_type for possible logging */ | |
703 | |
704 r->headers_out.content_type.len = b->last - p; | |
705 r->headers_out.content_type.data = p; | |
706 } | |
707 } | |
708 | |
709 if (r->headers_out.content_length == NULL) { | |
710 if (r->headers_out.content_length_n > 0) { | |
711 p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); | |
712 n = p - b->last; | |
713 | |
714 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
715 NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, | |
716 NULL, n); | |
717 | |
718 b->last = ngx_sprintf(b->last, "%O", | |
719 r->headers_out.content_length_n); | |
720 | |
721 } else if (r->headers_out.content_length_n == 0) { | |
722 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
723 NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); | |
724 } | |
725 } | |
726 | |
727 if (r->headers_out.last_modified == NULL | |
728 && r->headers_out.last_modified_time != -1) | |
729 { | |
730 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
731 NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, | |
732 sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); | |
733 | |
734 b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); | |
735 } | |
736 | |
737 if (host.data) { | |
738 n = sizeof("https://") - 1 + host.len | |
739 + r->headers_out.location->value.len; | |
740 | |
741 if (port) { | |
742 n += ngx_sprintf(b->last, ":%ui", port) - b->last; | |
743 } | |
744 | |
745 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
746 NGX_HTTP_V3_HEADER_LOCATION, | |
747 NULL, n); | |
748 | |
749 p = b->last; | |
750 b->last = ngx_cpymem(b->last, "https://", sizeof("https://") - 1); | |
751 b->last = ngx_cpymem(b->last, host.data, host.len); | |
752 | |
753 if (port) { | |
754 b->last = ngx_sprintf(b->last, ":%ui", port); | |
755 } | |
756 | |
757 b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, | |
758 r->headers_out.location->value.len); | |
759 | |
760 /* update r->headers_out.location->value for possible logging */ | |
761 | |
762 r->headers_out.location->value.len = b->last - p; | |
763 r->headers_out.location->value.data = p; | |
764 ngx_str_set(&r->headers_out.location->key, "Location"); | |
765 } | |
766 | |
767 #if (NGX_HTTP_GZIP) | |
768 if (r->gzip_vary) { | |
769 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
770 NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); | |
771 } | |
772 #endif | |
773 | |
774 part = &r->headers_out.headers.part; | |
775 header = part->elts; | |
776 | |
777 for (i = 0; /* void */; i++) { | |
778 | |
779 if (i >= part->nelts) { | |
780 if (part->next == NULL) { | |
781 break; | |
782 } | |
783 | |
784 part = part->next; | |
785 header = part->elts; | |
786 i = 0; | |
787 } | |
788 | |
789 if (header[i].hash == 0) { | |
790 continue; | |
791 } | |
792 | |
793 b->last = (u_char *) ngx_http_v3_encode_header_l(b->last, | |
794 &header[i].key, | |
795 &header[i].value); | |
796 } | |
797 | |
798 if (r->header_only) { | |
799 b->last_buf = 1; | |
800 } | |
801 | |
802 cl = ngx_alloc_chain_link(c->pool); | |
803 if (cl == NULL) { | |
804 return NULL; | |
805 } | |
806 | |
807 cl->buf = b; | |
808 cl->next = NULL; | |
809 | |
810 n = b->last - b->pos; | |
811 | |
812 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) | |
813 + ngx_http_v3_encode_varlen_int(NULL, n); | |
814 | |
815 b = ngx_create_temp_buf(c->pool, len); | |
816 if (b == NULL) { | |
817 return NULL; | |
818 } | |
819 | |
820 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
821 NGX_HTTP_V3_FRAME_HEADERS); | |
822 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); | |
823 | |
824 hl = ngx_alloc_chain_link(c->pool); | |
825 if (hl == NULL) { | |
826 return NULL; | |
827 } | |
828 | |
829 hl->buf = b; | |
830 hl->next = cl; | |
831 | |
832 *ll = hl; | |
833 ll = &cl->next; | |
834 | |
835 if (r->headers_out.content_length_n >= 0 && !r->header_only) { | |
836 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) | |
837 + ngx_http_v3_encode_varlen_int(NULL, | |
838 r->headers_out.content_length_n); | |
839 | |
840 b = ngx_create_temp_buf(c->pool, len); | |
841 if (b == NULL) { | |
842 return NULL; | |
843 } | |
844 | |
845 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
846 NGX_HTTP_V3_FRAME_DATA); | |
847 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
848 r->headers_out.content_length_n); | |
849 | |
850 cl = ngx_alloc_chain_link(c->pool); | |
851 if (cl == NULL) { | |
852 return NULL; | |
853 } | |
854 | |
855 cl->buf = b; | |
856 cl->next = NULL; | |
857 | |
858 *ll = cl; | |
859 } | |
860 | |
861 return out; | |
862 } | |
863 | |
864 | |
865 ngx_chain_t * | |
866 ngx_http_v3_create_trailers(ngx_http_request_t *r) | |
867 { | |
868 ngx_buf_t *b; | |
869 ngx_chain_t *cl; | |
870 | |
871 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
872 "http3 create trailers"); | |
873 | |
874 /* XXX */ | |
875 | |
876 b = ngx_calloc_buf(r->pool); | |
877 if (b == NULL) { | |
878 return NULL; | |
879 } | |
880 | |
881 b->last_buf = 1; | |
882 | |
883 cl = ngx_alloc_chain_link(r->pool); | |
884 if (cl == NULL) { | |
885 return NULL; | |
886 } | |
887 | |
888 cl->buf = b; | |
889 cl->next = NULL; | |
890 | |
891 return cl; | |
892 } | |
893 | |
894 | |
895 static ngx_int_t | |
896 ngx_http_v3_push_resources(ngx_http_request_t *r, ngx_chain_t ***out) | |
897 { | |
898 u_char *start, *end, *last; | |
899 ngx_str_t path; | |
900 ngx_int_t rc; | |
901 ngx_uint_t i, push; | |
902 ngx_table_elt_t **h; | |
903 ngx_http_v3_loc_conf_t *h3lcf; | |
904 ngx_http_complex_value_t *pushes; | |
905 | |
906 h3lcf = ngx_http_get_module_loc_conf(r, ngx_http_v3_module); | |
907 | |
908 if (h3lcf->pushes) { | |
909 pushes = h3lcf->pushes->elts; | |
910 | |
911 for (i = 0; i < h3lcf->pushes->nelts; i++) { | |
912 | |
913 if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) { | |
914 return NGX_ERROR; | |
915 } | |
916 | |
917 if (path.len == 0) { | |
918 continue; | |
919 } | |
920 | |
921 if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) { | |
922 continue; | |
923 } | |
924 | |
925 rc = ngx_http_v3_push_resource(r, &path, out); | |
926 | |
927 if (rc == NGX_ERROR) { | |
928 return NGX_ERROR; | |
929 } | |
930 | |
931 if (rc == NGX_ABORT) { | |
932 return NGX_OK; | |
933 } | |
934 | |
935 /* NGX_OK, NGX_DECLINED */ | |
936 } | |
937 } | |
938 | |
939 if (!h3lcf->push_preload) { | |
940 return NGX_OK; | |
941 } | |
942 | |
943 h = r->headers_out.link.elts; | |
944 | |
945 for (i = 0; i < r->headers_out.link.nelts; i++) { | |
946 | |
947 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
948 "http3 parse link: \"%V\"", &h[i]->value); | |
949 | |
950 start = h[i]->value.data; | |
951 end = h[i]->value.data + h[i]->value.len; | |
952 | |
953 next_link: | |
954 | |
955 while (start < end && *start == ' ') { start++; } | |
956 | |
957 if (start == end || *start++ != '<') { | |
958 continue; | |
959 } | |
960 | |
961 while (start < end && *start == ' ') { start++; } | |
962 | |
963 for (last = start; last < end && *last != '>'; last++) { | |
964 /* void */ | |
965 } | |
966 | |
967 if (last == start || last == end) { | |
968 continue; | |
969 } | |
970 | |
971 path.len = last - start; | |
972 path.data = start; | |
973 | |
974 start = last + 1; | |
975 | |
976 while (start < end && *start == ' ') { start++; } | |
977 | |
978 if (start == end) { | |
979 continue; | |
980 } | |
981 | |
982 if (*start == ',') { | |
983 start++; | |
984 goto next_link; | |
985 } | |
986 | |
987 if (*start++ != ';') { | |
988 continue; | |
989 } | |
990 | |
991 last = ngx_strlchr(start, end, ','); | |
992 | |
993 if (last == NULL) { | |
994 last = end; | |
995 } | |
996 | |
997 push = 0; | |
998 | |
999 for ( ;; ) { | |
1000 | |
1001 while (start < last && *start == ' ') { start++; } | |
1002 | |
1003 if (last - start >= 6 | |
1004 && ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0) | |
1005 { | |
1006 start += 6; | |
1007 | |
1008 if (start == last || *start == ' ' || *start == ';') { | |
1009 push = 0; | |
1010 break; | |
1011 } | |
1012 | |
1013 goto next_param; | |
1014 } | |
1015 | |
1016 if (last - start >= 11 | |
1017 && ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0) | |
1018 { | |
1019 start += 11; | |
1020 | |
1021 if (start == last || *start == ' ' || *start == ';') { | |
1022 push = 1; | |
1023 } | |
1024 | |
1025 goto next_param; | |
1026 } | |
1027 | |
1028 if (last - start >= 4 | |
1029 && ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0) | |
1030 { | |
1031 start += 4; | |
1032 | |
1033 while (start < last && *start == ' ') { start++; } | |
1034 | |
1035 if (start == last || *start++ != '"') { | |
1036 goto next_param; | |
1037 } | |
1038 | |
1039 for ( ;; ) { | |
1040 | |
1041 while (start < last && *start == ' ') { start++; } | |
1042 | |
1043 if (last - start >= 7 | |
1044 && ngx_strncasecmp(start, (u_char *) "preload", 7) == 0) | |
1045 { | |
1046 start += 7; | |
1047 | |
1048 if (start < last && (*start == ' ' || *start == '"')) { | |
1049 push = 1; | |
1050 break; | |
1051 } | |
1052 } | |
1053 | |
1054 while (start < last && *start != ' ' && *start != '"') { | |
1055 start++; | |
1056 } | |
1057 | |
1058 if (start == last) { | |
1059 break; | |
1060 } | |
1061 | |
1062 if (*start == '"') { | |
1063 break; | |
1064 } | |
1065 | |
1066 start++; | |
1067 } | |
1068 } | |
1069 | |
1070 next_param: | |
1071 | |
1072 start = ngx_strlchr(start, last, ';'); | |
1073 | |
1074 if (start == NULL) { | |
1075 break; | |
1076 } | |
1077 | |
1078 start++; | |
1079 } | |
1080 | |
1081 if (push) { | |
1082 while (path.len && path.data[path.len - 1] == ' ') { | |
1083 path.len--; | |
1084 } | |
1085 } | |
1086 | |
1087 if (push && path.len | |
1088 && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) | |
1089 { | |
1090 rc = ngx_http_v3_push_resource(r, &path, out); | |
1091 | |
1092 if (rc == NGX_ERROR) { | |
1093 return NGX_ERROR; | |
1094 } | |
1095 | |
1096 if (rc == NGX_ABORT) { | |
1097 return NGX_OK; | |
1098 } | |
1099 | |
1100 /* NGX_OK, NGX_DECLINED */ | |
1101 } | |
1102 | |
1103 if (last < end) { | |
1104 start = last + 1; | |
1105 goto next_link; | |
1106 } | |
1107 } | |
1108 | |
1109 return NGX_OK; | |
1110 } | |
1111 | |
1112 | |
1113 static ngx_int_t | |
1114 ngx_http_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path, | |
1115 ngx_chain_t ***ll) | |
1116 { | |
1117 uint64_t push_id; | |
1118 ngx_int_t rc; | |
1119 ngx_chain_t *cl; | |
1120 ngx_connection_t *c; | |
1121 ngx_http_v3_srv_conf_t *h3scf; | |
1122 ngx_http_v3_connection_t *h3c; | |
1123 | |
1124 c = r->connection; | |
1125 h3c = c->quic->parent->data; | |
1126 h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); | |
1127 | |
1128 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
1129 "http3 push \"%V\" pushing:%ui/%ui id:%uL/%uL", | |
1130 path, h3c->npushing, h3scf->max_concurrent_pushes, | |
1131 h3c->next_push_id, h3c->max_push_id); | |
1132 | |
1133 if (!ngx_path_separator(path->data[0])) { | |
1134 ngx_log_error(NGX_LOG_WARN, c->log, 0, | |
1135 "non-absolute path \"%V\" not pushed", path); | |
1136 return NGX_DECLINED; | |
1137 } | |
1138 | |
1139 if (h3c->next_push_id > h3c->max_push_id) { | |
1140 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
1141 "http3 abort pushes due to max_push_id"); | |
1142 return NGX_ABORT; | |
1143 } | |
1144 | |
1145 if (h3c->npushing >= h3scf->max_concurrent_pushes) { | |
1146 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
1147 "http3 abort pushes due to max_concurrent_pushes"); | |
1148 return NGX_ABORT; | |
1149 } | |
1150 | |
1151 push_id = h3c->next_push_id++; | |
1152 | |
1153 rc = ngx_http_v3_create_push_request(r, path, push_id); | |
1154 if (rc != NGX_OK) { | |
1155 return rc; | |
1156 } | |
1157 | |
1158 cl = ngx_http_v3_create_push_promise(r, path, push_id); | |
1159 if (cl == NULL) { | |
1160 return NGX_ERROR; | |
1161 } | |
1162 | |
1163 for (**ll = cl; **ll; *ll = &(**ll)->next); | |
1164 | |
1165 return NGX_OK; | |
1166 } | |
1167 | |
1168 | |
1169 static ngx_int_t | |
1170 ngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path, | |
1171 uint64_t push_id) | |
1172 { | |
1173 ngx_pool_t *pool; | |
1174 ngx_connection_t *c, *pc; | |
1175 ngx_http_request_t *r; | |
1176 ngx_http_log_ctx_t *ctx; | |
1177 ngx_http_connection_t *hc; | |
1178 ngx_http_core_srv_conf_t *cscf; | |
1179 ngx_http_v3_connection_t *h3c; | |
1180 | |
1181 pc = pr->connection; | |
1182 | |
1183 r = NULL; | |
1184 | |
1185 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, | |
1186 "http3 create push request id:%uL", push_id); | |
1187 | |
1188 c = ngx_http_v3_create_push_stream(pc, push_id); | |
1189 if (c == NULL) { | |
1190 return NGX_ABORT; | |
1191 } | |
1192 | |
1193 hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t)); | |
1194 if (hc == NULL) { | |
1195 goto failed; | |
1196 } | |
1197 | |
1198 h3c = c->quic->parent->data; | |
1199 ngx_memcpy(hc, h3c, sizeof(ngx_http_connection_t)); | |
1200 c->data = hc; | |
1201 | |
1202 ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); | |
1203 if (ctx == NULL) { | |
1204 goto failed; | |
1205 } | |
1206 | |
1207 ctx->connection = c; | |
1208 ctx->request = NULL; | |
1209 ctx->current_request = NULL; | |
1210 | |
1211 c->log->handler = ngx_http_log_error; | |
1212 c->log->data = ctx; | |
1213 c->log->action = "processing pushed request headers"; | |
1214 | |
1215 c->log_error = NGX_ERROR_INFO; | |
1216 | |
1217 r = ngx_http_create_request(c); | |
1218 if (r == NULL) { | |
1219 goto failed; | |
1220 } | |
1221 | |
1222 c->data = r; | |
1223 | |
1224 ngx_str_set(&r->http_protocol, "HTTP/3.0"); | |
1225 | |
1226 r->method_name = ngx_http_core_get_method; | |
1227 r->method = NGX_HTTP_GET; | |
1228 | |
1229 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
1230 | |
1231 r->header_in = ngx_create_temp_buf(r->pool, | |
1232 cscf->client_header_buffer_size); | |
1233 if (r->header_in == NULL) { | |
1234 goto failed; | |
1235 } | |
1236 | |
1237 if (ngx_list_init(&r->headers_in.headers, r->pool, 4, | |
1238 sizeof(ngx_table_elt_t)) | |
1239 != NGX_OK) | |
1240 { | |
1241 goto failed; | |
1242 } | |
1243 | |
1244 r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; | |
1245 | |
1246 r->schema.data = ngx_pstrdup(r->pool, &pr->schema); | |
1247 if (r->schema.data == NULL) { | |
1248 goto failed; | |
1249 } | |
1250 | |
1251 r->schema.len = pr->schema.len; | |
1252 | |
1253 r->uri_start = ngx_pstrdup(r->pool, path); | |
1254 if (r->uri_start == NULL) { | |
1255 goto failed; | |
1256 } | |
1257 | |
1258 r->uri_end = r->uri_start + path->len; | |
1259 | |
1260 if (ngx_http_parse_uri(r) != NGX_OK) { | |
1261 goto failed; | |
1262 } | |
1263 | |
1264 if (ngx_http_process_request_uri(r) != NGX_OK) { | |
1265 goto failed; | |
1266 } | |
1267 | |
1268 if (ngx_http_v3_set_push_header(r, "host", &pr->headers_in.server) | |
1269 != NGX_OK) | |
1270 { | |
1271 goto failed; | |
1272 } | |
1273 | |
1274 if (pr->headers_in.accept_encoding) { | |
1275 if (ngx_http_v3_set_push_header(r, "accept-encoding", | |
1276 &pr->headers_in.accept_encoding->value) | |
1277 != NGX_OK) | |
1278 { | |
1279 goto failed; | |
1280 } | |
1281 } | |
1282 | |
1283 if (pr->headers_in.accept_language) { | |
1284 if (ngx_http_v3_set_push_header(r, "accept-language", | |
1285 &pr->headers_in.accept_language->value) | |
1286 != NGX_OK) | |
1287 { | |
1288 goto failed; | |
1289 } | |
1290 } | |
1291 | |
1292 if (pr->headers_in.user_agent) { | |
1293 if (ngx_http_v3_set_push_header(r, "user-agent", | |
1294 &pr->headers_in.user_agent->value) | |
1295 != NGX_OK) | |
1296 { | |
1297 goto failed; | |
1298 } | |
1299 } | |
1300 | |
1301 c->read->handler = ngx_http_v3_push_request_handler; | |
1302 c->read->handler = ngx_http_v3_push_request_handler; | |
1303 | |
1304 ngx_post_event(c->read, &ngx_posted_events); | |
1305 | |
1306 return NGX_OK; | |
1307 | |
1308 failed: | |
1309 | |
1310 if (r) { | |
1311 ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
1312 } | |
1313 | |
1314 c->destroyed = 1; | |
1315 | |
1316 pool = c->pool; | |
1317 | |
1318 ngx_close_connection(c); | |
1319 | |
1320 ngx_destroy_pool(pool); | |
1321 | |
1322 return NGX_ERROR; | |
1323 } | |
1324 | |
1325 | |
1326 static ngx_int_t | |
1327 ngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name, | |
1328 ngx_str_t *value) | |
1329 { | |
1330 u_char *p; | |
1331 ngx_table_elt_t *h; | |
1332 ngx_http_header_t *hh; | |
1333 ngx_http_core_main_conf_t *cmcf; | |
1334 | |
1335 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1336 "http3 push header \"%s\": \"%V\"", name, value); | |
1337 | |
1338 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); | |
1339 | |
1340 p = ngx_pnalloc(r->pool, value->len + 1); | |
1341 if (p == NULL) { | |
1342 return NGX_ERROR; | |
1343 } | |
1344 | |
1345 ngx_memcpy(p, value->data, value->len); | |
1346 p[value->len] = '\0'; | |
1347 | |
1348 h = ngx_list_push(&r->headers_in.headers); | |
1349 if (h == NULL) { | |
1350 return NGX_ERROR; | |
1351 } | |
1352 | |
1353 h->key.data = (u_char *) name; | |
1354 h->key.len = ngx_strlen(name); | |
1355 h->hash = ngx_hash_key(h->key.data, h->key.len); | |
1356 h->lowcase_key = (u_char *) name; | |
1357 h->value.data = p; | |
1358 h->value.len = value->len; | |
1359 | |
1360 hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, | |
1361 h->lowcase_key, h->key.len); | |
1362 | |
1363 if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { | |
1364 return NGX_ERROR; | |
1365 } | |
1366 | |
1367 return NGX_OK; | |
1368 } | |
1369 | |
1370 | |
1371 static void | |
1372 ngx_http_v3_push_request_handler(ngx_event_t *ev) | |
1373 { | |
1374 ngx_connection_t *c; | |
1375 ngx_http_request_t *r; | |
1376 | |
1377 c = ev->data; | |
1378 r = c->data; | |
1379 | |
1380 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 push request handler"); | |
1381 | |
1382 ngx_http_process_request(r); | |
1383 } | |
1384 | |
1385 | |
1386 static ngx_chain_t * | |
1387 ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, | |
1388 uint64_t push_id) | |
1389 { | |
1390 size_t n, len; | |
1391 ngx_buf_t *b; | |
1392 ngx_chain_t *hl, *cl; | |
1393 | |
1394 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1395 "http3 create push promise id:%uL", push_id); | |
1396 | |
1397 len = ngx_http_v3_encode_varlen_int(NULL, push_id); | |
1398 | |
1399 len += ngx_http_v3_encode_header_block_prefix(NULL, 0, 0, 0); | |
1400 | |
1401 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
1402 NGX_HTTP_V3_HEADER_METHOD_GET); | |
1403 | |
1404 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1405 NGX_HTTP_V3_HEADER_AUTHORITY, | |
1406 NULL, r->headers_in.server.len); | |
1407 | |
1408 if (path->len == 1 && path->data[0] == '/') { | |
1409 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
1410 NGX_HTTP_V3_HEADER_PATH_ROOT); | |
1411 | |
1412 } else { | |
1413 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1414 NGX_HTTP_V3_HEADER_PATH_ROOT, | |
1415 NULL, path->len); | |
1416 } | |
1417 | |
1418 if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { | |
1419 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
1420 NGX_HTTP_V3_HEADER_SCHEME_HTTPS); | |
1421 | |
1422 } else if (r->schema.len == 4 | |
1423 && ngx_strncmp(r->schema.data, "http", 4) == 0) | |
1424 { | |
1425 len += ngx_http_v3_encode_header_ri(NULL, 0, | |
1426 NGX_HTTP_V3_HEADER_SCHEME_HTTP); | |
1427 | |
1428 } else { | |
1429 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1430 NGX_HTTP_V3_HEADER_SCHEME_HTTP, | |
1431 NULL, r->schema.len); | |
1432 } | |
1433 | |
1434 if (r->headers_in.accept_encoding) { | |
1435 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1436 NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, NULL, | |
1437 r->headers_in.accept_encoding->value.len); | |
1438 } | |
1439 | |
1440 if (r->headers_in.accept_language) { | |
1441 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1442 NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, NULL, | |
1443 r->headers_in.accept_language->value.len); | |
1444 } | |
1445 | |
1446 if (r->headers_in.user_agent) { | |
1447 len += ngx_http_v3_encode_header_lri(NULL, 0, | |
1448 NGX_HTTP_V3_HEADER_USER_AGENT, NULL, | |
1449 r->headers_in.user_agent->value.len); | |
1450 } | |
1451 | |
1452 b = ngx_create_temp_buf(r->pool, len); | |
1453 if (b == NULL) { | |
1454 return NULL; | |
1455 } | |
1456 | |
1457 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, push_id); | |
1458 | |
1459 b->last = (u_char *) ngx_http_v3_encode_header_block_prefix(b->last, | |
1460 0, 0, 0); | |
1461 | |
1462 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
1463 NGX_HTTP_V3_HEADER_METHOD_GET); | |
1464 | |
1465 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1466 NGX_HTTP_V3_HEADER_AUTHORITY, | |
1467 r->headers_in.server.data, | |
1468 r->headers_in.server.len); | |
1469 | |
1470 if (path->len == 1 && path->data[0] == '/') { | |
1471 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
1472 NGX_HTTP_V3_HEADER_PATH_ROOT); | |
1473 | |
1474 } else { | |
1475 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1476 NGX_HTTP_V3_HEADER_PATH_ROOT, | |
1477 path->data, path->len); | |
1478 } | |
1479 | |
1480 if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) { | |
1481 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
1482 NGX_HTTP_V3_HEADER_SCHEME_HTTPS); | |
1483 | |
1484 } else if (r->schema.len == 4 | |
1485 && ngx_strncmp(r->schema.data, "http", 4) == 0) | |
1486 { | |
1487 b->last = (u_char *) ngx_http_v3_encode_header_ri(b->last, 0, | |
1488 NGX_HTTP_V3_HEADER_SCHEME_HTTP); | |
1489 | |
1490 } else { | |
1491 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1492 NGX_HTTP_V3_HEADER_SCHEME_HTTP, | |
1493 r->schema.data, r->schema.len); | |
1494 } | |
1495 | |
1496 if (r->headers_in.accept_encoding) { | |
1497 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1498 NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, | |
1499 r->headers_in.accept_encoding->value.data, | |
1500 r->headers_in.accept_encoding->value.len); | |
1501 } | |
1502 | |
1503 if (r->headers_in.accept_language) { | |
1504 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1505 NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, | |
1506 r->headers_in.accept_language->value.data, | |
1507 r->headers_in.accept_language->value.len); | |
1508 } | |
1509 | |
1510 if (r->headers_in.user_agent) { | |
1511 b->last = (u_char *) ngx_http_v3_encode_header_lri(b->last, 0, | |
1512 NGX_HTTP_V3_HEADER_USER_AGENT, | |
1513 r->headers_in.user_agent->value.data, | |
1514 r->headers_in.user_agent->value.len); | |
1515 } | |
1516 | |
1517 cl = ngx_alloc_chain_link(r->pool); | |
1518 if (cl == NULL) { | |
1519 return NULL; | |
1520 } | |
1521 | |
1522 cl->buf = b; | |
1523 cl->next = NULL; | |
1524 | |
1525 n = b->last - b->pos; | |
1526 | |
1527 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE) | |
1528 + ngx_http_v3_encode_varlen_int(NULL, n); | |
1529 | |
1530 b = ngx_create_temp_buf(r->pool, len); | |
1531 if (b == NULL) { | |
1532 return NULL; | |
1533 } | |
1534 | |
1535 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, | |
1536 NGX_HTTP_V3_FRAME_PUSH_PROMISE); | |
1537 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); | |
1538 | |
1539 hl = ngx_alloc_chain_link(r->pool); | |
1540 if (hl == NULL) { | |
1541 return NULL; | |
1542 } | |
1543 | |
1544 hl->buf = b; | |
1545 hl->next = cl; | |
1546 | |
1547 return hl; | |
1548 } |