• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "src/core/ext/transport/chttp2/transport/internal.h"
23 
24 #include <string.h>
25 
26 #include "absl/strings/str_format.h"
27 
28 #include <grpc/support/alloc.h>
29 #include <grpc/support/log.h>
30 
31 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
32 #include "src/core/ext/transport/chttp2/transport/frame.h"
33 #include "src/core/lib/debug/trace.h"
34 #include "src/core/lib/transport/http2_errors.h"
35 
fill_header(uint8_t * out,uint32_t length,uint8_t flags)36 static uint8_t* fill_header(uint8_t* out, uint32_t length, uint8_t flags) {
37   *out++ = static_cast<uint8_t>(length >> 16);
38   *out++ = static_cast<uint8_t>(length >> 8);
39   *out++ = static_cast<uint8_t>(length);
40   *out++ = GRPC_CHTTP2_FRAME_SETTINGS;
41   *out++ = flags;
42   *out++ = 0;
43   *out++ = 0;
44   *out++ = 0;
45   *out++ = 0;
46   return out;
47 }
48 
grpc_chttp2_settings_create(uint32_t * old_settings,const uint32_t * new_settings,uint32_t force_mask,size_t count)49 grpc_slice grpc_chttp2_settings_create(uint32_t* old_settings,
50                                        const uint32_t* new_settings,
51                                        uint32_t force_mask, size_t count) {
52   size_t i;
53   uint32_t n = 0;
54   grpc_slice output;
55   uint8_t* p;
56 
57   for (i = 0; i < count; i++) {
58     n += (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0);
59   }
60 
61   output = GRPC_SLICE_MALLOC(9 + 6 * n);
62   p = fill_header(GRPC_SLICE_START_PTR(output), 6 * n, 0);
63 
64   for (i = 0; i < count; i++) {
65     if (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0) {
66       *p++ = static_cast<uint8_t>(grpc_setting_id_to_wire_id[i] >> 8);
67       *p++ = static_cast<uint8_t>(grpc_setting_id_to_wire_id[i]);
68       *p++ = static_cast<uint8_t>(new_settings[i] >> 24);
69       *p++ = static_cast<uint8_t>(new_settings[i] >> 16);
70       *p++ = static_cast<uint8_t>(new_settings[i] >> 8);
71       *p++ = static_cast<uint8_t>(new_settings[i]);
72       old_settings[i] = new_settings[i];
73     }
74   }
75 
76   GPR_ASSERT(p == GRPC_SLICE_END_PTR(output));
77 
78   return output;
79 }
80 
grpc_chttp2_settings_ack_create(void)81 grpc_slice grpc_chttp2_settings_ack_create(void) {
82   grpc_slice output = GRPC_SLICE_MALLOC(9);
83   fill_header(GRPC_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK);
84   return output;
85 }
86 
grpc_chttp2_settings_parser_begin_frame(grpc_chttp2_settings_parser * parser,uint32_t length,uint8_t flags,uint32_t * settings)87 grpc_error* grpc_chttp2_settings_parser_begin_frame(
88     grpc_chttp2_settings_parser* parser, uint32_t length, uint8_t flags,
89     uint32_t* settings) {
90   parser->target_settings = settings;
91   memcpy(parser->incoming_settings, settings,
92          GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
93   parser->is_ack = 0;
94   parser->state = GRPC_CHTTP2_SPS_ID0;
95   if (flags == GRPC_CHTTP2_FLAG_ACK) {
96     parser->is_ack = 1;
97     if (length != 0) {
98       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
99           "non-empty settings ack frame received");
100     }
101     return GRPC_ERROR_NONE;
102   } else if (flags != 0) {
103     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
104         "invalid flags on settings frame");
105   } else if (length % 6 != 0) {
106     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
107         "settings frames must be a multiple of six bytes");
108   } else {
109     return GRPC_ERROR_NONE;
110   }
111 }
112 
grpc_chttp2_settings_parser_parse(void * p,grpc_chttp2_transport * t,grpc_chttp2_stream *,const grpc_slice & slice,int is_last)113 grpc_error* grpc_chttp2_settings_parser_parse(void* p, grpc_chttp2_transport* t,
114                                               grpc_chttp2_stream* /*s*/,
115                                               const grpc_slice& slice,
116                                               int is_last) {
117   grpc_chttp2_settings_parser* parser =
118       static_cast<grpc_chttp2_settings_parser*>(p);
119   const uint8_t* cur = GRPC_SLICE_START_PTR(slice);
120   const uint8_t* end = GRPC_SLICE_END_PTR(slice);
121   grpc_chttp2_setting_id id;
122 
123   if (parser->is_ack) {
124     return GRPC_ERROR_NONE;
125   }
126 
127   for (;;) {
128     switch (parser->state) {
129       case GRPC_CHTTP2_SPS_ID0:
130         if (cur == end) {
131           parser->state = GRPC_CHTTP2_SPS_ID0;
132           if (is_last) {
133             memcpy(parser->target_settings, parser->incoming_settings,
134                    GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
135             t->num_pending_induced_frames++;
136             grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_settings_ack_create());
137             if (t->notify_on_receive_settings != nullptr) {
138               grpc_core::ExecCtx::Run(DEBUG_LOCATION,
139                                       t->notify_on_receive_settings,
140                                       GRPC_ERROR_NONE);
141               t->notify_on_receive_settings = nullptr;
142             }
143           }
144           return GRPC_ERROR_NONE;
145         }
146         parser->id = static_cast<uint16_t>((static_cast<uint16_t>(*cur)) << 8);
147         cur++;
148       /* fallthrough */
149       case GRPC_CHTTP2_SPS_ID1:
150         if (cur == end) {
151           parser->state = GRPC_CHTTP2_SPS_ID1;
152           return GRPC_ERROR_NONE;
153         }
154         parser->id = static_cast<uint16_t>(parser->id | (*cur));
155         cur++;
156       /* fallthrough */
157       case GRPC_CHTTP2_SPS_VAL0:
158         if (cur == end) {
159           parser->state = GRPC_CHTTP2_SPS_VAL0;
160           return GRPC_ERROR_NONE;
161         }
162         parser->value = (static_cast<uint32_t>(*cur)) << 24;
163         cur++;
164       /* fallthrough */
165       case GRPC_CHTTP2_SPS_VAL1:
166         if (cur == end) {
167           parser->state = GRPC_CHTTP2_SPS_VAL1;
168           return GRPC_ERROR_NONE;
169         }
170         parser->value |= (static_cast<uint32_t>(*cur)) << 16;
171         cur++;
172       /* fallthrough */
173       case GRPC_CHTTP2_SPS_VAL2:
174         if (cur == end) {
175           parser->state = GRPC_CHTTP2_SPS_VAL2;
176           return GRPC_ERROR_NONE;
177         }
178         parser->value |= (static_cast<uint32_t>(*cur)) << 8;
179         cur++;
180       /* fallthrough */
181       case GRPC_CHTTP2_SPS_VAL3:
182         if (cur == end) {
183           parser->state = GRPC_CHTTP2_SPS_VAL3;
184           return GRPC_ERROR_NONE;
185         } else {
186           parser->state = GRPC_CHTTP2_SPS_ID0;
187         }
188         parser->value |= *cur;
189         cur++;
190 
191         if (grpc_wire_id_to_setting_id(parser->id, &id)) {
192           const grpc_chttp2_setting_parameters* sp =
193               &grpc_chttp2_settings_parameters[id];
194           // If flow control is disabled we skip these.
195           if (!t->flow_control->flow_control_enabled() &&
196               (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE ||
197                id == GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE)) {
198             continue;
199           }
200           if (parser->value < sp->min_value || parser->value > sp->max_value) {
201             switch (sp->invalid_value_behavior) {
202               case GRPC_CHTTP2_CLAMP_INVALID_VALUE:
203                 parser->value =
204                     GPR_CLAMP(parser->value, sp->min_value, sp->max_value);
205                 break;
206               case GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE:
207                 grpc_chttp2_goaway_append(
208                     t->last_new_stream_id, sp->error_value,
209                     grpc_slice_from_static_string("HTTP2 settings error"),
210                     &t->qbuf);
211                 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
212                     absl::StrFormat("invalid value %u passed for %s",
213                                     parser->value, sp->name)
214                         .c_str());
215             }
216           }
217           if (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE &&
218               parser->incoming_settings[id] != parser->value) {
219             t->initial_window_update += static_cast<int64_t>(parser->value) -
220                                         parser->incoming_settings[id];
221             if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
222                 GRPC_TRACE_FLAG_ENABLED(grpc_flowctl_trace)) {
223               gpr_log(GPR_INFO, "%p[%s] adding %d for initial_window change", t,
224                       t->is_client ? "cli" : "svr",
225                       static_cast<int>(t->initial_window_update));
226             }
227           }
228           parser->incoming_settings[id] = parser->value;
229           if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
230             gpr_log(GPR_INFO, "CHTTP2:%s:%s: got setting %s = %d",
231                     t->is_client ? "CLI" : "SVR", t->peer_string.c_str(),
232                     sp->name, parser->value);
233           }
234         } else if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
235           gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)",
236                   parser->id, parser->value);
237         }
238         break;
239     }
240   }
241 }
242