Merge pull request #2965 from yang-g/lame_client_error_message

Let lame_client accept error status
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index 2d53325..56fd4db 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -561,7 +561,9 @@
                                            void *reserved);
 
 /** Create a lame client: this client fails every operation attempted on it. */
-grpc_channel *grpc_lame_client_channel_create(const char *target);
+grpc_channel *grpc_lame_client_channel_create(const char *target,
+                                              grpc_status_code error_code,
+                                              const char *error_message);
 
 /** Close and destroy a grpc channel */
 void grpc_channel_destroy(grpc_channel *channel);
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index c4215a2..80704cb 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -50,6 +50,8 @@
 typedef struct {
   grpc_mdctx *mdctx;
   grpc_channel *master;
+  grpc_status_code error_code;
+  const char *error_message;
 } channel_data;
 
 static void lame_start_transport_stream_op(grpc_call_element *elem,
@@ -64,11 +66,11 @@
   if (op->recv_ops != NULL) {
     char tmp[GPR_LTOA_MIN_BUFSIZE];
     grpc_metadata_batch mdb;
-    gpr_ltoa(GRPC_STATUS_UNKNOWN, tmp);
+    gpr_ltoa(chand->error_code, tmp);
     calld->status.md =
         grpc_mdelem_from_strings(chand->mdctx, "grpc-status", tmp);
     calld->details.md = grpc_mdelem_from_strings(chand->mdctx, "grpc-message",
-                                                 "Rpc sent on a lame channel.");
+                                                 chand->error_message);
     calld->status.prev = calld->details.next = NULL;
     calld->status.next = &calld->details;
     calld->details.prev = &calld->status;
@@ -138,8 +140,21 @@
     "lame-client",
 };
 
-grpc_channel *grpc_lame_client_channel_create(const char *target) {
+#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1))
+
+grpc_channel *grpc_lame_client_channel_create(const char *target,
+                                              grpc_status_code error_code,
+                                              const char *error_message) {
+  grpc_channel *channel;
+  grpc_channel_element *elem;
+  channel_data *chand;
   static const grpc_channel_filter *filters[] = {&lame_filter};
-  return grpc_channel_create_from_filters(target, filters, 1, NULL,
-                                          grpc_mdctx_create(), 1);
+  channel = grpc_channel_create_from_filters(target, filters, 1, NULL,
+                                             grpc_mdctx_create(), 1);
+  elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
+  GPR_ASSERT(elem->filter == &lame_filter);
+  chand = (channel_data *)elem->channel_data;
+  chand->error_code = error_code;
+  chand->error_message = error_message;
+  return channel;
 }
diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c
index c315025..5b03ba9 100644
--- a/src/core/surface/secure_channel_create.c
+++ b/src/core/surface/secure_channel_create.c
@@ -199,13 +199,17 @@
 
   if (grpc_find_security_connector_in_args(args) != NULL) {
     gpr_log(GPR_ERROR, "Cannot set security context in channel args.");
-    return grpc_lame_client_channel_create(target);
+    return grpc_lame_client_channel_create(
+        target, GRPC_STATUS_INVALID_ARGUMENT,
+        "Security connector exists in channel args.");
   }
 
   if (grpc_credentials_create_security_connector(
           creds, target, args, NULL, &connector, &new_args_from_connector) !=
       GRPC_SECURITY_OK) {
-    return grpc_lame_client_channel_create(target);
+    return grpc_lame_client_channel_create(
+        target, GRPC_STATUS_INVALID_ARGUMENT,
+        "Failed to create security connector.");
   }
   mdctx = grpc_mdctx_create();
 
diff --git a/src/cpp/client/create_channel.cc b/src/cpp/client/create_channel.cc
index 21d01b7..5ae772f 100644
--- a/src/cpp/client/create_channel.cc
+++ b/src/cpp/client/create_channel.cc
@@ -52,6 +52,8 @@
                     user_agent_prefix.str());
   return creds ? creds->CreateChannel(target, cp_args)
                : std::shared_ptr<ChannelInterface>(
-                     new Channel(grpc_lame_client_channel_create(NULL)));
+                     new Channel(grpc_lame_client_channel_create(
+                         NULL, GRPC_STATUS_INVALID_ARGUMENT,
+                         "Invalid credentials.")));
 }
 }  // namespace grpc
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
index 216eeca..0c53c95 100644
--- a/test/core/surface/lame_client_test.c
+++ b/test/core/surface/lame_client_test.c
@@ -58,7 +58,8 @@
 
   grpc_metadata_array_init(&trailing_metadata_recv);
 
-  chan = grpc_lame_client_channel_create("lampoon:national");
+  chan = grpc_lame_client_channel_create(
+      "lampoon:national", GRPC_STATUS_UNKNOWN, "Rpc sent on a lame channel.");
   GPR_ASSERT(chan);
   cq = grpc_completion_queue_create(NULL);
   call = grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq,
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 3827cdf..22af1fd 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -583,15 +583,15 @@
   Status s = stub->Echo(&context, request, &response);
   EXPECT_EQ("", response.message());
   EXPECT_FALSE(s.ok());
-  EXPECT_EQ(StatusCode::UNKNOWN, s.error_code());
-  EXPECT_EQ("Rpc sent on a lame channel.", s.error_message());
+  EXPECT_EQ(StatusCode::INVALID_ARGUMENT, s.error_code());
+  EXPECT_EQ("Invalid credentials.", s.error_message());
 
   ClientContext context2;
   auto stream = stub->BidiStream(&context2);
   s = stream->Finish();
   EXPECT_FALSE(s.ok());
-  EXPECT_EQ(StatusCode::UNKNOWN, s.error_code());
-  EXPECT_EQ("Rpc sent on a lame channel.", s.error_message());
+  EXPECT_EQ(StatusCode::INVALID_ARGUMENT, s.error_code());
+  EXPECT_EQ("Invalid credentials.", s.error_message());
 }
 
 void CancelRpc(ClientContext* context, int delay_us, TestServiceImpl* service) {