You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ap...@apache.org on 2016/08/19 19:31:09 UTC

[1/2] hbase git commit: HBASE-7621 REST client (RemoteHTable) doesn't support binary row keys

Repository: hbase
Updated Branches:
  refs/heads/branch-1 a857a60cc -> b3ca9a9b2
  refs/heads/master 34b668b0a -> 7145e46b7


HBASE-7621 REST client (RemoteHTable) doesn't support binary row keys

Signed-off-by: Andrew Purtell <ap...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/7145e46b
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/7145e46b
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/7145e46b

Branch: refs/heads/master
Commit: 7145e46b7afd6d76df1ef001557c7b60bfc6055b
Parents: 34b668b
Author: Keith David Winkler <kd...@monsanto.com>
Authored: Tue Aug 16 21:23:41 2016 -0500
Committer: Andrew Purtell <ap...@apache.org>
Committed: Fri Aug 19 12:08:07 2016 -0700

----------------------------------------------------------------------
 .../hadoop/hbase/rest/client/RemoteHTable.java  | 60 +++++++++++---------
 .../hbase/rest/client/TestRemoteTable.java      | 41 +++++++++----
 2 files changed, 64 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/7145e46b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
index 33c2fc2..5debf39 100644
--- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
+++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
@@ -21,6 +21,8 @@ package org.apache.hadoop.hbase.rest.client;
 
 import java.io.IOException;
 import java.io.InterruptedIOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -92,9 +94,9 @@ public class RemoteHTable implements Table {
       final long startTime, final long endTime, final int maxVersions) {
     StringBuffer sb = new StringBuffer();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
-    sb.append(Bytes.toStringBinary(row));
+    sb.append(toURLEncodedBytes(row));
     Set families = familyMap.entrySet();
     if (families != null) {
       Iterator i = familyMap.entrySet().iterator();
@@ -104,19 +106,18 @@ public class RemoteHTable implements Table {
         Collection quals = (Collection)e.getValue();
         if (quals == null || quals.isEmpty()) {
           // this is an unqualified family. append the family name and NO ':'
-          sb.append(Bytes.toStringBinary((byte[])e.getKey()));
+          sb.append(toURLEncodedBytes((byte[])e.getKey()));
         } else {
           Iterator ii = quals.iterator();
           while (ii.hasNext()) {
-            sb.append(Bytes.toStringBinary((byte[])e.getKey()));
+            sb.append(toURLEncodedBytes((byte[])e.getKey()));
             sb.append(':');
             Object o = ii.next();
             // Puts use byte[] but Deletes use KeyValue
             if (o instanceof byte[]) {
-              sb.append(Bytes.toStringBinary((byte[])o));
+              sb.append(toURLEncodedBytes((byte[])o));
             } else if (o instanceof KeyValue) {
-              sb.append(Bytes.toStringBinary(((KeyValue) o).getQualifierArray(),
-                ((KeyValue) o).getQualifierOffset(), ((KeyValue) o).getQualifierLength()));
+              sb.append(toURLEncodedBytes(CellUtil.cloneQualifier((KeyValue)o)));
             } else {
               throw new RuntimeException("object type not handled");
             }
@@ -151,7 +152,7 @@ public class RemoteHTable implements Table {
   protected String buildMultiRowSpec(final byte[][] rows, int maxVersions) {
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append("/multiget/");
     if (rows == null || rows.length == 0) {
       return sb.toString();
@@ -163,7 +164,7 @@ public class RemoteHTable implements Table {
         sb.append('&');
       }
       sb.append("row=");
-      sb.append(Bytes.toStringBinary(rk));
+      sb.append(toURLEncodedBytes(rk));
     }
     sb.append("&v=");
     sb.append(maxVersions);
@@ -211,8 +212,6 @@ public class RemoteHTable implements Table {
 
   /**
    * Constructor
-   * @param client
-   * @param name
    */
   public RemoteHTable(Client client, String name) {
     this(client, HBaseConfiguration.create(), Bytes.toBytes(name));
@@ -220,9 +219,6 @@ public class RemoteHTable implements Table {
 
   /**
    * Constructor
-   * @param client
-   * @param conf
-   * @param name
    */
   public RemoteHTable(Client client, Configuration conf, String name) {
     this(client, conf, Bytes.toBytes(name));
@@ -230,9 +226,6 @@ public class RemoteHTable implements Table {
 
   /**
    * Constructor
-   * @param client
-   * @param conf
-   * @param name
    */
   public RemoteHTable(Client client, Configuration conf, byte[] name) {
     this.client = client;
@@ -260,7 +253,7 @@ public class RemoteHTable implements Table {
   public HTableDescriptor getTableDescriptor() throws IOException {
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
     sb.append("schema");
     for (int i = 0; i < maxRetries; i++) {
@@ -402,9 +395,9 @@ public class RemoteHTable implements Table {
     CellSetModel model = buildModelFromPut(put);
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
-    sb.append(Bytes.toStringBinary(put.getRow()));
+    sb.append(toURLEncodedBytes(put.getRow()));
     for (int i = 0; i < maxRetries; i++) {
       Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF,
         model.createProtobufOutput());
@@ -459,7 +452,7 @@ public class RemoteHTable implements Table {
     // build path for multiput
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append("/$multiput"); // can be any nonexistent row
     for (int i = 0; i < maxRetries; i++) {
       Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF,
@@ -530,7 +523,7 @@ public class RemoteHTable implements Table {
       }
       StringBuffer sb = new StringBuffer();
       sb.append('/');
-      sb.append(Bytes.toStringBinary(name));
+      sb.append(Bytes.toString(name));
       sb.append('/');
       sb.append("scanner");
       for (int i = 0; i < maxRetries; i++) {
@@ -684,9 +677,9 @@ public class RemoteHTable implements Table {
     CellSetModel model = buildModelFromPut(put);
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
-    sb.append(Bytes.toStringBinary(put.getRow()));
+    sb.append(toURLEncodedBytes(put.getRow()));
     sb.append("?check=put");
 
     for (int i = 0; i < maxRetries; i++) {
@@ -728,9 +721,9 @@ public class RemoteHTable implements Table {
     CellSetModel model = buildModelFromPut(put);
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
-    sb.append(Bytes.toStringBinary(row));
+    sb.append(toURLEncodedBytes(row));
     sb.append("?check=delete");
 
     for (int i = 0; i < maxRetries; i++) {
@@ -890,4 +883,19 @@ public class RemoteHTable implements Table {
   public void setWriteRpcTimeout(int writeRpcTimeout) {
     throw new UnsupportedOperationException();
   }
+
+  /*
+   * Only a small subset of characters are valid in URLs.
+   *
+   * Row keys, column families, and qualifiers cannot be appended to URLs without first URL
+   * escaping. Table names are ok because they can only contain alphanumeric, ".","_", and "-"
+   * which are valid characters in URLs.
+   */
+  private static String toURLEncodedBytes(byte[] row) {
+    try {
+      return URLEncoder.encode(new String(row, "UTF-8"), "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      throw new IllegalStateException("URLEncoder doesn't support UTF-8", e);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/7145e46b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
index 1ac37fa..6d367c7 100644
--- a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
+++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
@@ -59,16 +59,35 @@ import org.junit.experimental.categories.Category;
 
 @Category({RestTests.class, MediumTests.class})
 public class TestRemoteTable {
-  private static final TableName TABLE = TableName.valueOf("TestRemoteTable");
-  private static final byte[] ROW_1 = Bytes.toBytes("testrow1");
-  private static final byte[] ROW_2 = Bytes.toBytes("testrow2");
-  private static final byte[] ROW_3 = Bytes.toBytes("testrow3");
-  private static final byte[] ROW_4 = Bytes.toBytes("testrow4");
-  private static final byte[] COLUMN_1 = Bytes.toBytes("a");
-  private static final byte[] COLUMN_2 = Bytes.toBytes("b");
-  private static final byte[] COLUMN_3 = Bytes.toBytes("c");
-  private static final byte[] QUALIFIER_1 = Bytes.toBytes("1");
-  private static final byte[] QUALIFIER_2 = Bytes.toBytes("2");
+
+  // Verify that invalid URL characters and arbitrary bytes are escaped when
+  // constructing REST URLs per HBASE-7621. RemoteHTable should support row keys
+  // and qualifiers containing any byte for all table operations.
+  private static final String INVALID_URL_CHARS_1 =
+      "|\"\\^{}\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000B\u000C";
+
+  // HColumnDescriptor prevents certain characters in column names.  The following
+  // are examples of characters are allowed in column names but are not valid in
+  // URLs.
+  private static final String INVALID_URL_CHARS_2 = "|^{}\u0242";
+
+  // Besides alphanumeric these characters can also be present in table names.
+  private static final String VALID_TABLE_NAME_CHARS = "_-.";
+
+  private static final TableName TABLE =
+      TableName.valueOf("TestRemoteTable" + VALID_TABLE_NAME_CHARS);
+
+  private static final byte[] ROW_1 = Bytes.toBytes("testrow1" + INVALID_URL_CHARS_1);
+  private static final byte[] ROW_2 = Bytes.toBytes("testrow2" + INVALID_URL_CHARS_1);
+  private static final byte[] ROW_3 = Bytes.toBytes("testrow3" + INVALID_URL_CHARS_1);
+  private static final byte[] ROW_4 = Bytes.toBytes("testrow4"+ INVALID_URL_CHARS_1);
+
+  private static final byte[] COLUMN_1 = Bytes.toBytes("a" + INVALID_URL_CHARS_2);
+  private static final byte[] COLUMN_2 = Bytes.toBytes("b" + INVALID_URL_CHARS_2);
+  private static final byte[] COLUMN_3 = Bytes.toBytes("c" + INVALID_URL_CHARS_2);
+
+  private static final byte[] QUALIFIER_1 = Bytes.toBytes("1" + INVALID_URL_CHARS_1);
+  private static final byte[] QUALIFIER_2 = Bytes.toBytes("2" + INVALID_URL_CHARS_1);
   private static final byte[] VALUE_1 = Bytes.toBytes("testvalue1");
   private static final byte[] VALUE_2 = Bytes.toBytes("testvalue2");
 
@@ -322,7 +341,7 @@ public class TestRemoteTable {
     assertNotNull(value);
     assertTrue(Bytes.equals(VALUE_2, value));
     
-    assertTrue(Bytes.equals(Bytes.toBytes("TestRemoteTable"), remoteTable.getTableName()));
+    assertTrue(Bytes.equals(Bytes.toBytes("TestRemoteTable" + VALID_TABLE_NAME_CHARS), remoteTable.getTableName()));
   }
 
   @Test


[2/2] hbase git commit: HBASE-7621 REST client (RemoteHTable) doesn't support binary row keys

Posted by ap...@apache.org.
HBASE-7621 REST client (RemoteHTable) doesn't support binary row keys

Signed-off-by: Andrew Purtell <ap...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/b3ca9a9b
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/b3ca9a9b
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/b3ca9a9b

Branch: refs/heads/branch-1
Commit: b3ca9a9b2f91ce5d43c30113cbfbbbaf8cade9c0
Parents: a857a60
Author: Keith David Winkler <kd...@monsanto.com>
Authored: Tue Aug 16 21:23:41 2016 -0500
Committer: Andrew Purtell <ap...@apache.org>
Committed: Fri Aug 19 12:24:14 2016 -0700

----------------------------------------------------------------------
 .../hadoop/hbase/rest/client/RemoteHTable.java  | 59 +++++++++++---------
 .../hbase/rest/client/TestRemoteTable.java      | 41 ++++++++++----
 2 files changed, 64 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/b3ca9a9b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
index 63b18b1..146cc72 100644
--- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
+++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
@@ -21,6 +21,8 @@ package org.apache.hadoop.hbase.rest.client;
 
 import java.io.IOException;
 import java.io.InterruptedIOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -92,9 +94,9 @@ public class RemoteHTable implements Table {
       final long startTime, final long endTime, final int maxVersions) {
     StringBuffer sb = new StringBuffer();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
-    sb.append(Bytes.toStringBinary(row));
+    sb.append(toURLEncodedBytes(row));
     Set families = familyMap.entrySet();
     if (families != null) {
       Iterator i = familyMap.entrySet().iterator();
@@ -104,18 +106,18 @@ public class RemoteHTable implements Table {
         Collection quals = (Collection)e.getValue();
         if (quals == null || quals.isEmpty()) {
           // this is an unqualified family. append the family name and NO ':'
-          sb.append(Bytes.toStringBinary((byte[])e.getKey()));
+          sb.append(toURLEncodedBytes((byte[])e.getKey()));
         } else {
           Iterator ii = quals.iterator();
           while (ii.hasNext()) {
-            sb.append(Bytes.toStringBinary((byte[])e.getKey()));
+            sb.append(toURLEncodedBytes((byte[])e.getKey()));
             sb.append(':');
             Object o = ii.next();
             // Puts use byte[] but Deletes use KeyValue
             if (o instanceof byte[]) {
-              sb.append(Bytes.toStringBinary((byte[])o));
+              sb.append(toURLEncodedBytes((byte[])o));
             } else if (o instanceof KeyValue) {
-              sb.append(Bytes.toStringBinary(((KeyValue)o).getQualifier()));
+              sb.append(toURLEncodedBytes(((KeyValue)o).getQualifier()));
             } else {
               throw new RuntimeException("object type not handled");
             }
@@ -150,7 +152,7 @@ public class RemoteHTable implements Table {
   protected String buildMultiRowSpec(final byte[][] rows, int maxVersions) {
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append("/multiget/");
     if (rows == null || rows.length == 0) {
       return sb.toString();
@@ -162,7 +164,7 @@ public class RemoteHTable implements Table {
         sb.append('&');
       }
       sb.append("row=");
-      sb.append(Bytes.toStringBinary(rk));
+      sb.append(toURLEncodedBytes(rk));
     }
     sb.append("&v=");
     sb.append(maxVersions);
@@ -210,8 +212,6 @@ public class RemoteHTable implements Table {
 
   /**
    * Constructor
-   * @param client
-   * @param name
    */
   public RemoteHTable(Client client, String name) {
     this(client, HBaseConfiguration.create(), Bytes.toBytes(name));
@@ -219,9 +219,6 @@ public class RemoteHTable implements Table {
 
   /**
    * Constructor
-   * @param client
-   * @param conf
-   * @param name
    */
   public RemoteHTable(Client client, Configuration conf, String name) {
     this(client, conf, Bytes.toBytes(name));
@@ -229,9 +226,6 @@ public class RemoteHTable implements Table {
 
   /**
    * Constructor
-   * @param client
-   * @param conf
-   * @param name
    */
   public RemoteHTable(Client client, Configuration conf, byte[] name) {
     this.client = client;
@@ -259,7 +253,7 @@ public class RemoteHTable implements Table {
   public HTableDescriptor getTableDescriptor() throws IOException {
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
     sb.append("schema");
     for (int i = 0; i < maxRetries; i++) {
@@ -401,9 +395,9 @@ public class RemoteHTable implements Table {
     CellSetModel model = buildModelFromPut(put);
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
-    sb.append(Bytes.toStringBinary(put.getRow()));
+    sb.append(toURLEncodedBytes(put.getRow()));
     for (int i = 0; i < maxRetries; i++) {
       Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF,
         model.createProtobufOutput());
@@ -458,7 +452,7 @@ public class RemoteHTable implements Table {
     // build path for multiput
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append("/$multiput"); // can be any nonexistent row
     for (int i = 0; i < maxRetries; i++) {
       Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF,
@@ -529,7 +523,7 @@ public class RemoteHTable implements Table {
       }
       StringBuffer sb = new StringBuffer();
       sb.append('/');
-      sb.append(Bytes.toStringBinary(name));
+      sb.append(Bytes.toString(name));
       sb.append('/');
       sb.append("scanner");
       for (int i = 0; i < maxRetries; i++) {
@@ -682,9 +676,9 @@ public class RemoteHTable implements Table {
     CellSetModel model = buildModelFromPut(put);
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
-    sb.append(Bytes.toStringBinary(put.getRow()));
+    sb.append(toURLEncodedBytes(put.getRow()));
     sb.append("?check=put");
 
     for (int i = 0; i < maxRetries; i++) {
@@ -726,9 +720,9 @@ public class RemoteHTable implements Table {
     CellSetModel model = buildModelFromPut(put);
     StringBuilder sb = new StringBuilder();
     sb.append('/');
-    sb.append(Bytes.toStringBinary(name));
+    sb.append(Bytes.toString(name));
     sb.append('/');
-    sb.append(Bytes.toStringBinary(row));
+    sb.append(toURLEncodedBytes(row));
     sb.append("?check=delete");
 
     for (int i = 0; i < maxRetries; i++) {
@@ -897,4 +891,19 @@ public class RemoteHTable implements Table {
   public void setWriteRpcTimeout(int writeRpcTimeout) {
     throw new UnsupportedOperationException();
   }
+
+  /*
+   * Only a small subset of characters are valid in URLs.
+   *
+   * Row keys, column families, and qualifiers cannot be appended to URLs without first URL
+   * escaping. Table names are ok because they can only contain alphanumeric, ".","_", and "-"
+   * which are valid characters in URLs.
+   */
+  private static String toURLEncodedBytes(byte[] row) {
+    try {
+      return URLEncoder.encode(new String(row, "UTF-8"), "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      throw new IllegalStateException("URLEncoder doesn't support UTF-8", e);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/b3ca9a9b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
index 121ff65..342fc4e 100644
--- a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
+++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
@@ -59,16 +59,35 @@ import org.junit.experimental.categories.Category;
 
 @Category(MediumTests.class)
 public class TestRemoteTable {
-  private static final TableName TABLE = TableName.valueOf("TestRemoteTable");
-  private static final byte[] ROW_1 = Bytes.toBytes("testrow1");
-  private static final byte[] ROW_2 = Bytes.toBytes("testrow2");
-  private static final byte[] ROW_3 = Bytes.toBytes("testrow3");
-  private static final byte[] ROW_4 = Bytes.toBytes("testrow4");
-  private static final byte[] COLUMN_1 = Bytes.toBytes("a");
-  private static final byte[] COLUMN_2 = Bytes.toBytes("b");
-  private static final byte[] COLUMN_3 = Bytes.toBytes("c");
-  private static final byte[] QUALIFIER_1 = Bytes.toBytes("1");
-  private static final byte[] QUALIFIER_2 = Bytes.toBytes("2");
+
+  // Verify that invalid URL characters and arbitrary bytes are escaped when
+  // constructing REST URLs per HBASE-7621. RemoteHTable should support row keys
+  // and qualifiers containing any byte for all table operations.
+  private static final String INVALID_URL_CHARS_1 =
+      "|\"\\^{}\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000B\u000C";
+
+  // HColumnDescriptor prevents certain characters in column names.  The following
+  // are examples of characters are allowed in column names but are not valid in
+  // URLs.
+  private static final String INVALID_URL_CHARS_2 = "|^{}\u0242";
+
+  // Besides alphanumeric these characters can also be present in table names.
+  private static final String VALID_TABLE_NAME_CHARS = "_-.";
+
+  private static final TableName TABLE =
+      TableName.valueOf("TestRemoteTable" + VALID_TABLE_NAME_CHARS);
+
+  private static final byte[] ROW_1 = Bytes.toBytes("testrow1" + INVALID_URL_CHARS_1);
+  private static final byte[] ROW_2 = Bytes.toBytes("testrow2" + INVALID_URL_CHARS_1);
+  private static final byte[] ROW_3 = Bytes.toBytes("testrow3" + INVALID_URL_CHARS_1);
+  private static final byte[] ROW_4 = Bytes.toBytes("testrow4"+ INVALID_URL_CHARS_1);
+
+  private static final byte[] COLUMN_1 = Bytes.toBytes("a" + INVALID_URL_CHARS_2);
+  private static final byte[] COLUMN_2 = Bytes.toBytes("b" + INVALID_URL_CHARS_2);
+  private static final byte[] COLUMN_3 = Bytes.toBytes("c" + INVALID_URL_CHARS_2);
+
+  private static final byte[] QUALIFIER_1 = Bytes.toBytes("1" + INVALID_URL_CHARS_1);
+  private static final byte[] QUALIFIER_2 = Bytes.toBytes("2" + INVALID_URL_CHARS_1);
   private static final byte[] VALUE_1 = Bytes.toBytes("testvalue1");
   private static final byte[] VALUE_2 = Bytes.toBytes("testvalue2");
 
@@ -322,7 +341,7 @@ public class TestRemoteTable {
     assertNotNull(value);
     assertTrue(Bytes.equals(VALUE_2, value));
     
-    assertTrue(Bytes.equals(Bytes.toBytes("TestRemoteTable"), remoteTable.getTableName()));
+    assertTrue(Bytes.equals(Bytes.toBytes("TestRemoteTable" + VALID_TABLE_NAME_CHARS), remoteTable.getTableName()));
   }
 
   @Test