You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by ta...@apache.org on 2020/08/04 23:16:23 UTC

[impala] 01/02: IMPALA-9909: Print body of http error code in Impala Shell.

This is an automated email from the ASF dual-hosted git repository.

tarmstrong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git

commit e187c4054340ed8f00c35711185a2fd6719f48b3
Author: Andrew Sherman <as...@cloudera.com>
AuthorDate: Wed May 20 11:57:22 2020 -0700

    IMPALA-9909: Print body of http error code in Impala Shell.
    
    Make Impala Shell closer to Impyla by printing the body of any http
    error code message received when using hs2-over-http. The common case is
    that there is nothing in the body, in which case the behavior is
    unchanged.
    
    TESTING
     Added a test for the new functionality.
     Ran all end-to-end tests.
    
    Change-Id: Iabc45eda0b87ca694b8359148cda6a7c1d5a8fff
    Reviewed-on: http://gerrit.cloudera.org:8080/16269
    Reviewed-by: Impala Public Jenkins <im...@cloudera.com>
    Tested-by: Impala Public Jenkins <im...@cloudera.com>
---
 shell/ImpalaHttpClient.py             |  10 +++-
 tests/shell/test_shell_interactive.py | 105 ++++++++++++++++++++++++++--------
 2 files changed, 90 insertions(+), 25 deletions(-)

diff --git a/shell/ImpalaHttpClient.py b/shell/ImpalaHttpClient.py
index cd79971..1820a1b 100644
--- a/shell/ImpalaHttpClient.py
+++ b/shell/ImpalaHttpClient.py
@@ -36,6 +36,7 @@ import six
 # The current changes that have been applied:
 # - Added logic for the 'Expect: 100-continue' header on large requests
 # - If an error code is received back in flush(), an exception is thrown.
+# Note there is a copy of this code in Impyla.
 class ImpalaHttpClient(TTransportBase):
   """Http implementation of TTransport base."""
 
@@ -151,6 +152,9 @@ class ImpalaHttpClient(TTransportBase):
   def read(self, sz):
     return self.__http_response.read(sz)
 
+  def readBody(self):
+    return self.__http_response.read()
+
   def write(self, buf):
     self.__wbuf.write(buf)
 
@@ -208,4 +212,8 @@ class ImpalaHttpClient(TTransportBase):
     if self.code >= 300:
       # Report any http response code that is not 1XX (informational response) or
       # 2XX (successful).
-      raise RPCException("HTTP code {}: {}".format(self.code, self.message))
+      body = self.readBody()
+      if not body:
+        raise RPCException("HTTP code {}: {}".format(self.code, self.message))
+      else:
+        raise RPCException("HTTP code {}: {} [{}]".format(self.code, self.message, body))
diff --git a/tests/shell/test_shell_interactive.py b/tests/shell/test_shell_interactive.py
index 245fb9b..f9668d6 100755
--- a/tests/shell/test_shell_interactive.py
+++ b/tests/shell/test_shell_interactive.py
@@ -76,43 +76,85 @@ def tmp_history_file(request):
   return tmp.name
 
 
-@pytest.yield_fixture
-def http_503_server():
-  class RequestHandler503(SimpleHTTPServer.SimpleHTTPRequestHandler):
-    """A custom http handler that checks for duplicate 'Host' headers from the most
-    recent http request, and always returns a 503 http code"""
+class RequestHandler503(SimpleHTTPServer.SimpleHTTPRequestHandler):
+  """A custom http handler that checks for duplicate 'Host' headers from the most
+  recent http request, and always returns a 503 http code."""
 
-    def do_POST(self):
-      # The unfortunately named self.headers here is an instance of mimetools.Message that
-      # contains the request headers.
-      request_headers = self.headers.headers
+  def __init__(self, request, client_address, server):
+    SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, request, client_address,
+                                                       server)
 
-      # Ensure that only one 'Host' header is contained in the request before responding.
-      host_hdr_count = sum([header.startswith('Host:') for header in request_headers])
-      assert host_hdr_count == 1, "duplicate 'Host:' headers in %s" % request_headers
+  def should_send_body_text(self):
+    # in RequestHandler503 we do not send any body text
+    return False
 
-      # Respond with 503.
-      self.send_response(code=httplib.SERVICE_UNAVAILABLE, message="Service Unavailable")
+  def do_POST(self):
+    # The unfortunately named self.headers here is an instance of mimetools.Message that
+    # contains the request headers.
+    request_headers = self.headers.headers
 
-  class TestHTTPServer503(object):
-    def __init__(self):
-      self.HOST = "localhost"
-      self.PORT = get_unused_port()
-      self.httpd = SocketServer.TCPServer((self.HOST, self.PORT), RequestHandler503)
+    # Ensure that only one 'Host' header is contained in the request before responding.
+    host_hdr_count = sum([header.startswith('Host:') for header in request_headers])
+    assert host_hdr_count == 1, "duplicate 'Host:' headers in %s" % request_headers
 
-      self.http_server_thread = threading.Thread(target=self.httpd.serve_forever)
-      self.http_server_thread.start()
+    # Respond with 503.
+    self.send_response(code=httplib.SERVICE_UNAVAILABLE, message="Service Unavailable")
+    if self.should_send_body_text():
+      # Optionally send ody text with 503 message.
+      self.end_headers()
+      self.wfile.write("EXTRA")
 
-  server = TestHTTPServer503()
-  yield server
 
-  # Cleanup after test.
+class RequestHandler503Extra(RequestHandler503):
+  """"Override RequestHandler503 so as to send body text with the 503 message."""
+
+  def __init__(self, request, client_address, server):
+    RequestHandler503.__init__(self, request, client_address, server)
+
+  def should_send_body_text(self):
+    # in RequestHandler503Extra we will send body text
+    return True
+
+
+class TestHTTPServer503(object):
+  def __init__(self, clazz):
+    self.HOST = "localhost"
+    self.PORT = get_unused_port()
+    self.httpd = SocketServer.TCPServer((self.HOST, self.PORT), clazz)
+
+    self.http_server_thread = threading.Thread(target=self.httpd.serve_forever)
+    self.http_server_thread.start()
+
+
+def shutdown_server(server):
+  """Helper method to shutdown a http server."""
   if server.httpd is not None:
     server.httpd.shutdown()
   if server.http_server_thread is not None:
     server.http_server_thread.join()
 
 
+@pytest.yield_fixture
+def http_503_server():
+  """A fixture that creates an http server that returns a 503 http code."""
+  server = TestHTTPServer503(RequestHandler503)
+  yield server
+
+  # Cleanup after test.
+  shutdown_server(server)
+
+
+@pytest.yield_fixture
+def http_503_server_extra():
+  """A fixture that creates an http server that returns a 503 http code with extra
+  body text."""
+  server = TestHTTPServer503(RequestHandler503Extra)
+  yield server
+
+  # Cleanup after test.
+  shutdown_server(server)
+
+
 class TestImpalaShellInteractive(ImpalaTestSuite):
   """Test the impala shell interactively"""
 
@@ -1026,6 +1068,21 @@ class TestImpalaShellInteractive(ImpalaTestSuite):
     shell_proc = spawn_shell([IMPALA_SHELL_EXECUTABLE] + shell_args)
     shell_proc.expect("HTTP code 503", timeout=10)
 
+  def test_http_interactions_extra(self, vector, http_503_server_extra):
+    """Test interactions with the http server when using hs2-http protocol.
+    Check that the shell prints a good message when the server returns a 503 error,
+    including the body text from the message."""
+    protocol = vector.get_value("protocol")
+    if protocol != 'hs2-http':
+      pytest.skip()
+
+    # Check that we get a message about the 503 error when we try to connect.
+    shell_args = ["--protocol={0}".format(protocol),
+                  "-i{0}:{1}".format(http_503_server_extra.HOST,
+                                     http_503_server_extra.PORT)]
+    shell_proc = spawn_shell([IMPALA_SHELL_EXECUTABLE] + shell_args)
+    shell_proc.expect("HTTP code 503: Service Unavailable \[EXTRA\]", timeout=10)
+
 
 def run_impala_shell_interactive(vector, input_lines, shell_args=None,
                                  wait_until_connected=True):