Enclose sin6_scope_id in uri
diff --git a/src/core/ext/client_channel/parse_address.c b/src/core/ext/client_channel/parse_address.c
index b1d55ad..eb1df84 100644
--- a/src/core/ext/client_channel/parse_address.c
+++ b/src/core/ext/client_channel/parse_address.c
@@ -44,6 +44,7 @@
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
+#include "src/core/lib/support/string.h"
 
 #ifdef GRPC_HAVE_UNIX_SOCKET
 
@@ -119,9 +120,28 @@
   memset(in6, 0, sizeof(*in6));
   resolved_addr->len = sizeof(*in6);
   in6->sin6_family = AF_INET6;
-  if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) {
-    gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host);
-    goto done;
+
+  char *host_end = (char *)gpr_memrchr(host, '%', strlen(host));
+  if (host_end != NULL) {
+    size_t host_without_scope_len = (size_t)(host_end - host);
+    char host_without_scope[host_without_scope_len + 1];
+    strncpy(host_without_scope, host, host_without_scope_len);
+    host_without_scope[host_without_scope_len] = '\0';
+    if (inet_pton(AF_INET6, host_without_scope, &in6->sin6_addr) == 0) {
+      gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope);
+      goto done;
+    }
+    if (gpr_parse_bytes_to_uint32(host_end + 1,
+                                  strlen(host) - host_without_scope_len - 1,
+                                  &in6->sin6_scope_id) == 0) {
+      gpr_log(GPR_ERROR, "invalid ipv6 scope id: '%s'", host_end + 1);
+      goto done;
+    }
+  } else {
+    if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) {
+      gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host);
+      goto done;
+    }
   }
 
   if (port != NULL) {
diff --git a/src/core/ext/client_channel/uri_parser.c b/src/core/ext/client_channel/uri_parser.c
index f8c946b..e521afd 100644
--- a/src/core/ext/client_channel/uri_parser.c
+++ b/src/core/ext/client_channel/uri_parser.c
@@ -42,6 +42,7 @@
 #include <grpc/support/port_platform.h>
 #include <grpc/support/string_util.h>
 
+#include "src/core/lib/slice/percent_encoding.h"
 #include "src/core/lib/support/string.h"
 
 /** a size_t default value... maps to all 1's */
@@ -68,11 +69,16 @@
   return NULL;
 }
 
-/** Returns a copy of \a src[begin, end) */
-static char *copy_component(const char *src, size_t begin, size_t end) {
-  char *out = gpr_malloc(end - begin + 1);
-  memcpy(out, src + begin, end - begin);
-  out[end - begin] = 0;
+/** Returns a copy of percent decoded \a src[begin, end) */
+static char *decode_and_copy_component(const char *src, size_t begin,
+                                       size_t end) {
+  grpc_slice component =
+      grpc_slice_from_copied_buffer(src + begin, end - begin);
+  grpc_slice decoded_component =
+      grpc_permissive_percent_decode_slice(component);
+  char *out = grpc_slice_to_c_string(decoded_component);
+  grpc_slice_unref(component);
+  grpc_slice_unref(decoded_component);
   return out;
 }
 
@@ -264,11 +270,13 @@
 
   uri = gpr_malloc(sizeof(*uri));
   memset(uri, 0, sizeof(*uri));
-  uri->scheme = copy_component(uri_text, scheme_begin, scheme_end);
-  uri->authority = copy_component(uri_text, authority_begin, authority_end);
-  uri->path = copy_component(uri_text, path_begin, path_end);
-  uri->query = copy_component(uri_text, query_begin, query_end);
-  uri->fragment = copy_component(uri_text, fragment_begin, fragment_end);
+  uri->scheme = decode_and_copy_component(uri_text, scheme_begin, scheme_end);
+  uri->authority =
+      decode_and_copy_component(uri_text, authority_begin, authority_end);
+  uri->path = decode_and_copy_component(uri_text, path_begin, path_end);
+  uri->query = decode_and_copy_component(uri_text, query_begin, query_end);
+  uri->fragment =
+      decode_and_copy_component(uri_text, fragment_begin, fragment_end);
   parse_query_parts(uri);
 
   return uri;
diff --git a/src/core/lib/iomgr/sockaddr_utils.c b/src/core/lib/iomgr/sockaddr_utils.c
index 44bc2f9..f23f988 100644
--- a/src/core/lib/iomgr/sockaddr_utils.c
+++ b/src/core/lib/iomgr/sockaddr_utils.c
@@ -162,6 +162,7 @@
   char ntop_buf[INET6_ADDRSTRLEN];
   const void *ip = NULL;
   int port;
+  uint32_t sin6_scope_id = 0;
   int ret;
 
   *out = NULL;
@@ -177,10 +178,19 @@
     const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
     ip = &addr6->sin6_addr;
     port = ntohs(addr6->sin6_port);
+    sin6_scope_id = addr6->sin6_scope_id;
   }
   if (ip != NULL &&
       grpc_inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) {
-    ret = gpr_join_host_port(out, ntop_buf, port);
+    if (sin6_scope_id != 0) {
+      char *host_with_scope;
+      /* Enclose sin6_scope_id with the format defined in RFC 6784 section 2. */
+      gpr_asprintf(&host_with_scope, "%s%%25%" PRIu32, ntop_buf, sin6_scope_id);
+      ret = gpr_join_host_port(out, host_with_scope, port);
+      gpr_free(host_with_scope);
+    } else {
+      ret = gpr_join_host_port(out, ntop_buf, port);
+    }
   } else {
     ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family);
   }
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
index 9a77c92..b40c31a 100644
--- a/src/core/lib/iomgr/tcp_client_posix.c
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -37,6 +37,9 @@
 
 #include "src/core/lib/iomgr/tcp_client_posix.h"
 
+#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
 #include <errno.h>
 #include <netinet/in.h>
 #include <string.h>
diff --git a/test/core/client_channel/uri_parser_test.c b/test/core/client_channel/uri_parser_test.c
index 5f32d32..489e4ec 100644
--- a/test/core/client_channel/uri_parser_test.c
+++ b/test/core/client_channel/uri_parser_test.c
@@ -142,6 +142,8 @@
   test_succeeds("http:?legit#twice", "http", "", "", "legit", "twice");
   test_succeeds("http://foo?bar#lol?", "http", "foo", "", "bar", "lol?");
   test_succeeds("http://foo?bar#lol?/", "http", "foo", "", "bar", "lol?/");
+  test_succeeds("ipv6:[2001:db8::1%252]:12345", "ipv6", "",
+                "[2001:db8::1%2]:12345", "", "");
 
   test_fails("xyz");
   test_fails("http:?dangling-pct-%0");
diff --git a/test/core/iomgr/sockaddr_utils_test.c b/test/core/iomgr/sockaddr_utils_test.c
index 8569c69..70a6c32 100644
--- a/test/core/iomgr/sockaddr_utils_test.c
+++ b/test/core/iomgr/sockaddr_utils_test.c
@@ -70,6 +70,12 @@
   return resolved_addr6;
 }
 
+static void set_addr6_scope_id(grpc_resolved_address *addr, uint32_t scope_id) {
+  struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr->addr;
+  GPR_ASSERT(addr6->sin6_family == AF_INET6);
+  addr6->sin6_scope_id = scope_id;
+}
+
 static const uint8_t kMapped[] = {0, 0, 0,    0,    0,   0, 0, 0,
                                   0, 0, 0xff, 0xff, 192, 0, 2, 1};
 
@@ -222,6 +228,16 @@
   expect_sockaddr_str("[2001:db8::1]:12345", &input6, 1);
   expect_sockaddr_uri("ipv6:[2001:db8::1]:12345", &input6);
 
+  set_addr6_scope_id(&input6, 2);
+  expect_sockaddr_str("[2001:db8::1%252]:12345", &input6, 0);
+  expect_sockaddr_str("[2001:db8::1%252]:12345", &input6, 1);
+  expect_sockaddr_uri("ipv6:[2001:db8::1%252]:12345", &input6);
+
+  set_addr6_scope_id(&input6, 101);
+  expect_sockaddr_str("[2001:db8::1%25101]:12345", &input6, 0);
+  expect_sockaddr_str("[2001:db8::1%25101]:12345", &input6, 1);
+  expect_sockaddr_uri("ipv6:[2001:db8::1%25101]:12345", &input6);
+
   input6 = make_addr6(kMapped, sizeof(kMapped));
   expect_sockaddr_str("[::ffff:192.0.2.1]:12345", &input6, 0);
   expect_sockaddr_str("192.0.2.1:12345", &input6, 1);
diff --git a/test/core/slice/percent_encoding_test.c b/test/core/slice/percent_encoding_test.c
index d71c99f..8859be8 100644
--- a/test/core/slice/percent_encoding_test.c
+++ b/test/core/slice/percent_encoding_test.c
@@ -146,6 +146,7 @@
   TEST_VECTOR("\x0f", "%0F", grpc_url_percent_encoding_unreserved_bytes);
   TEST_VECTOR("\xff", "%FF", grpc_url_percent_encoding_unreserved_bytes);
   TEST_VECTOR("\xee", "%EE", grpc_url_percent_encoding_unreserved_bytes);
+  TEST_VECTOR("%2", "%252", grpc_url_percent_encoding_unreserved_bytes);
   TEST_NONCONFORMANT_VECTOR("%", "%",
                             grpc_url_percent_encoding_unreserved_bytes);
   TEST_NONCONFORMANT_VECTOR("%A", "%A",