void grpc_error_get_status(grpc_error *error, gpr_timespec deadline,
                           grpc_status_code *code, grpc_slice *slice,
                           grpc_http2_error_code *http_error) {
  // Start with the parent error and recurse through the tree of children
  // until we find the first one that has a status code.
  grpc_error *found_error =
      recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS);
  if (found_error == NULL) {
    /// If no grpc-status exists, retry through the tree to find a http2 error
    /// code
    found_error =
        recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR);
  }

  // If we found an error with a status code above, use that; otherwise,
  // fall back to using the parent error.
  if (found_error == NULL) found_error = error;

  grpc_status_code status = GRPC_STATUS_UNKNOWN;
  intptr_t integer;
  if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) {
    status = (grpc_status_code)integer;
  } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR,
                                &integer)) {
    status = grpc_http2_error_to_grpc_status((grpc_http2_error_code)integer,
                                             deadline);
  }
  if (code != NULL) *code = status;

  if (http_error != NULL) {
    if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) {
      *http_error = (grpc_http2_error_code)integer;
    } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS,
                                  &integer)) {
      *http_error = grpc_status_to_http2_error((grpc_status_code)integer);
    } else {
      *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR
                                                   : GRPC_HTTP2_INTERNAL_ERROR;
    }
  }

  // If the error has a status message, use it.  Otherwise, fall back to
  // the error description.
  if (slice != NULL) {
    if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE, slice)) {
      if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION, slice)) {
        *slice = grpc_slice_from_static_string("unknown error");
      }
    }
  }

  if (found_error == NULL) found_error = error;
}
Example #2
0
static grpc_error *parse_frame_slice(grpc_exec_ctx *exec_ctx,
                                     grpc_chttp2_transport *t, grpc_slice slice,
                                     int is_last) {
  grpc_chttp2_stream *s = t->incoming_stream;
  grpc_error *err = t->parser(exec_ctx, t->parser_data, t, s, slice, is_last);
  if (err == GRPC_ERROR_NONE) {
    return err;
  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) {
    if (GRPC_TRACER_ON(grpc_http_trace)) {
      const char *msg = grpc_error_string(err);
      gpr_log(GPR_ERROR, "%s", msg);
    }
    grpc_chttp2_parsing_become_skip_parser(exec_ctx, 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;
}
static grpc_error *parse_frame_slice(
    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
    gpr_slice slice, int is_last) {
  grpc_chttp2_stream_parsing *stream_parsing =
      transport_parsing->incoming_stream;
  grpc_error *err = transport_parsing->parser(
      exec_ctx, transport_parsing->parser_data, transport_parsing,
      stream_parsing, slice, is_last);
  if (err == GRPC_ERROR_NONE) {
    if (stream_parsing) {
      grpc_chttp2_list_add_parsing_seen_stream(transport_parsing,
                                               stream_parsing);
    }
    return GRPC_ERROR_NONE;
  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) {
    if (grpc_http_trace) {
      const char *msg = grpc_error_string(err);
      gpr_log(GPR_ERROR, "%s", msg);
      grpc_error_free_string(msg);
    }
    grpc_chttp2_parsing_become_skip_parser(exec_ctx, transport_parsing);
    if (stream_parsing) {
      stream_parsing->forced_close_error = err;
      gpr_slice_buffer_add(
          &transport_parsing->qbuf,
          grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id,
                                        GRPC_CHTTP2_PROTOCOL_ERROR,
                                        &stream_parsing->stats.outgoing));
    } else {
      GRPC_ERROR_UNREF(err);
    }
  }
  return err;
}
Example #4
0
static grpc_error *init_data_frame_parser(grpc_exec_ctx *exec_ctx,
                                          grpc_chttp2_transport *t) {
  grpc_chttp2_stream *s =
      grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id);
  grpc_error *err = GRPC_ERROR_NONE;
  err = grpc_chttp2_flowctl_recv_data(&t->flow_control,
                                      s == NULL ? NULL : &s->flow_control,
                                      t->incoming_frame_size);
  grpc_chttp2_act_on_flowctl_action(
      exec_ctx, grpc_chttp2_flowctl_get_action(
                    &t->flow_control, s == NULL ? NULL : &s->flow_control),
      t, s);
  if (err != GRPC_ERROR_NONE) {
    goto error_handler;
  }
  if (s == NULL) {
    return init_skip_frame_parser(exec_ctx, 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(exec_ctx, 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:
  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.pings_before_data_required =
        t->ping_policy.max_pings_without_data;
    t->ping_state.last_ping_sent_time = gpr_inf_past(GPR_CLOCK_MONOTONIC);
    return GRPC_ERROR_NONE;
  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) {
    /* handle stream errors by closing the stream */
    if (s != NULL) {
      grpc_chttp2_mark_stream_closed(exec_ctx, 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(exec_ctx, t, 0);
  } else {
    return err;
  }
}
bool grpc_error_has_clear_grpc_status(grpc_error *error) {
  if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) {
    return true;
  }
  uint8_t slot = error->first_err;
  while (slot != UINT8_MAX) {
    grpc_linked_error *lerr = (grpc_linked_error *)(error->arena + slot);
    if (grpc_error_has_clear_grpc_status(lerr->err)) {
      return true;
    }
    slot = lerr->next;
  }
  return false;
}
static grpc_error *recursively_find_error_with_field(grpc_error *error,
                                                     grpc_error_ints which) {
  // If the error itself has a status code, return it.
  if (grpc_error_get_int(error, which, NULL)) {
    return error;
  }
  if (grpc_error_is_special(error)) return NULL;
  // Otherwise, search through its children.
  uint8_t slot = error->first_err;
  while (slot != UINT8_MAX) {
    grpc_linked_error *lerr = (grpc_linked_error *)(error->arena + slot);
    grpc_error *result = recursively_find_error_with_field(lerr->err, which);
    if (result) return result;
    slot = lerr->next;
  }
  return NULL;
}
Example #7
0
static grpc_error *init_data_frame_parser(grpc_exec_ctx *exec_ctx,
                                          grpc_chttp2_transport *t) {
  grpc_chttp2_stream *s =
      grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id);
  grpc_error *err = GRPC_ERROR_NONE;
  err = update_incoming_window(exec_ctx, t, s);
  if (err != GRPC_ERROR_NONE) {
    goto error_handler;
  }
  if (s == NULL) {
    return init_skip_frame_parser(exec_ctx, t, 0);
  }
  s->stats.incoming.framing_bytes += 9;
  if (err == GRPC_ERROR_NONE && s->read_closed) {
    return init_skip_frame_parser(exec_ctx, 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:
  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;
    return GRPC_ERROR_NONE;
  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) {
    /* handle stream errors by closing the stream */
    if (s != NULL) {
      grpc_chttp2_mark_stream_closed(exec_ctx, 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(exec_ctx, t, 0);
  } else {
    return err;
  }
}
static grpc_error *init_data_frame_parser(
    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) {
  grpc_chttp2_stream_parsing *stream_parsing =
      grpc_chttp2_parsing_lookup_stream(transport_parsing,
                                        transport_parsing->incoming_stream_id);
  grpc_error *err = GRPC_ERROR_NONE;
  if (stream_parsing == NULL) {
    return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
  }
  stream_parsing->stats.incoming.framing_bytes += 9;
  if (stream_parsing->received_close) {
    return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
  }
  if (err == GRPC_ERROR_NONE) {
    err = update_incoming_window(exec_ctx, transport_parsing, stream_parsing);
  }
  if (err == GRPC_ERROR_NONE) {
    err = grpc_chttp2_data_parser_begin_frame(
        &stream_parsing->data_parser, transport_parsing->incoming_frame_flags,
        stream_parsing->id);
  }
  if (err == GRPC_ERROR_NONE) {
    transport_parsing->incoming_stream = stream_parsing;
    transport_parsing->parser = grpc_chttp2_data_parser_parse;
    transport_parsing->parser_data = &stream_parsing->data_parser;
    return GRPC_ERROR_NONE;
  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) {
    /* handle stream errors by closing the stream */
    stream_parsing->received_close = 1;
    stream_parsing->forced_close_error = err;
    gpr_slice_buffer_add(
        &transport_parsing->qbuf,
        grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id,
                                      GRPC_CHTTP2_PROTOCOL_ERROR,
                                      &stream_parsing->stats.outgoing));
    return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
  } else {
    return err;
  }
}
void grpc_chttp2_publish_reads(
    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
    grpc_chttp2_transport_parsing *transport_parsing) {
  grpc_chttp2_stream_global *stream_global;
  grpc_chttp2_stream_parsing *stream_parsing;
  int was_zero;
  int is_zero;

  /* transport_parsing->last_incoming_stream_id is used as
     last-grpc_chttp2_stream-id when
     sending GOAWAY frame.
     https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-6.8
     says that last-grpc_chttp2_stream-id is peer-initiated grpc_chttp2_stream
     ID.  So,
     since we don't have server pushed streams, client should send
     GOAWAY last-grpc_chttp2_stream-id=0 in this case. */
  if (!transport_parsing->is_client) {
    transport_global->last_incoming_stream_id =
        transport_parsing->last_incoming_stream_id;
  }

  /* update global settings */
  if (transport_parsing->settings_updated) {
    memcpy(transport_global->settings[GRPC_PEER_SETTINGS],
           transport_parsing->settings, sizeof(transport_parsing->settings));
    transport_parsing->settings_updated = 0;
  }

  /* update settings based on ack if received */
  if (transport_parsing->settings_ack_received) {
    memcpy(transport_global->settings[GRPC_ACKED_SETTINGS],
           transport_global->settings[GRPC_SENT_SETTINGS],
           GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
    transport_parsing->settings_ack_received = 0;
    transport_global->sent_local_settings = 0;
  }

  /* move goaway to the global state if we received one (it will be
     published later */
  if (transport_parsing->goaway_received) {
    grpc_chttp2_add_incoming_goaway(exec_ctx, transport_global,
                                    (uint32_t)transport_parsing->goaway_error,
                                    transport_parsing->goaway_text);
    transport_parsing->goaway_text = gpr_empty_slice();
    transport_parsing->goaway_received = 0;
  }

  /* propagate flow control tokens to global state */
  was_zero = transport_global->outgoing_window <= 0;
  GRPC_CHTTP2_FLOW_MOVE_TRANSPORT("parsed", transport_global, outgoing_window,
                                  transport_parsing, outgoing_window);
  is_zero = transport_global->outgoing_window <= 0;
  if (was_zero && !is_zero) {
    while (grpc_chttp2_list_pop_stalled_by_transport(transport_global,
                                                     &stream_global)) {
      grpc_chttp2_become_writable(transport_global, stream_global);
    }
  }

  if (transport_parsing->incoming_window <
      transport_global->connection_window_target * 3 / 4) {
    int64_t announce_bytes = transport_global->connection_window_target -
                             transport_parsing->incoming_window;
    GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT("parsed", transport_global,
                                      announce_incoming_window, announce_bytes);
    GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT("parsed", transport_parsing,
                                      incoming_window, announce_bytes);
  }

  /* for each stream that saw an update, fixup global state */
  while (grpc_chttp2_list_pop_parsing_seen_stream(
      transport_global, transport_parsing, &stream_global, &stream_parsing)) {
    if (stream_parsing->seen_error) {
      stream_global->seen_error = true;
      stream_global->exceeded_metadata_size =
          stream_parsing->exceeded_metadata_size;
      grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
    }

    /* flush stats to global stream state */
    grpc_transport_move_stats(&stream_parsing->stats, &stream_global->stats);

    /* update outgoing flow control window */
    was_zero = stream_global->outgoing_window <= 0;
    GRPC_CHTTP2_FLOW_MOVE_STREAM("parsed", transport_global, stream_global,
                                 outgoing_window, stream_parsing,
                                 outgoing_window);
    is_zero = stream_global->outgoing_window <= 0;
    if (was_zero && !is_zero) {
      grpc_chttp2_become_writable(transport_global, stream_global);
    }

    stream_global->max_recv_bytes -= (uint32_t)GPR_MIN(
        stream_global->max_recv_bytes, stream_parsing->received_bytes);
    stream_parsing->received_bytes = 0;

    /* publish incoming stream ops */
    if (stream_global->incoming_frames.tail != NULL) {
      stream_global->incoming_frames.tail->is_tail = 0;
    }
    if (stream_parsing->data_parser.incoming_frames.head != NULL) {
      grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
    }
    grpc_chttp2_incoming_frame_queue_merge(
        &stream_global->incoming_frames,
        &stream_parsing->data_parser.incoming_frames);
    if (stream_global->incoming_frames.tail != NULL) {
      stream_global->incoming_frames.tail->is_tail = 1;
    }

    if (!stream_global->published_initial_metadata &&
        stream_parsing->got_metadata_on_parse[0]) {
      stream_parsing->got_metadata_on_parse[0] = 0;
      stream_global->published_initial_metadata = 1;
      GPR_SWAP(grpc_chttp2_incoming_metadata_buffer,
               stream_parsing->metadata_buffer[0],
               stream_global->received_initial_metadata);
      grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
    }
    if (!stream_global->published_trailing_metadata &&
        stream_parsing->got_metadata_on_parse[1]) {
      stream_parsing->got_metadata_on_parse[1] = 0;
      stream_global->published_trailing_metadata = 1;
      GPR_SWAP(grpc_chttp2_incoming_metadata_buffer,
               stream_parsing->metadata_buffer[1],
               stream_global->received_trailing_metadata);
      grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
    }

    if (stream_parsing->forced_close_error != GRPC_ERROR_NONE) {
      intptr_t reason;
      bool has_reason = grpc_error_get_int(stream_parsing->forced_close_error,
                                           GRPC_ERROR_INT_HTTP2_ERROR, &reason);
      if (has_reason && reason != GRPC_CHTTP2_NO_ERROR) {
        grpc_status_code status_code =
            has_reason
                ? grpc_chttp2_http2_error_to_grpc_status(
                      (grpc_chttp2_error_code)reason, stream_global->deadline)
                : GRPC_STATUS_INTERNAL;
        const char *status_details =
            grpc_error_string(stream_parsing->forced_close_error);
        gpr_slice slice_details = gpr_slice_from_copied_string(status_details);
        grpc_error_free_string(status_details);
        grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global,
                                status_code, &slice_details);
      }
      grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global,
                                     1, 1, stream_parsing->forced_close_error);
    }

    if (stream_parsing->received_close) {
      grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global,
                                     1, 0, GRPC_ERROR_NONE);
    }
  }
}