Beispiel #1
0
grpc_error *grpc_chttp2_window_update_parser_parse(
    grpc_exec_ctx *exec_ctx, void *parser, grpc_chttp2_transport *t,
    grpc_chttp2_stream *s, grpc_slice slice, int is_last) {
  uint8_t *const beg = GRPC_SLICE_START_PTR(slice);
  uint8_t *const end = GRPC_SLICE_END_PTR(slice);
  uint8_t *cur = beg;
  grpc_chttp2_window_update_parser *p = parser;

  while (p->byte != 4 && cur != end) {
    p->amount |= ((uint32_t)*cur) << (8 * (3 - p->byte));
    cur++;
    p->byte++;
  }

  if (s != NULL) {
    s->stats.incoming.framing_bytes += (uint32_t)(end - cur);
  }

  if (p->byte == 4) {
    uint32_t received_update = p->amount;
    if (received_update == 0 || (received_update & 0x80000000u)) {
      char *msg;
      gpr_asprintf(&msg, "invalid window update bytes: %d", p->amount);
      grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
      gpr_free(msg);
      return err;
    }
    GPR_ASSERT(is_last);

    if (t->incoming_stream_id != 0) {
      if (s != NULL) {
        GRPC_CHTTP2_FLOW_CREDIT_STREAM("parse", t, s, outgoing_window_delta,
                                       received_update);
        if (grpc_chttp2_list_remove_stalled_by_stream(t, s)) {
          grpc_chttp2_become_writable(
              exec_ctx, t, s, GRPC_CHTTP2_STREAM_WRITE_INITIATE_UNCOVERED,
              "stream.read_flow_control");
        }
      }
    } else {
      bool was_zero = t->outgoing_window <= 0;
      GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT("parse", t, outgoing_window,
                                        received_update);
      bool is_zero = t->outgoing_window <= 0;
      if (was_zero && !is_zero) {
        grpc_chttp2_initiate_write(exec_ctx, t, "new_global_flow_control");
      }
    }
  }

  return GRPC_ERROR_NONE;
}
Beispiel #2
0
int grpc_chttp2_unlocking_check_writes(
    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
    grpc_chttp2_transport_writing *transport_writing) {
  grpc_chttp2_stream_global *stream_global;
  grpc_chttp2_stream_writing *stream_writing;

  GPR_TIMER_BEGIN("grpc_chttp2_unlocking_check_writes", 0);

  /* simple writes are queued to qbuf, and flushed here */
  gpr_slice_buffer_swap(&transport_global->qbuf, &transport_writing->outbuf);
  GPR_ASSERT(transport_global->qbuf.count == 0);

  grpc_chttp2_hpack_compressor_set_max_table_size(
      &transport_writing->hpack_compressor,
      transport_global->settings[GRPC_PEER_SETTINGS]
                                [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);

  if (transport_global->dirtied_local_settings &&
      !transport_global->sent_local_settings) {
    gpr_slice_buffer_add(
        &transport_writing->outbuf,
        grpc_chttp2_settings_create(
            transport_global->settings[GRPC_SENT_SETTINGS],
            transport_global->settings[GRPC_LOCAL_SETTINGS],
            transport_global->force_send_settings, GRPC_CHTTP2_NUM_SETTINGS));
    transport_global->force_send_settings = 0;
    transport_global->dirtied_local_settings = 0;
    transport_global->sent_local_settings = 1;
  }

  GRPC_CHTTP2_FLOW_MOVE_TRANSPORT("write", transport_writing, outgoing_window,
                                  transport_global, outgoing_window);
  if (transport_writing->outgoing_window > 0) {
    while (grpc_chttp2_list_pop_stalled_by_transport(transport_global,
                                                     &stream_global)) {
      grpc_chttp2_become_writable(exec_ctx, transport_global, stream_global,
                                  false, "transport.read_flow_control");
    }
  }

  /* for each grpc_chttp2_stream that's become writable, frame it's data
     (according to available window sizes) and add to the output buffer */
  while (grpc_chttp2_list_pop_writable_stream(
      transport_global, transport_writing, &stream_global, &stream_writing)) {
    bool sent_initial_metadata = stream_writing->sent_initial_metadata;
    bool become_writable = false;

    stream_writing->id = stream_global->id;
    stream_writing->read_closed = stream_global->read_closed;

    GRPC_CHTTP2_FLOW_MOVE_STREAM("write", transport_writing, stream_writing,
                                 outgoing_window, stream_global,
                                 outgoing_window);

    if (!sent_initial_metadata && stream_global->send_initial_metadata) {
      stream_writing->send_initial_metadata =
          stream_global->send_initial_metadata;
      stream_global->send_initial_metadata = NULL;
      become_writable = true;
      sent_initial_metadata = true;
    }
    if (sent_initial_metadata) {
      if (stream_global->send_message != NULL) {
        gpr_slice hdr = gpr_slice_malloc(5);
        uint8_t *p = GPR_SLICE_START_PTR(hdr);
        uint32_t len = stream_global->send_message->length;
        GPR_ASSERT(stream_writing->send_message == NULL);
        p[0] = (stream_global->send_message->flags &
                GRPC_WRITE_INTERNAL_COMPRESS) != 0;
        p[1] = (uint8_t)(len >> 24);
        p[2] = (uint8_t)(len >> 16);
        p[3] = (uint8_t)(len >> 8);
        p[4] = (uint8_t)(len);
        gpr_slice_buffer_add(&stream_writing->flow_controlled_buffer, hdr);
        if (stream_global->send_message->length > 0) {
          stream_writing->send_message = stream_global->send_message;
        } else {
          stream_writing->send_message = NULL;
        }
        stream_writing->stream_fetched = 0;
        stream_global->send_message = NULL;
      }
      if ((stream_writing->send_message != NULL ||
           stream_writing->flow_controlled_buffer.length > 0) &&
          stream_writing->outgoing_window > 0) {
        if (transport_writing->outgoing_window > 0) {
          become_writable = true;
        } else {
          grpc_chttp2_list_add_stalled_by_transport(transport_writing,
                                                    stream_writing);
        }
      }
      if (stream_global->send_trailing_metadata) {
        stream_writing->send_trailing_metadata =
            stream_global->send_trailing_metadata;
        stream_global->send_trailing_metadata = NULL;
        become_writable = true;
      }
    }

    if (!stream_global->read_closed &&
        stream_global->unannounced_incoming_window_for_writing > 1024) {
      GRPC_CHTTP2_FLOW_MOVE_STREAM("write", transport_global, stream_writing,
                                   announce_window, stream_global,
                                   unannounced_incoming_window_for_writing);
      become_writable = true;
    }

    if (become_writable) {
      grpc_chttp2_list_add_writing_stream(transport_writing, stream_writing);
    } else {
      GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2_writing");
    }
  }
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);
    }
  }
}
Beispiel #4
0
static grpc_error *update_incoming_window(grpc_exec_ctx *exec_ctx,
                                          grpc_chttp2_transport *t,
                                          grpc_chttp2_stream *s) {
  uint32_t incoming_frame_size = t->incoming_frame_size;
  if (incoming_frame_size > t->incoming_window) {
    char *msg;
    gpr_asprintf(&msg, "frame of size %d overflows incoming window of %" PRId64,
                 t->incoming_frame_size, t->incoming_window);
    grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
    gpr_free(msg);
    return err;
  }

  if (s != NULL) {
    if (incoming_frame_size >
        s->incoming_window_delta +
            t->settings[GRPC_ACKED_SETTINGS]
                       [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]) {
      if (incoming_frame_size <=
          s->incoming_window_delta +
              t->settings[GRPC_SENT_SETTINGS]
                         [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]) {
        gpr_log(
            GPR_ERROR,
            "Incoming frame of size %d exceeds incoming window size of %" PRId64
            ".\n"
            "The (un-acked, future) window size would be %" PRId64
            " which is not exceeded.\n"
            "This would usually cause a disconnection, but allowing it due to "
            "broken HTTP2 implementations in the wild.\n"
            "See (for example) https://github.com/netty/netty/issues/6520.",
            t->incoming_frame_size,
            s->incoming_window_delta +
                t->settings[GRPC_ACKED_SETTINGS]
                           [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
            s->incoming_window_delta +
                t->settings[GRPC_SENT_SETTINGS]
                           [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]);
      } else {
        char *msg;
        gpr_asprintf(&msg,
                     "frame of size %d overflows incoming window of %" PRId64,
                     t->incoming_frame_size,
                     s->incoming_window_delta +
                         t->settings[GRPC_ACKED_SETTINGS]
                                    [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]);
        grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
        gpr_free(msg);
        return err;
      }
    }

    GRPC_CHTTP2_FLOW_DEBIT_STREAM_INCOMING_WINDOW_DELTA("parse", t, s,
                                                        incoming_frame_size);
    if ((int64_t)s->incoming_window_delta - (int64_t)s->announce_window <=
        -(int64_t)t->settings[GRPC_SENT_SETTINGS]
                             [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] /
            2) {
      grpc_chttp2_become_writable(exec_ctx, t, s,
                                  GRPC_CHTTP2_STREAM_WRITE_INITIATE_UNCOVERED,
                                  "window-update-required");
    }
    s->received_bytes += incoming_frame_size;
  }

  uint32_t target_incoming_window = grpc_chttp2_target_incoming_window(t);
  GRPC_CHTTP2_FLOW_DEBIT_TRANSPORT("parse", t, incoming_window,
                                   incoming_frame_size);
  if (t->incoming_window <= target_incoming_window / 2) {
    grpc_chttp2_initiate_write(exec_ctx, t, false, "flow_control");
  }

  return GRPC_ERROR_NONE;
}