You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/07/07 19:58:36 UTC

[5/5] incubator-calcite git commit: [CALCITE-780] HTTP error 413 when sending a long string to the Avatica server

[CALCITE-780] HTTP error 413 when sending a long string to the Avatica server


Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/03111d2b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/03111d2b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/03111d2b

Branch: refs/heads/master
Commit: 03111d2b680c4f4eb4886d60cab9beebd1c7bdcb
Parents: 40c55fd
Author: Julian Hyde <jh...@apache.org>
Authored: Tue Jul 7 00:20:48 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Jul 7 00:20:48 2015 -0700

----------------------------------------------------------------------
 .../calcite/avatica/server/AvaticaHandler.java  | 13 ++++-
 .../calcite/avatica/remote/RemoteMetaTest.java  | 51 +++++++++++++++++++-
 .../apache/calcite/avatica/AvaticaUtils.java    | 17 +++++++
 .../calcite/avatica/remote/RemoteService.java   | 28 ++++++-----
 4 files changed, 94 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/03111d2b/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaHandler.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaHandler.java b/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaHandler.java
index fa0e1ac..2ada444 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaHandler.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaHandler.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.avatica.server;
 
+import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.remote.JsonHandler;
 import org.apache.calcite.avatica.remote.Service;
 
@@ -27,6 +28,7 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
 
 import java.io.IOException;
 import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -48,9 +50,16 @@ public class AvaticaHandler extends AbstractHandler {
     response.setContentType("application/json;charset=utf-8");
     response.setStatus(HttpServletResponse.SC_OK);
     if (request.getMethod().equals("POST")) {
+      // First look for a request in the header, then look in the body.
+      // The latter allows very large requests without hitting HTTP 413.
+      String rawRequest = request.getHeader("request");
+      if (rawRequest == null) {
+        try (ServletInputStream inputStream = request.getInputStream()) {
+          rawRequest = AvaticaUtils.readFully(inputStream);
+        }
+      }
       final String jsonRequest =
-          new String(request.getHeader("request").getBytes("ISO-8859-1"),
-              "UTF-8");
+          new String(rawRequest.getBytes("ISO-8859-1"), "UTF-8");
       if (LOG.isTraceEnabled()) {
         LOG.trace("request: " + jsonRequest);
       }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/03111d2b/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java b/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
index 2439ac3..27dcfa4 100644
--- a/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
+++ b/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
@@ -41,8 +41,10 @@ import java.sql.Statement;
 import java.util.List;
 import java.util.Map;
 
+import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 /** Tests covering {@link RemoteMeta}. */
@@ -98,7 +100,7 @@ public class RemoteMetaTest {
     Method m = AvaticaConnection.class.getDeclaredMethod("prepareAndExecuteInternal",
       AvaticaStatement.class, String.class, int.class);
     m.setAccessible(true);
-    return (Meta.ExecuteResult) m.invoke(conn, statement, sql, new Integer(maxRowCount));
+    return (Meta.ExecuteResult) m.invoke(conn, statement, sql, maxRowCount);
   }
 
   private static Connection getConnection(JdbcMeta m, String id) throws Exception {
@@ -128,6 +130,53 @@ public class RemoteMetaTest {
     }
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-780">[CALCITE-780]
+   * HTTP error 413 when sending a long string to the Avatica server</a>. */
+  @Test public void testRemoteExecuteVeryLargeQuery() throws Exception {
+    // Before the bug was fixed, a value over 7998 caused an HTTP 413.
+    // 16K bytes, I guess.
+    checkLargeQuery(8);
+    checkLargeQuery(240);
+    checkLargeQuery(8000);
+    checkLargeQuery(240000);
+  }
+
+  private void checkLargeQuery(int n) throws Exception {
+    try (AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url)) {
+      final AvaticaStatement statement = conn.createStatement();
+      final String frenchDisko = "It said human existence is pointless\n"
+          + "As acts of rebellious solidarity\n"
+          + "Can bring sense in this world\n"
+          + "La resistance!\n";
+      final String sql = "select '"
+          + longString(frenchDisko, n)
+          + "' as s from (values 'x')";
+      prepareAndExecuteInternal(conn, statement, sql, -1);
+      ResultSet rs = statement.getResultSet();
+      int count = 0;
+      while (rs.next()) {
+        count++;
+      }
+      assertThat(count, is(1));
+      rs.close();
+      statement.close();
+      conn.close();
+    }
+  }
+
+  /** Creates a string of exactly {@code length} characters by concatenating
+   * {@code fragment}. */
+  private static String longString(String fragment, int length) {
+    assert fragment.length() > 0;
+    final StringBuilder buf = new StringBuilder();
+    while (buf.length() < length) {
+      buf.append(fragment);
+    }
+    buf.setLength(length);
+    return buf.toString();
+  }
+
   @Test public void testRemoteConnectionProperties() throws Exception {
     try (AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url)) {
       String id = conn.id;

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/03111d2b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
index 5ca5245..0ee030d 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
@@ -16,6 +16,9 @@
  */
 package org.apache.calcite.avatica;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.reflect.Field;
 import java.util.AbstractList;
 import java.util.HashMap;
@@ -165,6 +168,20 @@ public class AvaticaUtils {
           + "' not valid for plugin type " + pluginClass.getName(), e);
     }
   }
+
+  /** Reads the contents of an input stream and returns as a string. */
+  public static String readFully(InputStream inputStream) throws IOException {
+    final byte[] bytes = new byte[4096];
+    final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    for (;;) {
+      int count = inputStream.read(bytes, 0, bytes.length);
+      if (count < 0) {
+        break;
+      }
+      baos.write(bytes, 0, count);
+    }
+    return baos.toString();
+  }
 }
 
 // End AvaticaUtils.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/03111d2b/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteService.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteService.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteService.java
index 7cbdcf0..a5896f2 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteService.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteService.java
@@ -16,7 +16,9 @@
  */
 package org.apache.calcite.avatica.remote;
 
-import java.io.ByteArrayOutputStream;
+import org.apache.calcite.avatica.AvaticaUtils;
+
+import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
@@ -39,22 +41,24 @@ public class RemoteService extends JsonService {
       final HttpURLConnection connection =
           (HttpURLConnection) url.openConnection();
       connection.setRequestMethod("POST");
-      connection.setRequestProperty("request", request);
+      connection.setDoInput(true);
+      connection.setDoOutput(true);
+      if (request.length() < 256) {
+        connection.setRequestProperty("request", request);
+      } else {
+        try (DataOutputStream wr
+            = new DataOutputStream(connection.getOutputStream())) {
+          wr.writeBytes(request);
+          wr.flush();
+          wr.close();
+        }
+      }
       final int responseCode = connection.getResponseCode();
       if (responseCode != HttpURLConnection.HTTP_OK) {
         throw new RuntimeException("response code " + responseCode);
       }
       final InputStream inputStream = connection.getInputStream();
-      final byte[] bytes = new byte[4096];
-      final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-      for (;;) {
-        int count = inputStream.read(bytes, 0, bytes.length);
-        if (count < 0) {
-          break;
-        }
-        baos.write(bytes, 0, count);
-      }
-      return baos.toString();
+      return AvaticaUtils.readFully(inputStream);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }