comparison src/http/v2/ngx_http_v2_filter_module.c @ 7207:3d2b0b02bd3d

HTTP/2: push additional request headers (closes #1478). The Accept-Encoding, Accept-Language, and User-Agent header fields are now copied from the original request to pushed requests.
author Ruslan Ermilov <ru@nginx.com>
date Thu, 15 Feb 2018 17:51:32 +0300
parents e44c297a6b95
children 87e9e4aabf1b
comparison
equal deleted inserted replaced
7206:33edea74bd58 7207:3d2b0b02bd3d
48 #define NGX_HTTP_V2_STATUS_304_INDEX 11 48 #define NGX_HTTP_V2_STATUS_304_INDEX 11
49 #define NGX_HTTP_V2_STATUS_400_INDEX 12 49 #define NGX_HTTP_V2_STATUS_400_INDEX 12
50 #define NGX_HTTP_V2_STATUS_404_INDEX 13 50 #define NGX_HTTP_V2_STATUS_404_INDEX 13
51 #define NGX_HTTP_V2_STATUS_500_INDEX 14 51 #define NGX_HTTP_V2_STATUS_500_INDEX 14
52 52
53 #define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16
54 #define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17
53 #define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28 55 #define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28
54 #define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31 56 #define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31
55 #define NGX_HTTP_V2_DATE_INDEX 33 57 #define NGX_HTTP_V2_DATE_INDEX 33
56 #define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44 58 #define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44
57 #define NGX_HTTP_V2_LOCATION_INDEX 46 59 #define NGX_HTTP_V2_LOCATION_INDEX 46
58 #define NGX_HTTP_V2_SERVER_INDEX 54 60 #define NGX_HTTP_V2_SERVER_INDEX 54
61 #define NGX_HTTP_V2_USER_AGENT_INDEX 58
59 #define NGX_HTTP_V2_VARY_INDEX 59 62 #define NGX_HTTP_V2_VARY_INDEX 59
60 63
61 #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 64 #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1
65
66
67 typedef struct {
68 ngx_str_t name;
69 u_char index;
70 ngx_uint_t offset;
71 } ngx_http_v2_push_header_t;
72
73
74 static ngx_http_v2_push_header_t ngx_http_v2_push_headers[] = {
75 { ngx_string(":authority"), NGX_HTTP_V2_AUTHORITY_INDEX,
76 offsetof(ngx_http_headers_in_t, host) },
77
78 { ngx_string("accept-encoding"), NGX_HTTP_V2_ACCEPT_ENCODING_INDEX,
79 offsetof(ngx_http_headers_in_t, accept_encoding) },
80
81 { ngx_string("accept-language"), NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX,
82 offsetof(ngx_http_headers_in_t, accept_language) },
83
84 { ngx_string("user-agent"), NGX_HTTP_V2_USER_AGENT_INDEX,
85 offsetof(ngx_http_headers_in_t, user_agent) },
86 };
87
88 #define NGX_HTTP_V2_PUSH_HEADERS \
89 (sizeof(ngx_http_v2_push_headers) / sizeof(ngx_http_v2_push_header_t))
62 90
63 91
64 static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r); 92 static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r);
65 static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r, 93 static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r,
66 ngx_str_t *path, ngx_str_t *authority); 94 ngx_str_t *path, ngx_str_t *binary);
67 95
68 static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, 96 static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
69 u_char *tmp, ngx_uint_t lower); 97 u_char *tmp, ngx_uint_t lower);
70 static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, 98 static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
71 ngx_uint_t value); 99 ngx_uint_t value);
683 static ngx_int_t 711 static ngx_int_t
684 ngx_http_v2_push_resources(ngx_http_request_t *r) 712 ngx_http_v2_push_resources(ngx_http_request_t *r)
685 { 713 {
686 u_char *start, *end, *last; 714 u_char *start, *end, *last;
687 ngx_int_t rc; 715 ngx_int_t rc;
688 ngx_str_t path, authority; 716 ngx_str_t path;
689 ngx_uint_t i, push; 717 ngx_uint_t i, push;
690 ngx_table_elt_t **h; 718 ngx_table_elt_t **h;
691 ngx_http_v2_loc_conf_t *h2lcf; 719 ngx_http_v2_loc_conf_t *h2lcf;
692 ngx_http_complex_value_t *pushes; 720 ngx_http_complex_value_t *pushes;
721 ngx_str_t binary[NGX_HTTP_V2_PUSH_HEADERS];
693 722
694 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 723 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
695 "http2 push resources"); 724 "http2 push resources");
696 725
697 ngx_str_null(&authority); 726 ngx_memzero(binary, NGX_HTTP_V2_PUSH_HEADERS * sizeof(ngx_str_t));
698 727
699 h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module); 728 h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
700 729
701 if (h2lcf->pushes) { 730 if (h2lcf->pushes) {
702 pushes = h2lcf->pushes->elts; 731 pushes = h2lcf->pushes->elts;
713 742
714 if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) { 743 if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) {
715 continue; 744 continue;
716 } 745 }
717 746
718 rc = ngx_http_v2_push_resource(r, &path, &authority); 747 rc = ngx_http_v2_push_resource(r, &path, binary);
719 748
720 if (rc == NGX_ERROR) { 749 if (rc == NGX_ERROR) {
721 return NGX_ERROR; 750 return NGX_ERROR;
722 } 751 }
723 752
878 } 907 }
879 908
880 if (push && path.len 909 if (push && path.len
881 && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/')) 910 && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/'))
882 { 911 {
883 rc = ngx_http_v2_push_resource(r, &path, &authority); 912 rc = ngx_http_v2_push_resource(r, &path, binary);
884 913
885 if (rc == NGX_ERROR) { 914 if (rc == NGX_ERROR) {
886 return NGX_ERROR; 915 return NGX_ERROR;
887 } 916 }
888 917
903 } 932 }
904 933
905 934
906 static ngx_int_t 935 static ngx_int_t
907 ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path, 936 ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
908 ngx_str_t *authority) 937 ngx_str_t *binary)
909 { 938 {
910 u_char *start, *pos, *tmp; 939 u_char *start, *pos, *tmp;
911 size_t len; 940 size_t len;
912 ngx_table_elt_t *host; 941 ngx_str_t *value;
913 ngx_connection_t *fc; 942 ngx_uint_t i;
914 ngx_http_v2_stream_t *stream; 943 ngx_table_elt_t **h;
915 ngx_http_v2_out_frame_t *frame; 944 ngx_connection_t *fc;
916 ngx_http_v2_connection_t *h2c; 945 ngx_http_v2_stream_t *stream;
946 ngx_http_v2_out_frame_t *frame;
947 ngx_http_v2_connection_t *h2c;
948 ngx_http_v2_push_header_t *ph;
917 949
918 fc = r->connection; 950 fc = r->connection;
919 951
920 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push resource"); 952 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push resource");
921 953
942 974
943 if (path->len > NGX_HTTP_V2_MAX_FIELD) { 975 if (path->len > NGX_HTTP_V2_MAX_FIELD) {
944 return NGX_DECLINED; 976 return NGX_DECLINED;
945 } 977 }
946 978
947 host = r->headers_in.host; 979 if (r->headers_in.host == NULL) {
948
949 if (host == NULL) {
950 return NGX_ABORT; 980 return NGX_ABORT;
951 } 981 }
952 982
953 if (authority->len == 0) { 983 ph = ngx_http_v2_push_headers;
954 984
955 len = 1 + NGX_HTTP_V2_INT_OCTETS + host->value.len; 985 if (binary[0].len) {
986 tmp = ngx_palloc(r->pool, path->len);
987 if (tmp == NULL) {
988 return NGX_ERROR;
989 }
990
991 } else {
992 len = path->len;
993
994 for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
995 h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
996
997 if (*h) {
998 len = ngx_max(len, (*h)->value.len);
999 }
1000 }
956 1001
957 tmp = ngx_palloc(r->pool, len); 1002 tmp = ngx_palloc(r->pool, len);
958 pos = ngx_pnalloc(r->pool, len); 1003 if (tmp == NULL) {
959
960 if (pos == NULL || tmp == NULL) {
961 return NGX_ERROR; 1004 return NGX_ERROR;
962 } 1005 }
963 1006
964 authority->data = pos; 1007 for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
965 1008 h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
966 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX); 1009
967 pos = ngx_http_v2_write_value(pos, host->value.data, host->value.len, 1010 if (*h == NULL) {
968 tmp); 1011 continue;
969 1012 }
970 authority->len = pos - authority->data; 1013
1014 value = &(*h)->value;
1015
1016 len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len;
1017
1018 pos = ngx_pnalloc(r->pool, len);
1019 if (pos == NULL) {
1020 return NGX_ERROR;
1021 }
1022
1023 binary[i].data = pos;
1024
1025 *pos++ = ngx_http_v2_inc_indexed(ph[i].index);
1026 pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp);
1027
1028 binary[i].len = pos - binary[i].data;
1029 }
971 } 1030 }
972 1031
973 len = (h2c->table_update ? 1 : 0) 1032 len = (h2c->table_update ? 1 : 0)
974 + 1 1033 + 1
975 + 1 + NGX_HTTP_V2_INT_OCTETS + path->len 1034 + 1 + NGX_HTTP_V2_INT_OCTETS + path->len
976 + authority->len
977 + 1; 1035 + 1;
978 1036
979 tmp = ngx_palloc(r->pool, len); 1037 for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
1038 len += binary[i].len;
1039 }
1040
980 pos = ngx_pnalloc(r->pool, len); 1041 pos = ngx_pnalloc(r->pool, len);
981 1042 if (pos == NULL) {
982 if (pos == NULL || tmp == NULL) {
983 return NGX_ERROR; 1043 return NGX_ERROR;
984 } 1044 }
985 1045
986 start = pos; 1046 start = pos;
987 1047
1000 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, 1060 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1001 "http2 push header: \":path: %V\"", path); 1061 "http2 push header: \":path: %V\"", path);
1002 1062
1003 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); 1063 *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
1004 pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp); 1064 pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
1005
1006 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1007 "http2 push header: \":authority: %V\"", &host->value);
1008
1009 pos = ngx_cpymem(pos, authority->data, authority->len);
1010 1065
1011 #if (NGX_HTTP_SSL) 1066 #if (NGX_HTTP_SSL)
1012 if (fc->ssl) { 1067 if (fc->ssl) {
1013 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, 1068 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1014 "http2 push header: \":scheme: https\""); 1069 "http2 push header: \":scheme: https\"");
1020 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, 1075 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1021 "http2 push header: \":scheme: http\""); 1076 "http2 push header: \":scheme: http\"");
1022 *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX); 1077 *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
1023 } 1078 }
1024 1079
1080 for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
1081 h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
1082
1083 if (*h == NULL) {
1084 continue;
1085 }
1086
1087 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
1088 "http2 push header: \"%V: %V\"",
1089 &ph[i].name, &(*h)->value);
1090
1091 pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
1092 }
1093
1025 frame = ngx_http_v2_create_push_frame(r, start, pos); 1094 frame = ngx_http_v2_create_push_frame(r, start, pos);
1026 if (frame == NULL) { 1095 if (frame == NULL) {
1027 return NGX_ERROR; 1096 return NGX_ERROR;
1028 } 1097 }
1029 1098
1030 ngx_http_v2_queue_blocked_frame(h2c, frame); 1099 ngx_http_v2_queue_blocked_frame(h2c, frame);
1031 1100
1032 stream->queued++; 1101 stream->queued++;
1033 1102
1034 return ngx_http_v2_push_stream(h2c, stream->node->id, pos - start, 1103 stream = ngx_http_v2_push_stream(stream, path);
1035 path, &host->value); 1104
1105 if (stream) {
1106 stream->request->request_length = pos - start;
1107 return NGX_OK;
1108 }
1109
1110 return NGX_ERROR;
1036 } 1111 }
1037 1112
1038 1113
1039 static u_char * 1114 static u_char *
1040 ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, 1115 ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp,