1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/ext/transport/chttp2/transport/frame_settings.h"
22
23 #include <string.h>
24
25 #include <initializer_list>
26 #include <string>
27
28 #include "absl/base/attributes.h"
29 #include "absl/status/status.h"
30 #include "absl/strings/str_format.h"
31 #include "absl/strings/string_view.h"
32
33 #include <grpc/slice_buffer.h>
34 #include <grpc/support/log.h>
35
36 #include "src/core/ext/transport/chttp2/transport/flow_control.h"
37 #include "src/core/ext/transport/chttp2/transport/frame.h"
38 #include "src/core/ext/transport/chttp2/transport/frame_goaway.h"
39 #include "src/core/ext/transport/chttp2/transport/http_trace.h"
40 #include "src/core/ext/transport/chttp2/transport/internal.h"
41 #include "src/core/lib/debug/trace.h"
42 #include "src/core/lib/gpr/useful.h"
43 #include "src/core/lib/gprpp/debug_location.h"
44 #include "src/core/lib/iomgr/exec_ctx.h"
45 #include "src/core/lib/slice/slice.h"
46
fill_header(uint8_t * out,uint32_t length,uint8_t flags)47 static uint8_t* fill_header(uint8_t* out, uint32_t length, uint8_t flags) {
48 *out++ = static_cast<uint8_t>(length >> 16);
49 *out++ = static_cast<uint8_t>(length >> 8);
50 *out++ = static_cast<uint8_t>(length);
51 *out++ = GRPC_CHTTP2_FRAME_SETTINGS;
52 *out++ = flags;
53 *out++ = 0;
54 *out++ = 0;
55 *out++ = 0;
56 *out++ = 0;
57 return out;
58 }
59
grpc_chttp2_settings_create(uint32_t * old_settings,const uint32_t * new_settings,uint32_t force_mask,size_t count)60 grpc_slice grpc_chttp2_settings_create(uint32_t* old_settings,
61 const uint32_t* new_settings,
62 uint32_t force_mask, size_t count) {
63 size_t i;
64 uint32_t n = 0;
65 grpc_slice output;
66 uint8_t* p;
67
68 for (i = 0; i < count; i++) {
69 n += (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0);
70 }
71
72 output = GRPC_SLICE_MALLOC(9 + 6 * n);
73 p = fill_header(GRPC_SLICE_START_PTR(output), 6 * n, 0);
74
75 for (i = 0; i < count; i++) {
76 if (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0) {
77 *p++ = static_cast<uint8_t>(grpc_setting_id_to_wire_id[i] >> 8);
78 *p++ = static_cast<uint8_t>(grpc_setting_id_to_wire_id[i]);
79 *p++ = static_cast<uint8_t>(new_settings[i] >> 24);
80 *p++ = static_cast<uint8_t>(new_settings[i] >> 16);
81 *p++ = static_cast<uint8_t>(new_settings[i] >> 8);
82 *p++ = static_cast<uint8_t>(new_settings[i]);
83 old_settings[i] = new_settings[i];
84 }
85 }
86
87 GPR_ASSERT(p == GRPC_SLICE_END_PTR(output));
88
89 return output;
90 }
91
grpc_chttp2_settings_ack_create(void)92 grpc_slice grpc_chttp2_settings_ack_create(void) {
93 grpc_slice output = GRPC_SLICE_MALLOC(9);
94 fill_header(GRPC_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK);
95 return output;
96 }
97
grpc_chttp2_settings_parser_begin_frame(grpc_chttp2_settings_parser * parser,uint32_t length,uint8_t flags,uint32_t * settings)98 grpc_error_handle grpc_chttp2_settings_parser_begin_frame(
99 grpc_chttp2_settings_parser* parser, uint32_t length, uint8_t flags,
100 uint32_t* settings) {
101 parser->target_settings = settings;
102 memcpy(parser->incoming_settings, settings,
103 GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
104 parser->is_ack = 0;
105 parser->state = GRPC_CHTTP2_SPS_ID0;
106 if (flags == GRPC_CHTTP2_FLAG_ACK) {
107 parser->is_ack = 1;
108 if (length != 0) {
109 return GRPC_ERROR_CREATE("non-empty settings ack frame received");
110 }
111 return absl::OkStatus();
112 } else if (flags != 0) {
113 return GRPC_ERROR_CREATE("invalid flags on settings frame");
114 } else if (length % 6 != 0) {
115 return GRPC_ERROR_CREATE("settings frames must be a multiple of six bytes");
116 } else {
117 return absl::OkStatus();
118 }
119 }
120
grpc_chttp2_settings_parser_parse(void * p,grpc_chttp2_transport * t,grpc_chttp2_stream *,const grpc_slice & slice,int is_last)121 grpc_error_handle grpc_chttp2_settings_parser_parse(void* p,
122 grpc_chttp2_transport* t,
123 grpc_chttp2_stream* /*s*/,
124 const grpc_slice& slice,
125 int is_last) {
126 grpc_chttp2_settings_parser* parser =
127 static_cast<grpc_chttp2_settings_parser*>(p);
128 const uint8_t* cur = GRPC_SLICE_START_PTR(slice);
129 const uint8_t* end = GRPC_SLICE_END_PTR(slice);
130 grpc_chttp2_setting_id id;
131
132 if (parser->is_ack) {
133 return absl::OkStatus();
134 }
135
136 for (;;) {
137 switch (parser->state) {
138 case GRPC_CHTTP2_SPS_ID0:
139 if (cur == end) {
140 parser->state = GRPC_CHTTP2_SPS_ID0;
141 if (is_last) {
142 memcpy(parser->target_settings, parser->incoming_settings,
143 GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
144 t->num_pending_induced_frames++;
145 grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_settings_ack_create());
146 grpc_chttp2_initiate_write(t,
147 GRPC_CHTTP2_INITIATE_WRITE_SETTINGS_ACK);
148 if (t->notify_on_receive_settings != nullptr) {
149 grpc_core::ExecCtx::Run(DEBUG_LOCATION,
150 t->notify_on_receive_settings,
151 absl::OkStatus());
152 t->notify_on_receive_settings = nullptr;
153 }
154 }
155 return absl::OkStatus();
156 }
157 parser->id = static_cast<uint16_t>((static_cast<uint16_t>(*cur)) << 8);
158 cur++;
159 ABSL_FALLTHROUGH_INTENDED;
160 case GRPC_CHTTP2_SPS_ID1:
161 if (cur == end) {
162 parser->state = GRPC_CHTTP2_SPS_ID1;
163 return absl::OkStatus();
164 }
165 parser->id = static_cast<uint16_t>(parser->id | (*cur));
166 cur++;
167 ABSL_FALLTHROUGH_INTENDED;
168 case GRPC_CHTTP2_SPS_VAL0:
169 if (cur == end) {
170 parser->state = GRPC_CHTTP2_SPS_VAL0;
171 return absl::OkStatus();
172 }
173 parser->value = (static_cast<uint32_t>(*cur)) << 24;
174 cur++;
175 ABSL_FALLTHROUGH_INTENDED;
176 case GRPC_CHTTP2_SPS_VAL1:
177 if (cur == end) {
178 parser->state = GRPC_CHTTP2_SPS_VAL1;
179 return absl::OkStatus();
180 }
181 parser->value |= (static_cast<uint32_t>(*cur)) << 16;
182 cur++;
183 ABSL_FALLTHROUGH_INTENDED;
184 case GRPC_CHTTP2_SPS_VAL2:
185 if (cur == end) {
186 parser->state = GRPC_CHTTP2_SPS_VAL2;
187 return absl::OkStatus();
188 }
189 parser->value |= (static_cast<uint32_t>(*cur)) << 8;
190 cur++;
191 ABSL_FALLTHROUGH_INTENDED;
192 case GRPC_CHTTP2_SPS_VAL3:
193 if (cur == end) {
194 parser->state = GRPC_CHTTP2_SPS_VAL3;
195 return absl::OkStatus();
196 } else {
197 parser->state = GRPC_CHTTP2_SPS_ID0;
198 }
199 parser->value |= *cur;
200 cur++;
201
202 if (grpc_wire_id_to_setting_id(parser->id, &id)) {
203 const grpc_chttp2_setting_parameters* sp =
204 &grpc_chttp2_settings_parameters[id];
205 if (parser->value < sp->min_value || parser->value > sp->max_value) {
206 switch (sp->invalid_value_behavior) {
207 case GRPC_CHTTP2_CLAMP_INVALID_VALUE:
208 parser->value = grpc_core::Clamp(parser->value, sp->min_value,
209 sp->max_value);
210 break;
211 case GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE:
212 grpc_chttp2_goaway_append(
213 t->last_new_stream_id, sp->error_value,
214 grpc_slice_from_static_string("HTTP2 settings error"),
215 &t->qbuf);
216 return GRPC_ERROR_CREATE(absl::StrFormat(
217 "invalid value %u passed for %s", parser->value, sp->name));
218 }
219 }
220 if (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE &&
221 parser->incoming_settings[id] != parser->value) {
222 t->initial_window_update += static_cast<int64_t>(parser->value) -
223 parser->incoming_settings[id];
224 if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
225 GRPC_TRACE_FLAG_ENABLED(grpc_flowctl_trace)) {
226 gpr_log(GPR_INFO, "%p[%s] adding %d for initial_window change", t,
227 t->is_client ? "cli" : "svr",
228 static_cast<int>(t->initial_window_update));
229 }
230 }
231 parser->incoming_settings[id] = parser->value;
232 if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
233 gpr_log(GPR_INFO, "CHTTP2:%s:%s: got setting %s = %d",
234 t->is_client ? "CLI" : "SVR",
235 std::string(t->peer_string.as_string_view()).c_str(),
236 sp->name, parser->value);
237 }
238 } else if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
239 gpr_log(GPR_DEBUG, "CHTTP2: Ignoring unknown setting %d (value %d)",
240 parser->id, parser->value);
241 }
242 break;
243 }
244 }
245 }
246