| /* |
| * |
| * Copyright 2015 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include "src/core/ext/transport/chttp2/transport/internal.h" |
| |
| #include <string.h> |
| |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/string_util.h> |
| |
| #include "src/core/lib/profiling/timers.h" |
| #include "src/core/lib/slice/slice_string_helpers.h" |
| #include "src/core/lib/transport/http2_errors.h" |
| #include "src/core/lib/transport/static_metadata.h" |
| #include "src/core/lib/transport/status_conversion.h" |
| #include "src/core/lib/transport/timeout_encoding.h" |
| |
| static grpc_error* init_frame_parser(grpc_chttp2_transport* t); |
| static grpc_error* init_header_frame_parser(grpc_chttp2_transport* t, |
| int is_continuation); |
| static grpc_error* init_data_frame_parser(grpc_chttp2_transport* t); |
| static grpc_error* init_rst_stream_parser(grpc_chttp2_transport* t); |
| static grpc_error* init_settings_frame_parser(grpc_chttp2_transport* t); |
| static grpc_error* init_window_update_frame_parser(grpc_chttp2_transport* t); |
| static grpc_error* init_ping_parser(grpc_chttp2_transport* t); |
| static grpc_error* init_goaway_parser(grpc_chttp2_transport* t); |
| static grpc_error* init_skip_frame_parser(grpc_chttp2_transport* t, |
| int is_header); |
| |
| static grpc_error* parse_frame_slice(grpc_chttp2_transport* t, grpc_slice slice, |
| int is_last); |
| |
| grpc_error* grpc_chttp2_perform_read(grpc_chttp2_transport* t, |
| grpc_slice slice) { |
| uint8_t* beg = GRPC_SLICE_START_PTR(slice); |
| uint8_t* end = GRPC_SLICE_END_PTR(slice); |
| uint8_t* cur = beg; |
| grpc_error* err; |
| |
| if (cur == end) return GRPC_ERROR_NONE; |
| |
| switch (t->deframe_state) { |
| case GRPC_DTS_CLIENT_PREFIX_0: |
| case GRPC_DTS_CLIENT_PREFIX_1: |
| case GRPC_DTS_CLIENT_PREFIX_2: |
| case GRPC_DTS_CLIENT_PREFIX_3: |
| case GRPC_DTS_CLIENT_PREFIX_4: |
| case GRPC_DTS_CLIENT_PREFIX_5: |
| case GRPC_DTS_CLIENT_PREFIX_6: |
| case GRPC_DTS_CLIENT_PREFIX_7: |
| case GRPC_DTS_CLIENT_PREFIX_8: |
| case GRPC_DTS_CLIENT_PREFIX_9: |
| case GRPC_DTS_CLIENT_PREFIX_10: |
| case GRPC_DTS_CLIENT_PREFIX_11: |
| case GRPC_DTS_CLIENT_PREFIX_12: |
| case GRPC_DTS_CLIENT_PREFIX_13: |
| case GRPC_DTS_CLIENT_PREFIX_14: |
| case GRPC_DTS_CLIENT_PREFIX_15: |
| case GRPC_DTS_CLIENT_PREFIX_16: |
| case GRPC_DTS_CLIENT_PREFIX_17: |
| case GRPC_DTS_CLIENT_PREFIX_18: |
| case GRPC_DTS_CLIENT_PREFIX_19: |
| case GRPC_DTS_CLIENT_PREFIX_20: |
| case GRPC_DTS_CLIENT_PREFIX_21: |
| case GRPC_DTS_CLIENT_PREFIX_22: |
| case GRPC_DTS_CLIENT_PREFIX_23: |
| while (cur != end && t->deframe_state != GRPC_DTS_FH_0) { |
| if (*cur != GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state]) { |
| char* msg; |
| gpr_asprintf( |
| &msg, |
| "Connect string mismatch: expected '%c' (%d) got '%c' (%d) " |
| "at byte %d", |
| GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state], |
| static_cast<int>(static_cast<uint8_t>( |
| GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state])), |
| *cur, static_cast<int>(*cur), t->deframe_state); |
| err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); |
| gpr_free(msg); |
| return err; |
| } |
| ++cur; |
| t->deframe_state = static_cast<grpc_chttp2_deframe_transport_state>( |
| 1 + static_cast<int>(t->deframe_state)); |
| } |
| if (cur == end) { |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| dts_fh_0: |
| case GRPC_DTS_FH_0: |
| GPR_ASSERT(cur < end); |
| t->incoming_frame_size = (static_cast<uint32_t>(*cur)) << 16; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_1; |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FH_1: |
| GPR_ASSERT(cur < end); |
| t->incoming_frame_size |= (static_cast<uint32_t>(*cur)) << 8; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_2; |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FH_2: |
| GPR_ASSERT(cur < end); |
| t->incoming_frame_size |= *cur; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_3; |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FH_3: |
| GPR_ASSERT(cur < end); |
| t->incoming_frame_type = *cur; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_4; |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FH_4: |
| GPR_ASSERT(cur < end); |
| t->incoming_frame_flags = *cur; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_5; |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FH_5: |
| GPR_ASSERT(cur < end); |
| t->incoming_stream_id = ((static_cast<uint32_t>(*cur)) & 0x7f) << 24; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_6; |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FH_6: |
| GPR_ASSERT(cur < end); |
| t->incoming_stream_id |= (static_cast<uint32_t>(*cur)) << 16; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_7; |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FH_7: |
| GPR_ASSERT(cur < end); |
| t->incoming_stream_id |= (static_cast<uint32_t>(*cur)) << 8; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_8; |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FH_8: |
| GPR_ASSERT(cur < end); |
| t->incoming_stream_id |= (static_cast<uint32_t>(*cur)); |
| t->deframe_state = GRPC_DTS_FRAME; |
| err = init_frame_parser(t); |
| if (err != GRPC_ERROR_NONE) { |
| return err; |
| } |
| if (t->incoming_frame_size == 0) { |
| err = parse_frame_slice(t, grpc_empty_slice(), 1); |
| if (err != GRPC_ERROR_NONE) { |
| return err; |
| } |
| t->incoming_stream = nullptr; |
| if (++cur == end) { |
| t->deframe_state = GRPC_DTS_FH_0; |
| return GRPC_ERROR_NONE; |
| } |
| goto dts_fh_0; /* loop */ |
| } else if (t->flow_control->flow_control_enabled() && |
| t->incoming_frame_size > |
| t->settings[GRPC_ACKED_SETTINGS] |
| [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]) { |
| char* msg; |
| gpr_asprintf(&msg, "Frame size %d is larger than max frame size %d", |
| t->incoming_frame_size, |
| t->settings[GRPC_ACKED_SETTINGS] |
| [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]); |
| err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); |
| gpr_free(msg); |
| return err; |
| } |
| if (++cur == end) { |
| return GRPC_ERROR_NONE; |
| } |
| /* fallthrough */ |
| case GRPC_DTS_FRAME: |
| GPR_ASSERT(cur < end); |
| if (static_cast<uint32_t>(end - cur) == t->incoming_frame_size) { |
| err = parse_frame_slice( |
| t, |
| grpc_slice_sub_no_ref(slice, static_cast<size_t>(cur - beg), |
| static_cast<size_t>(end - beg)), |
| 1); |
| if (err != GRPC_ERROR_NONE) { |
| return err; |
| } |
| t->deframe_state = GRPC_DTS_FH_0; |
| t->incoming_stream = nullptr; |
| return GRPC_ERROR_NONE; |
| } else if (static_cast<uint32_t>(end - cur) > t->incoming_frame_size) { |
| size_t cur_offset = static_cast<size_t>(cur - beg); |
| err = parse_frame_slice( |
| t, |
| grpc_slice_sub_no_ref(slice, cur_offset, |
| cur_offset + t->incoming_frame_size), |
| 1); |
| if (err != GRPC_ERROR_NONE) { |
| return err; |
| } |
| cur += t->incoming_frame_size; |
| t->incoming_stream = nullptr; |
| goto dts_fh_0; /* loop */ |
| } else { |
| err = parse_frame_slice( |
| t, |
| grpc_slice_sub_no_ref(slice, static_cast<size_t>(cur - beg), |
| static_cast<size_t>(end - beg)), |
| 0); |
| if (err != GRPC_ERROR_NONE) { |
| return err; |
| } |
| t->incoming_frame_size -= static_cast<uint32_t>(end - cur); |
| return GRPC_ERROR_NONE; |
| } |
| GPR_UNREACHABLE_CODE(return nullptr); |
| } |
| |
| GPR_UNREACHABLE_CODE(return nullptr); |
| } |
| |
| static grpc_error* init_frame_parser(grpc_chttp2_transport* t) { |
| if (t->is_first_frame && |
| t->incoming_frame_type != GRPC_CHTTP2_FRAME_SETTINGS) { |
| char* msg; |
| gpr_asprintf( |
| &msg, "Expected SETTINGS frame as the first frame, got frame type %d", |
| t->incoming_frame_type); |
| grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); |
| gpr_free(msg); |
| return err; |
| } |
| t->is_first_frame = false; |
| if (t->expect_continuation_stream_id != 0) { |
| if (t->incoming_frame_type != GRPC_CHTTP2_FRAME_CONTINUATION) { |
| char* msg; |
| gpr_asprintf(&msg, "Expected CONTINUATION frame, got frame type %02x", |
| t->incoming_frame_type); |
| grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); |
| gpr_free(msg); |
| return err; |
| } |
| if (t->expect_continuation_stream_id != t->incoming_stream_id) { |
| char* msg; |
| gpr_asprintf( |
| &msg, |
| "Expected CONTINUATION frame for grpc_chttp2_stream %08x, got " |
| "grpc_chttp2_stream %08x", |
| t->expect_continuation_stream_id, t->incoming_stream_id); |
| grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); |
| gpr_free(msg); |
| return err; |
| } |
| return init_header_frame_parser(t, 1); |
| } |
| switch (t->incoming_frame_type) { |
| case GRPC_CHTTP2_FRAME_DATA: |
| return init_data_frame_parser(t); |
| case GRPC_CHTTP2_FRAME_HEADER: |
| return init_header_frame_parser(t, 0); |
| case GRPC_CHTTP2_FRAME_CONTINUATION: |
| return GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
| "Unexpected CONTINUATION frame"); |
| case GRPC_CHTTP2_FRAME_RST_STREAM: |
| return init_rst_stream_parser(t); |
| case GRPC_CHTTP2_FRAME_SETTINGS: |
| return init_settings_frame_parser(t); |
| case GRPC_CHTTP2_FRAME_WINDOW_UPDATE: |
| return init_window_update_frame_parser(t); |
| case GRPC_CHTTP2_FRAME_PING: |
| return init_ping_parser(t); |
| case GRPC_CHTTP2_FRAME_GOAWAY: |
| return init_goaway_parser(t); |
| default: |
| if (grpc_http_trace.enabled()) { |
| gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type); |
| } |
| return init_skip_frame_parser(t, 0); |
| } |
| } |
| |
| static grpc_error* skip_parser(void* parser, grpc_chttp2_transport* t, |
| grpc_chttp2_stream* s, grpc_slice slice, |
| int is_last) { |
| return GRPC_ERROR_NONE; |
| } |
| |
| static void skip_header(void* tp, grpc_mdelem md) { GRPC_MDELEM_UNREF(md); } |
| |
| static grpc_error* init_skip_frame_parser(grpc_chttp2_transport* t, |
| int is_header) { |
| if (is_header) { |
| uint8_t is_eoh = t->expect_continuation_stream_id != 0; |
| t->parser = grpc_chttp2_header_parser_parse; |
| t->parser_data = &t->hpack_parser; |
| t->hpack_parser.on_header = skip_header; |
| t->hpack_parser.on_header_user_data = nullptr; |
| t->hpack_parser.is_boundary = is_eoh; |
| t->hpack_parser.is_eof = static_cast<uint8_t>(is_eoh ? t->header_eof : 0); |
| } else { |
| t->parser = skip_parser; |
| } |
| return GRPC_ERROR_NONE; |
| } |
| |
| void grpc_chttp2_parsing_become_skip_parser(grpc_chttp2_transport* t) { |
| init_skip_frame_parser(t, t->parser == grpc_chttp2_header_parser_parse); |
| } |
| |
| static grpc_error* init_data_frame_parser(grpc_chttp2_transport* t) { |
| grpc_chttp2_stream* s = |
| grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); |
| grpc_error* err = GRPC_ERROR_NONE; |
| grpc_core::chttp2::FlowControlAction action; |
| if (s == nullptr) { |
| err = t->flow_control->RecvData(t->incoming_frame_size); |
| action = t->flow_control->MakeAction(); |
| } else { |
| err = s->flow_control->RecvData(t->incoming_frame_size); |
| action = s->flow_control->MakeAction(); |
| } |
| grpc_chttp2_act_on_flowctl_action(action, t, s); |
| if (err != GRPC_ERROR_NONE) { |
| goto error_handler; |
| } |
| if (s == nullptr) { |
| return init_skip_frame_parser(t, 0); |
| } |
| s->received_bytes += t->incoming_frame_size; |
| s->stats.incoming.framing_bytes += 9; |
| if (err == GRPC_ERROR_NONE && s->read_closed) { |
| return init_skip_frame_parser(t, 0); |
| } |
| if (err == GRPC_ERROR_NONE) { |
| err = grpc_chttp2_data_parser_begin_frame( |
| &s->data_parser, t->incoming_frame_flags, s->id, s); |
| } |
| error_handler: |
| intptr_t unused; |
| if (err == GRPC_ERROR_NONE) { |
| t->incoming_stream = s; |
| /* t->parser = grpc_chttp2_data_parser_parse;*/ |
| t->parser = grpc_chttp2_data_parser_parse; |
| t->parser_data = &s->data_parser; |
| t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST; |
| return GRPC_ERROR_NONE; |
| } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, &unused)) { |
| /* handle stream errors by closing the stream */ |
| if (s != nullptr) { |
| grpc_chttp2_mark_stream_closed(t, s, true, false, err); |
| } |
| grpc_slice_buffer_add( |
| &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, |
| GRPC_HTTP2_PROTOCOL_ERROR, |
| &s->stats.outgoing)); |
| return init_skip_frame_parser(t, 0); |
| } else { |
| return err; |
| } |
| } |
| |
| static void free_timeout(void* p) { gpr_free(p); } |
| |
| static void on_initial_header(void* tp, grpc_mdelem md) { |
| GPR_TIMER_SCOPE("on_initial_header", 0); |
| |
| grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp); |
| grpc_chttp2_stream* s = t->incoming_stream; |
| GPR_ASSERT(s != nullptr); |
| |
| if (grpc_http_trace.enabled()) { |
| char* key = grpc_slice_to_c_string(GRPC_MDKEY(md)); |
| char* value = |
| grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII); |
| gpr_log(GPR_INFO, "HTTP:%d:HDR:%s: %s: %s", s->id, |
| t->is_client ? "CLI" : "SVR", key, value); |
| gpr_free(key); |
| gpr_free(value); |
| } |
| |
| if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) { |
| // We don't use grpc_mdelem_eq here to avoid executing additional |
| // instructions. The reasoning is if the payload is not equal, we already |
| // know that the metadata elements are not equal because the md is |
| // confirmed to be static. If we had used grpc_mdelem_eq here, then if the |
| // payloads are not equal, grpc_mdelem_eq executes more instructions to |
| // determine if they're equal or not. |
| if (md.payload == GRPC_MDELEM_GRPC_STATUS_1.payload || |
| md.payload == GRPC_MDELEM_GRPC_STATUS_2.payload) { |
| s->seen_error = true; |
| } |
| } else { |
| if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && |
| !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { |
| /* TODO(ctiller): check for a status like " 0" */ |
| s->seen_error = true; |
| } |
| |
| if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) { |
| grpc_millis* cached_timeout = static_cast<grpc_millis*>( |
| grpc_mdelem_get_user_data(md, free_timeout)); |
| grpc_millis timeout; |
| if (cached_timeout != nullptr) { |
| timeout = *cached_timeout; |
| } else { |
| if (GPR_UNLIKELY( |
| !grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout))) { |
| char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); |
| gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val); |
| gpr_free(val); |
| timeout = GRPC_MILLIS_INF_FUTURE; |
| } |
| if (GRPC_MDELEM_IS_INTERNED(md)) { |
| /* store the result */ |
| cached_timeout = |
| static_cast<grpc_millis*>(gpr_malloc(sizeof(grpc_millis))); |
| *cached_timeout = timeout; |
| grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); |
| } |
| } |
| if (timeout != GRPC_MILLIS_INF_FUTURE) { |
| grpc_chttp2_incoming_metadata_buffer_set_deadline( |
| &s->metadata_buffer[0], grpc_core::ExecCtx::Get()->Now() + timeout); |
| } |
| GRPC_MDELEM_UNREF(md); |
| return; |
| } |
| } |
| |
| const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md); |
| const size_t metadata_size_limit = |
| t->settings[GRPC_ACKED_SETTINGS] |
| [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; |
| if (new_size > metadata_size_limit) { |
| gpr_log(GPR_DEBUG, |
| "received initial metadata size exceeds limit (%" PRIuPTR |
| " vs. %" PRIuPTR ")", |
| new_size, metadata_size_limit); |
| grpc_chttp2_cancel_stream( |
| t, s, |
| grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
| "received initial metadata size exceeds limit"), |
| GRPC_ERROR_INT_GRPC_STATUS, |
| GRPC_STATUS_RESOURCE_EXHAUSTED)); |
| grpc_chttp2_parsing_become_skip_parser(t); |
| s->seen_error = true; |
| GRPC_MDELEM_UNREF(md); |
| } else { |
| grpc_error* error = |
| grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[0], md); |
| if (error != GRPC_ERROR_NONE) { |
| grpc_chttp2_cancel_stream(t, s, error); |
| grpc_chttp2_parsing_become_skip_parser(t); |
| s->seen_error = true; |
| GRPC_MDELEM_UNREF(md); |
| } |
| } |
| } |
| |
| static void on_trailing_header(void* tp, grpc_mdelem md) { |
| GPR_TIMER_SCOPE("on_trailing_header", 0); |
| |
| grpc_chttp2_transport* t = static_cast<grpc_chttp2_transport*>(tp); |
| grpc_chttp2_stream* s = t->incoming_stream; |
| GPR_ASSERT(s != nullptr); |
| |
| if (grpc_http_trace.enabled()) { |
| char* key = grpc_slice_to_c_string(GRPC_MDKEY(md)); |
| char* value = |
| grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII); |
| gpr_log(GPR_INFO, "HTTP:%d:TRL:%s: %s: %s", s->id, |
| t->is_client ? "CLI" : "SVR", key, value); |
| gpr_free(key); |
| gpr_free(value); |
| } |
| |
| if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) { |
| // We don't use grpc_mdelem_eq here to avoid executing additional |
| // instructions. The reasoning is if the payload is not equal, we already |
| // know that the metadata elements are not equal because the md is |
| // confirmed to be static. If we had used grpc_mdelem_eq here, then if the |
| // payloads are not equal, grpc_mdelem_eq executes more instructions to |
| // determine if they're equal or not. |
| if (md.payload == GRPC_MDELEM_GRPC_STATUS_1.payload || |
| md.payload == GRPC_MDELEM_GRPC_STATUS_2.payload) { |
| s->seen_error = true; |
| } |
| } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && |
| !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { |
| /* TODO(ctiller): check for a status like " 0" */ |
| s->seen_error = true; |
| } |
| |
| const size_t new_size = s->metadata_buffer[1].size + GRPC_MDELEM_LENGTH(md); |
| const size_t metadata_size_limit = |
| t->settings[GRPC_ACKED_SETTINGS] |
| [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; |
| if (new_size > metadata_size_limit) { |
| gpr_log(GPR_DEBUG, |
| "received trailing metadata size exceeds limit (%" PRIuPTR |
| " vs. %" PRIuPTR ")", |
| new_size, metadata_size_limit); |
| grpc_chttp2_cancel_stream( |
| t, s, |
| grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
| "received trailing metadata size exceeds limit"), |
| GRPC_ERROR_INT_GRPC_STATUS, |
| GRPC_STATUS_RESOURCE_EXHAUSTED)); |
| grpc_chttp2_parsing_become_skip_parser(t); |
| s->seen_error = true; |
| GRPC_MDELEM_UNREF(md); |
| } else { |
| grpc_error* error = |
| grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[1], md); |
| if (error != GRPC_ERROR_NONE) { |
| grpc_chttp2_cancel_stream(t, s, error); |
| grpc_chttp2_parsing_become_skip_parser(t); |
| s->seen_error = true; |
| GRPC_MDELEM_UNREF(md); |
| } |
| } |
| } |
| |
| static grpc_error* init_header_frame_parser(grpc_chttp2_transport* t, |
| int is_continuation) { |
| uint8_t is_eoh = |
| (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0; |
| grpc_chttp2_stream* s; |
| |
| /* TODO(ctiller): when to increment header_frames_received? */ |
| |
| if (is_eoh) { |
| t->expect_continuation_stream_id = 0; |
| } else { |
| t->expect_continuation_stream_id = t->incoming_stream_id; |
| } |
| |
| if (!is_continuation) { |
| t->header_eof = |
| (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) != 0; |
| } |
| |
| t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST; |
| |
| /* could be a new grpc_chttp2_stream or an existing grpc_chttp2_stream */ |
| s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); |
| if (s == nullptr) { |
| if (GPR_UNLIKELY(is_continuation)) { |
| GRPC_CHTTP2_IF_TRACING( |
| gpr_log(GPR_ERROR, |
| "grpc_chttp2_stream disbanded before CONTINUATION received")); |
| return init_skip_frame_parser(t, 1); |
| } |
| if (t->is_client) { |
| if (GPR_LIKELY((t->incoming_stream_id & 1) && |
| t->incoming_stream_id < t->next_stream_id)) { |
| /* this is an old (probably cancelled) grpc_chttp2_stream */ |
| } else { |
| GRPC_CHTTP2_IF_TRACING(gpr_log( |
| GPR_ERROR, "ignoring new grpc_chttp2_stream creation on client")); |
| } |
| grpc_error* err = init_skip_frame_parser(t, 1); |
| if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY) { |
| grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser); |
| } |
| return err; |
| } else if (GPR_UNLIKELY(t->last_new_stream_id >= t->incoming_stream_id)) { |
| GRPC_CHTTP2_IF_TRACING(gpr_log( |
| GPR_ERROR, |
| "ignoring out of order new grpc_chttp2_stream request on server; " |
| "last grpc_chttp2_stream " |
| "id=%d, new grpc_chttp2_stream id=%d", |
| t->last_new_stream_id, t->incoming_stream_id)); |
| return init_skip_frame_parser(t, 1); |
| } else if (GPR_UNLIKELY((t->incoming_stream_id & 1) == 0)) { |
| GRPC_CHTTP2_IF_TRACING(gpr_log( |
| GPR_ERROR, |
| "ignoring grpc_chttp2_stream with non-client generated index %d", |
| t->incoming_stream_id)); |
| return init_skip_frame_parser(t, 1); |
| } else if (GPR_UNLIKELY( |
| grpc_chttp2_stream_map_size(&t->stream_map) >= |
| t->settings[GRPC_ACKED_SETTINGS] |
| [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS])) { |
| return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Max stream count exceeded"); |
| } |
| t->last_new_stream_id = t->incoming_stream_id; |
| s = t->incoming_stream = |
| grpc_chttp2_parsing_accept_stream(t, t->incoming_stream_id); |
| if (GPR_UNLIKELY(s == nullptr)) { |
| GRPC_CHTTP2_IF_TRACING( |
| gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted")); |
| return init_skip_frame_parser(t, 1); |
| } |
| if (t->channelz_socket != nullptr) { |
| t->channelz_socket->RecordStreamStartedFromRemote(); |
| } |
| } else { |
| t->incoming_stream = s; |
| } |
| GPR_ASSERT(s != nullptr); |
| s->stats.incoming.framing_bytes += 9; |
| if (GPR_UNLIKELY(s->read_closed)) { |
| GRPC_CHTTP2_IF_TRACING(gpr_log( |
| GPR_ERROR, "skipping already closed grpc_chttp2_stream header")); |
| t->incoming_stream = nullptr; |
| return init_skip_frame_parser(t, 1); |
| } |
| t->parser = grpc_chttp2_header_parser_parse; |
| t->parser_data = &t->hpack_parser; |
| if (t->header_eof) { |
| s->eos_received = true; |
| } |
| switch (s->header_frames_received) { |
| case 0: |
| if (t->is_client && t->header_eof) { |
| GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing Trailers-Only")); |
| if (s->trailing_metadata_available != nullptr) { |
| *s->trailing_metadata_available = true; |
| } |
| t->hpack_parser.on_header = on_trailing_header; |
| s->received_trailing_metadata = true; |
| } else { |
| GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing initial_metadata")); |
| t->hpack_parser.on_header = on_initial_header; |
| } |
| break; |
| case 1: |
| GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing trailing_metadata")); |
| t->hpack_parser.on_header = on_trailing_header; |
| s->received_trailing_metadata = true; |
| break; |
| case 2: |
| gpr_log(GPR_ERROR, "too many header frames received"); |
| return init_skip_frame_parser(t, 1); |
| } |
| t->hpack_parser.on_header_user_data = t; |
| t->hpack_parser.is_boundary = is_eoh; |
| t->hpack_parser.is_eof = static_cast<uint8_t>(is_eoh ? t->header_eof : 0); |
| if (!is_continuation && |
| (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY)) { |
| grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser); |
| } |
| return GRPC_ERROR_NONE; |
| } |
| |
| static grpc_error* init_window_update_frame_parser(grpc_chttp2_transport* t) { |
| grpc_error* err = grpc_chttp2_window_update_parser_begin_frame( |
| &t->simple.window_update, t->incoming_frame_size, |
| t->incoming_frame_flags); |
| if (err != GRPC_ERROR_NONE) return err; |
| if (t->incoming_stream_id != 0) { |
| grpc_chttp2_stream* s = t->incoming_stream = |
| grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); |
| if (s == nullptr) { |
| return init_skip_frame_parser(t, 0); |
| } |
| s->stats.incoming.framing_bytes += 9; |
| } |
| t->parser = grpc_chttp2_window_update_parser_parse; |
| t->parser_data = &t->simple.window_update; |
| return GRPC_ERROR_NONE; |
| } |
| |
| static grpc_error* init_ping_parser(grpc_chttp2_transport* t) { |
| grpc_error* err = grpc_chttp2_ping_parser_begin_frame( |
| &t->simple.ping, t->incoming_frame_size, t->incoming_frame_flags); |
| if (err != GRPC_ERROR_NONE) return err; |
| t->parser = grpc_chttp2_ping_parser_parse; |
| t->parser_data = &t->simple.ping; |
| return GRPC_ERROR_NONE; |
| } |
| |
| static grpc_error* init_rst_stream_parser(grpc_chttp2_transport* t) { |
| grpc_error* err = grpc_chttp2_rst_stream_parser_begin_frame( |
| &t->simple.rst_stream, t->incoming_frame_size, t->incoming_frame_flags); |
| if (err != GRPC_ERROR_NONE) return err; |
| grpc_chttp2_stream* s = t->incoming_stream = |
| grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); |
| if (!t->incoming_stream) { |
| return init_skip_frame_parser(t, 0); |
| } |
| s->stats.incoming.framing_bytes += 9; |
| t->parser = grpc_chttp2_rst_stream_parser_parse; |
| t->parser_data = &t->simple.rst_stream; |
| return GRPC_ERROR_NONE; |
| } |
| |
| static grpc_error* init_goaway_parser(grpc_chttp2_transport* t) { |
| grpc_error* err = grpc_chttp2_goaway_parser_begin_frame( |
| &t->goaway_parser, t->incoming_frame_size, t->incoming_frame_flags); |
| if (err != GRPC_ERROR_NONE) return err; |
| t->parser = grpc_chttp2_goaway_parser_parse; |
| t->parser_data = &t->goaway_parser; |
| return GRPC_ERROR_NONE; |
| } |
| |
| static grpc_error* init_settings_frame_parser(grpc_chttp2_transport* t) { |
| if (t->incoming_stream_id != 0) { |
| return GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
| "Settings frame received for grpc_chttp2_stream"); |
| } |
| |
| grpc_error* err = grpc_chttp2_settings_parser_begin_frame( |
| &t->simple.settings, t->incoming_frame_size, t->incoming_frame_flags, |
| t->settings[GRPC_PEER_SETTINGS]); |
| if (err != GRPC_ERROR_NONE) { |
| return err; |
| } |
| if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) { |
| memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS], |
| GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); |
| grpc_chttp2_hptbl_set_max_bytes( |
| &t->hpack_parser.table, |
| t->settings[GRPC_ACKED_SETTINGS] |
| [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]); |
| t->sent_local_settings = 0; |
| } |
| t->parser = grpc_chttp2_settings_parser_parse; |
| t->parser_data = &t->simple.settings; |
| return GRPC_ERROR_NONE; |
| } |
| |
| static grpc_error* parse_frame_slice(grpc_chttp2_transport* t, grpc_slice slice, |
| int is_last) { |
| grpc_chttp2_stream* s = t->incoming_stream; |
| grpc_error* err = t->parser(t->parser_data, t, s, slice, is_last); |
| intptr_t unused; |
| if (GPR_LIKELY(err == GRPC_ERROR_NONE)) { |
| return err; |
| } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, &unused)) { |
| if (grpc_http_trace.enabled()) { |
| const char* msg = grpc_error_string(err); |
| gpr_log(GPR_ERROR, "%s", msg); |
| } |
| grpc_chttp2_parsing_become_skip_parser(t); |
| if (s) { |
| s->forced_close_error = err; |
| grpc_slice_buffer_add( |
| &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, |
| GRPC_HTTP2_PROTOCOL_ERROR, |
| &s->stats.outgoing)); |
| } else { |
| GRPC_ERROR_UNREF(err); |
| } |
| } |
| return err; |
| } |