comparison src/http/v3/ngx_http_v3_request.c @ 8653: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
8652:e9bd4305e68b 8653: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 }