001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.http;
019
020import java.io.IOException;
021import java.util.Random;
022import javax.servlet.Filter;
023import javax.servlet.FilterChain;
024import javax.servlet.FilterConfig;
025import javax.servlet.ServletException;
026import javax.servlet.ServletRequest;
027import javax.servlet.ServletResponse;
028import javax.servlet.http.HttpServletRequest;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.hbase.HBaseClassTestRule;
031import org.apache.hadoop.hbase.testclassification.MiscTests;
032import org.apache.hadoop.hbase.testclassification.SmallTests;
033import org.apache.hadoop.net.NetUtils;
034import org.apache.hadoop.util.StringUtils;
035import org.junit.Assert;
036import org.junit.ClassRule;
037import org.junit.Ignore;
038import org.junit.Test;
039import org.junit.experimental.categories.Category;
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043@Category({MiscTests.class, SmallTests.class})
044public class TestServletFilter extends HttpServerFunctionalTest {
045  @ClassRule
046  public static final HBaseClassTestRule CLASS_RULE =
047      HBaseClassTestRule.forClass(TestServletFilter.class);
048
049  private static final Logger LOG = LoggerFactory.getLogger(HttpServer.class);
050  private static volatile String uri = null;
051
052  /** A very simple filter which record the uri filtered. */
053  static public class SimpleFilter implements Filter {
054    private FilterConfig filterConfig = null;
055
056    @Override
057    public void init(FilterConfig filterConfig) throws ServletException {
058      this.filterConfig = filterConfig;
059    }
060
061    @Override
062    public void destroy() {
063      this.filterConfig = null;
064    }
065
066    @Override
067    public void doFilter(ServletRequest request, ServletResponse response,
068        FilterChain chain) throws IOException, ServletException {
069      if (filterConfig == null) {
070        return;
071      }
072
073      uri = ((HttpServletRequest)request).getRequestURI();
074      LOG.info("filtering " + uri);
075      chain.doFilter(request, response);
076    }
077
078    /** Configuration for the filter */
079    static public class Initializer extends FilterInitializer {
080      public Initializer() {}
081
082      @Override
083      public void initFilter(FilterContainer container, Configuration conf) {
084        container.addFilter("simple", SimpleFilter.class.getName(), null);
085      }
086    }
087  }
088
089  private static void assertExceptionContains(String string, Throwable t) {
090    String msg = t.getMessage();
091    Assert.assertTrue(
092        "Expected to find '" + string + "' but got unexpected exception:"
093        + StringUtils.stringifyException(t), msg.contains(string));
094  }
095
096  @Test
097  @Ignore
098  //From stack
099  // Its a 'foreign' test, one that came in from hadoop when we copy/pasted http
100  // It's second class. Could comment it out if only failing test (as per @nkeywal – sort of)
101  public void testServletFilter() throws Exception {
102    Configuration conf = new Configuration();
103
104    //start an http server with CountingFilter
105    conf.set(HttpServer.FILTER_INITIALIZERS_PROPERTY,
106        SimpleFilter.Initializer.class.getName());
107    HttpServer http = createTestServer(conf);
108    http.start();
109
110    final String fsckURL = "/fsck";
111    final String stacksURL = "/stacks";
112    final String ajspURL = "/a.jsp";
113    final String logURL = "/logs/a.log";
114    final String hadooplogoURL = "/static/hadoop-logo.jpg";
115
116    final String[] urls = {fsckURL, stacksURL, ajspURL, logURL, hadooplogoURL};
117    final Random ran = new Random();
118    final int[] sequence = new int[50];
119
120    //generate a random sequence and update counts
121    for(int i = 0; i < sequence.length; i++) {
122      sequence[i] = ran.nextInt(urls.length);
123    }
124
125    //access the urls as the sequence
126    final String prefix = "http://"
127        + NetUtils.getHostPortString(http.getConnectorAddress(0));
128    try {
129      for (int aSequence : sequence) {
130        access(prefix + urls[aSequence]);
131
132        //make sure everything except fsck get filtered
133        if (aSequence == 0) {
134          assertNull(uri);
135        } else {
136          assertEquals(urls[aSequence], uri);
137          uri = null;
138        }
139      }
140    } finally {
141      http.stop();
142    }
143  }
144
145  static public class ErrorFilter extends SimpleFilter {
146    @Override
147    public void init(FilterConfig arg0) throws ServletException {
148      throw new ServletException("Throwing the exception from Filter init");
149    }
150
151    /** Configuration for the filter */
152    static public class Initializer extends FilterInitializer {
153      public Initializer() {
154      }
155
156      @Override
157      public void initFilter(FilterContainer container, Configuration conf) {
158        container.addFilter("simple", ErrorFilter.class.getName(), null);
159      }
160    }
161  }
162
163  @Test
164  public void testServletFilterWhenInitThrowsException() throws Exception {
165    Configuration conf = new Configuration();
166    // start an http server with ErrorFilter
167    conf.set(HttpServer.FILTER_INITIALIZERS_PROPERTY,
168        ErrorFilter.Initializer.class.getName());
169    HttpServer http = createTestServer(conf);
170    try {
171      http.start();
172      fail("expecting exception");
173    } catch (IOException e) {
174      assertExceptionContains("Problem starting http server", e);
175    }
176  }
177
178  /**
179   * Similar to the above test case, except that it uses a different API to add the
180   * filter. Regression test for HADOOP-8786.
181   */
182  @Test
183  public void testContextSpecificServletFilterWhenInitThrowsException()
184      throws Exception {
185    Configuration conf = new Configuration();
186    HttpServer http = createTestServer(conf);
187    HttpServer.defineFilter(http.webAppContext,
188        "ErrorFilter", ErrorFilter.class.getName(),
189        null, null);
190    try {
191      http.start();
192      fail("expecting exception");
193    } catch (IOException e) {
194      assertExceptionContains("Unable to initialize WebAppContext", e);
195    }
196  }
197}