View Javadoc

1   /*
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements. See the NOTICE file distributed with this
6    * work for additional information regarding copyright ownership. The ASF
7    * licenses this file to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   * http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16   * License for the specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.hadoop.hbase.thrift;
20  
21  import static org.junit.Assert.assertFalse;
22  import static org.junit.Assert.assertNull;
23  import static org.junit.Assert.fail;
24  
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.hbase.HBaseTestingUtility;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.testclassification.LargeTests;
34  import org.apache.hadoop.hbase.thrift.generated.Hbase;
35  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
36  import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper;
37  import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge;
38  import org.apache.thrift.protocol.TBinaryProtocol;
39  import org.apache.thrift.protocol.TProtocol;
40  import org.apache.thrift.transport.THttpClient;
41  import org.apache.thrift.transport.TTransportException;
42  import org.junit.AfterClass;
43  import org.junit.BeforeClass;
44  import org.junit.Test;
45  import org.junit.experimental.categories.Category;
46  import org.junit.Rule;
47  import org.junit.rules.ExpectedException;
48  
49  import com.google.common.base.Joiner;
50  
51  /**
52   * Start the HBase Thrift HTTP server on a random port through the command-line
53   * interface and talk to it from client side.
54   */
55  @Category(LargeTests.class)
56  
57  public class TestThriftHttpServer {
58  
59    private static final Log LOG =
60        LogFactory.getLog(TestThriftHttpServer.class);
61  
62    private static final HBaseTestingUtility TEST_UTIL =
63        new HBaseTestingUtility();
64  
65    private Thread httpServerThread;
66    private volatile Exception httpServerException;
67  
68    private Exception clientSideException;
69  
70    private ThriftServer thriftServer;
71    private int port;
72  
73    @BeforeClass
74    public static void setUpBeforeClass() throws Exception {
75      TEST_UTIL.getConfiguration().setBoolean("hbase.regionserver.thrift.http", true);
76      TEST_UTIL.getConfiguration().setBoolean("hbase.table.sanity.checks", false);
77      TEST_UTIL.startMiniCluster();
78      //ensure that server time increments every time we do an operation, otherwise
79      //successive puts having the same timestamp will override each other
80      EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge());
81    }
82  
83    @AfterClass
84    public static void tearDownAfterClass() throws Exception {
85      TEST_UTIL.shutdownMiniCluster();
86      EnvironmentEdgeManager.reset();
87    }
88  
89    @Test
90    public void testExceptionThrownWhenMisConfigured() throws Exception {
91      Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
92      conf.set("hbase.thrift.security.qop", "privacy");
93      conf.setBoolean("hbase.thrift.ssl.enabled", false);
94  
95      ThriftServerRunner runner = null;
96      ExpectedException thrown = ExpectedException.none();
97      try {
98        thrown.expect(IllegalArgumentException.class);
99        thrown.expectMessage("Thrift HTTP Server's QoP is privacy, " +
100           "but hbase.thrift.ssl.enabled is false");
101       runner = new ThriftServerRunner(conf);
102       fail("Thrift HTTP Server starts up even with wrong security configurations.");
103     } catch (Exception e) {
104     }
105 
106     assertNull(runner);
107   }
108 
109   private void startHttpServerThread(final String[] args) {
110     LOG.info("Starting HBase Thrift server with HTTP server: " + Joiner.on(" ").join(args));
111 
112     httpServerException = null;
113     httpServerThread = new Thread(new Runnable() {
114       @Override
115       public void run() {
116         try {
117           thriftServer.doMain(args);
118         } catch (Exception e) {
119           httpServerException = e;
120         }
121       }
122     });
123     httpServerThread.setName(ThriftServer.class.getSimpleName() +
124         "-httpServer");
125     httpServerThread.start();
126   }
127 
128   @Rule
129   public ExpectedException exception = ExpectedException.none();
130 
131   @Test(timeout=600000)
132   public void testRunThriftServerWithHeaderBufferLength() throws Exception {
133 
134     // Test thrift server with HTTP header length less than 64k
135     try {
136       runThriftServer(1024 * 63);
137     } catch (TTransportException tex) {
138       assertFalse(tex.getMessage().equals("HTTP Response code: 413"));
139     }
140 
141     // Test thrift server with HTTP header length more than 64k, expect an exception
142     exception.expect(TTransportException.class);
143     exception.expectMessage("HTTP Response code: 413");
144     runThriftServer(1024 * 64);
145   }
146 
147   @Test(timeout=600000)
148   public void testRunThriftServer() throws Exception {
149     runThriftServer(0);
150   }
151 
152   private void runThriftServer(int customHeaderSize) throws Exception {
153     List<String> args = new ArrayList<String>();
154     port = HBaseTestingUtility.randomFreePort();
155     args.add("-" + ThriftServer.PORT_OPTION);
156     args.add(String.valueOf(port));
157     args.add("start");
158 
159     thriftServer = new ThriftServer(TEST_UTIL.getConfiguration());
160     startHttpServerThread(args.toArray(new String[args.size()]));
161 
162     // wait up to 10s for the server to start
163     for (int i = 0; i < 100
164         && ( thriftServer.serverRunner == null ||  thriftServer.serverRunner.httpServer ==
165         null); i++) {
166       Thread.sleep(100);
167     }
168 
169     try {
170       talkToThriftServer(customHeaderSize);
171     } catch (Exception ex) {
172       clientSideException = ex;
173     } finally {
174       stopHttpServerThread();
175     }
176 
177     if (clientSideException != null) {
178       LOG.error("Thrift client threw an exception " + clientSideException);
179       if (clientSideException instanceof  TTransportException) {
180         throw clientSideException;
181       } else {
182         throw new Exception(clientSideException);
183       }
184     }
185   }
186 
187   private static volatile boolean tableCreated = false;
188 
189   private void talkToThriftServer(int customHeaderSize) throws Exception {
190     THttpClient httpClient = new THttpClient(
191         "http://"+ HConstants.LOCALHOST + ":" + port);
192     httpClient.open();
193 
194     if (customHeaderSize > 0) {
195       StringBuilder sb = new StringBuilder();
196       for (int i = 0; i < customHeaderSize; i++) {
197         sb.append("a");
198       }
199       httpClient.setCustomHeader("User-Agent", sb.toString());
200     }
201 
202     try {
203       TProtocol prot;
204       prot = new TBinaryProtocol(httpClient);
205       Hbase.Client client = new Hbase.Client(prot);
206       if (!tableCreated){
207         TestThriftServer.createTestTables(client);
208         tableCreated = true;
209       }
210       TestThriftServer.checkTableList(client);
211     } finally {
212       httpClient.close();
213     }
214   }
215 
216   private void stopHttpServerThread() throws Exception {
217     LOG.debug("Stopping " + " Thrift HTTP server");
218     thriftServer.stop();
219     httpServerThread.join();
220     if (httpServerException != null) {
221       LOG.error("Command-line invocation of HBase Thrift server threw an " +
222           "exception", httpServerException);
223       throw new Exception(httpServerException);
224     }
225   }
226 }