/*
* Copyright The Apache Software Foundation
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.apache.hadoop.hbase.thrift;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.testclassification.ClientTests;
import org.apache.hadoop.hbase.testclassification.LargeTests;
import org.apache.hadoop.hbase.thrift.generated.Hbase;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper;
import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransportException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import static org.junit.Assert.assertFalse;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import com.google.common.base.Joiner;
/**
* Start the HBase Thrift HTTP server on a random port through the command-line
* interface and talk to it from client side.
*/
@Category({ClientTests.class, LargeTests.class})
public class TestThriftHttpServer {
private static final Log LOG =
LogFactory.getLog(TestThriftHttpServer.class);
private static final HBaseTestingUtility TEST_UTIL =
new HBaseTestingUtility();
private Thread httpServerThread;
private volatile Exception httpServerException;
private Exception clientSideException;
private ThriftServer thriftServer;
private int port;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
TEST_UTIL.getConfiguration().setBoolean("hbase.regionserver.thrift.http", true);
TEST_UTIL.getConfiguration().setBoolean("hbase.table.sanity.checks", false);
TEST_UTIL.startMiniCluster();
//ensure that server time increments every time we do an operation, otherwise
//successive puts having the same timestamp will override each other
EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge());
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
TEST_UTIL.shutdownMiniCluster();
EnvironmentEdgeManager.reset();
}
private void startHttpServerThread(final String[] args) {
LOG.info("Starting HBase Thrift server with HTTP server: " + Joiner.on(" ").join(args));
httpServerException = null;
httpServerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
thriftServer.doMain(args);
} catch (Exception e) {
httpServerException = e;
}
}
});
httpServerThread.setName(ThriftServer.class.getSimpleName() +
"-httpServer");
httpServerThread.start();
}
@Rule
public ExpectedException exception = ExpectedException.none();
@Test(timeout=600000)
public void testRunThriftServerWithHeaderBufferLength() throws Exception {
// Test thrift server with HTTP header length less than 64k
try {
runThriftServer(1024 * 63);
} catch (TTransportException tex) {
assertFalse(tex.getMessage().equals("HTTP Response code: 413"));
}
// Test thrift server with HTTP header length more than 64k, expect an exception
exception.expect(TTransportException.class);
exception.expectMessage("HTTP Response code: 413");
runThriftServer(1024 * 64);
}
@Test(timeout=600000)
public void testRunThriftServer() throws Exception {
runThriftServer(0);
}
private void runThriftServer(int customHeaderSize) throws Exception {
List<String> args = new ArrayList<>(3);
port = HBaseTestingUtility.randomFreePort();
args.add("-" + ThriftServer.PORT_OPTION);
args.add(String.valueOf(port));
args.add("start");
thriftServer = new ThriftServer(TEST_UTIL.getConfiguration());
startHttpServerThread(args.toArray(new String[args.size()]));
// wait up to 10s for the server to start
for (int i = 0; i < 100
&& ( thriftServer.serverRunner == null || thriftServer.serverRunner.httpServer ==
null); i++) {
Thread.sleep(100);
}
try {
talkToThriftServer(customHeaderSize);
} catch (Exception ex) {
clientSideException = ex;
} finally {
stopHttpServerThread();
}
if (clientSideException != null) {
LOG.error("Thrift client threw an exception " + clientSideException);
if (clientSideException instanceof TTransportException) {
throw clientSideException;
} else {
throw new Exception(clientSideException);
}
}
}
private static volatile boolean tableCreated = false;
private void talkToThriftServer(int customHeaderSize) throws Exception {
THttpClient httpClient = new THttpClient(
"http://"+ HConstants.LOCALHOST + ":" + port);
httpClient.open();
if (customHeaderSize > 0) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < customHeaderSize; i++) {
sb.append("a");
}
httpClient.setCustomHeader("User-Agent", sb.toString());
}
try {
TProtocol prot;
prot = new TBinaryProtocol(httpClient);
Hbase.Client client = new Hbase.Client(prot);
if (!tableCreated){
TestThriftServer.createTestTables(client);
tableCreated = true;
}
TestThriftServer.checkTableList(client);
} finally {
httpClient.close();
}
}
private void stopHttpServerThread() throws Exception {
LOG.debug("Stopping " + " Thrift HTTP server");
thriftServer.stop();
httpServerThread.join();
if (httpServerException != null) {
LOG.error("Command-line invocation of HBase Thrift server threw an " +
"exception", httpServerException);
throw new Exception(httpServerException);
}
}
}