Make it more likely to correctly report deadline exceeded
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 38e782b..5aae753 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -513,6 +513,7 @@
       &s->global.received_trailing_metadata);
   grpc_chttp2_data_parser_init(&s->parsing.data_parser);
   gpr_slice_buffer_init(&s->writing.flow_controlled_buffer);
+  s->global.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
 
   REF_TRANSPORT(t, "stream");
 
@@ -988,6 +989,11 @@
     const size_t metadata_peer_limit =
         transport_global->settings[GRPC_PEER_SETTINGS]
                                   [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
+    if (transport_global->is_client) {
+      stream_global->deadline =
+          gpr_time_min(stream_global->deadline,
+                       stream_global->send_initial_metadata->deadline);
+    }
     if (metadata_size > metadata_peer_limit) {
       cancel_from_api(
           exec_ctx, transport_global, stream_global,
@@ -1366,7 +1372,7 @@
   GRPC_ERROR_UNREF(error);
 }
 
-static void status_codes_from_error(grpc_error *error,
+static void status_codes_from_error(grpc_error *error, gpr_timespec deadline,
                                     grpc_chttp2_error_code *http2_error,
                                     grpc_status_code *grpc_status) {
   intptr_t ip_http;
@@ -1386,8 +1392,8 @@
   if (have_grpc) {
     *grpc_status = (grpc_status_code)ip_grpc;
   } else if (have_http) {
-    *grpc_status =
-        grpc_chttp2_http2_error_to_grpc_status((grpc_chttp2_error_code)ip_http);
+    *grpc_status = grpc_chttp2_http2_error_to_grpc_status(
+        (grpc_chttp2_error_code)ip_http, deadline);
   } else {
     *grpc_status = GRPC_STATUS_INTERNAL;
   }
@@ -1400,7 +1406,8 @@
   if (!stream_global->read_closed || !stream_global->write_closed) {
     grpc_status_code grpc_status;
     grpc_chttp2_error_code http_error;
-    status_codes_from_error(due_to_error, &http_error, &grpc_status);
+    status_codes_from_error(due_to_error, stream_global->deadline, &http_error,
+                            &grpc_status);
 
     if (stream_global->id != 0) {
       gpr_slice_buffer_add(
@@ -1536,7 +1543,8 @@
   uint32_t len = 0;
   grpc_status_code grpc_status;
   grpc_chttp2_error_code http_error;
-  status_codes_from_error(error, &http_error, &grpc_status);
+  status_codes_from_error(error, stream_global->deadline, &http_error,
+                          &grpc_status);
 
   GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100);
 
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index b5180c6..8d79e93 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -447,6 +447,8 @@
   grpc_chttp2_incoming_metadata_buffer received_trailing_metadata;
 
   grpc_chttp2_incoming_frame_queue incoming_frames;
+
+  gpr_timespec deadline;
 } grpc_chttp2_stream_global;
 
 typedef struct {
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index 991d772..c5240ce 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -87,8 +87,8 @@
          transport_global->settings[GRPC_SENT_SETTINGS],
          sizeof(transport_parsing->last_sent_settings));
   transport_parsing->max_frame_size =
-      transport_global->settings[GRPC_ACKED_SETTINGS]
-                                [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE];
+      transport_global
+          ->settings[GRPC_ACKED_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE];
 
   /* update the parsing view of incoming window */
   while (grpc_chttp2_list_pop_unannounced_incoming_window_available(
@@ -236,9 +236,10 @@
                                            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)
-                       : GRPC_STATUS_INTERNAL;
+            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);
diff --git a/src/core/ext/transport/chttp2/transport/status_conversion.c b/src/core/ext/transport/chttp2/transport/status_conversion.c
index c42fb9b..5dce2f2 100644
--- a/src/core/ext/transport/chttp2/transport/status_conversion.c
+++ b/src/core/ext/transport/chttp2/transport/status_conversion.c
@@ -39,6 +39,8 @@
       return GRPC_CHTTP2_NO_ERROR;
     case GRPC_STATUS_CANCELLED:
       return GRPC_CHTTP2_CANCEL;
+    case GRPC_STATUS_DEADLINE_EXCEEDED:
+      return GRPC_CHTTP2_CANCEL;
     case GRPC_STATUS_RESOURCE_EXHAUSTED:
       return GRPC_CHTTP2_ENHANCE_YOUR_CALM;
     case GRPC_STATUS_PERMISSION_DENIED:
@@ -51,13 +53,17 @@
 }
 
 grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error) {
+    grpc_chttp2_error_code error, gpr_timespec deadline) {
   switch (error) {
     case GRPC_CHTTP2_NO_ERROR:
       /* should never be received */
       return GRPC_STATUS_INTERNAL;
     case GRPC_CHTTP2_CANCEL:
-      return GRPC_STATUS_CANCELLED;
+      /* http2 cancel translates to STATUS_CANCELLED iff deadline hasn't been
+       * exceeded */
+      return gpr_time_cmp(gpr_now(deadline.clock_type), deadline) >= 0
+                 ? GRPC_STATUS_DEADLINE_EXCEEDED
+                 : GRPC_STATUS_CANCELLED;
     case GRPC_CHTTP2_ENHANCE_YOUR_CALM:
       return GRPC_STATUS_RESOURCE_EXHAUSTED;
     case GRPC_CHTTP2_INADEQUATE_SECURITY:
diff --git a/src/core/ext/transport/chttp2/transport/status_conversion.h b/src/core/ext/transport/chttp2/transport/status_conversion.h
index e7285e6..953bc9f 100644
--- a/src/core/ext/transport/chttp2/transport/status_conversion.h
+++ b/src/core/ext/transport/chttp2/transport/status_conversion.h
@@ -41,7 +41,7 @@
 grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error(
     grpc_status_code status);
 grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error);
+    grpc_chttp2_error_code error, gpr_timespec deadline);
 
 /* Conversion of HTTP status codes (:status) to grpc status codes */
 grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status);
diff --git a/test/core/transport/chttp2/status_conversion_test.c b/test/core/transport/chttp2/status_conversion_test.c
index e6fc785..f5a5cd1 100644
--- a/test/core/transport/chttp2/status_conversion_test.c
+++ b/test/core/transport/chttp2/status_conversion_test.c
@@ -37,8 +37,8 @@
 
 #define GRPC_STATUS_TO_HTTP2_ERROR(a, b) \
   GPR_ASSERT(grpc_chttp2_grpc_status_to_http2_error(a) == (b))
-#define HTTP2_ERROR_TO_GRPC_STATUS(a, b) \
-  GPR_ASSERT(grpc_chttp2_http2_error_to_grpc_status(a) == (b))
+#define HTTP2_ERROR_TO_GRPC_STATUS(a, deadline, b) \
+  GPR_ASSERT(grpc_chttp2_http2_error_to_grpc_status(a, deadline) == (b))
 #define GRPC_STATUS_TO_HTTP2_STATUS(a, b) \
   GPR_ASSERT(grpc_chttp2_grpc_status_to_http2_status(a) == (b))
 #define HTTP2_STATUS_TO_GRPC_STATUS(a, b) \
@@ -54,8 +54,7 @@
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNKNOWN, GRPC_CHTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_INVALID_ARGUMENT,
                              GRPC_CHTTP2_INTERNAL_ERROR);
-  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DEADLINE_EXCEEDED,
-                             GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DEADLINE_EXCEEDED, GRPC_CHTTP2_CANCEL);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_NOT_FOUND, GRPC_CHTTP2_INTERNAL_ERROR);
   GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_ALREADY_EXISTS,
                              GRPC_CHTTP2_INTERNAL_ERROR);
@@ -95,25 +94,60 @@
   GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNAVAILABLE, 200);
   GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_DATA_LOSS, 200);
 
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_NO_ERROR, GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_PROTOCOL_ERROR, GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INTERNAL_ERROR, GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FLOW_CONTROL_ERROR,
+  const gpr_timespec before_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_NO_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_SETTINGS_TIMEOUT,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_PROTOCOL_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_STREAM_CLOSED, GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FRAME_SIZE_ERROR,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INTERNAL_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_REFUSED_STREAM,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FLOW_CONTROL_ERROR, before_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_SETTINGS_TIMEOUT, before_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_STREAM_CLOSED, before_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FRAME_SIZE_ERROR, before_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_REFUSED_STREAM, before_deadline,
                              GRPC_STATUS_UNAVAILABLE);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CANCEL, GRPC_STATUS_CANCELLED);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_COMPRESSION_ERROR,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CANCEL, before_deadline,
+                             GRPC_STATUS_CANCELLED);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_COMPRESSION_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CONNECT_ERROR, GRPC_STATUS_INTERNAL);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_ENHANCE_YOUR_CALM,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CONNECT_ERROR, before_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_ENHANCE_YOUR_CALM, before_deadline,
                              GRPC_STATUS_RESOURCE_EXHAUSTED);
-  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INADEQUATE_SECURITY,
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INADEQUATE_SECURITY, before_deadline,
+                             GRPC_STATUS_PERMISSION_DENIED);
+
+  const gpr_timespec after_deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_NO_ERROR, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_PROTOCOL_ERROR, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INTERNAL_ERROR, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FLOW_CONTROL_ERROR, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_SETTINGS_TIMEOUT, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_STREAM_CLOSED, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FRAME_SIZE_ERROR, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_REFUSED_STREAM, after_deadline,
+                             GRPC_STATUS_UNAVAILABLE);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CANCEL, after_deadline,
+                             GRPC_STATUS_DEADLINE_EXCEEDED);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_COMPRESSION_ERROR, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CONNECT_ERROR, after_deadline,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_ENHANCE_YOUR_CALM, after_deadline,
+                             GRPC_STATUS_RESOURCE_EXHAUSTED);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INADEQUATE_SECURITY, after_deadline,
                              GRPC_STATUS_PERMISSION_DENIED);
 
   HTTP2_STATUS_TO_GRPC_STATUS(200, GRPC_STATUS_OK);